diff options
Diffstat (limited to 'src/basic')
192 files changed, 0 insertions, 45515 deletions
diff --git a/src/basic/.gitignore b/src/basic/.gitignore deleted file mode 100644 index e22411e484..0000000000 --- a/src/basic/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -/cap-from-name.gperf -/cap-from-name.h -/cap-list.txt -/cap-to-name.h -/errno-from-name.gperf -/errno-from-name.h -/errno-list.txt -/errno-to-name.h -/af-from-name.gperf -/af-from-name.h -/af-list.txt -/af-to-name.h -/arphrd-from-name.gperf -/arphrd-from-name.h -/arphrd-list.txt -/arphrd-to-name.h diff --git a/src/basic/Makefile b/src/basic/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/basic/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile
\ No newline at end of file diff --git a/src/basic/MurmurHash2.c b/src/basic/MurmurHash2.c deleted file mode 100644 index 9020793930..0000000000 --- a/src/basic/MurmurHash2.c +++ /dev/null @@ -1,86 +0,0 @@ -//----------------------------------------------------------------------------- -// MurmurHash2 was written by Austin Appleby, and is placed in the public -// domain. The author hereby disclaims copyright to this source code. - -// Note - This code makes a few assumptions about how your machine behaves - - -// 1. We can read a 4-byte value from any address without crashing -// 2. sizeof(int) == 4 - -// And it has a few limitations - - -// 1. It will not work incrementally. -// 2. It will not produce the same results on little-endian and big-endian -// machines. - -#include "MurmurHash2.h" - -//----------------------------------------------------------------------------- -// Platform-specific functions and macros - -// Microsoft Visual Studio - -#if defined(_MSC_VER) - -#define BIG_CONSTANT(x) (x) - -// Other compilers - -#else // defined(_MSC_VER) - -#define BIG_CONSTANT(x) (x##LLU) - -#endif // !defined(_MSC_VER) - -//----------------------------------------------------------------------------- - -uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed ) -{ - // 'm' and 'r' are mixing constants generated offline. - // They're not really 'magic', they just happen to work well. - - const uint32_t m = 0x5bd1e995; - const int r = 24; - - // Initialize the hash to a 'random' value - - uint32_t h = seed ^ len; - - // Mix 4 bytes at a time into the hash - - const unsigned char * data = (const unsigned char *)key; - - while (len >= 4) - { - uint32_t k = *(uint32_t*)data; - - k *= m; - k ^= k >> r; - k *= m; - - h *= m; - h ^= k; - - data += 4; - len -= 4; - } - - // Handle the last few bytes of the input array - - switch(len) - { - case 3: h ^= data[2] << 16; - case 2: h ^= data[1] << 8; - case 1: h ^= data[0]; - h *= m; - }; - - // Do a few final mixes of the hash to ensure the last few - // bytes are well-incorporated. - - h ^= h >> 13; - h *= m; - h ^= h >> 15; - - return h; -} diff --git a/src/basic/MurmurHash2.h b/src/basic/MurmurHash2.h deleted file mode 100644 index 93362dd485..0000000000 --- a/src/basic/MurmurHash2.h +++ /dev/null @@ -1,33 +0,0 @@ -//----------------------------------------------------------------------------- -// MurmurHash2 was written by Austin Appleby, and is placed in the public -// domain. The author hereby disclaims copyright to this source code. - -#ifndef _MURMURHASH2_H_ -#define _MURMURHASH2_H_ - -//----------------------------------------------------------------------------- -// Platform-specific functions and macros - -// Microsoft Visual Studio - -#if defined(_MSC_VER) - -typedef unsigned char uint8_t; -typedef unsigned long uint32_t; -typedef unsigned __int64 uint64_t; - -// Other compilers - -#else // defined(_MSC_VER) - -#include <stdint.h> - -#endif // !defined(_MSC_VER) - -//----------------------------------------------------------------------------- - -uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed ); - -//----------------------------------------------------------------------------- - -#endif // _MURMURHASH2_H_ diff --git a/src/basic/af-list.c b/src/basic/af-list.c deleted file mode 100644 index 3fac9c508b..0000000000 --- a/src/basic/af-list.c +++ /dev/null @@ -1,56 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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.h> -#include <sys/socket.h> - -#include "af-list.h" -#include "macro.h" - -static const struct af_name* lookup_af(register const char *str, register unsigned int len); - -#include "af-from-name.h" -#include "af-to-name.h" - -const char *af_to_name(int id) { - - if (id <= 0) - return NULL; - - if (id >= (int) ELEMENTSOF(af_names)) - return NULL; - - return af_names[id]; -} - -int af_from_name(const char *name) { - const struct af_name *sc; - - assert(name); - - sc = lookup_af(name, strlen(name)); - if (!sc) - return AF_UNSPEC; - - return sc->id; -} - -int af_max(void) { - return ELEMENTSOF(af_names); -} diff --git a/src/basic/af-list.h b/src/basic/af-list.h deleted file mode 100644 index 6a4cc03839..0000000000 --- a/src/basic/af-list.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 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-util.h" - -const char *af_to_name(int id); -int af_from_name(const char *name); - -static inline const char* af_to_name_short(int id) { - const char *f; - - if (id == AF_UNSPEC) - return "*"; - - f = af_to_name(id); - if (!f) - return "unknown"; - - assert(startswith(f, "AF_")); - return f + 3; -} - -int af_max(void); diff --git a/src/basic/alloc-util.c b/src/basic/alloc-util.c deleted file mode 100644 index b540dcddf5..0000000000 --- a/src/basic/alloc-util.c +++ /dev/null @@ -1,83 +0,0 @@ -/*** - 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 <stdint.h> -#include <string.h> - -#include "alloc-util.h" -#include "macro.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/basic/alloc-util.h b/src/basic/alloc-util.h deleted file mode 100644 index a44dd473c1..0000000000 --- a/src/basic/alloc-util.h +++ /dev/null @@ -1,119 +0,0 @@ -#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 <stddef.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; -} - -#define free_and_replace(a, b) \ - ({ \ - free(a); \ - (a) = (b); \ - (b) = NULL; \ - 0; \ - }) - -void* memdup(const void *p, size_t l) _alloc_(2); - -static inline void freep(void *p) { - free(*(void**) p); -} - -#define _cleanup_free_ _cleanup_(freep) - -static inline bool size_multiply_overflow(size_t size, size_t need) { - return _unlikely_(need != 0 && size > (SIZE_MAX / need)); -} - -_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t need) { - if (size_multiply_overflow(size, need)) - return NULL; - - return malloc(size * need); -} - -_alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t size, size_t need) { - if (size_multiply_overflow(size, need)) - return NULL; - - return realloc(p, size * need); -} - -_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) { - if (size_multiply_overflow(size, need)) - return NULL; - - return memdup(p, size * need); -} - -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/basic/architecture.c b/src/basic/architecture.c deleted file mode 100644 index b74dc0db78..0000000000 --- a/src/basic/architecture.c +++ /dev/null @@ -1,189 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 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 <sys/utsname.h> - -#include "architecture.h" -#include "macro.h" -#include "string-table.h" -#include "string-util.h" - -int uname_architecture(void) { - - /* Return a sanitized enum identifying the architecture we are - * running on. This is based on uname(), and the user may - * hence control what this returns by using - * personality(). This puts the user in control on systems - * that can run binaries of multiple architectures. - * - * We do not translate the string returned by uname() - * 1:1. Instead we try to clean it up and break down the - * confusion on x86 and arm in particular. - * - * We do not try to distinguish CPUs not CPU features, but - * actual architectures, i.e. that have genuinely different - * code. */ - - static const struct { - const char *machine; - int arch; - } arch_map[] = { -#if defined(__x86_64__) || defined(__i386__) - { "x86_64", ARCHITECTURE_X86_64 }, - { "i686", ARCHITECTURE_X86 }, - { "i586", ARCHITECTURE_X86 }, - { "i486", ARCHITECTURE_X86 }, - { "i386", ARCHITECTURE_X86 }, -#elif defined(__powerpc__) || defined(__powerpc64__) - { "ppc64", ARCHITECTURE_PPC64 }, - { "ppc64le", ARCHITECTURE_PPC64_LE }, - { "ppc", ARCHITECTURE_PPC }, - { "ppcle", ARCHITECTURE_PPC_LE }, -#elif defined(__ia64__) - { "ia64", ARCHITECTURE_IA64 }, -#elif defined(__hppa__) || defined(__hppa64__) - { "parisc64", ARCHITECTURE_PARISC64 }, - { "parisc", ARCHITECTURE_PARISC }, -#elif defined(__s390__) || defined(__s390x__) - { "s390x", ARCHITECTURE_S390X }, - { "s390", ARCHITECTURE_S390 }, -#elif defined(__sparc__) - { "sparc64", ARCHITECTURE_SPARC64 }, - { "sparc", ARCHITECTURE_SPARC }, -#elif defined(__mips__) || defined(__mips64__) - { "mips64", ARCHITECTURE_MIPS64 }, - { "mips", ARCHITECTURE_MIPS }, -#elif defined(__alpha__) - { "alpha" , ARCHITECTURE_ALPHA }, -#elif defined(__arm__) || defined(__aarch64__) - { "aarch64", ARCHITECTURE_ARM64 }, - { "aarch64_be", ARCHITECTURE_ARM64_BE }, - { "armv4l", ARCHITECTURE_ARM }, - { "armv4b", ARCHITECTURE_ARM_BE }, - { "armv4tl", ARCHITECTURE_ARM }, - { "armv4tb", ARCHITECTURE_ARM_BE }, - { "armv5tl", ARCHITECTURE_ARM }, - { "armv5tb", ARCHITECTURE_ARM_BE }, - { "armv5tel", ARCHITECTURE_ARM }, - { "armv5teb" , ARCHITECTURE_ARM_BE }, - { "armv5tejl", ARCHITECTURE_ARM }, - { "armv5tejb", ARCHITECTURE_ARM_BE }, - { "armv6l", ARCHITECTURE_ARM }, - { "armv6b", ARCHITECTURE_ARM_BE }, - { "armv7l", ARCHITECTURE_ARM }, - { "armv7b", ARCHITECTURE_ARM_BE }, - { "armv7ml", ARCHITECTURE_ARM }, - { "armv7mb", ARCHITECTURE_ARM_BE }, - { "armv4l", ARCHITECTURE_ARM }, - { "armv4b", ARCHITECTURE_ARM_BE }, - { "armv4tl", ARCHITECTURE_ARM }, - { "armv4tb", ARCHITECTURE_ARM_BE }, - { "armv5tl", ARCHITECTURE_ARM }, - { "armv5tb", ARCHITECTURE_ARM_BE }, - { "armv5tel", ARCHITECTURE_ARM }, - { "armv5teb", ARCHITECTURE_ARM_BE }, - { "armv5tejl", ARCHITECTURE_ARM }, - { "armv5tejb", ARCHITECTURE_ARM_BE }, - { "armv6l", ARCHITECTURE_ARM }, - { "armv6b", ARCHITECTURE_ARM_BE }, - { "armv7l", ARCHITECTURE_ARM }, - { "armv7b", ARCHITECTURE_ARM_BE }, - { "armv7ml", ARCHITECTURE_ARM }, - { "armv7mb", ARCHITECTURE_ARM_BE }, - { "armv8l", ARCHITECTURE_ARM }, - { "armv8b", ARCHITECTURE_ARM_BE }, -#elif defined(__sh__) || defined(__sh64__) - { "sh5", ARCHITECTURE_SH64 }, - { "sh2", ARCHITECTURE_SH }, - { "sh2a", ARCHITECTURE_SH }, - { "sh3", ARCHITECTURE_SH }, - { "sh4", ARCHITECTURE_SH }, - { "sh4a", ARCHITECTURE_SH }, -#elif defined(__m68k__) - { "m68k", ARCHITECTURE_M68K }, -#elif defined(__tilegx__) - { "tilegx", ARCHITECTURE_TILEGX }, -#elif defined(__cris__) - { "crisv32", ARCHITECTURE_CRIS }, -#elif defined(__nios2__) - { "nios2", ARCHITECTURE_NIOS2 }, -#elif defined(__riscv__) - { "riscv32", ARCHITECTURE_RISCV32 }, - { "riscv64", ARCHITECTURE_RISCV64 }, -# if __SIZEOF_POINTER__ == 4 - { "riscv", ARCHITECTURE_RISCV32 }, -# elif __SIZEOF_POINTER__ == 8 - { "riscv", ARCHITECTURE_RISCV64 }, -# endif -#else -#error "Please register your architecture here!" -#endif - }; - - static int cached = _ARCHITECTURE_INVALID; - struct utsname u; - unsigned i; - - if (cached != _ARCHITECTURE_INVALID) - return cached; - - assert_se(uname(&u) >= 0); - - for (i = 0; i < ELEMENTSOF(arch_map); i++) - if (streq(arch_map[i].machine, u.machine)) - return cached = arch_map[i].arch; - - assert_not_reached("Couldn't identify architecture. You need to patch systemd."); - return _ARCHITECTURE_INVALID; -} - -static const char *const architecture_table[_ARCHITECTURE_MAX] = { - [ARCHITECTURE_X86] = "x86", - [ARCHITECTURE_X86_64] = "x86-64", - [ARCHITECTURE_PPC] = "ppc", - [ARCHITECTURE_PPC_LE] = "ppc-le", - [ARCHITECTURE_PPC64] = "ppc64", - [ARCHITECTURE_PPC64_LE] = "ppc64-le", - [ARCHITECTURE_IA64] = "ia64", - [ARCHITECTURE_PARISC] = "parisc", - [ARCHITECTURE_PARISC64] = "parisc64", - [ARCHITECTURE_S390] = "s390", - [ARCHITECTURE_S390X] = "s390x", - [ARCHITECTURE_SPARC] = "sparc", - [ARCHITECTURE_SPARC64] = "sparc64", - [ARCHITECTURE_MIPS] = "mips", - [ARCHITECTURE_MIPS_LE] = "mips-le", - [ARCHITECTURE_MIPS64] = "mips64", - [ARCHITECTURE_MIPS64_LE] = "mips64-le", - [ARCHITECTURE_ALPHA] = "alpha", - [ARCHITECTURE_ARM] = "arm", - [ARCHITECTURE_ARM_BE] = "arm-be", - [ARCHITECTURE_ARM64] = "arm64", - [ARCHITECTURE_ARM64_BE] = "arm64-be", - [ARCHITECTURE_SH] = "sh", - [ARCHITECTURE_SH64] = "sh64", - [ARCHITECTURE_M68K] = "m68k", - [ARCHITECTURE_TILEGX] = "tilegx", - [ARCHITECTURE_CRIS] = "cris", - [ARCHITECTURE_NIOS2] = "nios2", - [ARCHITECTURE_RISCV32] = "riscv32", - [ARCHITECTURE_RISCV64] = "riscv64", -}; - -DEFINE_STRING_TABLE_LOOKUP(architecture, int); diff --git a/src/basic/architecture.h b/src/basic/architecture.h deleted file mode 100644 index 5a77c31932..0000000000 --- a/src/basic/architecture.h +++ /dev/null @@ -1,211 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 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 <endian.h> - -#include "macro.h" -#include "util.h" - -/* A cleaned up architecture definition. We don't want to get lost in - * processor features, models, generations or even ABIs. Hence we - * focus on general family, and distinguish word width and - * endianness. */ - -enum { - ARCHITECTURE_X86 = 0, - ARCHITECTURE_X86_64, - ARCHITECTURE_PPC, - ARCHITECTURE_PPC_LE, - ARCHITECTURE_PPC64, - ARCHITECTURE_PPC64_LE, - ARCHITECTURE_IA64, - ARCHITECTURE_PARISC, - ARCHITECTURE_PARISC64, - ARCHITECTURE_S390, - ARCHITECTURE_S390X, - ARCHITECTURE_SPARC, - ARCHITECTURE_SPARC64, - ARCHITECTURE_MIPS, - ARCHITECTURE_MIPS_LE, - ARCHITECTURE_MIPS64, - ARCHITECTURE_MIPS64_LE, - ARCHITECTURE_ALPHA, - ARCHITECTURE_ARM, - ARCHITECTURE_ARM_BE, - ARCHITECTURE_ARM64, - ARCHITECTURE_ARM64_BE, - ARCHITECTURE_SH, - ARCHITECTURE_SH64, - ARCHITECTURE_M68K, - ARCHITECTURE_TILEGX, - ARCHITECTURE_CRIS, - ARCHITECTURE_NIOS2, - ARCHITECTURE_RISCV32, - ARCHITECTURE_RISCV64, - _ARCHITECTURE_MAX, - _ARCHITECTURE_INVALID = -1 -}; - -int uname_architecture(void); - -/* - * LIB_ARCH_TUPLE should resolve to the local library path - * architecture tuple systemd is built for, according to the Debian - * tuple list: - * - * https://wiki.debian.org/Multiarch/Tuples - * - * This is used in library search paths that should understand - * Debian's paths on all distributions. - */ - -#if defined(__x86_64__) -# define native_architecture() ARCHITECTURE_X86_64 -# define LIB_ARCH_TUPLE "x86_64-linux-gnu" -# define SECONDARY_ARCHITECTURE ARCHITECTURE_X86 -#elif defined(__i386__) -# define native_architecture() ARCHITECTURE_X86 -# define LIB_ARCH_TUPLE "i386-linux-gnu" -#elif defined(__powerpc64__) -# if __BYTE_ORDER == __BIG_ENDIAN -# define native_architecture() ARCHITECTURE_PPC64 -# define LIB_ARCH_TUPLE "ppc64-linux-gnu" -# define SECONDARY_ARCHITECTURE ARCHITECTURE_PPC -# else -# define native_architecture() ARCHITECTURE_PPC64_LE -# define LIB_ARCH_TUPLE "powerpc64le-linux-gnu" -# define SECONDARY_ARCHITECTURE ARCHITECTURE_PPC_LE -# endif -#elif defined(__powerpc__) -# if __BYTE_ORDER == __BIG_ENDIAN -# define native_architecture() ARCHITECTURE_PPC -# define LIB_ARCH_TUPLE "powerpc-linux-gnu" -# else -# define native_architecture() ARCHITECTURE_PPC_LE -# error "Missing LIB_ARCH_TUPLE for PPCLE" -# endif -#elif defined(__ia64__) -# define native_architecture() ARCHITECTURE_IA64 -# define LIB_ARCH_TUPLE "ia64-linux-gnu" -#elif defined(__hppa64__) -# define native_architecture() ARCHITECTURE_PARISC64 -# error "Missing LIB_ARCH_TUPLE for HPPA64" -#elif defined(__hppa__) -# define native_architecture() ARCHITECTURE_PARISC -# define LIB_ARCH_TUPLE "hppa‑linux‑gnu" -#elif defined(__s390x__) -# define native_architecture() ARCHITECTURE_S390X -# define LIB_ARCH_TUPLE "s390x-linux-gnu" -# define SECONDARY_ARCHITECTURE ARCHITECTURE_S390 -#elif defined(__s390__) -# define native_architecture() ARCHITECTURE_S390 -# define LIB_ARCH_TUPLE "s390-linux-gnu" -#elif defined(__sparc__) && defined (__arch64__) -# define native_architecture() ARCHITECTURE_SPARC64 -# define LIB_ARCH_TUPLE "sparc64-linux-gnu" -#elif defined(__sparc__) -# define native_architecture() ARCHITECTURE_SPARC -# define LIB_ARCH_TUPLE "sparc-linux-gnu" -#elif defined(__mips64__) -# if __BYTE_ORDER == __BIG_ENDIAN -# define native_architecture() ARCHITECTURE_MIPS64 -# error "Missing LIB_ARCH_TUPLE for MIPS64" -# else -# define native_architecture() ARCHITECTURE_MIPS64_LE -# error "Missing LIB_ARCH_TUPLE for MIPS64_LE" -# endif -#elif defined(__mips__) -# if __BYTE_ORDER == __BIG_ENDIAN -# define native_architecture() ARCHITECTURE_MIPS -# define LIB_ARCH_TUPLE "mips-linux-gnu" -# else -# define native_architecture() ARCHITECTURE_MIPS_LE -# define LIB_ARCH_TUPLE "mipsel-linux-gnu" -# endif -#elif defined(__alpha__) -# define native_architecture() ARCHITECTURE_ALPHA -# define LIB_ARCH_TUPLE "alpha-linux-gnu" -#elif defined(__aarch64__) -# if __BYTE_ORDER == __BIG_ENDIAN -# define native_architecture() ARCHITECTURE_ARM64_BE -# define LIB_ARCH_TUPLE "aarch64_be-linux-gnu" -# else -# define native_architecture() ARCHITECTURE_ARM64 -# define LIB_ARCH_TUPLE "aarch64-linux-gnu" -# endif -#elif defined(__arm__) -# if __BYTE_ORDER == __BIG_ENDIAN -# define native_architecture() ARCHITECTURE_ARM_BE -# if defined(__ARM_EABI__) -# if defined(__ARM_PCS_VFP) -# define LIB_ARCH_TUPLE "armeb-linux-gnueabihf" -# else -# define LIB_ARCH_TUPLE "armeb-linux-gnueabi" -# endif -# else -# define LIB_ARCH_TUPLE "armeb-linux-gnu" -# endif -# else -# define native_architecture() ARCHITECTURE_ARM -# if defined(__ARM_EABI__) -# if defined(__ARM_PCS_VFP) -# define LIB_ARCH_TUPLE "arm-linux-gnueabihf" -# else -# define LIB_ARCH_TUPLE "arm-linux-gnueabi" -# endif -# else -# define LIB_ARCH_TUPLE "arm-linux-gnu" -# endif -# endif -#elif defined(__sh64__) -# define native_architecture() ARCHITECTURE_SH64 -# error "Missing LIB_ARCH_TUPLE for SH64" -#elif defined(__sh__) -# define native_architecture() ARCHITECTURE_SH -# define LIB_ARCH_TUPLE "sh4-linux-gnu" -#elif defined(__m68k__) -# define native_architecture() ARCHITECTURE_M68K -# define LIB_ARCH_TUPLE "m68k-linux-gnu" -#elif defined(__tilegx__) -# define native_architecture() ARCHITECTURE_TILEGX -# error "Missing LIB_ARCH_TUPLE for TILEGX" -#elif defined(__cris__) -# define native_architecture() ARCHITECTURE_CRIS -# error "Missing LIB_ARCH_TUPLE for CRIS" -#elif defined(__nios2__) -# define native_architecture() ARCHITECTURE_NIOS2 -# define LIB_ARCH_TUPLE "nios2-linux-gnu" -#elif defined(__riscv__) -# if __SIZEOF_POINTER__ == 4 -# define native_architecture() ARCHITECTURE_RISCV32 -# define LIB_ARCH_TUPLE "riscv32-linux-gnu" -# elif __SIZEOF_POINTER__ == 8 -# define native_architecture() ARCHITECTURE_RISCV64 -# define LIB_ARCH_TUPLE "riscv64-linux-gnu" -# else -# error "Unrecognized riscv architecture variant" -# endif -#else -# error "Please register your architecture here!" -#endif - -const char *architecture_to_string(int a) _const_; -int architecture_from_string(const char *s) _pure_; diff --git a/src/basic/arphrd-list.c b/src/basic/arphrd-list.c deleted file mode 100644 index 6792d1ee3f..0000000000 --- a/src/basic/arphrd-list.c +++ /dev/null @@ -1,56 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 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 <net/if_arp.h> -#include <string.h> - -#include "arphrd-list.h" -#include "macro.h" - -static const struct arphrd_name* lookup_arphrd(register const char *str, register unsigned int len); - -#include "arphrd-from-name.h" -#include "arphrd-to-name.h" - -const char *arphrd_to_name(int id) { - - if (id <= 0) - return NULL; - - if (id >= (int) ELEMENTSOF(arphrd_names)) - return NULL; - - return arphrd_names[id]; -} - -int arphrd_from_name(const char *name) { - const struct arphrd_name *sc; - - assert(name); - - sc = lookup_arphrd(name, strlen(name)); - if (!sc) - return 0; - - return sc->id; -} - -int arphrd_max(void) { - return ELEMENTSOF(arphrd_names); -} diff --git a/src/basic/arphrd-list.h b/src/basic/arphrd-list.h deleted file mode 100644 index c0f8758dbe..0000000000 --- a/src/basic/arphrd-list.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 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/>. -***/ - -const char *arphrd_to_name(int id); -int arphrd_from_name(const char *name); - -int arphrd_max(void); diff --git a/src/basic/async.c b/src/basic/async.c deleted file mode 100644 index a1f163f27b..0000000000 --- a/src/basic/async.c +++ /dev/null @@ -1,94 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 <errno.h> -#include <pthread.h> -#include <stddef.h> -#include <unistd.h> - -#include "async.h" -#include "fd-util.h" -#include "log.h" -#include "macro.h" -#include "util.h" - -int asynchronous_job(void* (*func)(void *p), void *arg) { - pthread_attr_t a; - pthread_t t; - int r; - - /* It kinda sucks that we have to resort to threads to - * implement an asynchronous sync(), but well, such is - * life. - * - * Note that issuing this command right before exiting a - * process will cause the process to wait for the sync() to - * complete. This function hence is nicely asynchronous really - * only in long running processes. */ - - r = pthread_attr_init(&a); - if (r > 0) - return -r; - - r = pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED); - if (r > 0) - goto finish; - - r = pthread_create(&t, &a, func, arg); - -finish: - pthread_attr_destroy(&a); - return -r; -} - -static void *sync_thread(void *p) { - sync(); - return NULL; -} - -int asynchronous_sync(void) { - log_debug("Spawning new thread for sync"); - - return asynchronous_job(sync_thread, NULL); -} - -static void *close_thread(void *p) { - assert_se(close_nointr(PTR_TO_FD(p)) != -EBADF); - return NULL; -} - -int asynchronous_close(int fd) { - int r; - - /* This is supposed to behave similar to safe_close(), but - * actually invoke close() asynchronously, so that it will - * never block. Ideally the kernel would have an API for this, - * but it doesn't, so we work around it, and hide this as a - * far away as we can. */ - - if (fd >= 0) { - PROTECT_ERRNO; - - r = asynchronous_job(close_thread, FD_TO_PTR(fd)); - if (r < 0) - assert_se(close_nointr(fd) != -EBADF); - } - - return -1; -} diff --git a/src/basic/async.h b/src/basic/async.h deleted file mode 100644 index 9bd13ff6e0..0000000000 --- a/src/basic/async.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 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/>. -***/ - -int asynchronous_job(void* (*func)(void *p), void *arg); - -int asynchronous_sync(void); -int asynchronous_close(int fd); diff --git a/src/basic/audit-util.c b/src/basic/audit-util.c deleted file mode 100644 index d1c9695973..0000000000 --- a/src/basic/audit-util.c +++ /dev/null @@ -1,107 +0,0 @@ -/*** - 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 <errno.h> -#include <linux/netlink.h> -#include <stdio.h> -#include <sys/socket.h> - -#include "alloc-util.h" -#include "audit-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "macro.h" -#include "parse-util.h" -#include "process-util.h" -#include "user-util.h" - -int audit_session_from_pid(pid_t pid, uint32_t *id) { - _cleanup_free_ char *s = NULL; - const char *p; - uint32_t u; - int r; - - assert(id); - - /* We don't convert ENOENT to ESRCH here, since we can't - * really distuingish between "audit is not available in the - * kernel" and "the process does not exist", both which will - * result in ENOENT. */ - - p = procfs_file_alloca(pid, "sessionid"); - - r = read_one_line_file(p, &s); - if (r < 0) - return r; - - r = safe_atou32(s, &u); - if (r < 0) - return r; - - if (u == AUDIT_SESSION_INVALID || u <= 0) - return -ENODATA; - - *id = u; - return 0; -} - -int audit_loginuid_from_pid(pid_t pid, uid_t *uid) { - _cleanup_free_ char *s = NULL; - const char *p; - uid_t u; - int r; - - assert(uid); - - p = procfs_file_alloca(pid, "loginuid"); - - r = read_one_line_file(p, &s); - if (r < 0) - return r; - - r = parse_uid(s, &u); - if (r == -ENXIO) /* the UID was -1 */ - return -ENODATA; - if (r < 0) - return r; - - *uid = (uid_t) u; - return 0; -} - -bool use_audit(void) { - static int cached_use = -1; - - if (cached_use < 0) { - int fd; - - fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT); - if (fd < 0) { - cached_use = !IN_SET(errno, EAFNOSUPPORT, EPROTONOSUPPORT, EPERM); - if (errno == EPERM) - log_debug_errno(errno, "Audit access prohibited, won't talk to audit"); - } - else { - cached_use = true; - safe_close(fd); - } - } - - return cached_use; -} diff --git a/src/basic/audit-util.h b/src/basic/audit-util.h deleted file mode 100644 index e048503991..0000000000 --- a/src/basic/audit-util.h +++ /dev/null @@ -1,31 +0,0 @@ -#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 <stdint.h> -#include <sys/types.h> - -#define AUDIT_SESSION_INVALID ((uint32_t) -1) - -int audit_session_from_pid(pid_t pid, uint32_t *id); -int audit_loginuid_from_pid(pid_t pid, uid_t *uid); - -bool use_audit(void); diff --git a/src/basic/barrier.c b/src/basic/barrier.c deleted file mode 100644 index 2da633b311..0000000000 --- a/src/basic/barrier.c +++ /dev/null @@ -1,415 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 David Herrmann <dh.herrmann@gmail.com> - - 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 <errno.h> -#include <fcntl.h> -#include <poll.h> -#include <stdbool.h> -#include <stdint.h> -#include <stdlib.h> -#include <sys/eventfd.h> -#include <sys/types.h> -#include <unistd.h> - -#include "barrier.h" -#include "fd-util.h" -#include "macro.h" - -/** - * Barriers - * This barrier implementation provides a simple synchronization method based - * on file-descriptors that can safely be used between threads and processes. A - * barrier object contains 2 shared counters based on eventfd. Both processes - * can now place barriers and wait for the other end to reach a random or - * specific barrier. - * Barriers are numbered, so you can either wait for the other end to reach any - * barrier or the last barrier that you placed. This way, you can use barriers - * for one-way *and* full synchronization. Note that even-though barriers are - * numbered, these numbers are internal and recycled once both sides reached the - * same barrier (implemented as a simple signed counter). It is thus not - * possible to address barriers by their ID. - * - * Barrier-API: Both ends can place as many barriers via barrier_place() as - * they want and each pair of barriers on both sides will be implicitly linked. - * Each side can use the barrier_wait/sync_*() family of calls to wait for the - * other side to place a specific barrier. barrier_wait_next() waits until the - * other side calls barrier_place(). No links between the barriers are - * considered and this simply serves as most basic asynchronous barrier. - * barrier_sync_next() is like barrier_wait_next() and waits for the other side - * to place their next barrier via barrier_place(). However, it only waits for - * barriers that are linked to a barrier we already placed. If the other side - * already placed more barriers than we did, barrier_sync_next() returns - * immediately. - * barrier_sync() extends barrier_sync_next() and waits until the other end - * placed as many barriers via barrier_place() as we did. If they already placed - * as many as we did (or more), it returns immediately. - * - * Additionally to basic barriers, an abortion event is available. - * barrier_abort() places an abortion event that cannot be undone. An abortion - * immediately cancels all placed barriers and replaces them. Any running and - * following wait/sync call besides barrier_wait_abortion() will immediately - * return false on both sides (otherwise, they always return true). - * barrier_abort() can be called multiple times on both ends and will be a - * no-op if already called on this side. - * barrier_wait_abortion() can be used to wait for the other side to call - * barrier_abort() and is the only wait/sync call that does not return - * immediately if we aborted outself. It only returns once the other side - * called barrier_abort(). - * - * Barriers can be used for in-process and inter-process synchronization. - * However, for in-process synchronization you could just use mutexes. - * Therefore, main target is IPC and we require both sides to *not* share the FD - * table. If that's given, barriers provide target tracking: If the remote side - * exit()s, an abortion event is implicitly queued on the other side. This way, - * a sync/wait call will be woken up if the remote side crashed or exited - * unexpectedly. However, note that these abortion events are only queued if the - * barrier-queue has been drained. Therefore, it is safe to place a barrier and - * exit. The other side can safely wait on the barrier even though the exit - * queued an abortion event. Usually, the abortion event would overwrite the - * barrier, however, that's not true for exit-abortion events. Those are only - * queued if the barrier-queue is drained (thus, the receiving side has placed - * more barriers than the remote side). - */ - -/** - * barrier_create() - Initialize a barrier object - * @obj: barrier to initialize - * - * This initializes a barrier object. The caller is responsible of allocating - * the memory and keeping it valid. The memory does not have to be zeroed - * beforehand. - * Two eventfd objects are allocated for each barrier. If allocation fails, an - * error is returned. - * - * If this function fails, the barrier is reset to an invalid state so it is - * safe to call barrier_destroy() on the object regardless whether the - * initialization succeeded or not. - * - * The caller is responsible to destroy the object via barrier_destroy() before - * releasing the underlying memory. - * - * Returns: 0 on success, negative error code on failure. - */ -int barrier_create(Barrier *b) { - _cleanup_(barrier_destroyp) Barrier *staging = b; - int r; - - assert(b); - - b->me = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); - if (b->me < 0) - return -errno; - - b->them = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); - if (b->them < 0) - return -errno; - - r = pipe2(b->pipe, O_CLOEXEC | O_NONBLOCK); - if (r < 0) - return -errno; - - staging = NULL; - return 0; -} - -/** - * barrier_destroy() - Destroy a barrier object - * @b: barrier to destroy or NULL - * - * This destroys a barrier object that has previously been passed to - * barrier_create(). The object is released and reset to invalid - * state. Therefore, it is safe to call barrier_destroy() multiple - * times or even if barrier_create() failed. However, barrier must be - * always initialized with BARRIER_NULL. - * - * If @b is NULL, this is a no-op. - */ -void barrier_destroy(Barrier *b) { - if (!b) - return; - - b->me = safe_close(b->me); - b->them = safe_close(b->them); - safe_close_pair(b->pipe); - b->barriers = 0; -} - -/** - * barrier_set_role() - Set the local role of the barrier - * @b: barrier to operate on - * @role: role to set on the barrier - * - * This sets the roles on a barrier object. This is needed to know - * which side of the barrier you're on. Usually, the parent creates - * the barrier via barrier_create() and then calls fork() or clone(). - * Therefore, the FDs are duplicated and the child retains the same - * barrier object. - * - * Both sides need to call barrier_set_role() after fork() or clone() - * are done. If this is not done, barriers will not work correctly. - * - * Note that barriers could be supported without fork() or clone(). However, - * this is currently not needed so it hasn't been implemented. - */ -void barrier_set_role(Barrier *b, unsigned int role) { - int fd; - - assert(b); - assert(role == BARRIER_PARENT || role == BARRIER_CHILD); - /* make sure this is only called once */ - assert(b->pipe[0] >= 0 && b->pipe[1] >= 0); - - if (role == BARRIER_PARENT) - b->pipe[1] = safe_close(b->pipe[1]); - else { - b->pipe[0] = safe_close(b->pipe[0]); - - /* swap me/them for children */ - fd = b->me; - b->me = b->them; - b->them = fd; - } -} - -/* places barrier; returns false if we aborted, otherwise true */ -static bool barrier_write(Barrier *b, uint64_t buf) { - ssize_t len; - - /* prevent new sync-points if we already aborted */ - if (barrier_i_aborted(b)) - return false; - - assert(b->me >= 0); - do { - len = write(b->me, &buf, sizeof(buf)); - } while (len < 0 && IN_SET(errno, EAGAIN, EINTR)); - - if (len != sizeof(buf)) - goto error; - - /* lock if we aborted */ - if (buf >= (uint64_t)BARRIER_ABORTION) { - if (barrier_they_aborted(b)) - b->barriers = BARRIER_WE_ABORTED; - else - b->barriers = BARRIER_I_ABORTED; - } else if (!barrier_is_aborted(b)) - b->barriers += buf; - - return !barrier_i_aborted(b); - -error: - /* If there is an unexpected error, we have to make this fatal. There - * is no way we can recover from sync-errors. Therefore, we close the - * pipe-ends and treat this as abortion. The other end will notice the - * pipe-close and treat it as abortion, too. */ - - safe_close_pair(b->pipe); - b->barriers = BARRIER_WE_ABORTED; - return false; -} - -/* waits for barriers; returns false if they aborted, otherwise true */ -static bool barrier_read(Barrier *b, int64_t comp) { - if (barrier_they_aborted(b)) - return false; - - while (b->barriers > comp) { - struct pollfd pfd[2] = { - { .fd = b->pipe[0] >= 0 ? b->pipe[0] : b->pipe[1], - .events = POLLHUP }, - { .fd = b->them, - .events = POLLIN }}; - uint64_t buf; - int r; - - r = poll(pfd, 2, -1); - if (r < 0 && IN_SET(errno, EAGAIN, EINTR)) - continue; - else if (r < 0) - goto error; - - if (pfd[1].revents) { - ssize_t len; - - /* events on @them signal new data for us */ - len = read(b->them, &buf, sizeof(buf)); - if (len < 0 && IN_SET(errno, EAGAIN, EINTR)) - continue; - - if (len != sizeof(buf)) - goto error; - } else if (pfd[0].revents & (POLLHUP | POLLERR | POLLNVAL)) - /* POLLHUP on the pipe tells us the other side exited. - * We treat this as implicit abortion. But we only - * handle it if there's no event on the eventfd. This - * guarantees that exit-abortions do not overwrite real - * barriers. */ - buf = BARRIER_ABORTION; - else - continue; - - /* lock if they aborted */ - if (buf >= (uint64_t)BARRIER_ABORTION) { - if (barrier_i_aborted(b)) - b->barriers = BARRIER_WE_ABORTED; - else - b->barriers = BARRIER_THEY_ABORTED; - } else if (!barrier_is_aborted(b)) - b->barriers -= buf; - } - - return !barrier_they_aborted(b); - -error: - /* If there is an unexpected error, we have to make this fatal. There - * is no way we can recover from sync-errors. Therefore, we close the - * pipe-ends and treat this as abortion. The other end will notice the - * pipe-close and treat it as abortion, too. */ - - safe_close_pair(b->pipe); - b->barriers = BARRIER_WE_ABORTED; - return false; -} - -/** - * barrier_place() - Place a new barrier - * @b: barrier object - * - * This places a new barrier on the barrier object. If either side already - * aborted, this is a no-op and returns "false". Otherwise, the barrier is - * placed and this returns "true". - * - * Returns: true if barrier was placed, false if either side aborted. - */ -bool barrier_place(Barrier *b) { - assert(b); - - if (barrier_is_aborted(b)) - return false; - - barrier_write(b, BARRIER_SINGLE); - return true; -} - -/** - * barrier_abort() - Abort the synchronization - * @b: barrier object to abort - * - * This aborts the barrier-synchronization. If barrier_abort() was already - * called on this side, this is a no-op. Otherwise, the barrier is put into the - * ABORT-state and will stay there. The other side is notified about the - * abortion. Any following attempt to place normal barriers or to wait on normal - * barriers will return immediately as "false". - * - * You can wait for the other side to call barrier_abort(), too. Use - * barrier_wait_abortion() for that. - * - * Returns: false if the other side already aborted, true otherwise. - */ -bool barrier_abort(Barrier *b) { - assert(b); - - barrier_write(b, BARRIER_ABORTION); - return !barrier_they_aborted(b); -} - -/** - * barrier_wait_next() - Wait for the next barrier of the other side - * @b: barrier to operate on - * - * This waits until the other side places its next barrier. This is independent - * of any barrier-links and just waits for any next barrier of the other side. - * - * If either side aborted, this returns false. - * - * Returns: false if either side aborted, true otherwise. - */ -bool barrier_wait_next(Barrier *b) { - assert(b); - - if (barrier_is_aborted(b)) - return false; - - barrier_read(b, b->barriers - 1); - return !barrier_is_aborted(b); -} - -/** - * barrier_wait_abortion() - Wait for the other side to abort - * @b: barrier to operate on - * - * This waits until the other side called barrier_abort(). This can be called - * regardless whether the local side already called barrier_abort() or not. - * - * If the other side has already aborted, this returns immediately. - * - * Returns: false if the local side aborted, true otherwise. - */ -bool barrier_wait_abortion(Barrier *b) { - assert(b); - - barrier_read(b, BARRIER_THEY_ABORTED); - return !barrier_i_aborted(b); -} - -/** - * barrier_sync_next() - Wait for the other side to place a next linked barrier - * @b: barrier to operate on - * - * This is like barrier_wait_next() and waits for the other side to call - * barrier_place(). However, this only waits for linked barriers. That means, if - * the other side already placed more barriers than (or as much as) we did, this - * returns immediately instead of waiting. - * - * If either side aborted, this returns false. - * - * Returns: false if either side aborted, true otherwise. - */ -bool barrier_sync_next(Barrier *b) { - assert(b); - - if (barrier_is_aborted(b)) - return false; - - barrier_read(b, MAX((int64_t)0, b->barriers - 1)); - return !barrier_is_aborted(b); -} - -/** - * barrier_sync() - Wait for the other side to place as many barriers as we did - * @b: barrier to operate on - * - * This is like barrier_sync_next() but waits for the other side to call - * barrier_place() as often as we did (in total). If they already placed as much - * as we did (or more), this returns immediately instead of waiting. - * - * If either side aborted, this returns false. - * - * Returns: false if either side aborted, true otherwise. - */ -bool barrier_sync(Barrier *b) { - assert(b); - - if (barrier_is_aborted(b)) - return false; - - barrier_read(b, 0); - return !barrier_is_aborted(b); -} diff --git a/src/basic/barrier.h b/src/basic/barrier.h deleted file mode 100644 index 6347fddc4d..0000000000 --- a/src/basic/barrier.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 David Herrmann <dh.herrmann@gmail.com> - - 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 <stdint.h> -#include <sys/types.h> - -#include "macro.h" - -/* See source file for an API description. */ - -typedef struct Barrier Barrier; - -enum { - BARRIER_SINGLE = 1LL, - BARRIER_ABORTION = INT64_MAX, - - /* bias values to store state; keep @WE < @THEY < @I */ - BARRIER_BIAS = INT64_MIN, - BARRIER_WE_ABORTED = BARRIER_BIAS + 1LL, - BARRIER_THEY_ABORTED = BARRIER_BIAS + 2LL, - BARRIER_I_ABORTED = BARRIER_BIAS + 3LL, -}; - -enum { - BARRIER_PARENT, - BARRIER_CHILD, -}; - -struct Barrier { - int me; - int them; - int pipe[2]; - int64_t barriers; -}; - -#define BARRIER_NULL {-1, -1, {-1, -1}, 0} - -int barrier_create(Barrier *obj); -void barrier_destroy(Barrier *b); - -DEFINE_TRIVIAL_CLEANUP_FUNC(Barrier*, barrier_destroy); - -void barrier_set_role(Barrier *b, unsigned int role); - -bool barrier_place(Barrier *b); -bool barrier_abort(Barrier *b); - -bool barrier_wait_next(Barrier *b); -bool barrier_wait_abortion(Barrier *b); -bool barrier_sync_next(Barrier *b); -bool barrier_sync(Barrier *b); - -static inline bool barrier_i_aborted(Barrier *b) { - return b->barriers == BARRIER_I_ABORTED || b->barriers == BARRIER_WE_ABORTED; -} - -static inline bool barrier_they_aborted(Barrier *b) { - return b->barriers == BARRIER_THEY_ABORTED || b->barriers == BARRIER_WE_ABORTED; -} - -static inline bool barrier_we_aborted(Barrier *b) { - return b->barriers == BARRIER_WE_ABORTED; -} - -static inline bool barrier_is_aborted(Barrier *b) { - return b->barriers == BARRIER_I_ABORTED || b->barriers == BARRIER_THEY_ABORTED || b->barriers == BARRIER_WE_ABORTED; -} - -static inline bool barrier_place_and_sync(Barrier *b) { - (void) barrier_place(b); - return barrier_sync(b); -} diff --git a/src/basic/bitmap.c b/src/basic/bitmap.c deleted file mode 100644 index f6212e6151..0000000000 --- a/src/basic/bitmap.c +++ /dev/null @@ -1,235 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 Tom Gundersen - - 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 <errno.h> -#include <stddef.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "bitmap.h" -#include "hashmap.h" -#include "macro.h" - -struct Bitmap { - uint64_t *bitmaps; - size_t n_bitmaps; - size_t bitmaps_allocated; -}; - -/* Bitmaps are only meant to store relatively small numbers - * (corresponding to, say, an enum), so it is ok to limit - * the max entry. 64k should be plenty. */ -#define BITMAPS_MAX_ENTRY 0xffff - -/* This indicates that we reached the end of the bitmap */ -#define BITMAP_END ((unsigned) -1) - -#define BITMAP_NUM_TO_OFFSET(n) ((n) / (sizeof(uint64_t) * 8)) -#define BITMAP_NUM_TO_REM(n) ((n) % (sizeof(uint64_t) * 8)) -#define BITMAP_OFFSET_TO_NUM(offset, rem) ((offset) * sizeof(uint64_t) * 8 + (rem)) - -Bitmap *bitmap_new(void) { - return new0(Bitmap, 1); -} - -Bitmap *bitmap_copy(Bitmap *b) { - Bitmap *ret; - - ret = bitmap_new(); - if (!ret) - return NULL; - - ret->bitmaps = newdup(uint64_t, b->bitmaps, b->n_bitmaps); - if (!ret->bitmaps) - return mfree(ret); - - ret->n_bitmaps = ret->bitmaps_allocated = b->n_bitmaps; - return ret; -} - -void bitmap_free(Bitmap *b) { - if (!b) - return; - - free(b->bitmaps); - free(b); -} - -int bitmap_ensure_allocated(Bitmap **b) { - Bitmap *a; - - assert(b); - - if (*b) - return 0; - - a = bitmap_new(); - if (!a) - return -ENOMEM; - - *b = a; - - return 0; -} - -int bitmap_set(Bitmap *b, unsigned n) { - uint64_t bitmask; - unsigned offset; - - assert(b); - - /* we refuse to allocate huge bitmaps */ - if (n > BITMAPS_MAX_ENTRY) - return -ERANGE; - - offset = BITMAP_NUM_TO_OFFSET(n); - - if (offset >= b->n_bitmaps) { - if (!GREEDY_REALLOC0(b->bitmaps, b->bitmaps_allocated, offset + 1)) - return -ENOMEM; - - b->n_bitmaps = offset + 1; - } - - bitmask = UINT64_C(1) << BITMAP_NUM_TO_REM(n); - - b->bitmaps[offset] |= bitmask; - - return 0; -} - -void bitmap_unset(Bitmap *b, unsigned n) { - uint64_t bitmask; - unsigned offset; - - if (!b) - return; - - offset = BITMAP_NUM_TO_OFFSET(n); - - if (offset >= b->n_bitmaps) - return; - - bitmask = UINT64_C(1) << BITMAP_NUM_TO_REM(n); - - b->bitmaps[offset] &= ~bitmask; -} - -bool bitmap_isset(Bitmap *b, unsigned n) { - uint64_t bitmask; - unsigned offset; - - if (!b) - return false; - - offset = BITMAP_NUM_TO_OFFSET(n); - - if (offset >= b->n_bitmaps) - return false; - - bitmask = UINT64_C(1) << BITMAP_NUM_TO_REM(n); - - return !!(b->bitmaps[offset] & bitmask); -} - -bool bitmap_isclear(Bitmap *b) { - unsigned i; - - if (!b) - return true; - - for (i = 0; i < b->n_bitmaps; i++) - if (b->bitmaps[i] != 0) - return false; - - return true; -} - -void bitmap_clear(Bitmap *b) { - - if (!b) - return; - - b->bitmaps = mfree(b->bitmaps); - b->n_bitmaps = 0; - b->bitmaps_allocated = 0; -} - -bool bitmap_iterate(Bitmap *b, Iterator *i, unsigned *n) { - uint64_t bitmask; - unsigned offset, rem; - - assert(i); - assert(n); - - if (!b || i->idx == BITMAP_END) - return false; - - offset = BITMAP_NUM_TO_OFFSET(i->idx); - rem = BITMAP_NUM_TO_REM(i->idx); - bitmask = UINT64_C(1) << rem; - - for (; offset < b->n_bitmaps; offset ++) { - if (b->bitmaps[offset]) { - for (; bitmask; bitmask <<= 1, rem ++) { - if (b->bitmaps[offset] & bitmask) { - *n = BITMAP_OFFSET_TO_NUM(offset, rem); - i->idx = *n + 1; - - return true; - } - } - } - - rem = 0; - bitmask = 1; - } - - i->idx = BITMAP_END; - - return false; -} - -bool bitmap_equal(Bitmap *a, Bitmap *b) { - size_t common_n_bitmaps; - Bitmap *c; - unsigned i; - - if (a == b) - return true; - - if (!a != !b) - return false; - - if (!a) - return true; - - common_n_bitmaps = MIN(a->n_bitmaps, b->n_bitmaps); - if (memcmp(a->bitmaps, b->bitmaps, sizeof(uint64_t) * common_n_bitmaps) != 0) - return false; - - c = a->n_bitmaps > b->n_bitmaps ? a : b; - for (i = common_n_bitmaps; i < c->n_bitmaps; i++) - if (c->bitmaps[i] != 0) - return false; - - return true; -} diff --git a/src/basic/bitmap.h b/src/basic/bitmap.h deleted file mode 100644 index 63fdbe8bea..0000000000 --- a/src/basic/bitmap.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2015 Tom Gundersen - - 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 "hashmap.h" -#include "macro.h" - -typedef struct Bitmap Bitmap; - -Bitmap *bitmap_new(void); -Bitmap *bitmap_copy(Bitmap *b); -int bitmap_ensure_allocated(Bitmap **b); -void bitmap_free(Bitmap *b); - -int bitmap_set(Bitmap *b, unsigned n); -void bitmap_unset(Bitmap *b, unsigned n); -bool bitmap_isset(Bitmap *b, unsigned n); -bool bitmap_isclear(Bitmap *b); -void bitmap_clear(Bitmap *b); - -bool bitmap_iterate(Bitmap *b, Iterator *i, unsigned *n); - -bool bitmap_equal(Bitmap *a, Bitmap *b); - -#define BITMAP_FOREACH(n, b, i) \ - for ((i).idx = 0; bitmap_iterate((b), &(i), (unsigned*)&(n)); ) - -DEFINE_TRIVIAL_CLEANUP_FUNC(Bitmap*, bitmap_free); - -#define _cleanup_bitmap_free_ _cleanup_(bitmap_freep) diff --git a/src/basic/blkid-util.h b/src/basic/blkid-util.h deleted file mode 100644 index 7aa75eb091..0000000000 --- a/src/basic/blkid-util.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 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/>. -***/ - -#ifdef HAVE_BLKID -#include <blkid/blkid.h> -#endif - -#include "util.h" - -#ifdef HAVE_BLKID -DEFINE_TRIVIAL_CLEANUP_FUNC(blkid_probe, blkid_free_probe); -#define _cleanup_blkid_free_probe_ _cleanup_(blkid_free_probep) -#endif diff --git a/src/basic/btrfs-ctree.h b/src/basic/btrfs-ctree.h deleted file mode 100644 index 66bdf9736e..0000000000 --- a/src/basic/btrfs-ctree.h +++ /dev/null @@ -1,96 +0,0 @@ -#pragma once - -#include "macro.h" -#include "sparse-endian.h" - -/* Stolen from btrfs' ctree.h */ - -struct btrfs_timespec { - le64_t sec; - le32_t nsec; -} _packed_; - -struct btrfs_disk_key { - le64_t objectid; - uint8_t type; - le64_t offset; -} _packed_; - -struct btrfs_inode_item { - le64_t generation; - le64_t transid; - le64_t size; - le64_t nbytes; - le64_t block_group; - le32_t nlink; - le32_t uid; - le32_t gid; - le32_t mode; - le64_t rdev; - le64_t flags; - le64_t sequence; - le64_t reserved[4]; - struct btrfs_timespec atime; - struct btrfs_timespec ctime; - struct btrfs_timespec mtime; - struct btrfs_timespec otime; -} _packed_; - -struct btrfs_root_item { - struct btrfs_inode_item inode; - le64_t generation; - le64_t root_dirid; - le64_t bytenr; - le64_t byte_limit; - le64_t bytes_used; - le64_t last_snapshot; - le64_t flags; - le32_t refs; - struct btrfs_disk_key drop_progress; - uint8_t drop_level; - uint8_t level; - le64_t generation_v2; - uint8_t uuid[BTRFS_UUID_SIZE]; - uint8_t parent_uuid[BTRFS_UUID_SIZE]; - uint8_t received_uuid[BTRFS_UUID_SIZE]; - le64_t ctransid; - le64_t otransid; - le64_t stransid; - le64_t rtransid; - struct btrfs_timespec ctime; - struct btrfs_timespec otime; - struct btrfs_timespec stime; - struct btrfs_timespec rtime; - le64_t reserved[8]; -} _packed_; - -#define BTRFS_ROOT_SUBVOL_RDONLY (1ULL << 0) - -struct btrfs_qgroup_info_item { - le64_t generation; - le64_t rfer; - le64_t rfer_cmpr; - le64_t excl; - le64_t excl_cmpr; -} _packed_; - -#define BTRFS_QGROUP_LIMIT_MAX_RFER (1ULL << 0) -#define BTRFS_QGROUP_LIMIT_MAX_EXCL (1ULL << 1) -#define BTRFS_QGROUP_LIMIT_RSV_RFER (1ULL << 2) -#define BTRFS_QGROUP_LIMIT_RSV_EXCL (1ULL << 3) -#define BTRFS_QGROUP_LIMIT_RFER_CMPR (1ULL << 4) -#define BTRFS_QGROUP_LIMIT_EXCL_CMPR (1ULL << 5) - -struct btrfs_qgroup_limit_item { - le64_t flags; - le64_t max_rfer; - le64_t max_excl; - le64_t rsv_rfer; - le64_t rsv_excl; -} _packed_; - -struct btrfs_root_ref { - le64_t dirid; - le64_t sequence; - le16_t name_len; -} _packed_; diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c deleted file mode 100644 index 359d85f2e8..0000000000 --- a/src/basic/btrfs-util.c +++ /dev/null @@ -1,2075 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 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 <errno.h> -#include <fcntl.h> -#include <inttypes.h> -#include <linux/loop.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <sys/statfs.h> -#include <sys/sysmacros.h> -#include <unistd.h> - -#ifdef HAVE_LINUX_BTRFS_H -#include <linux/btrfs.h> -#endif - -#include "alloc-util.h" -#include "btrfs-ctree.h" -#include "btrfs-util.h" -#include "copy.h" -#include "fd-util.h" -#include "fileio.h" -#include "io-util.h" -#include "macro.h" -#include "missing.h" -#include "path-util.h" -#include "selinux-util.h" -#include "smack-util.h" -#include "sparse-endian.h" -#include "stat-util.h" -#include "string-util.h" -#include "time-util.h" -#include "util.h" - -/* WARNING: Be careful with file system ioctls! When we get an fd, we - * need to make sure it either refers to only a regular file or - * directory, or that it is located on btrfs, before invoking any - * btrfs ioctls. The ioctl numbers are reused by some device drivers - * (such as DRM), and hence might have bad effects when invoked on - * device nodes (that reference drivers) rather than fds to normal - * files or directories. */ - -static int validate_subvolume_name(const char *name) { - - if (!filename_is_valid(name)) - return -EINVAL; - - if (strlen(name) > BTRFS_SUBVOL_NAME_MAX) - return -E2BIG; - - return 0; -} - -static int open_parent(const char *path, int flags) { - _cleanup_free_ char *parent = NULL; - int fd; - - assert(path); - - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; - - fd = open(parent, flags); - if (fd < 0) - return -errno; - - return fd; -} - -static int extract_subvolume_name(const char *path, const char **subvolume) { - const char *fn; - int r; - - assert(path); - assert(subvolume); - - fn = basename(path); - - r = validate_subvolume_name(fn); - if (r < 0) - return r; - - *subvolume = fn; - return 0; -} - -int btrfs_is_filesystem(int fd) { - struct statfs sfs; - - assert(fd >= 0); - - if (fstatfs(fd, &sfs) < 0) - return -errno; - - return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC); -} - -int btrfs_is_subvol_fd(int fd) { - struct stat st; - - assert(fd >= 0); - - /* On btrfs subvolumes always have the inode 256 */ - - if (fstat(fd, &st) < 0) - return -errno; - - if (!S_ISDIR(st.st_mode) || st.st_ino != 256) - return 0; - - return btrfs_is_filesystem(fd); -} - -int btrfs_is_subvol(const char *path) { - _cleanup_close_ int fd = -1; - - assert(path); - - fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (fd < 0) - return -errno; - - return btrfs_is_subvol_fd(fd); -} - -int btrfs_subvol_make(const char *path) { - struct btrfs_ioctl_vol_args args = {}; - _cleanup_close_ int fd = -1; - const char *subvolume; - int r; - - assert(path); - - r = extract_subvolume_name(path, &subvolume); - if (r < 0) - return r; - - fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (fd < 0) - return fd; - - strncpy(args.name, subvolume, sizeof(args.name)-1); - - if (ioctl(fd, BTRFS_IOC_SUBVOL_CREATE, &args) < 0) - return -errno; - - return 0; -} - -int btrfs_subvol_make_label(const char *path) { - int r; - - assert(path); - - r = mac_selinux_create_file_prepare(path, S_IFDIR); - if (r < 0) - return r; - - r = btrfs_subvol_make(path); - mac_selinux_create_file_clear(); - - if (r < 0) - return r; - - return mac_smack_fix(path, false, false); -} - -int btrfs_subvol_set_read_only_fd(int fd, bool b) { - uint64_t flags, nflags; - struct stat st; - - assert(fd >= 0); - - if (fstat(fd, &st) < 0) - return -errno; - - if (!S_ISDIR(st.st_mode) || st.st_ino != 256) - return -EINVAL; - - if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0) - return -errno; - - if (b) - nflags = flags | BTRFS_SUBVOL_RDONLY; - else - nflags = flags & ~BTRFS_SUBVOL_RDONLY; - - if (flags == nflags) - return 0; - - if (ioctl(fd, BTRFS_IOC_SUBVOL_SETFLAGS, &nflags) < 0) - return -errno; - - return 0; -} - -int btrfs_subvol_set_read_only(const char *path, bool b) { - _cleanup_close_ int fd = -1; - - fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (fd < 0) - return -errno; - - return btrfs_subvol_set_read_only_fd(fd, b); -} - -int btrfs_subvol_get_read_only_fd(int fd) { - uint64_t flags; - struct stat st; - - assert(fd >= 0); - - if (fstat(fd, &st) < 0) - return -errno; - - if (!S_ISDIR(st.st_mode) || st.st_ino != 256) - return -EINVAL; - - if (ioctl(fd, BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0) - return -errno; - - return !!(flags & BTRFS_SUBVOL_RDONLY); -} - -int btrfs_reflink(int infd, int outfd) { - struct stat st; - int r; - - assert(infd >= 0); - assert(outfd >= 0); - - /* Make sure we invoke the ioctl on a regular file, so that no - * device driver accidentally gets it. */ - - if (fstat(outfd, &st) < 0) - return -errno; - - if (!S_ISREG(st.st_mode)) - return -EINVAL; - - r = ioctl(outfd, BTRFS_IOC_CLONE, infd); - if (r < 0) - return -errno; - - return 0; -} - -int btrfs_clone_range(int infd, uint64_t in_offset, int outfd, uint64_t out_offset, uint64_t sz) { - struct btrfs_ioctl_clone_range_args args = { - .src_fd = infd, - .src_offset = in_offset, - .src_length = sz, - .dest_offset = out_offset, - }; - struct stat st; - int r; - - assert(infd >= 0); - assert(outfd >= 0); - assert(sz > 0); - - if (fstat(outfd, &st) < 0) - return -errno; - - if (!S_ISREG(st.st_mode)) - return -EINVAL; - - r = ioctl(outfd, BTRFS_IOC_CLONE_RANGE, &args); - if (r < 0) - return -errno; - - return 0; -} - -int btrfs_get_block_device_fd(int fd, dev_t *dev) { - struct btrfs_ioctl_fs_info_args fsi = {}; - uint64_t id; - int r; - - assert(fd >= 0); - assert(dev); - - r = btrfs_is_filesystem(fd); - if (r < 0) - return r; - if (!r) - return -ENOTTY; - - if (ioctl(fd, BTRFS_IOC_FS_INFO, &fsi) < 0) - return -errno; - - /* We won't do this for btrfs RAID */ - if (fsi.num_devices != 1) - return 0; - - for (id = 1; id <= fsi.max_id; id++) { - struct btrfs_ioctl_dev_info_args di = { - .devid = id, - }; - struct stat st; - - if (ioctl(fd, BTRFS_IOC_DEV_INFO, &di) < 0) { - if (errno == ENODEV) - continue; - - return -errno; - } - - if (stat((char*) di.path, &st) < 0) - return -errno; - - if (!S_ISBLK(st.st_mode)) - return -ENODEV; - - if (major(st.st_rdev) == 0) - return -ENODEV; - - *dev = st.st_rdev; - return 1; - } - - return -ENODEV; -} - -int btrfs_get_block_device(const char *path, dev_t *dev) { - _cleanup_close_ int fd = -1; - - assert(path); - assert(dev); - - fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return -errno; - - return btrfs_get_block_device_fd(fd, dev); -} - -int btrfs_subvol_get_id_fd(int fd, uint64_t *ret) { - struct btrfs_ioctl_ino_lookup_args args = { - .objectid = BTRFS_FIRST_FREE_OBJECTID - }; - int r; - - assert(fd >= 0); - assert(ret); - - r = btrfs_is_filesystem(fd); - if (r < 0) - return r; - if (!r) - return -ENOTTY; - - if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &args) < 0) - return -errno; - - *ret = args.treeid; - return 0; -} - -int btrfs_subvol_get_id(int fd, const char *subvol, uint64_t *ret) { - _cleanup_close_ int subvol_fd = -1; - - assert(fd >= 0); - assert(ret); - - subvol_fd = openat(fd, subvol, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (subvol_fd < 0) - return -errno; - - return btrfs_subvol_get_id_fd(subvol_fd, ret); -} - -static bool btrfs_ioctl_search_args_inc(struct btrfs_ioctl_search_args *args) { - assert(args); - - /* the objectid, type, offset together make up the btrfs key, - * which is considered a single 136byte integer when - * comparing. This call increases the counter by one, dealing - * with the overflow between the overflows */ - - if (args->key.min_offset < (uint64_t) -1) { - args->key.min_offset++; - return true; - } - - if (args->key.min_type < (uint8_t) -1) { - args->key.min_type++; - args->key.min_offset = 0; - return true; - } - - if (args->key.min_objectid < (uint64_t) -1) { - args->key.min_objectid++; - args->key.min_offset = 0; - args->key.min_type = 0; - return true; - } - - return 0; -} - -static void btrfs_ioctl_search_args_set(struct btrfs_ioctl_search_args *args, const struct btrfs_ioctl_search_header *h) { - assert(args); - assert(h); - - args->key.min_objectid = h->objectid; - args->key.min_type = h->type; - args->key.min_offset = h->offset; -} - -static int btrfs_ioctl_search_args_compare(const struct btrfs_ioctl_search_args *args) { - assert(args); - - /* Compare min and max */ - - if (args->key.min_objectid < args->key.max_objectid) - return -1; - if (args->key.min_objectid > args->key.max_objectid) - return 1; - - if (args->key.min_type < args->key.max_type) - return -1; - if (args->key.min_type > args->key.max_type) - return 1; - - if (args->key.min_offset < args->key.max_offset) - return -1; - if (args->key.min_offset > args->key.max_offset) - return 1; - - return 0; -} - -#define FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) \ - for ((i) = 0, \ - (sh) = (const struct btrfs_ioctl_search_header*) (args).buf; \ - (i) < (args).key.nr_items; \ - (i)++, \ - (sh) = (const struct btrfs_ioctl_search_header*) ((uint8_t*) (sh) + sizeof(struct btrfs_ioctl_search_header) + (sh)->len)) - -#define BTRFS_IOCTL_SEARCH_HEADER_BODY(sh) \ - ((void*) ((uint8_t*) sh + sizeof(struct btrfs_ioctl_search_header))) - -int btrfs_subvol_get_info_fd(int fd, uint64_t subvol_id, BtrfsSubvolInfo *ret) { - struct btrfs_ioctl_search_args args = { - /* Tree of tree roots */ - .key.tree_id = BTRFS_ROOT_TREE_OBJECTID, - - /* Look precisely for the subvolume items */ - .key.min_type = BTRFS_ROOT_ITEM_KEY, - .key.max_type = BTRFS_ROOT_ITEM_KEY, - - .key.min_offset = 0, - .key.max_offset = (uint64_t) -1, - - /* No restrictions on the other components */ - .key.min_transid = 0, - .key.max_transid = (uint64_t) -1, - }; - - bool found = false; - int r; - - assert(fd >= 0); - assert(ret); - - if (subvol_id == 0) { - r = btrfs_subvol_get_id_fd(fd, &subvol_id); - if (r < 0) - return r; - } else { - r = btrfs_is_filesystem(fd); - if (r < 0) - return r; - if (!r) - return -ENOTTY; - } - - args.key.min_objectid = args.key.max_objectid = subvol_id; - - while (btrfs_ioctl_search_args_compare(&args) <= 0) { - const struct btrfs_ioctl_search_header *sh; - unsigned i; - - args.key.nr_items = 256; - if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) - return -errno; - - if (args.key.nr_items <= 0) - break; - - FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) { - - const struct btrfs_root_item *ri; - - /* Make sure we start the next search at least from this entry */ - btrfs_ioctl_search_args_set(&args, sh); - - if (sh->objectid != subvol_id) - continue; - if (sh->type != BTRFS_ROOT_ITEM_KEY) - continue; - - /* Older versions of the struct lacked the otime setting */ - if (sh->len < offsetof(struct btrfs_root_item, otime) + sizeof(struct btrfs_timespec)) - continue; - - ri = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); - - ret->otime = (usec_t) le64toh(ri->otime.sec) * USEC_PER_SEC + - (usec_t) le32toh(ri->otime.nsec) / NSEC_PER_USEC; - - ret->subvol_id = subvol_id; - ret->read_only = !!(le64toh(ri->flags) & BTRFS_ROOT_SUBVOL_RDONLY); - - assert_cc(sizeof(ri->uuid) == sizeof(ret->uuid)); - memcpy(&ret->uuid, ri->uuid, sizeof(ret->uuid)); - memcpy(&ret->parent_uuid, ri->parent_uuid, sizeof(ret->parent_uuid)); - - found = true; - goto finish; - } - - /* Increase search key by one, to read the next item, if we can. */ - if (!btrfs_ioctl_search_args_inc(&args)) - break; - } - -finish: - if (!found) - return -ENODATA; - - return 0; -} - -int btrfs_qgroup_get_quota_fd(int fd, uint64_t qgroupid, BtrfsQuotaInfo *ret) { - - struct btrfs_ioctl_search_args args = { - /* Tree of quota items */ - .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID, - - /* The object ID is always 0 */ - .key.min_objectid = 0, - .key.max_objectid = 0, - - /* Look precisely for the quota items */ - .key.min_type = BTRFS_QGROUP_STATUS_KEY, - .key.max_type = BTRFS_QGROUP_LIMIT_KEY, - - /* No restrictions on the other components */ - .key.min_transid = 0, - .key.max_transid = (uint64_t) -1, - }; - - bool found_info = false, found_limit = false; - int r; - - assert(fd >= 0); - assert(ret); - - if (qgroupid == 0) { - r = btrfs_subvol_get_id_fd(fd, &qgroupid); - if (r < 0) - return r; - } else { - r = btrfs_is_filesystem(fd); - if (r < 0) - return r; - if (!r) - return -ENOTTY; - } - - args.key.min_offset = args.key.max_offset = qgroupid; - - while (btrfs_ioctl_search_args_compare(&args) <= 0) { - const struct btrfs_ioctl_search_header *sh; - unsigned i; - - args.key.nr_items = 256; - if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) { - if (errno == ENOENT) /* quota tree is missing: quota disabled */ - break; - - return -errno; - } - - if (args.key.nr_items <= 0) - break; - - FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) { - - /* Make sure we start the next search at least from this entry */ - btrfs_ioctl_search_args_set(&args, sh); - - if (sh->objectid != 0) - continue; - if (sh->offset != qgroupid) - continue; - - if (sh->type == BTRFS_QGROUP_INFO_KEY) { - const struct btrfs_qgroup_info_item *qii = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); - - ret->referenced = le64toh(qii->rfer); - ret->exclusive = le64toh(qii->excl); - - found_info = true; - - } else if (sh->type == BTRFS_QGROUP_LIMIT_KEY) { - const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); - - if (le64toh(qli->flags) & BTRFS_QGROUP_LIMIT_MAX_RFER) - ret->referenced_max = le64toh(qli->max_rfer); - else - ret->referenced_max = (uint64_t) -1; - - if (le64toh(qli->flags) & BTRFS_QGROUP_LIMIT_MAX_EXCL) - ret->exclusive_max = le64toh(qli->max_excl); - else - ret->exclusive_max = (uint64_t) -1; - - found_limit = true; - } - - if (found_info && found_limit) - goto finish; - } - - /* Increase search key by one, to read the next item, if we can. */ - if (!btrfs_ioctl_search_args_inc(&args)) - break; - } - -finish: - if (!found_limit && !found_info) - return -ENODATA; - - if (!found_info) { - ret->referenced = (uint64_t) -1; - ret->exclusive = (uint64_t) -1; - } - - if (!found_limit) { - ret->referenced_max = (uint64_t) -1; - ret->exclusive_max = (uint64_t) -1; - } - - return 0; -} - -int btrfs_qgroup_get_quota(const char *path, uint64_t qgroupid, BtrfsQuotaInfo *ret) { - _cleanup_close_ int fd = -1; - - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd < 0) - return -errno; - - return btrfs_qgroup_get_quota_fd(fd, qgroupid, ret); -} - -int btrfs_subvol_find_subtree_qgroup(int fd, uint64_t subvol_id, uint64_t *ret) { - uint64_t level, lowest = (uint64_t) -1, lowest_qgroupid = 0; - _cleanup_free_ uint64_t *qgroups = NULL; - int r, n, i; - - assert(fd >= 0); - assert(ret); - - /* This finds the "subtree" qgroup for a specific - * subvolume. This only works for subvolumes that have been - * prepared with btrfs_subvol_auto_qgroup_fd() with - * insert_intermediary_qgroup=true (or equivalent). For others - * it will return the leaf qgroup instead. The two cases may - * be distuingished via the return value, which is 1 in case - * an appropriate "subtree" qgroup was found, and 0 - * otherwise. */ - - if (subvol_id == 0) { - r = btrfs_subvol_get_id_fd(fd, &subvol_id); - if (r < 0) - return r; - } - - r = btrfs_qgroupid_split(subvol_id, &level, NULL); - if (r < 0) - return r; - if (level != 0) /* Input must be a leaf qgroup */ - return -EINVAL; - - n = btrfs_qgroup_find_parents(fd, subvol_id, &qgroups); - if (n < 0) - return n; - - for (i = 0; i < n; i++) { - uint64_t id; - - r = btrfs_qgroupid_split(qgroups[i], &level, &id); - if (r < 0) - return r; - - if (id != subvol_id) - continue; - - if (lowest == (uint64_t) -1 || level < lowest) { - lowest_qgroupid = qgroups[i]; - lowest = level; - } - } - - if (lowest == (uint64_t) -1) { - /* No suitable higher-level qgroup found, let's return - * the leaf qgroup instead, and indicate that with the - * return value. */ - - *ret = subvol_id; - return 0; - } - - *ret = lowest_qgroupid; - return 1; -} - -int btrfs_subvol_get_subtree_quota_fd(int fd, uint64_t subvol_id, BtrfsQuotaInfo *ret) { - uint64_t qgroupid; - int r; - - assert(fd >= 0); - assert(ret); - - /* This determines the quota data of the qgroup with the - * lowest level, that shares the id part with the specified - * subvolume. This is useful for determining the quota data - * for entire subvolume subtrees, as long as the subtrees have - * been set up with btrfs_qgroup_subvol_auto_fd() or in a - * compatible way */ - - r = btrfs_subvol_find_subtree_qgroup(fd, subvol_id, &qgroupid); - if (r < 0) - return r; - - return btrfs_qgroup_get_quota_fd(fd, qgroupid, ret); -} - -int btrfs_subvol_get_subtree_quota(const char *path, uint64_t subvol_id, BtrfsQuotaInfo *ret) { - _cleanup_close_ int fd = -1; - - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd < 0) - return -errno; - - return btrfs_subvol_get_subtree_quota_fd(fd, subvol_id, ret); -} - -int btrfs_defrag_fd(int fd) { - struct stat st; - - assert(fd >= 0); - - if (fstat(fd, &st) < 0) - return -errno; - - if (!S_ISREG(st.st_mode)) - return -EINVAL; - - if (ioctl(fd, BTRFS_IOC_DEFRAG, NULL) < 0) - return -errno; - - return 0; -} - -int btrfs_defrag(const char *p) { - _cleanup_close_ int fd = -1; - - fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd < 0) - return -errno; - - return btrfs_defrag_fd(fd); -} - -int btrfs_quota_enable_fd(int fd, bool b) { - struct btrfs_ioctl_quota_ctl_args args = { - .cmd = b ? BTRFS_QUOTA_CTL_ENABLE : BTRFS_QUOTA_CTL_DISABLE, - }; - int r; - - assert(fd >= 0); - - r = btrfs_is_filesystem(fd); - if (r < 0) - return r; - if (!r) - return -ENOTTY; - - if (ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args) < 0) - return -errno; - - return 0; -} - -int btrfs_quota_enable(const char *path, bool b) { - _cleanup_close_ int fd = -1; - - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd < 0) - return -errno; - - return btrfs_quota_enable_fd(fd, b); -} - -int btrfs_qgroup_set_limit_fd(int fd, uint64_t qgroupid, uint64_t referenced_max) { - - struct btrfs_ioctl_qgroup_limit_args args = { - .lim.max_rfer = referenced_max, - .lim.flags = BTRFS_QGROUP_LIMIT_MAX_RFER, - }; - unsigned c; - int r; - - assert(fd >= 0); - - if (qgroupid == 0) { - r = btrfs_subvol_get_id_fd(fd, &qgroupid); - if (r < 0) - return r; - } else { - r = btrfs_is_filesystem(fd); - if (r < 0) - return r; - if (!r) - return -ENOTTY; - } - - args.qgroupid = qgroupid; - - for (c = 0;; c++) { - if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args) < 0) { - - if (errno == EBUSY && c < 10) { - (void) btrfs_quota_scan_wait(fd); - continue; - } - - return -errno; - } - - break; - } - - return 0; -} - -int btrfs_qgroup_set_limit(const char *path, uint64_t qgroupid, uint64_t referenced_max) { - _cleanup_close_ int fd = -1; - - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd < 0) - return -errno; - - return btrfs_qgroup_set_limit_fd(fd, qgroupid, referenced_max); -} - -int btrfs_subvol_set_subtree_quota_limit_fd(int fd, uint64_t subvol_id, uint64_t referenced_max) { - uint64_t qgroupid; - int r; - - assert(fd >= 0); - - r = btrfs_subvol_find_subtree_qgroup(fd, subvol_id, &qgroupid); - if (r < 0) - return r; - - return btrfs_qgroup_set_limit_fd(fd, qgroupid, referenced_max); -} - -int btrfs_subvol_set_subtree_quota_limit(const char *path, uint64_t subvol_id, uint64_t referenced_max) { - _cleanup_close_ int fd = -1; - - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd < 0) - return -errno; - - return btrfs_subvol_set_subtree_quota_limit_fd(fd, subvol_id, referenced_max); -} - -int btrfs_resize_loopback_fd(int fd, uint64_t new_size, bool grow_only) { - struct btrfs_ioctl_vol_args args = {}; - _cleanup_free_ char *p = NULL, *loop = NULL, *backing = NULL; - _cleanup_close_ int loop_fd = -1, backing_fd = -1; - struct stat st; - dev_t dev = 0; - int r; - - /* In contrast to btrfs quota ioctls ftruncate() cannot make sense of "infinity" or file sizes > 2^31 */ - if (!FILE_SIZE_VALID(new_size)) - return -EINVAL; - - /* btrfs cannot handle file systems < 16M, hence use this as minimum */ - if (new_size < 16*1024*1024) - new_size = 16*1024*1024; - - r = btrfs_get_block_device_fd(fd, &dev); - if (r < 0) - return r; - if (r == 0) - return -ENODEV; - - if (asprintf(&p, "/sys/dev/block/%u:%u/loop/backing_file", major(dev), minor(dev)) < 0) - return -ENOMEM; - r = read_one_line_file(p, &backing); - if (r == -ENOENT) - return -ENODEV; - if (r < 0) - return r; - if (isempty(backing) || !path_is_absolute(backing)) - return -ENODEV; - - backing_fd = open(backing, O_RDWR|O_CLOEXEC|O_NOCTTY); - if (backing_fd < 0) - return -errno; - - if (fstat(backing_fd, &st) < 0) - return -errno; - if (!S_ISREG(st.st_mode)) - return -ENODEV; - - if (new_size == (uint64_t) st.st_size) - return 0; - - if (grow_only && new_size < (uint64_t) st.st_size) - return -EINVAL; - - if (asprintf(&loop, "/dev/block/%u:%u", major(dev), minor(dev)) < 0) - return -ENOMEM; - loop_fd = open(loop, O_RDWR|O_CLOEXEC|O_NOCTTY); - if (loop_fd < 0) - return -errno; - - if (snprintf(args.name, sizeof(args.name), "%" PRIu64, new_size) >= (int) sizeof(args.name)) - return -EINVAL; - - if (new_size < (uint64_t) st.st_size) { - /* Decrease size: first decrease btrfs size, then shorten loopback */ - if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0) - return -errno; - } - - if (ftruncate(backing_fd, new_size) < 0) - return -errno; - - if (ioctl(loop_fd, LOOP_SET_CAPACITY, 0) < 0) - return -errno; - - if (new_size > (uint64_t) st.st_size) { - /* Increase size: first enlarge loopback, then increase btrfs size */ - if (ioctl(fd, BTRFS_IOC_RESIZE, &args) < 0) - return -errno; - } - - /* Make sure the free disk space is correctly updated for both file systems */ - (void) fsync(fd); - (void) fsync(backing_fd); - - return 1; -} - -int btrfs_resize_loopback(const char *p, uint64_t new_size, bool grow_only) { - _cleanup_close_ int fd = -1; - - fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return -errno; - - return btrfs_resize_loopback_fd(fd, new_size, grow_only); -} - -int btrfs_qgroupid_make(uint64_t level, uint64_t id, uint64_t *ret) { - assert(ret); - - if (level >= (UINT64_C(1) << (64 - BTRFS_QGROUP_LEVEL_SHIFT))) - return -EINVAL; - - if (id >= (UINT64_C(1) << BTRFS_QGROUP_LEVEL_SHIFT)) - return -EINVAL; - - *ret = (level << BTRFS_QGROUP_LEVEL_SHIFT) | id; - return 0; -} - -int btrfs_qgroupid_split(uint64_t qgroupid, uint64_t *level, uint64_t *id) { - assert(level || id); - - if (level) - *level = qgroupid >> BTRFS_QGROUP_LEVEL_SHIFT; - - if (id) - *id = qgroupid & ((UINT64_C(1) << BTRFS_QGROUP_LEVEL_SHIFT) - 1); - - return 0; -} - -static int qgroup_create_or_destroy(int fd, bool b, uint64_t qgroupid) { - - struct btrfs_ioctl_qgroup_create_args args = { - .create = b, - .qgroupid = qgroupid, - }; - unsigned c; - int r; - - r = btrfs_is_filesystem(fd); - if (r < 0) - return r; - if (r == 0) - return -ENOTTY; - - for (c = 0;; c++) { - if (ioctl(fd, BTRFS_IOC_QGROUP_CREATE, &args) < 0) { - - /* If quota is not enabled, we get EINVAL. Turn this into a recognizable error */ - if (errno == EINVAL) - return -ENOPROTOOPT; - - if (errno == EBUSY && c < 10) { - (void) btrfs_quota_scan_wait(fd); - continue; - } - - return -errno; - } - - break; - } - - return 0; -} - -int btrfs_qgroup_create(int fd, uint64_t qgroupid) { - return qgroup_create_or_destroy(fd, true, qgroupid); -} - -int btrfs_qgroup_destroy(int fd, uint64_t qgroupid) { - return qgroup_create_or_destroy(fd, false, qgroupid); -} - -int btrfs_qgroup_destroy_recursive(int fd, uint64_t qgroupid) { - _cleanup_free_ uint64_t *qgroups = NULL; - uint64_t subvol_id; - int i, n, r; - - /* Destroys the specified qgroup, but unassigns it from all - * its parents first. Also, it recursively destroys all - * qgroups it is assgined to that have the same id part of the - * qgroupid as the specified group. */ - - r = btrfs_qgroupid_split(qgroupid, NULL, &subvol_id); - if (r < 0) - return r; - - n = btrfs_qgroup_find_parents(fd, qgroupid, &qgroups); - if (n < 0) - return n; - - for (i = 0; i < n; i++) { - uint64_t id; - - r = btrfs_qgroupid_split(qgroups[i], NULL, &id); - if (r < 0) - return r; - - r = btrfs_qgroup_unassign(fd, qgroupid, qgroups[i]); - if (r < 0) - return r; - - if (id != subvol_id) - continue; - - /* The parent qgroupid shares the same id part with - * us? If so, destroy it too. */ - - (void) btrfs_qgroup_destroy_recursive(fd, qgroups[i]); - } - - return btrfs_qgroup_destroy(fd, qgroupid); -} - -int btrfs_quota_scan_start(int fd) { - struct btrfs_ioctl_quota_rescan_args args = {}; - - assert(fd >= 0); - - if (ioctl(fd, BTRFS_IOC_QUOTA_RESCAN, &args) < 0) - return -errno; - - return 0; -} - -int btrfs_quota_scan_wait(int fd) { - assert(fd >= 0); - - if (ioctl(fd, BTRFS_IOC_QUOTA_RESCAN_WAIT) < 0) - return -errno; - - return 0; -} - -int btrfs_quota_scan_ongoing(int fd) { - struct btrfs_ioctl_quota_rescan_args args = {}; - - assert(fd >= 0); - - if (ioctl(fd, BTRFS_IOC_QUOTA_RESCAN_STATUS, &args) < 0) - return -errno; - - return !!args.flags; -} - -static int qgroup_assign_or_unassign(int fd, bool b, uint64_t child, uint64_t parent) { - struct btrfs_ioctl_qgroup_assign_args args = { - .assign = b, - .src = child, - .dst = parent, - }; - unsigned c; - int r; - - r = btrfs_is_filesystem(fd); - if (r < 0) - return r; - if (r == 0) - return -ENOTTY; - - for (c = 0;; c++) { - r = ioctl(fd, BTRFS_IOC_QGROUP_ASSIGN, &args); - if (r < 0) { - if (errno == EBUSY && c < 10) { - (void) btrfs_quota_scan_wait(fd); - continue; - } - - return -errno; - } - - if (r == 0) - return 0; - - /* If the return value is > 0, we need to request a rescan */ - - (void) btrfs_quota_scan_start(fd); - return 1; - } -} - -int btrfs_qgroup_assign(int fd, uint64_t child, uint64_t parent) { - return qgroup_assign_or_unassign(fd, true, child, parent); -} - -int btrfs_qgroup_unassign(int fd, uint64_t child, uint64_t parent) { - return qgroup_assign_or_unassign(fd, false, child, parent); -} - -static int subvol_remove_children(int fd, const char *subvolume, uint64_t subvol_id, BtrfsRemoveFlags flags) { - struct btrfs_ioctl_search_args args = { - .key.tree_id = BTRFS_ROOT_TREE_OBJECTID, - - .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID, - .key.max_objectid = BTRFS_LAST_FREE_OBJECTID, - - .key.min_type = BTRFS_ROOT_BACKREF_KEY, - .key.max_type = BTRFS_ROOT_BACKREF_KEY, - - .key.min_transid = 0, - .key.max_transid = (uint64_t) -1, - }; - - struct btrfs_ioctl_vol_args vol_args = {}; - _cleanup_close_ int subvol_fd = -1; - struct stat st; - bool made_writable = false; - int r; - - assert(fd >= 0); - assert(subvolume); - - if (fstat(fd, &st) < 0) - return -errno; - - if (!S_ISDIR(st.st_mode)) - return -EINVAL; - - subvol_fd = openat(fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (subvol_fd < 0) - return -errno; - - if (subvol_id == 0) { - r = btrfs_subvol_get_id_fd(subvol_fd, &subvol_id); - if (r < 0) - return r; - } - - /* First, try to remove the subvolume. If it happens to be - * already empty, this will just work. */ - strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1); - if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) >= 0) { - (void) btrfs_qgroup_destroy_recursive(fd, subvol_id); /* for the leaf subvolumes, the qgroup id is identical to the subvol id */ - return 0; - } - if (!(flags & BTRFS_REMOVE_RECURSIVE) || errno != ENOTEMPTY) - return -errno; - - /* OK, the subvolume is not empty, let's look for child - * subvolumes, and remove them, first */ - - args.key.min_offset = args.key.max_offset = subvol_id; - - while (btrfs_ioctl_search_args_compare(&args) <= 0) { - const struct btrfs_ioctl_search_header *sh; - unsigned i; - - args.key.nr_items = 256; - if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) - return -errno; - - if (args.key.nr_items <= 0) - break; - - FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) { - _cleanup_free_ char *p = NULL; - const struct btrfs_root_ref *ref; - struct btrfs_ioctl_ino_lookup_args ino_args; - - btrfs_ioctl_search_args_set(&args, sh); - - if (sh->type != BTRFS_ROOT_BACKREF_KEY) - continue; - if (sh->offset != subvol_id) - continue; - - ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); - - p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len)); - if (!p) - return -ENOMEM; - - zero(ino_args); - ino_args.treeid = subvol_id; - ino_args.objectid = htole64(ref->dirid); - - if (ioctl(fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0) - return -errno; - - if (!made_writable) { - r = btrfs_subvol_set_read_only_fd(subvol_fd, false); - if (r < 0) - return r; - - made_writable = true; - } - - if (isempty(ino_args.name)) - /* Subvolume is in the top-level - * directory of the subvolume. */ - r = subvol_remove_children(subvol_fd, p, sh->objectid, flags); - else { - _cleanup_close_ int child_fd = -1; - - /* Subvolume is somewhere further down, - * hence we need to open the - * containing directory first */ - - child_fd = openat(subvol_fd, ino_args.name, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (child_fd < 0) - return -errno; - - r = subvol_remove_children(child_fd, p, sh->objectid, flags); - } - if (r < 0) - return r; - } - - /* Increase search key by one, to read the next item, if we can. */ - if (!btrfs_ioctl_search_args_inc(&args)) - break; - } - - /* OK, the child subvolumes should all be gone now, let's try - * again to remove the subvolume */ - if (ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &vol_args) < 0) - return -errno; - - (void) btrfs_qgroup_destroy_recursive(fd, subvol_id); - return 0; -} - -int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags) { - _cleanup_close_ int fd = -1; - const char *subvolume; - int r; - - assert(path); - - r = extract_subvolume_name(path, &subvolume); - if (r < 0) - return r; - - fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (fd < 0) - return fd; - - return subvol_remove_children(fd, subvolume, 0, flags); -} - -int btrfs_subvol_remove_fd(int fd, const char *subvolume, BtrfsRemoveFlags flags) { - return subvol_remove_children(fd, subvolume, 0, flags); -} - -int btrfs_qgroup_copy_limits(int fd, uint64_t old_qgroupid, uint64_t new_qgroupid) { - - struct btrfs_ioctl_search_args args = { - /* Tree of quota items */ - .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID, - - /* The object ID is always 0 */ - .key.min_objectid = 0, - .key.max_objectid = 0, - - /* Look precisely for the quota items */ - .key.min_type = BTRFS_QGROUP_LIMIT_KEY, - .key.max_type = BTRFS_QGROUP_LIMIT_KEY, - - /* For our qgroup */ - .key.min_offset = old_qgroupid, - .key.max_offset = old_qgroupid, - - /* No restrictions on the other components */ - .key.min_transid = 0, - .key.max_transid = (uint64_t) -1, - }; - - int r; - - r = btrfs_is_filesystem(fd); - if (r < 0) - return r; - if (!r) - return -ENOTTY; - - while (btrfs_ioctl_search_args_compare(&args) <= 0) { - const struct btrfs_ioctl_search_header *sh; - unsigned i; - - args.key.nr_items = 256; - if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) { - if (errno == ENOENT) /* quota tree missing: quota is not enabled, hence nothing to copy */ - break; - - return -errno; - } - - if (args.key.nr_items <= 0) - break; - - FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) { - const struct btrfs_qgroup_limit_item *qli = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); - struct btrfs_ioctl_qgroup_limit_args qargs; - unsigned c; - - /* Make sure we start the next search at least from this entry */ - btrfs_ioctl_search_args_set(&args, sh); - - if (sh->objectid != 0) - continue; - if (sh->type != BTRFS_QGROUP_LIMIT_KEY) - continue; - if (sh->offset != old_qgroupid) - continue; - - /* We found the entry, now copy things over. */ - - qargs = (struct btrfs_ioctl_qgroup_limit_args) { - .qgroupid = new_qgroupid, - - .lim.max_rfer = le64toh(qli->max_rfer), - .lim.max_excl = le64toh(qli->max_excl), - .lim.rsv_rfer = le64toh(qli->rsv_rfer), - .lim.rsv_excl = le64toh(qli->rsv_excl), - - .lim.flags = le64toh(qli->flags) & (BTRFS_QGROUP_LIMIT_MAX_RFER| - BTRFS_QGROUP_LIMIT_MAX_EXCL| - BTRFS_QGROUP_LIMIT_RSV_RFER| - BTRFS_QGROUP_LIMIT_RSV_EXCL), - }; - - for (c = 0;; c++) { - if (ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &qargs) < 0) { - if (errno == EBUSY && c < 10) { - (void) btrfs_quota_scan_wait(fd); - continue; - } - return -errno; - } - - break; - } - - return 1; - } - - /* Increase search key by one, to read the next item, if we can. */ - if (!btrfs_ioctl_search_args_inc(&args)) - break; - } - - return 0; -} - -static int copy_quota_hierarchy(int fd, uint64_t old_subvol_id, uint64_t new_subvol_id) { - _cleanup_free_ uint64_t *old_qgroups = NULL, *old_parent_qgroups = NULL; - bool copy_from_parent = false, insert_intermediary_qgroup = false; - int n_old_qgroups, n_old_parent_qgroups, r, i; - uint64_t old_parent_id; - - assert(fd >= 0); - - /* Copies a reduced form of quota information from the old to - * the new subvolume. */ - - n_old_qgroups = btrfs_qgroup_find_parents(fd, old_subvol_id, &old_qgroups); - if (n_old_qgroups <= 0) /* Nothing to copy */ - return n_old_qgroups; - - r = btrfs_subvol_get_parent(fd, old_subvol_id, &old_parent_id); - if (r == -ENXIO) - /* We have no parent, hence nothing to copy. */ - n_old_parent_qgroups = 0; - else if (r < 0) - return r; - else { - n_old_parent_qgroups = btrfs_qgroup_find_parents(fd, old_parent_id, &old_parent_qgroups); - if (n_old_parent_qgroups < 0) - return n_old_parent_qgroups; - } - - for (i = 0; i < n_old_qgroups; i++) { - uint64_t id; - int j; - - r = btrfs_qgroupid_split(old_qgroups[i], NULL, &id); - if (r < 0) - return r; - - if (id == old_subvol_id) { - /* The old subvolume was member of a qgroup - * that had the same id, but a different level - * as it self. Let's set up something similar - * in the destination. */ - insert_intermediary_qgroup = true; - break; - } - - for (j = 0; j < n_old_parent_qgroups; j++) - if (old_parent_qgroups[j] == old_qgroups[i]) { - /* The old subvolume shared a common - * parent qgroup with its parent - * subvolume. Let's set up something - * similar in the destination. */ - copy_from_parent = true; - } - } - - if (!insert_intermediary_qgroup && !copy_from_parent) - return 0; - - return btrfs_subvol_auto_qgroup_fd(fd, new_subvol_id, insert_intermediary_qgroup); -} - -static int copy_subtree_quota_limits(int fd, uint64_t old_subvol, uint64_t new_subvol) { - uint64_t old_subtree_qgroup, new_subtree_qgroup; - bool changed; - int r; - - /* First copy the leaf limits */ - r = btrfs_qgroup_copy_limits(fd, old_subvol, new_subvol); - if (r < 0) - return r; - changed = r > 0; - - /* Then, try to copy the subtree limits, if there are any. */ - r = btrfs_subvol_find_subtree_qgroup(fd, old_subvol, &old_subtree_qgroup); - if (r < 0) - return r; - if (r == 0) - return changed; - - r = btrfs_subvol_find_subtree_qgroup(fd, new_subvol, &new_subtree_qgroup); - if (r < 0) - return r; - if (r == 0) - return changed; - - r = btrfs_qgroup_copy_limits(fd, old_subtree_qgroup, new_subtree_qgroup); - if (r != 0) - return r; - - return changed; -} - -static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolume, uint64_t old_subvol_id, BtrfsSnapshotFlags flags) { - - struct btrfs_ioctl_search_args args = { - .key.tree_id = BTRFS_ROOT_TREE_OBJECTID, - - .key.min_objectid = BTRFS_FIRST_FREE_OBJECTID, - .key.max_objectid = BTRFS_LAST_FREE_OBJECTID, - - .key.min_type = BTRFS_ROOT_BACKREF_KEY, - .key.max_type = BTRFS_ROOT_BACKREF_KEY, - - .key.min_transid = 0, - .key.max_transid = (uint64_t) -1, - }; - - struct btrfs_ioctl_vol_args_v2 vol_args = { - .flags = flags & BTRFS_SNAPSHOT_READ_ONLY ? BTRFS_SUBVOL_RDONLY : 0, - .fd = old_fd, - }; - _cleanup_close_ int subvolume_fd = -1; - uint64_t new_subvol_id; - int r; - - assert(old_fd >= 0); - assert(new_fd >= 0); - assert(subvolume); - - strncpy(vol_args.name, subvolume, sizeof(vol_args.name)-1); - - if (ioctl(new_fd, BTRFS_IOC_SNAP_CREATE_V2, &vol_args) < 0) - return -errno; - - if (!(flags & BTRFS_SNAPSHOT_RECURSIVE) && - !(flags & BTRFS_SNAPSHOT_QUOTA)) - return 0; - - if (old_subvol_id == 0) { - r = btrfs_subvol_get_id_fd(old_fd, &old_subvol_id); - if (r < 0) - return r; - } - - r = btrfs_subvol_get_id(new_fd, vol_args.name, &new_subvol_id); - if (r < 0) - return r; - - if (flags & BTRFS_SNAPSHOT_QUOTA) - (void) copy_quota_hierarchy(new_fd, old_subvol_id, new_subvol_id); - - if (!(flags & BTRFS_SNAPSHOT_RECURSIVE)) { - - if (flags & BTRFS_SNAPSHOT_QUOTA) - (void) copy_subtree_quota_limits(new_fd, old_subvol_id, new_subvol_id); - - return 0; - } - - args.key.min_offset = args.key.max_offset = old_subvol_id; - - while (btrfs_ioctl_search_args_compare(&args) <= 0) { - const struct btrfs_ioctl_search_header *sh; - unsigned i; - - args.key.nr_items = 256; - if (ioctl(old_fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) - return -errno; - - if (args.key.nr_items <= 0) - break; - - FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) { - _cleanup_free_ char *p = NULL, *c = NULL, *np = NULL; - struct btrfs_ioctl_ino_lookup_args ino_args; - const struct btrfs_root_ref *ref; - _cleanup_close_ int old_child_fd = -1, new_child_fd = -1; - - btrfs_ioctl_search_args_set(&args, sh); - - if (sh->type != BTRFS_ROOT_BACKREF_KEY) - continue; - - /* Avoid finding the source subvolume a second - * time */ - if (sh->offset != old_subvol_id) - continue; - - /* Avoid running into loops if the new - * subvolume is below the old one. */ - if (sh->objectid == new_subvol_id) - continue; - - ref = BTRFS_IOCTL_SEARCH_HEADER_BODY(sh); - p = strndup((char*) ref + sizeof(struct btrfs_root_ref), le64toh(ref->name_len)); - if (!p) - return -ENOMEM; - - zero(ino_args); - ino_args.treeid = old_subvol_id; - ino_args.objectid = htole64(ref->dirid); - - if (ioctl(old_fd, BTRFS_IOC_INO_LOOKUP, &ino_args) < 0) - return -errno; - - /* The kernel returns an empty name if the - * subvolume is in the top-level directory, - * and otherwise appends a slash, so that we - * can just concatenate easily here, without - * adding a slash. */ - c = strappend(ino_args.name, p); - if (!c) - return -ENOMEM; - - old_child_fd = openat(old_fd, c, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (old_child_fd < 0) - return -errno; - - np = strjoin(subvolume, "/", ino_args.name, NULL); - if (!np) - return -ENOMEM; - - new_child_fd = openat(new_fd, np, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (new_child_fd < 0) - return -errno; - - if (flags & BTRFS_SNAPSHOT_READ_ONLY) { - /* If the snapshot is read-only we - * need to mark it writable - * temporarily, to put the subsnapshot - * into place. */ - - if (subvolume_fd < 0) { - subvolume_fd = openat(new_fd, subvolume, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (subvolume_fd < 0) - return -errno; - } - - r = btrfs_subvol_set_read_only_fd(subvolume_fd, false); - if (r < 0) - return r; - } - - /* When btrfs clones the subvolumes, child - * subvolumes appear as empty directories. Remove - * them, so that we can create a new snapshot - * in their place */ - if (unlinkat(new_child_fd, p, AT_REMOVEDIR) < 0) { - int k = -errno; - - if (flags & BTRFS_SNAPSHOT_READ_ONLY) - (void) btrfs_subvol_set_read_only_fd(subvolume_fd, true); - - return k; - } - - r = subvol_snapshot_children(old_child_fd, new_child_fd, p, sh->objectid, flags & ~BTRFS_SNAPSHOT_FALLBACK_COPY); - - /* Restore the readonly flag */ - if (flags & BTRFS_SNAPSHOT_READ_ONLY) { - int k; - - k = btrfs_subvol_set_read_only_fd(subvolume_fd, true); - if (r >= 0 && k < 0) - return k; - } - - if (r < 0) - return r; - } - - /* Increase search key by one, to read the next item, if we can. */ - if (!btrfs_ioctl_search_args_inc(&args)) - break; - } - - if (flags & BTRFS_SNAPSHOT_QUOTA) - (void) copy_subtree_quota_limits(new_fd, old_subvol_id, new_subvol_id); - - return 0; -} - -int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags) { - _cleanup_close_ int new_fd = -1; - const char *subvolume; - int r; - - assert(old_fd >= 0); - assert(new_path); - - r = btrfs_is_subvol_fd(old_fd); - if (r < 0) - return r; - if (r == 0) { - if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY)) - return -EISDIR; - - r = btrfs_subvol_make(new_path); - if (r < 0) - return r; - - r = copy_directory_fd(old_fd, new_path, true); - if (r < 0) { - (void) btrfs_subvol_remove(new_path, BTRFS_REMOVE_QUOTA); - return r; - } - - if (flags & BTRFS_SNAPSHOT_READ_ONLY) { - r = btrfs_subvol_set_read_only(new_path, true); - if (r < 0) { - (void) btrfs_subvol_remove(new_path, BTRFS_REMOVE_QUOTA); - return r; - } - } - - return 0; - } - - r = extract_subvolume_name(new_path, &subvolume); - if (r < 0) - return r; - - new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (new_fd < 0) - return new_fd; - - return subvol_snapshot_children(old_fd, new_fd, subvolume, 0, flags); -} - -int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags) { - _cleanup_close_ int old_fd = -1; - - assert(old_path); - assert(new_path); - - old_fd = open(old_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (old_fd < 0) - return -errno; - - return btrfs_subvol_snapshot_fd(old_fd, new_path, flags); -} - -int btrfs_qgroup_find_parents(int fd, uint64_t qgroupid, uint64_t **ret) { - - struct btrfs_ioctl_search_args args = { - /* Tree of quota items */ - .key.tree_id = BTRFS_QUOTA_TREE_OBJECTID, - - /* Look precisely for the quota relation items */ - .key.min_type = BTRFS_QGROUP_RELATION_KEY, - .key.max_type = BTRFS_QGROUP_RELATION_KEY, - - /* No restrictions on the other components */ - .key.min_offset = 0, - .key.max_offset = (uint64_t) -1, - - .key.min_transid = 0, - .key.max_transid = (uint64_t) -1, - }; - - _cleanup_free_ uint64_t *items = NULL; - size_t n_items = 0, n_allocated = 0; - int r; - - assert(fd >= 0); - assert(ret); - - if (qgroupid == 0) { - r = btrfs_subvol_get_id_fd(fd, &qgroupid); - if (r < 0) - return r; - } else { - r = btrfs_is_filesystem(fd); - if (r < 0) - return r; - if (!r) - return -ENOTTY; - } - - args.key.min_objectid = args.key.max_objectid = qgroupid; - - while (btrfs_ioctl_search_args_compare(&args) <= 0) { - const struct btrfs_ioctl_search_header *sh; - unsigned i; - - args.key.nr_items = 256; - if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) { - if (errno == ENOENT) /* quota tree missing: quota is disabled */ - break; - - return -errno; - } - - if (args.key.nr_items <= 0) - break; - - FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) { - - /* Make sure we start the next search at least from this entry */ - btrfs_ioctl_search_args_set(&args, sh); - - if (sh->type != BTRFS_QGROUP_RELATION_KEY) - continue; - if (sh->offset < sh->objectid) - continue; - if (sh->objectid != qgroupid) - continue; - - if (!GREEDY_REALLOC(items, n_allocated, n_items+1)) - return -ENOMEM; - - items[n_items++] = sh->offset; - } - - /* Increase search key by one, to read the next item, if we can. */ - if (!btrfs_ioctl_search_args_inc(&args)) - break; - } - - if (n_items <= 0) { - *ret = NULL; - return 0; - } - - *ret = items; - items = NULL; - - return (int) n_items; -} - -int btrfs_subvol_auto_qgroup_fd(int fd, uint64_t subvol_id, bool insert_intermediary_qgroup) { - _cleanup_free_ uint64_t *qgroups = NULL; - uint64_t parent_subvol; - bool changed = false; - int n = 0, r; - - assert(fd >= 0); - - /* - * Sets up the specified subvolume's qgroup automatically in - * one of two ways: - * - * If insert_intermediary_qgroup is false, the subvolume's - * leaf qgroup will be assigned to the same parent qgroups as - * the subvolume's parent subvolume. - * - * If insert_intermediary_qgroup is true a new intermediary - * higher-level qgroup is created, with a higher level number, - * but reusing the id of the subvolume. The level number is - * picked as one smaller than the lowest level qgroup the - * parent subvolume is a member of. If the parent subvolume's - * leaf qgroup is assigned to no higher-level qgroup a new - * qgroup of level 255 is created instead. Either way, the new - * qgroup is then assigned to the parent's higher-level - * qgroup, and the subvolume itself is assigned to it. - * - * If the subvolume is already assigned to a higher level - * qgroup, no operation is executed. - * - * Effectively this means: regardless if - * insert_intermediary_qgroup is true or not, after this - * function is invoked the subvolume will be accounted within - * the same qgroups as the parent. However, if it is true, it - * will also get its own higher-level qgroup, which may in - * turn be used by subvolumes created beneath this subvolume - * later on. - * - * This hence defines a simple default qgroup setup for - * subvolumes, as long as this function is invoked on each - * created subvolume: each subvolume is always accounting - * together with its immediate parents. Optionally, if - * insert_intermediary_qgroup is true, it will also get a - * qgroup that then includes all its own child subvolumes. - */ - - if (subvol_id == 0) { - r = btrfs_is_subvol_fd(fd); - if (r < 0) - return r; - if (!r) - return -ENOTTY; - - r = btrfs_subvol_get_id_fd(fd, &subvol_id); - if (r < 0) - return r; - } - - n = btrfs_qgroup_find_parents(fd, subvol_id, &qgroups); - if (n < 0) - return n; - if (n > 0) /* already parent qgroups set up, let's bail */ - return 0; - - qgroups = mfree(qgroups); - - r = btrfs_subvol_get_parent(fd, subvol_id, &parent_subvol); - if (r == -ENXIO) - /* No parent, hence no qgroup memberships */ - n = 0; - else if (r < 0) - return r; - else { - n = btrfs_qgroup_find_parents(fd, parent_subvol, &qgroups); - if (n < 0) - return n; - } - - if (insert_intermediary_qgroup) { - uint64_t lowest = 256, new_qgroupid; - bool created = false; - int i; - - /* Determine the lowest qgroup that the parent - * subvolume is assigned to. */ - - for (i = 0; i < n; i++) { - uint64_t level; - - r = btrfs_qgroupid_split(qgroups[i], &level, NULL); - if (r < 0) - return r; - - if (level < lowest) - lowest = level; - } - - if (lowest <= 1) /* There are no levels left we could use insert an intermediary qgroup at */ - return -EBUSY; - - r = btrfs_qgroupid_make(lowest - 1, subvol_id, &new_qgroupid); - if (r < 0) - return r; - - /* Create the new intermediary group, unless it already exists */ - r = btrfs_qgroup_create(fd, new_qgroupid); - if (r < 0 && r != -EEXIST) - return r; - if (r >= 0) - changed = created = true; - - for (i = 0; i < n; i++) { - r = btrfs_qgroup_assign(fd, new_qgroupid, qgroups[i]); - if (r < 0 && r != -EEXIST) { - if (created) - (void) btrfs_qgroup_destroy_recursive(fd, new_qgroupid); - - return r; - } - if (r >= 0) - changed = true; - } - - r = btrfs_qgroup_assign(fd, subvol_id, new_qgroupid); - if (r < 0 && r != -EEXIST) { - if (created) - (void) btrfs_qgroup_destroy_recursive(fd, new_qgroupid); - return r; - } - if (r >= 0) - changed = true; - - } else { - int i; - - /* Assign our subvolume to all the same qgroups as the parent */ - - for (i = 0; i < n; i++) { - r = btrfs_qgroup_assign(fd, subvol_id, qgroups[i]); - if (r < 0 && r != -EEXIST) - return r; - if (r >= 0) - changed = true; - } - } - - return changed; -} - -int btrfs_subvol_auto_qgroup(const char *path, uint64_t subvol_id, bool create_intermediary_qgroup) { - _cleanup_close_ int fd = -1; - - fd = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (fd < 0) - return -errno; - - return btrfs_subvol_auto_qgroup_fd(fd, subvol_id, create_intermediary_qgroup); -} - -int btrfs_subvol_get_parent(int fd, uint64_t subvol_id, uint64_t *ret) { - - struct btrfs_ioctl_search_args args = { - /* Tree of tree roots */ - .key.tree_id = BTRFS_ROOT_TREE_OBJECTID, - - /* Look precisely for the subvolume items */ - .key.min_type = BTRFS_ROOT_BACKREF_KEY, - .key.max_type = BTRFS_ROOT_BACKREF_KEY, - - /* No restrictions on the other components */ - .key.min_offset = 0, - .key.max_offset = (uint64_t) -1, - - .key.min_transid = 0, - .key.max_transid = (uint64_t) -1, - }; - int r; - - assert(fd >= 0); - assert(ret); - - if (subvol_id == 0) { - r = btrfs_subvol_get_id_fd(fd, &subvol_id); - if (r < 0) - return r; - } else { - r = btrfs_is_filesystem(fd); - if (r < 0) - return r; - if (!r) - return -ENOTTY; - } - - args.key.min_objectid = args.key.max_objectid = subvol_id; - - while (btrfs_ioctl_search_args_compare(&args) <= 0) { - const struct btrfs_ioctl_search_header *sh; - unsigned i; - - args.key.nr_items = 256; - if (ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args) < 0) - return negative_errno(); - - if (args.key.nr_items <= 0) - break; - - FOREACH_BTRFS_IOCTL_SEARCH_HEADER(i, sh, args) { - - if (sh->type != BTRFS_ROOT_BACKREF_KEY) - continue; - if (sh->objectid != subvol_id) - continue; - - *ret = sh->offset; - return 0; - } - } - - return -ENXIO; -} diff --git a/src/basic/btrfs-util.h b/src/basic/btrfs-util.h deleted file mode 100644 index 1d852d502c..0000000000 --- a/src/basic/btrfs-util.h +++ /dev/null @@ -1,131 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 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 <stdint.h> -#include <sys/types.h> - -#include "sd-id128.h" - -#include "time-util.h" - -typedef struct BtrfsSubvolInfo { - uint64_t subvol_id; - usec_t otime; - - sd_id128_t uuid; - sd_id128_t parent_uuid; - - bool read_only; -} BtrfsSubvolInfo; - -typedef struct BtrfsQuotaInfo { - uint64_t referenced; - uint64_t exclusive; - uint64_t referenced_max; - uint64_t exclusive_max; -} BtrfsQuotaInfo; - -typedef enum BtrfsSnapshotFlags { - BTRFS_SNAPSHOT_FALLBACK_COPY = 1, - BTRFS_SNAPSHOT_READ_ONLY = 2, - BTRFS_SNAPSHOT_RECURSIVE = 4, - BTRFS_SNAPSHOT_QUOTA = 8, -} BtrfsSnapshotFlags; - -typedef enum BtrfsRemoveFlags { - BTRFS_REMOVE_RECURSIVE = 1, - BTRFS_REMOVE_QUOTA = 2, -} BtrfsRemoveFlags; - -int btrfs_is_filesystem(int fd); - -int btrfs_is_subvol_fd(int fd); -int btrfs_is_subvol(const char *path); - -int btrfs_reflink(int infd, int outfd); -int btrfs_clone_range(int infd, uint64_t in_offset, int ofd, uint64_t out_offset, uint64_t sz); - -int btrfs_get_block_device_fd(int fd, dev_t *dev); -int btrfs_get_block_device(const char *path, dev_t *dev); - -int btrfs_defrag_fd(int fd); -int btrfs_defrag(const char *p); - -int btrfs_quota_enable_fd(int fd, bool b); -int btrfs_quota_enable(const char *path, bool b); - -int btrfs_quota_scan_start(int fd); -int btrfs_quota_scan_wait(int fd); -int btrfs_quota_scan_ongoing(int fd); - -int btrfs_resize_loopback_fd(int fd, uint64_t size, bool grow_only); -int btrfs_resize_loopback(const char *path, uint64_t size, bool grow_only); - -int btrfs_subvol_make(const char *path); -int btrfs_subvol_make_label(const char *path); - -int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags); -int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags); - -int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags); -int btrfs_subvol_remove_fd(int fd, const char *subvolume, BtrfsRemoveFlags flags); - -int btrfs_subvol_set_read_only_fd(int fd, bool b); -int btrfs_subvol_set_read_only(const char *path, bool b); -int btrfs_subvol_get_read_only_fd(int fd); - -int btrfs_subvol_get_id(int fd, const char *subvolume, uint64_t *ret); -int btrfs_subvol_get_id_fd(int fd, uint64_t *ret); -int btrfs_subvol_get_parent(int fd, uint64_t subvol_id, uint64_t *ret); - -int btrfs_subvol_get_info_fd(int fd, uint64_t subvol_id, BtrfsSubvolInfo *info); - -int btrfs_subvol_find_subtree_qgroup(int fd, uint64_t subvol_id, uint64_t *ret); - -int btrfs_subvol_get_subtree_quota(const char *path, uint64_t subvol_id, BtrfsQuotaInfo *quota); -int btrfs_subvol_get_subtree_quota_fd(int fd, uint64_t subvol_id, BtrfsQuotaInfo *quota); - -int btrfs_subvol_set_subtree_quota_limit(const char *path, uint64_t subvol_id, uint64_t referenced_max); -int btrfs_subvol_set_subtree_quota_limit_fd(int fd, uint64_t subvol_id, uint64_t referenced_max); - -int btrfs_subvol_auto_qgroup_fd(int fd, uint64_t subvol_id, bool new_qgroup); -int btrfs_subvol_auto_qgroup(const char *path, uint64_t subvol_id, bool create_intermediary_qgroup); - -int btrfs_qgroupid_make(uint64_t level, uint64_t id, uint64_t *ret); -int btrfs_qgroupid_split(uint64_t qgroupid, uint64_t *level, uint64_t *id); - -int btrfs_qgroup_create(int fd, uint64_t qgroupid); -int btrfs_qgroup_destroy(int fd, uint64_t qgroupid); -int btrfs_qgroup_destroy_recursive(int fd, uint64_t qgroupid); - -int btrfs_qgroup_set_limit_fd(int fd, uint64_t qgroupid, uint64_t referenced_max); -int btrfs_qgroup_set_limit(const char *path, uint64_t qgroupid, uint64_t referenced_max); - -int btrfs_qgroup_copy_limits(int fd, uint64_t old_qgroupid, uint64_t new_qgroupid); - -int btrfs_qgroup_assign(int fd, uint64_t child, uint64_t parent); -int btrfs_qgroup_unassign(int fd, uint64_t child, uint64_t parent); - -int btrfs_qgroup_find_parents(int fd, uint64_t qgroupid, uint64_t **ret); - -int btrfs_qgroup_get_quota_fd(int fd, uint64_t qgroupid, BtrfsQuotaInfo *quota); -int btrfs_qgroup_get_quota(const char *path, uint64_t qgroupid, BtrfsQuotaInfo *quota); diff --git a/src/basic/build.h b/src/basic/build.h deleted file mode 100644 index 633c2aaccb..0000000000 --- a/src/basic/build.h +++ /dev/null @@ -1,155 +0,0 @@ -#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/>. -***/ - -#ifdef HAVE_PAM -#define _PAM_FEATURE_ "+PAM" -#else -#define _PAM_FEATURE_ "-PAM" -#endif - -#ifdef HAVE_AUDIT -#define _AUDIT_FEATURE_ "+AUDIT" -#else -#define _AUDIT_FEATURE_ "-AUDIT" -#endif - -#ifdef HAVE_SELINUX -#define _SELINUX_FEATURE_ "+SELINUX" -#else -#define _SELINUX_FEATURE_ "-SELINUX" -#endif - -#ifdef HAVE_APPARMOR -#define _APPARMOR_FEATURE_ "+APPARMOR" -#else -#define _APPARMOR_FEATURE_ "-APPARMOR" -#endif - -#ifdef HAVE_IMA -#define _IMA_FEATURE_ "+IMA" -#else -#define _IMA_FEATURE_ "-IMA" -#endif - -#ifdef HAVE_SMACK -#define _SMACK_FEATURE_ "+SMACK" -#else -#define _SMACK_FEATURE_ "-SMACK" -#endif - -#ifdef HAVE_SYSV_COMPAT -#define _SYSVINIT_FEATURE_ "+SYSVINIT" -#else -#define _SYSVINIT_FEATURE_ "-SYSVINIT" -#endif - -#ifdef HAVE_UTMP -#define _UTMP_FEATURE_ "+UTMP" -#else -#define _UTMP_FEATURE_ "-UTMP" -#endif - -#ifdef HAVE_LIBCRYPTSETUP -#define _LIBCRYPTSETUP_FEATURE_ "+LIBCRYPTSETUP" -#else -#define _LIBCRYPTSETUP_FEATURE_ "-LIBCRYPTSETUP" -#endif - -#ifdef HAVE_GCRYPT -#define _GCRYPT_FEATURE_ "+GCRYPT" -#else -#define _GCRYPT_FEATURE_ "-GCRYPT" -#endif - -#ifdef HAVE_GNUTLS -#define _GNUTLS_FEATURE_ "+GNUTLS" -#else -#define _GNUTLS_FEATURE_ "-GNUTLS" -#endif - -#ifdef HAVE_ACL -#define _ACL_FEATURE_ "+ACL" -#else -#define _ACL_FEATURE_ "-ACL" -#endif - -#ifdef HAVE_XZ -#define _XZ_FEATURE_ "+XZ" -#else -#define _XZ_FEATURE_ "-XZ" -#endif - -#ifdef HAVE_LZ4 -#define _LZ4_FEATURE_ "+LZ4" -#else -#define _LZ4_FEATURE_ "-LZ4" -#endif - -#ifdef HAVE_SECCOMP -#define _SECCOMP_FEATURE_ "+SECCOMP" -#else -#define _SECCOMP_FEATURE_ "-SECCOMP" -#endif - -#ifdef HAVE_BLKID -#define _BLKID_FEATURE_ "+BLKID" -#else -#define _BLKID_FEATURE_ "-BLKID" -#endif - -#ifdef HAVE_ELFUTILS -#define _ELFUTILS_FEATURE_ "+ELFUTILS" -#else -#define _ELFUTILS_FEATURE_ "-ELFUTILS" -#endif - -#ifdef HAVE_KMOD -#define _KMOD_FEATURE_ "+KMOD" -#else -#define _KMOD_FEATURE_ "-KMOD" -#endif - -#ifdef HAVE_LIBIDN -#define _IDN_FEATURE_ "+IDN" -#else -#define _IDN_FEATURE_ "-IDN" -#endif - -#define SYSTEMD_FEATURES \ - _PAM_FEATURE_ " " \ - _AUDIT_FEATURE_ " " \ - _SELINUX_FEATURE_ " " \ - _IMA_FEATURE_ " " \ - _APPARMOR_FEATURE_ " " \ - _SMACK_FEATURE_ " " \ - _SYSVINIT_FEATURE_ " " \ - _UTMP_FEATURE_ " " \ - _LIBCRYPTSETUP_FEATURE_ " " \ - _GCRYPT_FEATURE_ " " \ - _GNUTLS_FEATURE_ " " \ - _ACL_FEATURE_ " " \ - _XZ_FEATURE_ " " \ - _LZ4_FEATURE_ " " \ - _SECCOMP_FEATURE_ " " \ - _BLKID_FEATURE_ " " \ - _ELFUTILS_FEATURE_ " " \ - _KMOD_FEATURE_ " " \ - _IDN_FEATURE_ diff --git a/src/basic/bus-label.c b/src/basic/bus-label.c deleted file mode 100644 index d4531c7947..0000000000 --- a/src/basic/bus-label.c +++ /dev/null @@ -1,98 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 <stdlib.h> - -#include "alloc-util.h" -#include "bus-label.h" -#include "hexdecoct.h" -#include "macro.h" - -char *bus_label_escape(const char *s) { - char *r, *t; - const char *f; - - assert_return(s, NULL); - - /* Escapes all chars that D-Bus' object path cannot deal - * with. Can be reversed with bus_path_unescape(). We special - * case the empty string. */ - - if (*s == 0) - return strdup("_"); - - r = new(char, strlen(s)*3 + 1); - if (!r) - return NULL; - - for (f = s, t = r; *f; f++) { - - /* Escape everything that is not a-zA-Z0-9. We also - * escape 0-9 if it's the first character */ - - if (!(*f >= 'A' && *f <= 'Z') && - !(*f >= 'a' && *f <= 'z') && - !(f > s && *f >= '0' && *f <= '9')) { - *(t++) = '_'; - *(t++) = hexchar(*f >> 4); - *(t++) = hexchar(*f); - } else - *(t++) = *f; - } - - *t = 0; - - return r; -} - -char *bus_label_unescape_n(const char *f, size_t l) { - char *r, *t; - size_t i; - - assert_return(f, NULL); - - /* Special case for the empty string */ - if (l == 1 && *f == '_') - return strdup(""); - - r = new(char, l + 1); - if (!r) - return NULL; - - for (i = 0, t = r; i < l; ++i) { - if (f[i] == '_') { - int a, b; - - if (l - i < 3 || - (a = unhexchar(f[i + 1])) < 0 || - (b = unhexchar(f[i + 2])) < 0) { - /* Invalid escape code, let's take it literal then */ - *(t++) = '_'; - } else { - *(t++) = (char) ((a << 4) | b); - i += 2; - } - } else - *(t++) = f[i]; - } - - *t = 0; - - return r; -} diff --git a/src/basic/bus-label.h b/src/basic/bus-label.h deleted file mode 100644 index 62fb2c450c..0000000000 --- a/src/basic/bus-label.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 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 <stdlib.h> -#include <string.h> - -char *bus_label_escape(const char *s); -char *bus_label_unescape_n(const char *f, size_t l); - -static inline char *bus_label_unescape(const char *f) { - return bus_label_unescape_n(f, f ? strlen(f) : 0); -} diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c deleted file mode 100644 index fda293fcb9..0000000000 --- a/src/basic/calendarspec.c +++ /dev/null @@ -1,1166 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 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 <errno.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#include "alloc-util.h" -#include "calendarspec.h" -#include "fileio.h" -#include "macro.h" -#include "parse-util.h" -#include "string-util.h" - -/* Longest valid date/time range is 1970..2199 */ -#define MAX_RANGE_LEN 230 -#define BITS_WEEKDAYS 127 - -static void free_chain(CalendarComponent *c) { - CalendarComponent *n; - - while (c) { - n = c->next; - free(c); - c = n; - } -} - -void calendar_spec_free(CalendarSpec *c) { - - if (!c) - return; - - free_chain(c->year); - free_chain(c->month); - free_chain(c->day); - free_chain(c->hour); - free_chain(c->minute); - free_chain(c->microsecond); - - free(c); -} - -static int component_compare(const void *_a, const void *_b) { - CalendarComponent * const *a = _a, * const *b = _b; - - if ((*a)->value < (*b)->value) - return -1; - if ((*a)->value > (*b)->value) - return 1; - - if ((*a)->repeat < (*b)->repeat) - return -1; - if ((*a)->repeat > (*b)->repeat) - return 1; - - return 0; -} - -static void sort_chain(CalendarComponent **c) { - unsigned n = 0, k; - CalendarComponent **b, *i, **j, *next; - - assert(c); - - for (i = *c; i; i = i->next) - n++; - - if (n <= 1) - return; - - j = b = alloca(sizeof(CalendarComponent*) * n); - for (i = *c; i; i = i->next) - *(j++) = i; - - qsort(b, n, sizeof(CalendarComponent*), component_compare); - - b[n-1]->next = NULL; - next = b[n-1]; - - /* Drop non-unique entries */ - for (k = n-1; k > 0; k--) { - if (b[k-1]->value == next->value && - b[k-1]->repeat == next->repeat) { - free(b[k-1]); - continue; - } - - b[k-1]->next = next; - next = b[k-1]; - } - - *c = next; -} - -static void fix_year(CalendarComponent *c) { - /* Turns 12 → 2012, 89 → 1989 */ - - while (c) { - CalendarComponent *n = c->next; - - if (c->value >= 0 && c->value < 70) - c->value += 2000; - - if (c->value >= 70 && c->value < 100) - c->value += 1900; - - c = n; - } -} - -int calendar_spec_normalize(CalendarSpec *c) { - assert(c); - - if (c->weekdays_bits <= 0 || c->weekdays_bits >= BITS_WEEKDAYS) - c->weekdays_bits = -1; - - fix_year(c->year); - - sort_chain(&c->year); - sort_chain(&c->month); - sort_chain(&c->day); - sort_chain(&c->hour); - sort_chain(&c->minute); - sort_chain(&c->microsecond); - - return 0; -} - -_pure_ static bool chain_valid(CalendarComponent *c, int from, int to) { - if (!c) - return true; - - if (c->value < from || c->value > to) - return false; - - if (c->value + c->repeat > to) - return false; - - if (c->next) - return chain_valid(c->next, from, to); - - return true; -} - -_pure_ bool calendar_spec_valid(CalendarSpec *c) { - assert(c); - - if (c->weekdays_bits > BITS_WEEKDAYS) - return false; - - if (!chain_valid(c->year, 1970, 2199)) - return false; - - if (!chain_valid(c->month, 1, 12)) - return false; - - if (!chain_valid(c->day, 1, 31)) - return false; - - if (!chain_valid(c->hour, 0, 23)) - return false; - - if (!chain_valid(c->minute, 0, 59)) - return false; - - if (!chain_valid(c->microsecond, 0, 60*USEC_PER_SEC-1)) - return false; - - return true; -} - -static void format_weekdays(FILE *f, const CalendarSpec *c) { - static const char *const days[] = { - "Mon", - "Tue", - "Wed", - "Thu", - "Fri", - "Sat", - "Sun" - }; - - int l, x; - bool need_comma = false; - - assert(f); - assert(c); - assert(c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS); - - for (x = 0, l = -1; x < (int) ELEMENTSOF(days); x++) { - - if (c->weekdays_bits & (1 << x)) { - - if (l < 0) { - if (need_comma) - fputc(',', f); - else - need_comma = true; - - fputs(days[x], f); - l = x; - } - - } else if (l >= 0) { - - if (x > l + 1) { - fputs(x > l + 2 ? ".." : ",", f); - fputs(days[x-1], f); - } - - l = -1; - } - } - - if (l >= 0 && x > l + 1) { - fputs(x > l + 2 ? ".." : ",", f); - fputs(days[x-1], f); - } -} - -static void format_chain(FILE *f, int space, const CalendarComponent *c, bool usec) { - assert(f); - - if (!c) { - fputc('*', f); - return; - } - - assert(c->value >= 0); - if (!usec) - fprintf(f, "%0*i", space, c->value); - else if (c->value % USEC_PER_SEC == 0) - fprintf(f, "%0*i", space, (int) (c->value / USEC_PER_SEC)); - else - fprintf(f, "%0*i.%06i", space, (int) (c->value / USEC_PER_SEC), (int) (c->value % USEC_PER_SEC)); - - if (c->repeat > 0) { - if (!usec) - fprintf(f, "/%i", c->repeat); - else if (c->repeat % USEC_PER_SEC == 0) - fprintf(f, "/%i", (int) (c->repeat / USEC_PER_SEC)); - else - fprintf(f, "/%i.%06i", (int) (c->repeat / USEC_PER_SEC), (int) (c->repeat % USEC_PER_SEC)); - } - - if (c->next) { - fputc(',', f); - format_chain(f, space, c->next, usec); - } -} - -int calendar_spec_to_string(const CalendarSpec *c, char **p) { - char *buf = NULL; - size_t sz = 0; - FILE *f; - int r; - - assert(c); - assert(p); - - f = open_memstream(&buf, &sz); - if (!f) - return -ENOMEM; - - if (c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS) { - format_weekdays(f, c); - fputc(' ', f); - } - - format_chain(f, 4, c->year, false); - fputc('-', f); - format_chain(f, 2, c->month, false); - fputc('-', f); - format_chain(f, 2, c->day, false); - fputc(' ', f); - format_chain(f, 2, c->hour, false); - fputc(':', f); - format_chain(f, 2, c->minute, false); - fputc(':', f); - format_chain(f, 2, c->microsecond, true); - - if (c->utc) - fputs(" UTC", f); - else if (IN_SET(c->dst, 0, 1)) { - - /* If daylight saving is explicitly on or off, let's show the used timezone. */ - - tzset(); - - if (!isempty(tzname[c->dst])) { - fputc(' ', f); - fputs(tzname[c->dst], f); - } - } - - r = fflush_and_check(f); - if (r < 0) { - free(buf); - fclose(f); - return r; - } - - fclose(f); - - *p = buf; - return 0; -} - -static int parse_weekdays(const char **p, CalendarSpec *c) { - static const struct { - const char *name; - const int nr; - } day_nr[] = { - { "Monday", 0 }, - { "Mon", 0 }, - { "Tuesday", 1 }, - { "Tue", 1 }, - { "Wednesday", 2 }, - { "Wed", 2 }, - { "Thursday", 3 }, - { "Thu", 3 }, - { "Friday", 4 }, - { "Fri", 4 }, - { "Saturday", 5 }, - { "Sat", 5 }, - { "Sunday", 6 }, - { "Sun", 6 } - }; - - int l = -1; - bool first = true; - - assert(p); - assert(*p); - assert(c); - - for (;;) { - unsigned i; - - if (!first && **p == ' ') - return 0; - - for (i = 0; i < ELEMENTSOF(day_nr); i++) { - size_t skip; - - if (!startswith_no_case(*p, day_nr[i].name)) - continue; - - skip = strlen(day_nr[i].name); - - if ((*p)[skip] != '-' && - (*p)[skip] != '.' && - (*p)[skip] != ',' && - (*p)[skip] != ' ' && - (*p)[skip] != 0) - return -EINVAL; - - c->weekdays_bits |= 1 << day_nr[i].nr; - - if (l >= 0) { - int j; - - if (l > day_nr[i].nr) - return -EINVAL; - - for (j = l + 1; j < day_nr[i].nr; j++) - c->weekdays_bits |= 1 << j; - } - - *p += skip; - break; - } - - /* Couldn't find this prefix, so let's assume the - weekday was not specified and let's continue with - the date */ - if (i >= ELEMENTSOF(day_nr)) - return first ? 0 : -EINVAL; - - /* We reached the end of the string */ - if (**p == 0) - return 0; - - /* We reached the end of the weekday spec part */ - if (**p == ' ') { - *p += strspn(*p, " "); - return 0; - } - - if (**p == '.') { - if (l >= 0) - return -EINVAL; - - if ((*p)[1] != '.') - return -EINVAL; - - l = day_nr[i].nr; - *p += 1; - - /* Support ranges with "-" for backwards compatibility */ - } else if (**p == '-') { - if (l >= 0) - return -EINVAL; - - l = day_nr[i].nr; - } else - l = -1; - - *p += 1; - first = false; - } -} - -static int parse_component_decimal(const char **p, bool usec, unsigned long *res) { - unsigned long value; - const char *e = NULL; - char *ee = NULL; - int r; - - errno = 0; - value = strtoul(*p, &ee, 10); - if (errno > 0) - return -errno; - if (ee == *p) - return -EINVAL; - if ((unsigned long) (int) value != value) - return -ERANGE; - e = ee; - - if (usec) { - if (value * USEC_PER_SEC / USEC_PER_SEC != value) - return -ERANGE; - - value *= USEC_PER_SEC; - if (*e == '.') { - unsigned add; - - e++; - r = parse_fractional_part_u(&e, 6, &add); - if (r < 0) - return r; - - if (add + value < value) - return -ERANGE; - value += add; - } - } - - *p = e; - *res = value; - - return 0; -} - -static int const_chain(int value, CalendarComponent **c) { - CalendarComponent *cc = NULL; - - assert(c); - - cc = new0(CalendarComponent, 1); - if (!cc) - return -ENOMEM; - - cc->value = value; - cc->repeat = 0; - cc->next = *c; - - *c = cc; - - return 0; -} - -static int prepend_component(const char **p, bool usec, CalendarComponent **c) { - unsigned long i, value, range_end, range_inc, repeat = 0; - CalendarComponent *cc; - int r; - const char *e; - - assert(p); - assert(c); - - e = *p; - - r = parse_component_decimal(&e, usec, &value); - if (r < 0) - return r; - - if (*e == '/') { - e++; - r = parse_component_decimal(&e, usec, &repeat); - if (r < 0) - return r; - - if (repeat == 0) - return -ERANGE; - } else if (e[0] == '.' && e[1] == '.') { - e += 2; - r = parse_component_decimal(&e, usec, &range_end); - if (r < 0) - return r; - - if (value >= range_end) - return -EINVAL; - - range_inc = usec ? USEC_PER_SEC : 1; - - /* Don't allow impossibly large ranges... */ - if (range_end - value >= MAX_RANGE_LEN * range_inc) - return -EINVAL; - - /* ...or ranges with only a single element */ - if (range_end - value < range_inc) - return -EINVAL; - - for (i = value; i <= range_end; i += range_inc) { - r = const_chain(i, c); - if (r < 0) - return r; - } - } - - if (*e != 0 && *e != ' ' && *e != ',' && *e != '-' && *e != ':') - return -EINVAL; - - cc = new0(CalendarComponent, 1); - if (!cc) - return -ENOMEM; - - cc->value = value; - cc->repeat = repeat; - cc->next = *c; - - *p = e; - *c = cc; - - if (*e ==',') { - *p += 1; - return prepend_component(p, usec, c); - } - - return 0; -} - -static int parse_chain(const char **p, bool usec, CalendarComponent **c) { - const char *t; - CalendarComponent *cc = NULL; - int r; - - assert(p); - assert(c); - - t = *p; - - if (t[0] == '*') { - if (usec) { - r = const_chain(0, c); - if (r < 0) - return r; - (*c)->repeat = USEC_PER_SEC; - } else - *c = NULL; - - *p = t + 1; - return 0; - } - - r = prepend_component(&t, usec, &cc); - if (r < 0) { - free_chain(cc); - return r; - } - - *p = t; - *c = cc; - return 0; -} - -static int parse_date(const char **p, CalendarSpec *c) { - const char *t; - int r; - CalendarComponent *first, *second, *third; - - assert(p); - assert(*p); - assert(c); - - t = *p; - - if (*t == 0) - return 0; - - r = parse_chain(&t, false, &first); - if (r < 0) - return r; - - /* Already the end? A ':' as separator? In that case this was a time, not a date */ - if (*t == 0 || *t == ':') { - free_chain(first); - return 0; - } - - if (*t != '-') { - free_chain(first); - return -EINVAL; - } - - t++; - r = parse_chain(&t, false, &second); - if (r < 0) { - free_chain(first); - return r; - } - - /* Got two parts, hence it's month and day */ - if (*t == ' ' || *t == 0) { - *p = t + strspn(t, " "); - c->month = first; - c->day = second; - return 0; - } - - if (*t != '-') { - free_chain(first); - free_chain(second); - return -EINVAL; - } - - t++; - r = parse_chain(&t, false, &third); - if (r < 0) { - free_chain(first); - free_chain(second); - return r; - } - - /* Got tree parts, hence it is year, month and day */ - if (*t == ' ' || *t == 0) { - *p = t + strspn(t, " "); - c->year = first; - c->month = second; - c->day = third; - return 0; - } - - free_chain(first); - free_chain(second); - free_chain(third); - return -EINVAL; -} - -static int parse_calendar_time(const char **p, CalendarSpec *c) { - CalendarComponent *h = NULL, *m = NULL, *s = NULL; - const char *t; - int r; - - assert(p); - assert(*p); - assert(c); - - t = *p; - - if (*t == 0) { - /* If no time is specified at all, but a date of some - * kind, then this means 00:00:00 */ - if (c->day || c->weekdays_bits > 0) - goto null_hour; - - goto finish; - } - - r = parse_chain(&t, false, &h); - if (r < 0) - goto fail; - - if (*t != ':') { - r = -EINVAL; - goto fail; - } - - t++; - r = parse_chain(&t, false, &m); - if (r < 0) - goto fail; - - /* Already at the end? Then it's hours and minutes, and seconds are 0 */ - if (*t == 0) { - if (m != NULL) - goto null_second; - - goto finish; - } - - if (*t != ':') { - r = -EINVAL; - goto fail; - } - - t++; - r = parse_chain(&t, true, &s); - if (r < 0) - goto fail; - - /* At the end? Then it's hours, minutes and seconds */ - if (*t == 0) - goto finish; - - r = -EINVAL; - goto fail; - -null_hour: - r = const_chain(0, &h); - if (r < 0) - goto fail; - - r = const_chain(0, &m); - if (r < 0) - goto fail; - -null_second: - r = const_chain(0, &s); - if (r < 0) - goto fail; - -finish: - *p = t; - c->hour = h; - c->minute = m; - c->microsecond = s; - - return 0; - -fail: - free_chain(h); - free_chain(m); - free_chain(s); - return r; -} - -int calendar_spec_from_string(const char *p, CalendarSpec **spec) { - const char *utc; - CalendarSpec *c; - int r; - - assert(p); - assert(spec); - - if (isempty(p)) - return -EINVAL; - - c = new0(CalendarSpec, 1); - if (!c) - return -ENOMEM; - c->dst = -1; - - utc = endswith_no_case(p, " UTC"); - if (utc) { - c->utc = true; - p = strndupa(p, utc - p); - } else { - const char *e = NULL; - int j; - - tzset(); - - /* Check if the local timezone was specified? */ - for (j = 0; j <= 1; j++) { - if (isempty(tzname[j])) - continue; - - e = endswith_no_case(p, tzname[j]); - if(!e) - continue; - if (e == p) - continue; - if (e[-1] != ' ') - continue; - - break; - } - - /* Found one of the two timezones specified? */ - if (IN_SET(j, 0, 1)) { - p = strndupa(p, e - p - 1); - c->dst = j; - } - } - - if (strcaseeq(p, "minutely")) { - r = const_chain(0, &c->microsecond); - if (r < 0) - goto fail; - - } else if (strcaseeq(p, "hourly")) { - r = const_chain(0, &c->minute); - if (r < 0) - goto fail; - r = const_chain(0, &c->microsecond); - if (r < 0) - goto fail; - - } else if (strcaseeq(p, "daily")) { - r = const_chain(0, &c->hour); - if (r < 0) - goto fail; - r = const_chain(0, &c->minute); - if (r < 0) - goto fail; - r = const_chain(0, &c->microsecond); - if (r < 0) - goto fail; - - } else if (strcaseeq(p, "monthly")) { - r = const_chain(1, &c->day); - if (r < 0) - goto fail; - r = const_chain(0, &c->hour); - if (r < 0) - goto fail; - r = const_chain(0, &c->minute); - if (r < 0) - goto fail; - r = const_chain(0, &c->microsecond); - if (r < 0) - goto fail; - - } else if (strcaseeq(p, "annually") || - strcaseeq(p, "yearly") || - strcaseeq(p, "anually") /* backwards compatibility */ ) { - - r = const_chain(1, &c->month); - if (r < 0) - goto fail; - r = const_chain(1, &c->day); - if (r < 0) - goto fail; - r = const_chain(0, &c->hour); - if (r < 0) - goto fail; - r = const_chain(0, &c->minute); - if (r < 0) - goto fail; - r = const_chain(0, &c->microsecond); - if (r < 0) - goto fail; - - } else if (strcaseeq(p, "weekly")) { - - c->weekdays_bits = 1; - - r = const_chain(0, &c->hour); - if (r < 0) - goto fail; - r = const_chain(0, &c->minute); - if (r < 0) - goto fail; - r = const_chain(0, &c->microsecond); - if (r < 0) - goto fail; - - } else if (strcaseeq(p, "quarterly")) { - - r = const_chain(1, &c->month); - if (r < 0) - goto fail; - r = const_chain(4, &c->month); - if (r < 0) - goto fail; - r = const_chain(7, &c->month); - if (r < 0) - goto fail; - r = const_chain(10, &c->month); - if (r < 0) - goto fail; - r = const_chain(1, &c->day); - if (r < 0) - goto fail; - r = const_chain(0, &c->hour); - if (r < 0) - goto fail; - r = const_chain(0, &c->minute); - if (r < 0) - goto fail; - r = const_chain(0, &c->microsecond); - if (r < 0) - goto fail; - - } else if (strcaseeq(p, "biannually") || - strcaseeq(p, "bi-annually") || - strcaseeq(p, "semiannually") || - strcaseeq(p, "semi-annually")) { - - r = const_chain(1, &c->month); - if (r < 0) - goto fail; - r = const_chain(7, &c->month); - if (r < 0) - goto fail; - r = const_chain(1, &c->day); - if (r < 0) - goto fail; - r = const_chain(0, &c->hour); - if (r < 0) - goto fail; - r = const_chain(0, &c->minute); - if (r < 0) - goto fail; - r = const_chain(0, &c->microsecond); - if (r < 0) - goto fail; - - } else { - r = parse_weekdays(&p, c); - if (r < 0) - goto fail; - - r = parse_date(&p, c); - if (r < 0) - goto fail; - - r = parse_calendar_time(&p, c); - if (r < 0) - goto fail; - - if (*p != 0) { - r = -EINVAL; - goto fail; - } - } - - r = calendar_spec_normalize(c); - if (r < 0) - goto fail; - - if (!calendar_spec_valid(c)) { - r = -EINVAL; - goto fail; - } - - *spec = c; - return 0; - -fail: - calendar_spec_free(c); - return r; -} - -static int find_matching_component(const CalendarComponent *c, int *val) { - const CalendarComponent *n; - int d = -1; - bool d_set = false; - int r; - - assert(val); - - if (!c) - return 0; - - while (c) { - n = c->next; - - if (c->value >= *val) { - - if (!d_set || c->value < d) { - d = c->value; - d_set = true; - } - - } else if (c->repeat > 0) { - int k; - - k = c->value + c->repeat * ((*val - c->value + c->repeat -1) / c->repeat); - - if (!d_set || k < d) { - d = k; - d_set = true; - } - } - - c = n; - } - - if (!d_set) - return -ENOENT; - - r = *val != d; - *val = d; - return r; -} - -static bool tm_out_of_bounds(const struct tm *tm, bool utc) { - struct tm t; - assert(tm); - - t = *tm; - - if (mktime_or_timegm(&t, utc) == (time_t) -1) - return true; - - /* Did any normalization take place? If so, it was out of bounds before */ - return - t.tm_year != tm->tm_year || - t.tm_mon != tm->tm_mon || - t.tm_mday != tm->tm_mday || - t.tm_hour != tm->tm_hour || - t.tm_min != tm->tm_min || - t.tm_sec != tm->tm_sec; -} - -static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) { - struct tm t; - int k; - - if (weekdays_bits < 0 || weekdays_bits >= BITS_WEEKDAYS) - return true; - - t = *tm; - if (mktime_or_timegm(&t, utc) == (time_t) -1) - return false; - - k = t.tm_wday == 0 ? 6 : t.tm_wday - 1; - return (weekdays_bits & (1 << k)); -} - -static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { - struct tm c; - int tm_usec; - int r; - - assert(spec); - assert(tm); - - c = *tm; - tm_usec = *usec; - - for (;;) { - /* Normalize the current date */ - (void) mktime_or_timegm(&c, spec->utc); - c.tm_isdst = spec->dst; - - c.tm_year += 1900; - r = find_matching_component(spec->year, &c.tm_year); - c.tm_year -= 1900; - - if (r > 0) { - c.tm_mon = 0; - c.tm_mday = 1; - c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; - } - if (r < 0) - return r; - if (tm_out_of_bounds(&c, spec->utc)) - return -ENOENT; - - c.tm_mon += 1; - r = find_matching_component(spec->month, &c.tm_mon); - c.tm_mon -= 1; - - if (r > 0) { - c.tm_mday = 1; - c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; - } - if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { - c.tm_year++; - c.tm_mon = 0; - c.tm_mday = 1; - c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; - continue; - } - - r = find_matching_component(spec->day, &c.tm_mday); - if (r > 0) - c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; - if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { - c.tm_mon++; - c.tm_mday = 1; - c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; - continue; - } - - if (!matches_weekday(spec->weekdays_bits, &c, spec->utc)) { - c.tm_mday++; - c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; - continue; - } - - r = find_matching_component(spec->hour, &c.tm_hour); - if (r > 0) - c.tm_min = c.tm_sec = tm_usec = 0; - if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { - c.tm_mday++; - c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; - continue; - } - - r = find_matching_component(spec->minute, &c.tm_min); - if (r > 0) - c.tm_sec = tm_usec = 0; - if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { - c.tm_hour++; - c.tm_min = c.tm_sec = tm_usec = 0; - continue; - } - - c.tm_sec = c.tm_sec * USEC_PER_SEC + tm_usec; - r = find_matching_component(spec->microsecond, &c.tm_sec); - tm_usec = c.tm_sec % USEC_PER_SEC; - c.tm_sec /= USEC_PER_SEC; - - if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { - c.tm_min++; - c.tm_sec = tm_usec = 0; - continue; - } - - *tm = c; - *usec = tm_usec; - return 0; - } -} - -int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) { - struct tm tm; - time_t t; - int r; - usec_t tm_usec; - - assert(spec); - assert(next); - - usec++; - t = (time_t) (usec / USEC_PER_SEC); - assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc)); - tm_usec = usec % USEC_PER_SEC; - - r = find_next(spec, &tm, &tm_usec); - if (r < 0) - return r; - - t = mktime_or_timegm(&tm, spec->utc); - if (t == (time_t) -1) - return -EINVAL; - - *next = (usec_t) t * USEC_PER_SEC + tm_usec; - return 0; -} diff --git a/src/basic/calendarspec.h b/src/basic/calendarspec.h deleted file mode 100644 index c6087228fd..0000000000 --- a/src/basic/calendarspec.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 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/>. -***/ - -/* A structure for specifying (possibly repetitive) points in calendar - * time, a la cron */ - -#include <stdbool.h> - -#include "time-util.h" -#include "util.h" - -typedef struct CalendarComponent { - int value; - int repeat; - - struct CalendarComponent *next; -} CalendarComponent; - -typedef struct CalendarSpec { - int weekdays_bits; - bool utc; - int dst; - - CalendarComponent *year; - CalendarComponent *month; - CalendarComponent *day; - - CalendarComponent *hour; - CalendarComponent *minute; - CalendarComponent *microsecond; -} CalendarSpec; - -void calendar_spec_free(CalendarSpec *c); - -int calendar_spec_normalize(CalendarSpec *spec); -bool calendar_spec_valid(CalendarSpec *spec); - -int calendar_spec_to_string(const CalendarSpec *spec, char **p); -int calendar_spec_from_string(const char *p, CalendarSpec **spec); - -int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next); diff --git a/src/basic/cap-list.c b/src/basic/cap-list.c deleted file mode 100644 index 3e773a06f5..0000000000 --- a/src/basic/cap-list.c +++ /dev/null @@ -1,66 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 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 <errno.h> -#include <string.h> - -#include "cap-list.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "util.h" - -static const struct capability_name* lookup_capability(register const char *str, register unsigned int len); - -#include "cap-from-name.h" -#include "cap-to-name.h" - -const char *capability_to_name(int id) { - - if (id < 0) - return NULL; - - if (id >= (int) ELEMENTSOF(capability_names)) - return NULL; - - return capability_names[id]; -} - -int capability_from_name(const char *name) { - const struct capability_name *sc; - int r, i; - - assert(name); - - /* Try to parse numeric capability */ - r = safe_atoi(name, &i); - if (r >= 0 && i >= 0) - return i; - - /* Try to parse string capability */ - sc = lookup_capability(name, strlen(name)); - if (!sc) - return -EINVAL; - - return sc->id; -} - -int capability_list_length(void) { - return (int) ELEMENTSOF(capability_names); -} diff --git a/src/basic/cap-list.h b/src/basic/cap-list.h deleted file mode 100644 index c1f6b94ad3..0000000000 --- a/src/basic/cap-list.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 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/>. -***/ - -const char *capability_to_name(int id); -int capability_from_name(const char *name); -int capability_list_length(void); diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c deleted file mode 100644 index c3de20a0e8..0000000000 --- a/src/basic/capability-util.c +++ /dev/null @@ -1,363 +0,0 @@ -/*** - 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 <errno.h> -#include <grp.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/capability.h> -#include <sys/prctl.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "capability-util.h" -#include "fileio.h" -#include "log.h" -#include "macro.h" -#include "parse-util.h" -#include "user-util.h" -#include "util.h" - -int have_effective_cap(int value) { - _cleanup_cap_free_ cap_t cap; - cap_flag_value_t fv; - - cap = cap_get_proc(); - if (!cap) - return -errno; - - if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0) - return -errno; - else - return fv == CAP_SET; -} - -unsigned long cap_last_cap(void) { - static thread_local unsigned long saved; - static thread_local bool valid = false; - _cleanup_free_ char *content = NULL; - unsigned long p = 0; - int r; - - if (valid) - return saved; - - /* available since linux-3.2 */ - r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content); - if (r >= 0) { - r = safe_atolu(content, &p); - if (r >= 0) { - saved = p; - valid = true; - return p; - } - } - - /* fall back to syscall-probing for pre linux-3.2 */ - p = (unsigned long) CAP_LAST_CAP; - - if (prctl(PR_CAPBSET_READ, p) < 0) { - - /* Hmm, look downwards, until we find one that - * works */ - for (p--; p > 0; p --) - if (prctl(PR_CAPBSET_READ, p) >= 0) - break; - - } else { - - /* Hmm, look upwards, until we find one that doesn't - * work */ - for (;; p++) - if (prctl(PR_CAPBSET_READ, p+1) < 0) - break; - } - - saved = p; - valid = true; - - return p; -} - -int capability_update_inherited_set(cap_t caps, uint64_t set) { - unsigned long i; - - /* Add capabilities in the set to the inherited caps. Do not apply - * them yet. */ - - for (i = 0; i < cap_last_cap(); i++) { - - if (set & (UINT64_C(1) << i)) { - cap_value_t v; - - v = (cap_value_t) i; - - /* Make the capability inheritable. */ - if (cap_set_flag(caps, CAP_INHERITABLE, 1, &v, CAP_SET) < 0) - return -errno; - } - } - - return 0; -} - -int capability_ambient_set_apply(uint64_t set, bool also_inherit) { - unsigned long i; - _cleanup_cap_free_ cap_t caps = NULL; - - /* Add the capabilities to the ambient set. */ - - if (also_inherit) { - int r; - caps = cap_get_proc(); - if (!caps) - return -errno; - - r = capability_update_inherited_set(caps, set); - if (r < 0) - return -errno; - - if (cap_set_proc(caps) < 0) - return -errno; - } - - for (i = 0; i < cap_last_cap(); i++) { - - if (set & (UINT64_C(1) << i)) { - - /* Add the capability to the ambient set. */ - if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) < 0) - return -errno; - } - } - - return 0; -} - -int capability_bounding_set_drop(uint64_t keep, bool right_now) { - _cleanup_cap_free_ cap_t after_cap = NULL; - cap_flag_value_t fv; - unsigned long i; - int r; - - /* If we are run as PID 1 we will lack CAP_SETPCAP by default - * in the effective set (yes, the kernel drops that when - * executing init!), so get it back temporarily so that we can - * call PR_CAPBSET_DROP. */ - - after_cap = cap_get_proc(); - if (!after_cap) - return -errno; - - if (cap_get_flag(after_cap, CAP_SETPCAP, CAP_EFFECTIVE, &fv) < 0) - return -errno; - - if (fv != CAP_SET) { - _cleanup_cap_free_ cap_t temp_cap = NULL; - static const cap_value_t v = CAP_SETPCAP; - - temp_cap = cap_dup(after_cap); - if (!temp_cap) { - r = -errno; - goto finish; - } - - if (cap_set_flag(temp_cap, CAP_EFFECTIVE, 1, &v, CAP_SET) < 0) { - r = -errno; - goto finish; - } - - if (cap_set_proc(temp_cap) < 0) { - r = -errno; - goto finish; - } - } - - for (i = 0; i <= cap_last_cap(); i++) { - - if (!(keep & (UINT64_C(1) << i))) { - cap_value_t v; - - /* Drop it from the bounding set */ - if (prctl(PR_CAPBSET_DROP, i) < 0) { - r = -errno; - goto finish; - } - v = (cap_value_t) i; - - /* Also drop it from the inheritable set, so - * that anything we exec() loses the - * capability for good. */ - if (cap_set_flag(after_cap, CAP_INHERITABLE, 1, &v, CAP_CLEAR) < 0) { - r = -errno; - goto finish; - } - - /* If we shall apply this right now drop it - * also from our own capability sets. */ - if (right_now) { - if (cap_set_flag(after_cap, CAP_PERMITTED, 1, &v, CAP_CLEAR) < 0 || - cap_set_flag(after_cap, CAP_EFFECTIVE, 1, &v, CAP_CLEAR) < 0) { - r = -errno; - goto finish; - } - } - } - } - - r = 0; - -finish: - if (cap_set_proc(after_cap) < 0) - return -errno; - - return r; -} - -static int drop_from_file(const char *fn, uint64_t keep) { - int r, k; - uint32_t hi, lo; - uint64_t current, after; - char *p; - - r = read_one_line_file(fn, &p); - if (r < 0) - return r; - - assert_cc(sizeof(hi) == sizeof(unsigned)); - assert_cc(sizeof(lo) == sizeof(unsigned)); - - k = sscanf(p, "%u %u", &lo, &hi); - free(p); - - if (k != 2) - return -EIO; - - current = (uint64_t) lo | ((uint64_t) hi << 32ULL); - after = current & keep; - - if (current == after) - return 0; - - lo = (unsigned) (after & 0xFFFFFFFFULL); - hi = (unsigned) ((after >> 32ULL) & 0xFFFFFFFFULL); - - if (asprintf(&p, "%u %u", lo, hi) < 0) - return -ENOMEM; - - r = write_string_file(fn, p, WRITE_STRING_FILE_CREATE); - free(p); - - return r; -} - -int capability_bounding_set_drop_usermode(uint64_t keep) { - int r; - - r = drop_from_file("/proc/sys/kernel/usermodehelper/inheritable", keep); - if (r < 0) - return r; - - r = drop_from_file("/proc/sys/kernel/usermodehelper/bset", keep); - if (r < 0) - return r; - - return r; -} - -int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) { - _cleanup_cap_free_ cap_t d = NULL; - unsigned i, j = 0; - int r; - - /* Unfortunately we cannot leave privilege dropping to PID 1 - * here, since we want to run as user but want to keep some - * capabilities. Since file capabilities have been introduced - * this cannot be done across exec() anymore, unless our - * binary has the capability configured in the file system, - * which we want to avoid. */ - - if (setresgid(gid, gid, gid) < 0) - return log_error_errno(errno, "Failed to change group ID: %m"); - - r = maybe_setgroups(0, NULL); - if (r < 0) - return log_error_errno(r, "Failed to drop auxiliary groups list: %m"); - - /* Ensure we keep the permitted caps across the setresuid() */ - if (prctl(PR_SET_KEEPCAPS, 1) < 0) - return log_error_errno(errno, "Failed to enable keep capabilities flag: %m"); - - r = setresuid(uid, uid, uid); - if (r < 0) - return log_error_errno(errno, "Failed to change user ID: %m"); - - if (prctl(PR_SET_KEEPCAPS, 0) < 0) - return log_error_errno(errno, "Failed to disable keep capabilities flag: %m"); - - /* Drop all caps from the bounding set, except the ones we want */ - r = capability_bounding_set_drop(keep_capabilities, true); - if (r < 0) - return log_error_errno(r, "Failed to drop capabilities: %m"); - - /* Now upgrade the permitted caps we still kept to effective caps */ - d = cap_init(); - if (!d) - return log_oom(); - - if (keep_capabilities) { - cap_value_t bits[u64log2(keep_capabilities) + 1]; - - for (i = 0; i < ELEMENTSOF(bits); i++) - if (keep_capabilities & (1ULL << i)) - bits[j++] = i; - - /* use enough bits */ - assert(i == 64 || (keep_capabilities >> i) == 0); - /* don't use too many bits */ - assert(keep_capabilities & (1ULL << (i - 1))); - - if (cap_set_flag(d, CAP_EFFECTIVE, j, bits, CAP_SET) < 0 || - cap_set_flag(d, CAP_PERMITTED, j, bits, CAP_SET) < 0) - return log_error_errno(errno, "Failed to enable capabilities bits: %m"); - - if (cap_set_proc(d) < 0) - return log_error_errno(errno, "Failed to increase capabilities: %m"); - } - - return 0; -} - -int drop_capability(cap_value_t cv) { - _cleanup_cap_free_ cap_t tmp_cap = NULL; - - tmp_cap = cap_get_proc(); - if (!tmp_cap) - return -errno; - - if ((cap_set_flag(tmp_cap, CAP_INHERITABLE, 1, &cv, CAP_CLEAR) < 0) || - (cap_set_flag(tmp_cap, CAP_PERMITTED, 1, &cv, CAP_CLEAR) < 0) || - (cap_set_flag(tmp_cap, CAP_EFFECTIVE, 1, &cv, CAP_CLEAR) < 0)) - return -errno; - - if (cap_set_proc(tmp_cap) < 0) - return -errno; - - return 0; -} diff --git a/src/basic/capability-util.h b/src/basic/capability-util.h deleted file mode 100644 index 35a896e229..0000000000 --- a/src/basic/capability-util.h +++ /dev/null @@ -1,57 +0,0 @@ -#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 <stdint.h> -#include <sys/capability.h> -#include <sys/types.h> - -#include "macro.h" -#include "util.h" - -#define CAP_ALL (uint64_t) -1 - -unsigned long cap_last_cap(void); -int have_effective_cap(int value); -int capability_bounding_set_drop(uint64_t keep, bool right_now); -int capability_bounding_set_drop_usermode(uint64_t keep); - -int capability_ambient_set_apply(uint64_t set, bool also_inherit); -int capability_update_inherited_set(cap_t caps, uint64_t ambient_set); - -int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities); - -int drop_capability(cap_value_t cv); - -DEFINE_TRIVIAL_CLEANUP_FUNC(cap_t, cap_free); -#define _cleanup_cap_free_ _cleanup_(cap_freep) - -static inline void cap_free_charpp(char **p) { - if (*p) - cap_free(*p); -} -#define _cleanup_cap_free_charp_ _cleanup_(cap_free_charpp) - -static inline bool cap_test_all(uint64_t caps) { - uint64_t m; - m = (UINT64_C(1) << (cap_last_cap() + 1)) - 1; - return (caps & m) == m; -} diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c deleted file mode 100644 index 134e6e3664..0000000000 --- a/src/basic/cgroup-util.c +++ /dev/null @@ -1,2541 +0,0 @@ -/*** - 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 <errno.h> -#include <ftw.h> -#include <limits.h> -#include <signal.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/statfs.h> -#include <sys/types.h> -#include <sys/xattr.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "cgroup-util.h" -#include "def.h" -#include "dirent-util.h" -#include "extract-word.h" -#include "fd-util.h" -#include "fileio.h" -#include "formats-util.h" -#include "fs-util.h" -#include "log.h" -#include "login-util.h" -#include "macro.h" -#include "missing.h" -#include "mkdir.h" -#include "parse-util.h" -#include "path-util.h" -#include "proc-cmdline.h" -#include "process-util.h" -#include "set.h" -#include "special.h" -#include "stat-util.h" -#include "stdio-util.h" -#include "string-table.h" -#include "string-util.h" -#include "unit-name.h" -#include "user-util.h" - -int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) { - _cleanup_free_ char *fs = NULL; - FILE *f; - int r; - - assert(_f); - - r = cg_get_path(controller, path, "cgroup.procs", &fs); - if (r < 0) - return r; - - f = fopen(fs, "re"); - if (!f) - return -errno; - - *_f = f; - return 0; -} - -int cg_read_pid(FILE *f, pid_t *_pid) { - unsigned long ul; - - /* Note that the cgroup.procs might contain duplicates! See - * cgroups.txt for details. */ - - assert(f); - assert(_pid); - - errno = 0; - if (fscanf(f, "%lu", &ul) != 1) { - - if (feof(f)) - return 0; - - return errno > 0 ? -errno : -EIO; - } - - if (ul <= 0) - return -EIO; - - *_pid = (pid_t) ul; - return 1; -} - -int cg_read_event(const char *controller, const char *path, const char *event, - char **val) -{ - _cleanup_free_ char *events = NULL, *content = NULL; - char *p, *line; - int r; - - r = cg_get_path(controller, path, "cgroup.events", &events); - if (r < 0) - return r; - - r = read_full_file(events, &content, NULL); - if (r < 0) - return r; - - p = content; - while ((line = strsep(&p, "\n"))) { - char *key; - - key = strsep(&line, " "); - if (!key || !line) - return -EINVAL; - - if (strcmp(key, event)) - continue; - - *val = strdup(line); - return 0; - } - - return -ENOENT; -} - -bool cg_ns_supported(void) { - static thread_local int enabled = -1; - - if (enabled >= 0) - return enabled; - - if (access("/proc/self/ns/cgroup", F_OK) == 0) - enabled = 1; - else - enabled = 0; - - return enabled; -} - -int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) { - _cleanup_free_ char *fs = NULL; - int r; - DIR *d; - - assert(_d); - - /* This is not recursive! */ - - r = cg_get_path(controller, path, NULL, &fs); - if (r < 0) - return r; - - d = opendir(fs); - if (!d) - return -errno; - - *_d = d; - return 0; -} - -int cg_read_subgroup(DIR *d, char **fn) { - struct dirent *de; - - assert(d); - assert(fn); - - FOREACH_DIRENT_ALL(de, d, return -errno) { - char *b; - - if (de->d_type != DT_DIR) - continue; - - if (streq(de->d_name, ".") || - streq(de->d_name, "..")) - continue; - - b = strdup(de->d_name); - if (!b) - return -ENOMEM; - - *fn = b; - return 1; - } - - return 0; -} - -int cg_rmdir(const char *controller, const char *path) { - _cleanup_free_ char *p = NULL; - int r; - - r = cg_get_path(controller, path, NULL, &p); - if (r < 0) - return r; - - r = rmdir(p); - if (r < 0 && errno != ENOENT) - return -errno; - - return 0; -} - -int cg_kill( - const char *controller, - const char *path, - int sig, - CGroupFlags flags, - Set *s, - cg_kill_log_func_t log_kill, - void *userdata) { - - _cleanup_set_free_ Set *allocated_set = NULL; - bool done = false; - int r, ret = 0; - pid_t my_pid; - - assert(sig >= 0); - - /* Don't send SIGCONT twice. Also, SIGKILL always works even when process is suspended, hence don't send - * SIGCONT on SIGKILL. */ - if (IN_SET(sig, SIGCONT, SIGKILL)) - flags &= ~CGROUP_SIGCONT; - - /* This goes through the tasks list and kills them all. This - * is repeated until no further processes are added to the - * tasks list, to properly handle forking processes */ - - if (!s) { - s = allocated_set = set_new(NULL); - if (!s) - return -ENOMEM; - } - - my_pid = getpid(); - - do { - _cleanup_fclose_ FILE *f = NULL; - pid_t pid = 0; - done = true; - - r = cg_enumerate_processes(controller, path, &f); - if (r < 0) { - if (ret >= 0 && r != -ENOENT) - return r; - - return ret; - } - - while ((r = cg_read_pid(f, &pid)) > 0) { - - if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid) - continue; - - if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid)) - continue; - - if (log_kill) - log_kill(pid, sig, userdata); - - /* If we haven't killed this process yet, kill - * it */ - if (kill(pid, sig) < 0) { - if (ret >= 0 && errno != ESRCH) - ret = -errno; - } else { - if (flags & CGROUP_SIGCONT) - (void) kill(pid, SIGCONT); - - if (ret == 0) - ret = 1; - } - - done = false; - - r = set_put(s, PID_TO_PTR(pid)); - if (r < 0) { - if (ret >= 0) - return r; - - return ret; - } - } - - if (r < 0) { - if (ret >= 0) - return r; - - return ret; - } - - /* To avoid racing against processes which fork - * quicker than we can kill them we repeat this until - * no new pids need to be killed. */ - - } while (!done); - - return ret; -} - -int cg_kill_recursive( - const char *controller, - const char *path, - int sig, - CGroupFlags flags, - Set *s, - cg_kill_log_func_t log_kill, - void *userdata) { - - _cleanup_set_free_ Set *allocated_set = NULL; - _cleanup_closedir_ DIR *d = NULL; - int r, ret; - char *fn; - - assert(path); - assert(sig >= 0); - - if (!s) { - s = allocated_set = set_new(NULL); - if (!s) - return -ENOMEM; - } - - ret = cg_kill(controller, path, sig, flags, s, log_kill, userdata); - - r = cg_enumerate_subgroups(controller, path, &d); - if (r < 0) { - if (ret >= 0 && r != -ENOENT) - return r; - - return ret; - } - - while ((r = cg_read_subgroup(d, &fn)) > 0) { - _cleanup_free_ char *p = NULL; - - p = strjoin(path, "/", fn, NULL); - free(fn); - if (!p) - return -ENOMEM; - - r = cg_kill_recursive(controller, p, sig, flags, s, log_kill, userdata); - if (r != 0 && ret >= 0) - ret = r; - } - if (ret >= 0 && r < 0) - ret = r; - - if (flags & CGROUP_REMOVE) { - r = cg_rmdir(controller, path); - if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY) - return r; - } - - return ret; -} - -int cg_migrate( - const char *cfrom, - const char *pfrom, - const char *cto, - const char *pto, - CGroupFlags flags) { - - bool done = false; - _cleanup_set_free_ Set *s = NULL; - int r, ret = 0; - pid_t my_pid; - - assert(cfrom); - assert(pfrom); - assert(cto); - assert(pto); - - s = set_new(NULL); - if (!s) - return -ENOMEM; - - my_pid = getpid(); - - do { - _cleanup_fclose_ FILE *f = NULL; - pid_t pid = 0; - done = true; - - r = cg_enumerate_processes(cfrom, pfrom, &f); - if (r < 0) { - if (ret >= 0 && r != -ENOENT) - return r; - - return ret; - } - - while ((r = cg_read_pid(f, &pid)) > 0) { - - /* This might do weird stuff if we aren't a - * single-threaded program. However, we - * luckily know we are not */ - if ((flags & CGROUP_IGNORE_SELF) && pid == my_pid) - continue; - - if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid)) - continue; - - /* Ignore kernel threads. Since they can only - * exist in the root cgroup, we only check for - * them there. */ - if (cfrom && - (isempty(pfrom) || path_equal(pfrom, "/")) && - is_kernel_thread(pid) > 0) - continue; - - r = cg_attach(cto, pto, pid); - if (r < 0) { - if (ret >= 0 && r != -ESRCH) - ret = r; - } else if (ret == 0) - ret = 1; - - done = false; - - r = set_put(s, PID_TO_PTR(pid)); - if (r < 0) { - if (ret >= 0) - return r; - - return ret; - } - } - - if (r < 0) { - if (ret >= 0) - return r; - - return ret; - } - } while (!done); - - return ret; -} - -int cg_migrate_recursive( - const char *cfrom, - const char *pfrom, - const char *cto, - const char *pto, - CGroupFlags flags) { - - _cleanup_closedir_ DIR *d = NULL; - int r, ret = 0; - char *fn; - - assert(cfrom); - assert(pfrom); - assert(cto); - assert(pto); - - ret = cg_migrate(cfrom, pfrom, cto, pto, flags); - - r = cg_enumerate_subgroups(cfrom, pfrom, &d); - if (r < 0) { - if (ret >= 0 && r != -ENOENT) - return r; - - return ret; - } - - while ((r = cg_read_subgroup(d, &fn)) > 0) { - _cleanup_free_ char *p = NULL; - - p = strjoin(pfrom, "/", fn, NULL); - free(fn); - if (!p) - return -ENOMEM; - - r = cg_migrate_recursive(cfrom, p, cto, pto, flags); - if (r != 0 && ret >= 0) - ret = r; - } - - if (r < 0 && ret >= 0) - ret = r; - - if (flags & CGROUP_REMOVE) { - r = cg_rmdir(cfrom, pfrom); - if (r < 0 && ret >= 0 && r != -ENOENT && r != -EBUSY) - return r; - } - - return ret; -} - -int cg_migrate_recursive_fallback( - const char *cfrom, - const char *pfrom, - const char *cto, - const char *pto, - CGroupFlags flags) { - - int r; - - assert(cfrom); - assert(pfrom); - assert(cto); - assert(pto); - - r = cg_migrate_recursive(cfrom, pfrom, cto, pto, flags); - if (r < 0) { - char prefix[strlen(pto) + 1]; - - /* This didn't work? Then let's try all prefixes of the destination */ - - PATH_FOREACH_PREFIX(prefix, pto) { - int q; - - q = cg_migrate_recursive(cfrom, pfrom, cto, prefix, flags); - if (q >= 0) - return q; - } - } - - return r; -} - -static const char *controller_to_dirname(const char *controller) { - const char *e; - - assert(controller); - - /* Converts a controller name to the directory name below - * /sys/fs/cgroup/ we want to mount it to. Effectively, this - * just cuts off the name= prefixed used for named - * hierarchies, if it is specified. */ - - e = startswith(controller, "name="); - if (e) - return e; - - return controller; -} - -static int join_path_legacy(const char *controller, const char *path, const char *suffix, char **fs) { - const char *dn; - char *t = NULL; - - assert(fs); - assert(controller); - - dn = controller_to_dirname(controller); - - if (isempty(path) && isempty(suffix)) - t = strappend("/sys/fs/cgroup/", dn); - else if (isempty(path)) - t = strjoin("/sys/fs/cgroup/", dn, "/", suffix, NULL); - else if (isempty(suffix)) - t = strjoin("/sys/fs/cgroup/", dn, "/", path, NULL); - else - t = strjoin("/sys/fs/cgroup/", dn, "/", path, "/", suffix, NULL); - if (!t) - return -ENOMEM; - - *fs = t; - return 0; -} - -static int join_path_unified(const char *path, const char *suffix, char **fs) { - char *t; - - assert(fs); - - if (isempty(path) && isempty(suffix)) - t = strdup("/sys/fs/cgroup"); - else if (isempty(path)) - t = strappend("/sys/fs/cgroup/", suffix); - else if (isempty(suffix)) - t = strappend("/sys/fs/cgroup/", path); - else - t = strjoin("/sys/fs/cgroup/", path, "/", suffix, NULL); - if (!t) - return -ENOMEM; - - *fs = t; - return 0; -} - -int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) { - int unified, r; - - assert(fs); - - if (!controller) { - char *t; - - /* If no controller is specified, we return the path - * *below* the controllers, without any prefix. */ - - if (!path && !suffix) - return -EINVAL; - - if (!suffix) - t = strdup(path); - else if (!path) - t = strdup(suffix); - else - t = strjoin(path, "/", suffix, NULL); - if (!t) - return -ENOMEM; - - *fs = path_kill_slashes(t); - return 0; - } - - if (!cg_controller_is_valid(controller)) - return -EINVAL; - - unified = cg_all_unified(); - if (unified < 0) - return unified; - - if (unified > 0) - r = join_path_unified(path, suffix, fs); - else - r = join_path_legacy(controller, path, suffix, fs); - if (r < 0) - return r; - - path_kill_slashes(*fs); - return 0; -} - -static int controller_is_accessible(const char *controller) { - int unified; - - assert(controller); - - /* Checks whether a specific controller is accessible, - * i.e. its hierarchy mounted. In the unified hierarchy all - * controllers are considered accessible, except for the named - * hierarchies */ - - if (!cg_controller_is_valid(controller)) - return -EINVAL; - - unified = cg_all_unified(); - if (unified < 0) - return unified; - if (unified > 0) { - /* We don't support named hierarchies if we are using - * the unified hierarchy. */ - - if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) - return 0; - - if (startswith(controller, "name=")) - return -EOPNOTSUPP; - - } else { - const char *cc, *dn; - - dn = controller_to_dirname(controller); - cc = strjoina("/sys/fs/cgroup/", dn); - - if (laccess(cc, F_OK) < 0) - return -errno; - } - - return 0; -} - -int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) { - int r; - - assert(controller); - assert(fs); - - /* Check if the specified controller is actually accessible */ - r = controller_is_accessible(controller); - if (r < 0) - return r; - - return cg_get_path(controller, path, suffix, fs); -} - -static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { - assert(path); - assert(sb); - assert(ftwbuf); - - if (typeflag != FTW_DP) - return 0; - - if (ftwbuf->level < 1) - return 0; - - (void) rmdir(path); - return 0; -} - -int cg_trim(const char *controller, const char *path, bool delete_root) { - _cleanup_free_ char *fs = NULL; - int r = 0; - - assert(path); - - r = cg_get_path(controller, path, NULL, &fs); - if (r < 0) - return r; - - errno = 0; - if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) { - if (errno == ENOENT) - r = 0; - else if (errno > 0) - r = -errno; - else - r = -EIO; - } - - if (delete_root) { - if (rmdir(fs) < 0 && errno != ENOENT) - return -errno; - } - - return r; -} - -int cg_create(const char *controller, const char *path) { - _cleanup_free_ char *fs = NULL; - int r; - - r = cg_get_path_and_check(controller, path, NULL, &fs); - if (r < 0) - return r; - - r = mkdir_parents(fs, 0755); - if (r < 0) - return r; - - if (mkdir(fs, 0755) < 0) { - - if (errno == EEXIST) - return 0; - - return -errno; - } - - return 1; -} - -int cg_create_and_attach(const char *controller, const char *path, pid_t pid) { - int r, q; - - assert(pid >= 0); - - r = cg_create(controller, path); - if (r < 0) - return r; - - q = cg_attach(controller, path, pid); - if (q < 0) - return q; - - /* This does not remove the cgroup on failure */ - return r; -} - -int cg_attach(const char *controller, const char *path, pid_t pid) { - _cleanup_free_ char *fs = NULL; - char c[DECIMAL_STR_MAX(pid_t) + 2]; - int r; - - assert(path); - assert(pid >= 0); - - r = cg_get_path_and_check(controller, path, "cgroup.procs", &fs); - if (r < 0) - return r; - - if (pid == 0) - pid = getpid(); - - xsprintf(c, PID_FMT "\n", pid); - - return write_string_file(fs, c, 0); -} - -int cg_attach_fallback(const char *controller, const char *path, pid_t pid) { - int r; - - assert(controller); - assert(path); - assert(pid >= 0); - - r = cg_attach(controller, path, pid); - if (r < 0) { - char prefix[strlen(path) + 1]; - - /* This didn't work? Then let's try all prefixes of - * the destination */ - - PATH_FOREACH_PREFIX(prefix, path) { - int q; - - q = cg_attach(controller, prefix, pid); - if (q >= 0) - return q; - } - } - - return r; -} - -int cg_set_group_access( - const char *controller, - const char *path, - mode_t mode, - uid_t uid, - gid_t gid) { - - _cleanup_free_ char *fs = NULL; - int r; - - if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID) - return 0; - - if (mode != MODE_INVALID) - mode &= 0777; - - r = cg_get_path(controller, path, NULL, &fs); - if (r < 0) - return r; - - return chmod_and_chown(fs, mode, uid, gid); -} - -int cg_set_task_access( - const char *controller, - const char *path, - mode_t mode, - uid_t uid, - gid_t gid) { - - _cleanup_free_ char *fs = NULL, *procs = NULL; - int r, unified; - - assert(path); - - if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID) - return 0; - - if (mode != MODE_INVALID) - mode &= 0666; - - r = cg_get_path(controller, path, "cgroup.procs", &fs); - if (r < 0) - return r; - - r = chmod_and_chown(fs, mode, uid, gid); - if (r < 0) - return r; - - unified = cg_unified(controller); - if (unified < 0) - return unified; - if (unified) - return 0; - - /* Compatibility, Always keep values for "tasks" in sync with - * "cgroup.procs" */ - if (cg_get_path(controller, path, "tasks", &procs) >= 0) - (void) chmod_and_chown(procs, mode, uid, gid); - - return 0; -} - -int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags) { - _cleanup_free_ char *fs = NULL; - int r; - - assert(path); - assert(name); - assert(value || size <= 0); - - r = cg_get_path(controller, path, NULL, &fs); - if (r < 0) - return r; - - if (setxattr(fs, name, value, size, flags) < 0) - return -errno; - - return 0; -} - -int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size) { - _cleanup_free_ char *fs = NULL; - ssize_t n; - int r; - - assert(path); - assert(name); - - r = cg_get_path(controller, path, NULL, &fs); - if (r < 0) - return r; - - n = getxattr(fs, name, value, size); - if (n < 0) - return -errno; - - return (int) n; -} - -int cg_pid_get_path(const char *controller, pid_t pid, char **path) { - _cleanup_fclose_ FILE *f = NULL; - char line[LINE_MAX]; - const char *fs; - size_t cs = 0; - int unified; - - assert(path); - assert(pid >= 0); - - if (controller) { - if (!cg_controller_is_valid(controller)) - return -EINVAL; - } else - controller = SYSTEMD_CGROUP_CONTROLLER; - - unified = cg_unified(controller); - if (unified < 0) - return unified; - if (unified == 0) - cs = strlen(controller); - - fs = procfs_file_alloca(pid, "cgroup"); - f = fopen(fs, "re"); - if (!f) - return errno == ENOENT ? -ESRCH : -errno; - - FOREACH_LINE(line, f, return -errno) { - char *e, *p; - - truncate_nl(line); - - if (unified) { - e = startswith(line, "0:"); - if (!e) - continue; - - e = strchr(e, ':'); - if (!e) - continue; - } else { - char *l; - size_t k; - const char *word, *state; - bool found = false; - - l = strchr(line, ':'); - if (!l) - continue; - - l++; - e = strchr(l, ':'); - if (!e) - continue; - - *e = 0; - FOREACH_WORD_SEPARATOR(word, k, l, ",", state) { - if (k == cs && memcmp(word, controller, cs) == 0) { - found = true; - break; - } - } - - if (!found) - continue; - } - - p = strdup(e + 1); - if (!p) - return -ENOMEM; - - *path = p; - return 0; - } - - return -ENODATA; -} - -int cg_install_release_agent(const char *controller, const char *agent) { - _cleanup_free_ char *fs = NULL, *contents = NULL; - const char *sc; - int r, unified; - - assert(agent); - - unified = cg_unified(controller); - if (unified < 0) - return unified; - if (unified) /* doesn't apply to unified hierarchy */ - return -EOPNOTSUPP; - - r = cg_get_path(controller, NULL, "release_agent", &fs); - if (r < 0) - return r; - - r = read_one_line_file(fs, &contents); - if (r < 0) - return r; - - sc = strstrip(contents); - if (isempty(sc)) { - r = write_string_file(fs, agent, 0); - if (r < 0) - return r; - } else if (!path_equal(sc, agent)) - return -EEXIST; - - fs = mfree(fs); - r = cg_get_path(controller, NULL, "notify_on_release", &fs); - if (r < 0) - return r; - - contents = mfree(contents); - r = read_one_line_file(fs, &contents); - if (r < 0) - return r; - - sc = strstrip(contents); - if (streq(sc, "0")) { - r = write_string_file(fs, "1", 0); - if (r < 0) - return r; - - return 1; - } - - if (!streq(sc, "1")) - return -EIO; - - return 0; -} - -int cg_uninstall_release_agent(const char *controller) { - _cleanup_free_ char *fs = NULL; - int r, unified; - - unified = cg_unified(controller); - if (unified < 0) - return unified; - if (unified) /* Doesn't apply to unified hierarchy */ - return -EOPNOTSUPP; - - r = cg_get_path(controller, NULL, "notify_on_release", &fs); - if (r < 0) - return r; - - r = write_string_file(fs, "0", 0); - if (r < 0) - return r; - - fs = mfree(fs); - - r = cg_get_path(controller, NULL, "release_agent", &fs); - if (r < 0) - return r; - - r = write_string_file(fs, "", 0); - if (r < 0) - return r; - - return 0; -} - -int cg_is_empty(const char *controller, const char *path) { - _cleanup_fclose_ FILE *f = NULL; - pid_t pid; - int r; - - assert(path); - - r = cg_enumerate_processes(controller, path, &f); - if (r == -ENOENT) - return 1; - if (r < 0) - return r; - - r = cg_read_pid(f, &pid); - if (r < 0) - return r; - - return r == 0; -} - -int cg_is_empty_recursive(const char *controller, const char *path) { - int unified, r; - - assert(path); - - /* The root cgroup is always populated */ - if (controller && (isempty(path) || path_equal(path, "/"))) - return false; - - unified = cg_unified(controller); - if (unified < 0) - return unified; - - if (unified > 0) { - _cleanup_free_ char *t = NULL; - - /* On the unified hierarchy we can check empty state - * via the "populated" attribute of "cgroup.events". */ - - r = cg_read_event(controller, path, "populated", &t); - if (r < 0) - return r; - - return streq(t, "0"); - } else { - _cleanup_closedir_ DIR *d = NULL; - char *fn; - - r = cg_is_empty(controller, path); - if (r <= 0) - return r; - - r = cg_enumerate_subgroups(controller, path, &d); - if (r == -ENOENT) - return 1; - if (r < 0) - return r; - - while ((r = cg_read_subgroup(d, &fn)) > 0) { - _cleanup_free_ char *p = NULL; - - p = strjoin(path, "/", fn, NULL); - free(fn); - if (!p) - return -ENOMEM; - - r = cg_is_empty_recursive(controller, p); - if (r <= 0) - return r; - } - if (r < 0) - return r; - - return true; - } -} - -int cg_split_spec(const char *spec, char **controller, char **path) { - char *t = NULL, *u = NULL; - const char *e; - - assert(spec); - - if (*spec == '/') { - if (!path_is_safe(spec)) - return -EINVAL; - - if (path) { - t = strdup(spec); - if (!t) - return -ENOMEM; - - *path = path_kill_slashes(t); - } - - if (controller) - *controller = NULL; - - return 0; - } - - e = strchr(spec, ':'); - if (!e) { - if (!cg_controller_is_valid(spec)) - return -EINVAL; - - if (controller) { - t = strdup(spec); - if (!t) - return -ENOMEM; - - *controller = t; - } - - if (path) - *path = NULL; - - return 0; - } - - t = strndup(spec, e-spec); - if (!t) - return -ENOMEM; - if (!cg_controller_is_valid(t)) { - free(t); - return -EINVAL; - } - - if (isempty(e+1)) - u = NULL; - else { - u = strdup(e+1); - if (!u) { - free(t); - return -ENOMEM; - } - - if (!path_is_safe(u) || - !path_is_absolute(u)) { - free(t); - free(u); - return -EINVAL; - } - - path_kill_slashes(u); - } - - if (controller) - *controller = t; - else - free(t); - - if (path) - *path = u; - else - free(u); - - return 0; -} - -int cg_mangle_path(const char *path, char **result) { - _cleanup_free_ char *c = NULL, *p = NULL; - char *t; - int r; - - assert(path); - assert(result); - - /* First, check if it already is a filesystem path */ - if (path_startswith(path, "/sys/fs/cgroup")) { - - t = strdup(path); - if (!t) - return -ENOMEM; - - *result = path_kill_slashes(t); - return 0; - } - - /* Otherwise, treat it as cg spec */ - r = cg_split_spec(path, &c, &p); - if (r < 0) - return r; - - return cg_get_path(c ?: SYSTEMD_CGROUP_CONTROLLER, p ?: "/", NULL, result); -} - -int cg_get_root_path(char **path) { - char *p, *e; - int r; - - assert(path); - - r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 1, &p); - if (r < 0) - return r; - - e = endswith(p, "/" SPECIAL_INIT_SCOPE); - if (!e) - e = endswith(p, "/" SPECIAL_SYSTEM_SLICE); /* legacy */ - if (!e) - e = endswith(p, "/system"); /* even more legacy */ - if (e) - *e = 0; - - *path = p; - return 0; -} - -int cg_shift_path(const char *cgroup, const char *root, const char **shifted) { - _cleanup_free_ char *rt = NULL; - char *p; - int r; - - assert(cgroup); - assert(shifted); - - if (!root) { - /* If the root was specified let's use that, otherwise - * let's determine it from PID 1 */ - - r = cg_get_root_path(&rt); - if (r < 0) - return r; - - root = rt; - } - - p = path_startswith(cgroup, root); - if (p && p > cgroup) - *shifted = p - 1; - else - *shifted = cgroup; - - return 0; -} - -int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) { - _cleanup_free_ char *raw = NULL; - const char *c; - int r; - - assert(pid >= 0); - assert(cgroup); - - r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &raw); - if (r < 0) - return r; - - r = cg_shift_path(raw, root, &c); - if (r < 0) - return r; - - if (c == raw) { - *cgroup = raw; - raw = NULL; - } else { - char *n; - - n = strdup(c); - if (!n) - return -ENOMEM; - - *cgroup = n; - } - - return 0; -} - -int cg_path_decode_unit(const char *cgroup, char **unit) { - char *c, *s; - size_t n; - - assert(cgroup); - assert(unit); - - n = strcspn(cgroup, "/"); - if (n < 3) - return -ENXIO; - - c = strndupa(cgroup, n); - c = cg_unescape(c); - - if (!unit_name_is_valid(c, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) - return -ENXIO; - - s = strdup(c); - if (!s) - return -ENOMEM; - - *unit = s; - return 0; -} - -static bool valid_slice_name(const char *p, size_t n) { - - if (!p) - return false; - - if (n < strlen("x.slice")) - return false; - - if (memcmp(p + n - 6, ".slice", 6) == 0) { - char buf[n+1], *c; - - memcpy(buf, p, n); - buf[n] = 0; - - c = cg_unescape(buf); - - return unit_name_is_valid(c, UNIT_NAME_PLAIN); - } - - return false; -} - -static const char *skip_slices(const char *p) { - assert(p); - - /* Skips over all slice assignments */ - - for (;;) { - size_t n; - - p += strspn(p, "/"); - - n = strcspn(p, "/"); - if (!valid_slice_name(p, n)) - return p; - - p += n; - } -} - -int cg_path_get_unit(const char *path, char **ret) { - const char *e; - char *unit; - int r; - - assert(path); - assert(ret); - - e = skip_slices(path); - - r = cg_path_decode_unit(e, &unit); - if (r < 0) - return r; - - /* We skipped over the slices, don't accept any now */ - if (endswith(unit, ".slice")) { - free(unit); - return -ENXIO; - } - - *ret = unit; - return 0; -} - -int cg_pid_get_unit(pid_t pid, char **unit) { - _cleanup_free_ char *cgroup = NULL; - int r; - - assert(unit); - - r = cg_pid_get_path_shifted(pid, NULL, &cgroup); - if (r < 0) - return r; - - return cg_path_get_unit(cgroup, unit); -} - -/** - * Skip session-*.scope, but require it to be there. - */ -static const char *skip_session(const char *p) { - size_t n; - - if (isempty(p)) - return NULL; - - p += strspn(p, "/"); - - n = strcspn(p, "/"); - if (n < strlen("session-x.scope")) - return NULL; - - if (memcmp(p, "session-", 8) == 0 && memcmp(p + n - 6, ".scope", 6) == 0) { - char buf[n - 8 - 6 + 1]; - - memcpy(buf, p + 8, n - 8 - 6); - buf[n - 8 - 6] = 0; - - /* Note that session scopes never need unescaping, - * since they cannot conflict with the kernel's own - * names, hence we don't need to call cg_unescape() - * here. */ - - if (!session_id_valid(buf)) - return false; - - p += n; - p += strspn(p, "/"); - return p; - } - - return NULL; -} - -/** - * Skip user@*.service, but require it to be there. - */ -static const char *skip_user_manager(const char *p) { - size_t n; - - if (isempty(p)) - return NULL; - - p += strspn(p, "/"); - - n = strcspn(p, "/"); - if (n < strlen("user@x.service")) - return NULL; - - if (memcmp(p, "user@", 5) == 0 && memcmp(p + n - 8, ".service", 8) == 0) { - char buf[n - 5 - 8 + 1]; - - memcpy(buf, p + 5, n - 5 - 8); - buf[n - 5 - 8] = 0; - - /* Note that user manager services never need unescaping, - * since they cannot conflict with the kernel's own - * names, hence we don't need to call cg_unescape() - * here. */ - - if (parse_uid(buf, NULL) < 0) - return NULL; - - p += n; - p += strspn(p, "/"); - - return p; - } - - return NULL; -} - -static const char *skip_user_prefix(const char *path) { - const char *e, *t; - - assert(path); - - /* Skip slices, if there are any */ - e = skip_slices(path); - - /* Skip the user manager, if it's in the path now... */ - t = skip_user_manager(e); - if (t) - return t; - - /* Alternatively skip the user session if it is in the path... */ - return skip_session(e); -} - -int cg_path_get_user_unit(const char *path, char **ret) { - const char *t; - - assert(path); - assert(ret); - - t = skip_user_prefix(path); - if (!t) - return -ENXIO; - - /* And from here on it looks pretty much the same as for a - * system unit, hence let's use the same parser from here - * on. */ - return cg_path_get_unit(t, ret); -} - -int cg_pid_get_user_unit(pid_t pid, char **unit) { - _cleanup_free_ char *cgroup = NULL; - int r; - - assert(unit); - - r = cg_pid_get_path_shifted(pid, NULL, &cgroup); - if (r < 0) - return r; - - return cg_path_get_user_unit(cgroup, unit); -} - -int cg_path_get_machine_name(const char *path, char **machine) { - _cleanup_free_ char *u = NULL; - const char *sl; - int r; - - r = cg_path_get_unit(path, &u); - if (r < 0) - return r; - - sl = strjoina("/run/systemd/machines/unit:", u); - return readlink_malloc(sl, machine); -} - -int cg_pid_get_machine_name(pid_t pid, char **machine) { - _cleanup_free_ char *cgroup = NULL; - int r; - - assert(machine); - - r = cg_pid_get_path_shifted(pid, NULL, &cgroup); - if (r < 0) - return r; - - return cg_path_get_machine_name(cgroup, machine); -} - -int cg_path_get_session(const char *path, char **session) { - _cleanup_free_ char *unit = NULL; - char *start, *end; - int r; - - assert(path); - - r = cg_path_get_unit(path, &unit); - if (r < 0) - return r; - - start = startswith(unit, "session-"); - if (!start) - return -ENXIO; - end = endswith(start, ".scope"); - if (!end) - return -ENXIO; - - *end = 0; - if (!session_id_valid(start)) - return -ENXIO; - - if (session) { - char *rr; - - rr = strdup(start); - if (!rr) - return -ENOMEM; - - *session = rr; - } - - return 0; -} - -int cg_pid_get_session(pid_t pid, char **session) { - _cleanup_free_ char *cgroup = NULL; - int r; - - r = cg_pid_get_path_shifted(pid, NULL, &cgroup); - if (r < 0) - return r; - - return cg_path_get_session(cgroup, session); -} - -int cg_path_get_owner_uid(const char *path, uid_t *uid) { - _cleanup_free_ char *slice = NULL; - char *start, *end; - int r; - - assert(path); - - r = cg_path_get_slice(path, &slice); - if (r < 0) - return r; - - start = startswith(slice, "user-"); - if (!start) - return -ENXIO; - end = endswith(start, ".slice"); - if (!end) - return -ENXIO; - - *end = 0; - if (parse_uid(start, uid) < 0) - return -ENXIO; - - return 0; -} - -int cg_pid_get_owner_uid(pid_t pid, uid_t *uid) { - _cleanup_free_ char *cgroup = NULL; - int r; - - r = cg_pid_get_path_shifted(pid, NULL, &cgroup); - if (r < 0) - return r; - - return cg_path_get_owner_uid(cgroup, uid); -} - -int cg_path_get_slice(const char *p, char **slice) { - const char *e = NULL; - - assert(p); - assert(slice); - - /* Finds the right-most slice unit from the beginning, but - * stops before we come to the first non-slice unit. */ - - for (;;) { - size_t n; - - p += strspn(p, "/"); - - n = strcspn(p, "/"); - if (!valid_slice_name(p, n)) { - - if (!e) { - char *s; - - s = strdup(SPECIAL_ROOT_SLICE); - if (!s) - return -ENOMEM; - - *slice = s; - return 0; - } - - return cg_path_decode_unit(e, slice); - } - - e = p; - p += n; - } -} - -int cg_pid_get_slice(pid_t pid, char **slice) { - _cleanup_free_ char *cgroup = NULL; - int r; - - assert(slice); - - r = cg_pid_get_path_shifted(pid, NULL, &cgroup); - if (r < 0) - return r; - - return cg_path_get_slice(cgroup, slice); -} - -int cg_path_get_user_slice(const char *p, char **slice) { - const char *t; - assert(p); - assert(slice); - - t = skip_user_prefix(p); - if (!t) - return -ENXIO; - - /* And now it looks pretty much the same as for a system - * slice, so let's just use the same parser from here on. */ - return cg_path_get_slice(t, slice); -} - -int cg_pid_get_user_slice(pid_t pid, char **slice) { - _cleanup_free_ char *cgroup = NULL; - int r; - - assert(slice); - - r = cg_pid_get_path_shifted(pid, NULL, &cgroup); - if (r < 0) - return r; - - return cg_path_get_user_slice(cgroup, slice); -} - -char *cg_escape(const char *p) { - bool need_prefix = false; - - /* This implements very minimal escaping for names to be used - * as file names in the cgroup tree: any name which might - * conflict with a kernel name or is prefixed with '_' is - * prefixed with a '_'. That way, when reading cgroup names it - * is sufficient to remove a single prefixing underscore if - * there is one. */ - - /* The return value of this function (unlike cg_unescape()) - * needs free()! */ - - if (p[0] == 0 || - p[0] == '_' || - p[0] == '.' || - streq(p, "notify_on_release") || - streq(p, "release_agent") || - streq(p, "tasks") || - startswith(p, "cgroup.")) - need_prefix = true; - else { - const char *dot; - - dot = strrchr(p, '.'); - if (dot) { - CGroupController c; - size_t l = dot - p; - - for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { - const char *n; - - n = cgroup_controller_to_string(c); - - if (l != strlen(n)) - continue; - - if (memcmp(p, n, l) != 0) - continue; - - need_prefix = true; - break; - } - } - } - - if (need_prefix) - return strappend("_", p); - - return strdup(p); -} - -char *cg_unescape(const char *p) { - assert(p); - - /* The return value of this function (unlike cg_escape()) - * doesn't need free()! */ - - if (p[0] == '_') - return (char*) p+1; - - return (char*) p; -} - -#define CONTROLLER_VALID \ - DIGITS LETTERS \ - "_" - -bool cg_controller_is_valid(const char *p) { - const char *t, *s; - - if (!p) - return false; - - s = startswith(p, "name="); - if (s) - p = s; - - if (*p == 0 || *p == '_') - return false; - - for (t = p; *t; t++) - if (!strchr(CONTROLLER_VALID, *t)) - return false; - - if (t - p > FILENAME_MAX) - return false; - - return true; -} - -int cg_slice_to_path(const char *unit, char **ret) { - _cleanup_free_ char *p = NULL, *s = NULL, *e = NULL; - const char *dash; - int r; - - assert(unit); - assert(ret); - - if (streq(unit, SPECIAL_ROOT_SLICE)) { - char *x; - - x = strdup(""); - if (!x) - return -ENOMEM; - *ret = x; - return 0; - } - - if (!unit_name_is_valid(unit, UNIT_NAME_PLAIN)) - return -EINVAL; - - if (!endswith(unit, ".slice")) - return -EINVAL; - - r = unit_name_to_prefix(unit, &p); - if (r < 0) - return r; - - dash = strchr(p, '-'); - - /* Don't allow initial dashes */ - if (dash == p) - return -EINVAL; - - while (dash) { - _cleanup_free_ char *escaped = NULL; - char n[dash - p + sizeof(".slice")]; - - /* Don't allow trailing or double dashes */ - if (dash[1] == 0 || dash[1] == '-') - return -EINVAL; - - strcpy(stpncpy(n, p, dash - p), ".slice"); - if (!unit_name_is_valid(n, UNIT_NAME_PLAIN)) - return -EINVAL; - - escaped = cg_escape(n); - if (!escaped) - return -ENOMEM; - - if (!strextend(&s, escaped, "/", NULL)) - return -ENOMEM; - - dash = strchr(dash+1, '-'); - } - - e = cg_escape(unit); - if (!e) - return -ENOMEM; - - if (!strextend(&s, e, NULL)) - return -ENOMEM; - - *ret = s; - s = NULL; - - return 0; -} - -int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) { - _cleanup_free_ char *p = NULL; - int r; - - r = cg_get_path(controller, path, attribute, &p); - if (r < 0) - return r; - - return write_string_file(p, value, 0); -} - -int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret) { - _cleanup_free_ char *p = NULL; - int r; - - r = cg_get_path(controller, path, attribute, &p); - if (r < 0) - return r; - - return read_one_line_file(p, ret); -} - -int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, const char **keys, char **values) { - _cleanup_free_ char *filename = NULL, *content = NULL; - char *line, *p; - int i, r; - - for (i = 0; keys[i]; i++) - values[i] = NULL; - - r = cg_get_path(controller, path, attribute, &filename); - if (r < 0) - return r; - - r = read_full_file(filename, &content, NULL); - if (r < 0) - return r; - - p = content; - while ((line = strsep(&p, "\n"))) { - char *key; - - key = strsep(&line, " "); - - for (i = 0; keys[i]; i++) { - if (streq(key, keys[i])) { - values[i] = strdup(line); - break; - } - } - } - - for (i = 0; keys[i]; i++) { - if (!values[i]) { - for (i = 0; keys[i]; i++) { - free(values[i]); - values[i] = NULL; - } - return -ENOENT; - } - } - - return 0; -} - -int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) { - CGroupController c; - int r, unified; - - /* This one will create a cgroup in our private tree, but also - * duplicate it in the trees specified in mask, and remove it - * in all others */ - - /* First create the cgroup in our own hierarchy. */ - r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path); - if (r < 0) - return r; - - /* If we are in the unified hierarchy, we are done now */ - unified = cg_all_unified(); - if (unified < 0) - return unified; - if (unified > 0) - return 0; - - /* Otherwise, do the same in the other hierarchies */ - for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { - CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c); - const char *n; - - n = cgroup_controller_to_string(c); - - if (mask & bit) - (void) cg_create(n, path); - else if (supported & bit) - (void) cg_trim(n, path, true); - } - - return 0; -} - -int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) { - CGroupController c; - int r, unified; - - r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid); - if (r < 0) - return r; - - unified = cg_all_unified(); - if (unified < 0) - return unified; - if (unified > 0) - return 0; - - for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { - CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c); - const char *p = NULL; - - if (!(supported & bit)) - continue; - - if (path_callback) - p = path_callback(bit, userdata); - - if (!p) - p = path; - - (void) cg_attach_fallback(cgroup_controller_to_string(c), p, pid); - } - - return 0; -} - -int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) { - Iterator i; - void *pidp; - int r = 0; - - SET_FOREACH(pidp, pids, i) { - pid_t pid = PTR_TO_PID(pidp); - int q; - - q = cg_attach_everywhere(supported, path, pid, path_callback, userdata); - if (q < 0 && r >= 0) - r = q; - } - - return r; -} - -int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) { - CGroupController c; - int r = 0, unified; - - if (!path_equal(from, to)) { - r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, CGROUP_REMOVE); - if (r < 0) - return r; - } - - unified = cg_all_unified(); - if (unified < 0) - return unified; - if (unified > 0) - return r; - - for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { - CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c); - const char *p = NULL; - - if (!(supported & bit)) - continue; - - if (to_callback) - p = to_callback(bit, userdata); - - if (!p) - p = to; - - (void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, 0); - } - - return 0; -} - -int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) { - CGroupController c; - int r, unified; - - r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root); - if (r < 0) - return r; - - unified = cg_all_unified(); - if (unified < 0) - return unified; - if (unified > 0) - return r; - - for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { - CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c); - - if (!(supported & bit)) - continue; - - (void) cg_trim(cgroup_controller_to_string(c), path, delete_root); - } - - return 0; -} - -int cg_mask_supported(CGroupMask *ret) { - CGroupMask mask = 0; - int r, unified; - - /* Determines the mask of supported cgroup controllers. Only - * includes controllers we can make sense of and that are - * actually accessible. */ - - unified = cg_all_unified(); - if (unified < 0) - return unified; - if (unified > 0) { - _cleanup_free_ char *root = NULL, *controllers = NULL, *path = NULL; - const char *c; - - /* In the unified hierarchy we can read the supported - * and accessible controllers from a the top-level - * cgroup attribute */ - - r = cg_get_root_path(&root); - if (r < 0) - return r; - - r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, root, "cgroup.controllers", &path); - if (r < 0) - return r; - - r = read_one_line_file(path, &controllers); - if (r < 0) - return r; - - c = controllers; - for (;;) { - _cleanup_free_ char *n = NULL; - CGroupController v; - - r = extract_first_word(&c, &n, NULL, 0); - if (r < 0) - return r; - if (r == 0) - break; - - v = cgroup_controller_from_string(n); - if (v < 0) - continue; - - mask |= CGROUP_CONTROLLER_TO_MASK(v); - } - - /* Currently, we support the cpu, memory, io and pids - * controller in the unified hierarchy, mask - * everything else off. */ - mask &= CGROUP_MASK_CPU | CGROUP_MASK_MEMORY | CGROUP_MASK_IO | CGROUP_MASK_PIDS; - - } else { - CGroupController c; - - /* In the legacy hierarchy, we check whether which - * hierarchies are mounted. */ - - for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { - const char *n; - - n = cgroup_controller_to_string(c); - if (controller_is_accessible(n) >= 0) - mask |= CGROUP_CONTROLLER_TO_MASK(c); - } - } - - *ret = mask; - return 0; -} - -int cg_kernel_controllers(Set *controllers) { - _cleanup_fclose_ FILE *f = NULL; - char buf[LINE_MAX]; - int r; - - assert(controllers); - - /* Determines the full list of kernel-known controllers. Might - * include controllers we don't actually support, arbitrary - * named hierarchies and controllers that aren't currently - * accessible (because not mounted). */ - - f = fopen("/proc/cgroups", "re"); - if (!f) { - if (errno == ENOENT) - return 0; - return -errno; - } - - /* Ignore the header line */ - (void) fgets(buf, sizeof(buf), f); - - for (;;) { - char *controller; - int enabled = 0; - - errno = 0; - if (fscanf(f, "%ms %*i %*i %i", &controller, &enabled) != 2) { - - if (feof(f)) - break; - - if (ferror(f) && errno > 0) - return -errno; - - return -EBADMSG; - } - - if (!enabled) { - free(controller); - continue; - } - - if (!cg_controller_is_valid(controller)) { - free(controller); - return -EBADMSG; - } - - r = set_consume(controllers, controller); - if (r < 0) - return r; - } - - return 0; -} - -static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN; - -static int cg_update_unified(void) { - - struct statfs fs; - - /* Checks if we support the unified hierarchy. Returns an - * error when the cgroup hierarchies aren't mounted yet or we - * have any other trouble determining if the unified hierarchy - * is supported. */ - - if (unified_cache >= CGROUP_UNIFIED_NONE) - return 0; - - if (statfs("/sys/fs/cgroup/", &fs) < 0) - return -errno; - - if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) - unified_cache = CGROUP_UNIFIED_ALL; - else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) { - if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0) - return -errno; - - unified_cache = F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC) ? - CGROUP_UNIFIED_SYSTEMD : CGROUP_UNIFIED_NONE; - } else - return -ENOMEDIUM; - - return 0; -} - -int cg_unified(const char *controller) { - - int r; - - r = cg_update_unified(); - if (r < 0) - return r; - - if (streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER)) - return unified_cache >= CGROUP_UNIFIED_SYSTEMD; - else - return unified_cache >= CGROUP_UNIFIED_ALL; -} - -int cg_all_unified(void) { - - return cg_unified(NULL); -} - -void cg_unified_flush(void) { - unified_cache = CGROUP_UNIFIED_UNKNOWN; -} - -int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) { - _cleanup_free_ char *fs = NULL; - CGroupController c; - int r, unified; - - assert(p); - - if (supported == 0) - return 0; - - unified = cg_all_unified(); - if (unified < 0) - return unified; - if (!unified) /* on the legacy hiearchy there's no joining of controllers defined */ - return 0; - - r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs); - if (r < 0) - return r; - - for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { - CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c); - const char *n; - - if (!(supported & bit)) - continue; - - n = cgroup_controller_to_string(c); - { - char s[1 + strlen(n) + 1]; - - s[0] = mask & bit ? '+' : '-'; - strcpy(s + 1, n); - - r = write_string_file(fs, s, 0); - if (r < 0) - log_debug_errno(r, "Failed to enable controller %s for %s (%s): %m", n, p, fs); - } - } - - return 0; -} - -bool cg_is_unified_wanted(void) { - static thread_local int wanted = -1; - int r, unified; - - /* If the hierarchy is already mounted, then follow whatever - * was chosen for it. */ - unified = cg_all_unified(); - if (unified >= 0) - return unified; - - /* Otherwise, let's see what the kernel command line has to - * say. Since checking that is expensive, let's cache the - * result. */ - if (wanted >= 0) - return wanted; - - r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy", NULL); - if (r > 0) - return (wanted = true); - else { - _cleanup_free_ char *value = NULL; - - r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy=", &value); - if (r < 0) - return false; - if (r == 0) - return (wanted = false); - - return (wanted = parse_boolean(value) > 0); - } -} - -bool cg_is_legacy_wanted(void) { - return !cg_is_unified_wanted(); -} - -bool cg_is_unified_systemd_controller_wanted(void) { - static thread_local int wanted = -1; - int r, unified; - - /* If the unified hierarchy is requested in full, no need to - * bother with this. */ - if (cg_is_unified_wanted()) - return 0; - - /* If the hierarchy is already mounted, then follow whatever - * was chosen for it. */ - unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER); - if (unified >= 0) - return unified; - - /* Otherwise, let's see what the kernel command line has to - * say. Since checking that is expensive, let's cache the - * result. */ - if (wanted >= 0) - return wanted; - - r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller", NULL); - if (r > 0) - wanted = false; - else { - _cleanup_free_ char *value = NULL; - - r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller=", &value); - if (r < 0) - return false; - - if (r == 0) - wanted = false; - else - wanted = parse_boolean(value) <= 0; - } - - return wanted; -} - -bool cg_is_legacy_systemd_controller_wanted(void) { - return cg_is_legacy_wanted() && !cg_is_unified_systemd_controller_wanted(); -} - -int cg_weight_parse(const char *s, uint64_t *ret) { - uint64_t u; - int r; - - if (isempty(s)) { - *ret = CGROUP_WEIGHT_INVALID; - return 0; - } - - r = safe_atou64(s, &u); - if (r < 0) - return r; - - if (u < CGROUP_WEIGHT_MIN || u > CGROUP_WEIGHT_MAX) - return -ERANGE; - - *ret = u; - return 0; -} - -const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX] = { - [CGROUP_IO_RBPS_MAX] = CGROUP_LIMIT_MAX, - [CGROUP_IO_WBPS_MAX] = CGROUP_LIMIT_MAX, - [CGROUP_IO_RIOPS_MAX] = CGROUP_LIMIT_MAX, - [CGROUP_IO_WIOPS_MAX] = CGROUP_LIMIT_MAX, -}; - -static const char* const cgroup_io_limit_type_table[_CGROUP_IO_LIMIT_TYPE_MAX] = { - [CGROUP_IO_RBPS_MAX] = "IOReadBandwidthMax", - [CGROUP_IO_WBPS_MAX] = "IOWriteBandwidthMax", - [CGROUP_IO_RIOPS_MAX] = "IOReadIOPSMax", - [CGROUP_IO_WIOPS_MAX] = "IOWriteIOPSMax", -}; - -DEFINE_STRING_TABLE_LOOKUP(cgroup_io_limit_type, CGroupIOLimitType); - -int cg_cpu_shares_parse(const char *s, uint64_t *ret) { - uint64_t u; - int r; - - if (isempty(s)) { - *ret = CGROUP_CPU_SHARES_INVALID; - return 0; - } - - r = safe_atou64(s, &u); - if (r < 0) - return r; - - if (u < CGROUP_CPU_SHARES_MIN || u > CGROUP_CPU_SHARES_MAX) - return -ERANGE; - - *ret = u; - return 0; -} - -int cg_blkio_weight_parse(const char *s, uint64_t *ret) { - uint64_t u; - int r; - - if (isempty(s)) { - *ret = CGROUP_BLKIO_WEIGHT_INVALID; - return 0; - } - - r = safe_atou64(s, &u); - if (r < 0) - return r; - - if (u < CGROUP_BLKIO_WEIGHT_MIN || u > CGROUP_BLKIO_WEIGHT_MAX) - return -ERANGE; - - *ret = u; - return 0; -} - -bool is_cgroup_fs(const struct statfs *s) { - return is_fs_type(s, CGROUP_SUPER_MAGIC) || - is_fs_type(s, CGROUP2_SUPER_MAGIC); -} - -bool fd_is_cgroup_fs(int fd) { - struct statfs s; - - if (fstatfs(fd, &s) < 0) - return -errno; - - return is_cgroup_fs(&s); -} - -static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = { - [CGROUP_CONTROLLER_CPU] = "cpu", - [CGROUP_CONTROLLER_CPUACCT] = "cpuacct", - [CGROUP_CONTROLLER_IO] = "io", - [CGROUP_CONTROLLER_BLKIO] = "blkio", - [CGROUP_CONTROLLER_MEMORY] = "memory", - [CGROUP_CONTROLLER_DEVICES] = "devices", - [CGROUP_CONTROLLER_PIDS] = "pids", -}; - -DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController); diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h deleted file mode 100644 index 0aa27c4cd7..0000000000 --- a/src/basic/cgroup-util.h +++ /dev/null @@ -1,260 +0,0 @@ -#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 <stdint.h> -#include <stdio.h> -#include <sys/statfs.h> -#include <sys/types.h> - -#include "def.h" -#include "hashmap.h" -#include "macro.h" -#include "set.h" - -/* An enum of well known cgroup controllers */ -typedef enum CGroupController { - CGROUP_CONTROLLER_CPU, - CGROUP_CONTROLLER_CPUACCT, - CGROUP_CONTROLLER_IO, - CGROUP_CONTROLLER_BLKIO, - CGROUP_CONTROLLER_MEMORY, - CGROUP_CONTROLLER_DEVICES, - CGROUP_CONTROLLER_PIDS, - _CGROUP_CONTROLLER_MAX, - _CGROUP_CONTROLLER_INVALID = -1, -} CGroupController; - -#define CGROUP_CONTROLLER_TO_MASK(c) (1 << (c)) - -/* A bit mask of well known cgroup controllers */ -typedef enum CGroupMask { - CGROUP_MASK_CPU = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPU), - CGROUP_MASK_CPUACCT = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPUACCT), - CGROUP_MASK_IO = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_IO), - CGROUP_MASK_BLKIO = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BLKIO), - CGROUP_MASK_MEMORY = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_MEMORY), - CGROUP_MASK_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_DEVICES), - CGROUP_MASK_PIDS = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_PIDS), - _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1 -} CGroupMask; - -/* Special values for all weight knobs on unified hierarchy */ -#define CGROUP_WEIGHT_INVALID ((uint64_t) -1) -#define CGROUP_WEIGHT_MIN UINT64_C(1) -#define CGROUP_WEIGHT_MAX UINT64_C(10000) -#define CGROUP_WEIGHT_DEFAULT UINT64_C(100) - -#define CGROUP_LIMIT_MIN UINT64_C(0) -#define CGROUP_LIMIT_MAX ((uint64_t) -1) - -static inline bool CGROUP_WEIGHT_IS_OK(uint64_t x) { - return - x == CGROUP_WEIGHT_INVALID || - (x >= CGROUP_WEIGHT_MIN && x <= CGROUP_WEIGHT_MAX); -} - -/* IO limits on unified hierarchy */ -typedef enum CGroupIOLimitType { - CGROUP_IO_RBPS_MAX, - CGROUP_IO_WBPS_MAX, - CGROUP_IO_RIOPS_MAX, - CGROUP_IO_WIOPS_MAX, - - _CGROUP_IO_LIMIT_TYPE_MAX, - _CGROUP_IO_LIMIT_TYPE_INVALID = -1 -} CGroupIOLimitType; - -extern const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX]; - -const char* cgroup_io_limit_type_to_string(CGroupIOLimitType t) _const_; -CGroupIOLimitType cgroup_io_limit_type_from_string(const char *s) _pure_; - -/* Special values for the cpu.shares attribute */ -#define CGROUP_CPU_SHARES_INVALID ((uint64_t) -1) -#define CGROUP_CPU_SHARES_MIN UINT64_C(2) -#define CGROUP_CPU_SHARES_MAX UINT64_C(262144) -#define CGROUP_CPU_SHARES_DEFAULT UINT64_C(1024) - -static inline bool CGROUP_CPU_SHARES_IS_OK(uint64_t x) { - return - x == CGROUP_CPU_SHARES_INVALID || - (x >= CGROUP_CPU_SHARES_MIN && x <= CGROUP_CPU_SHARES_MAX); -} - -/* Special values for the blkio.weight attribute */ -#define CGROUP_BLKIO_WEIGHT_INVALID ((uint64_t) -1) -#define CGROUP_BLKIO_WEIGHT_MIN UINT64_C(10) -#define CGROUP_BLKIO_WEIGHT_MAX UINT64_C(1000) -#define CGROUP_BLKIO_WEIGHT_DEFAULT UINT64_C(500) - -static inline bool CGROUP_BLKIO_WEIGHT_IS_OK(uint64_t x) { - return - x == CGROUP_BLKIO_WEIGHT_INVALID || - (x >= CGROUP_BLKIO_WEIGHT_MIN && x <= CGROUP_BLKIO_WEIGHT_MAX); -} - -/* Default resource limits */ -#define DEFAULT_TASKS_MAX_PERCENTAGE 15U /* 15% of PIDs, 4915 on default settings */ -#define DEFAULT_USER_TASKS_MAX_PERCENTAGE 33U /* 33% of PIDs, 10813 on default settings */ - -typedef enum CGroupUnified { - CGROUP_UNIFIED_UNKNOWN = -1, - CGROUP_UNIFIED_NONE = 0, /* Both systemd and controllers on legacy */ - CGROUP_UNIFIED_SYSTEMD = 1, /* Only systemd on unified */ - CGROUP_UNIFIED_ALL = 2, /* Both systemd and controllers on unified */ -} CGroupUnified; - -/* - * General rules: - * - * We accept named hierarchies in the syntax "foo" and "name=foo". - * - * We expect that named hierarchies do not conflict in name with a - * kernel hierarchy, modulo the "name=" prefix. - * - * We always generate "normalized" controller names, i.e. without the - * "name=" prefix. - * - * We require absolute cgroup paths. When returning, we will always - * generate paths with multiple adjacent / removed. - */ - -int cg_enumerate_processes(const char *controller, const char *path, FILE **_f); -int cg_read_pid(FILE *f, pid_t *_pid); -int cg_read_event(const char *controller, const char *path, const char *event, - char **val); - -int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d); -int cg_read_subgroup(DIR *d, char **fn); - -typedef enum CGroupFlags { - CGROUP_SIGCONT = 1, - CGROUP_IGNORE_SELF = 2, - CGROUP_REMOVE = 4, -} CGroupFlags; - -typedef void (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata); - -int cg_kill(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata); -int cg_kill_recursive(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata); - -int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags); -int cg_migrate_recursive(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags); -int cg_migrate_recursive_fallback(const char *cfrom, const char *pfrom, const char *cto, const char *pto, CGroupFlags flags); - -int cg_split_spec(const char *spec, char **controller, char **path); -int cg_mangle_path(const char *path, char **result); - -int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs); -int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs); - -int cg_pid_get_path(const char *controller, pid_t pid, char **path); - -int cg_trim(const char *controller, const char *path, bool delete_root); - -int cg_rmdir(const char *controller, const char *path); - -int cg_create(const char *controller, const char *path); -int cg_attach(const char *controller, const char *path, pid_t pid); -int cg_attach_fallback(const char *controller, const char *path, pid_t pid); -int cg_create_and_attach(const char *controller, const char *path, pid_t pid); - -int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value); -int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret); -int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, const char **keys, char **values); - -int cg_set_group_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid); -int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid); - -int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags); -int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size); - -int cg_install_release_agent(const char *controller, const char *agent); -int cg_uninstall_release_agent(const char *controller); - -int cg_is_empty(const char *controller, const char *path); -int cg_is_empty_recursive(const char *controller, const char *path); - -int cg_get_root_path(char **path); - -int cg_path_get_session(const char *path, char **session); -int cg_path_get_owner_uid(const char *path, uid_t *uid); -int cg_path_get_unit(const char *path, char **unit); -int cg_path_get_user_unit(const char *path, char **unit); -int cg_path_get_machine_name(const char *path, char **machine); -int cg_path_get_slice(const char *path, char **slice); -int cg_path_get_user_slice(const char *path, char **slice); - -int cg_shift_path(const char *cgroup, const char *cached_root, const char **shifted); -int cg_pid_get_path_shifted(pid_t pid, const char *cached_root, char **cgroup); - -int cg_pid_get_session(pid_t pid, char **session); -int cg_pid_get_owner_uid(pid_t pid, uid_t *uid); -int cg_pid_get_unit(pid_t pid, char **unit); -int cg_pid_get_user_unit(pid_t pid, char **unit); -int cg_pid_get_machine_name(pid_t pid, char **machine); -int cg_pid_get_slice(pid_t pid, char **slice); -int cg_pid_get_user_slice(pid_t pid, char **slice); - -int cg_path_decode_unit(const char *cgroup, char **unit); - -char *cg_escape(const char *p); -char *cg_unescape(const char *p) _pure_; - -bool cg_controller_is_valid(const char *p); - -int cg_slice_to_path(const char *unit, char **ret); - -typedef const char* (*cg_migrate_callback_t)(CGroupMask mask, void *userdata); - -int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path); -int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t callback, void *userdata); -int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata); -int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata); -int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root); -int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p); - -int cg_mask_supported(CGroupMask *ret); - -int cg_kernel_controllers(Set *controllers); - -bool cg_ns_supported(void); - -int cg_all_unified(void); -int cg_unified(const char *controller); -void cg_unified_flush(void); - -bool cg_is_unified_wanted(void); -bool cg_is_legacy_wanted(void); -bool cg_is_unified_systemd_controller_wanted(void); -bool cg_is_legacy_systemd_controller_wanted(void); - -const char* cgroup_controller_to_string(CGroupController c) _const_; -CGroupController cgroup_controller_from_string(const char *s) _pure_; - -int cg_weight_parse(const char *s, uint64_t *ret); -int cg_cpu_shares_parse(const char *s, uint64_t *ret); -int cg_blkio_weight_parse(const char *s, uint64_t *ret); - -bool is_cgroup_fs(const struct statfs *s); -bool fd_is_cgroup_fs(int fd); diff --git a/src/basic/chattr-util.c b/src/basic/chattr-util.c deleted file mode 100644 index 2896a729af..0000000000 --- a/src/basic/chattr-util.c +++ /dev/null @@ -1,107 +0,0 @@ -/*** - 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 <errno.h> -#include <fcntl.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <linux/fs.h> - -#include "chattr-util.h" -#include "fd-util.h" -#include "macro.h" - -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); -} diff --git a/src/basic/chattr-util.h b/src/basic/chattr-util.h deleted file mode 100644 index 960cf6d5b3..0000000000 --- a/src/basic/chattr-util.h +++ /dev/null @@ -1,26 +0,0 @@ -#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/>. -***/ - -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); diff --git a/src/basic/clock-util.c b/src/basic/clock-util.c deleted file mode 100644 index 7fe8d35ea5..0000000000 --- a/src/basic/clock-util.c +++ /dev/null @@ -1,165 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010-2012 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 <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <stdbool.h> -#include <time.h> -#include <linux/rtc.h> -#include <stdio.h> -#include <sys/ioctl.h> -#include <sys/time.h> - -#include "clock-util.h" -#include "fd-util.h" -#include "macro.h" -#include "string-util.h" -#include "util.h" - -int clock_get_hwclock(struct tm *tm) { - _cleanup_close_ int fd = -1; - - assert(tm); - - fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC); - if (fd < 0) - return -errno; - - /* This leaves the timezone fields of struct tm - * uninitialized! */ - if (ioctl(fd, RTC_RD_TIME, tm) < 0) - return -errno; - - /* We don't know daylight saving, so we reset this in order not - * to confuse mktime(). */ - tm->tm_isdst = -1; - - return 0; -} - -int clock_set_hwclock(const struct tm *tm) { - _cleanup_close_ int fd = -1; - - assert(tm); - - fd = open("/dev/rtc", O_RDONLY|O_CLOEXEC); - if (fd < 0) - return -errno; - - if (ioctl(fd, RTC_SET_TIME, tm) < 0) - return -errno; - - return 0; -} - -int clock_is_localtime(const char* adjtime_path) { - _cleanup_fclose_ FILE *f; - - if (adjtime_path == NULL) - adjtime_path = "/etc/adjtime"; - - /* - * The third line of adjtime is "UTC" or "LOCAL" or nothing. - * # /etc/adjtime - * 0.0 0 0 - * 0 - * UTC - */ - f = fopen(adjtime_path, "re"); - if (f) { - char line[LINE_MAX]; - bool b; - - b = fgets(line, sizeof(line), f) && - fgets(line, sizeof(line), f) && - fgets(line, sizeof(line), f); - if (!b) - /* less than three lines -> default to UTC */ - return 0; - - truncate_nl(line); - return streq(line, "LOCAL"); - - } else if (errno != ENOENT) - return -errno; - - /* adjtime not present -> default to UTC */ - return 0; -} - -int clock_set_timezone(int *min) { - const struct timeval *tv_null = NULL; - struct timespec ts; - struct tm *tm; - int minutesdelta; - struct timezone tz; - - assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0); - assert_se(tm = localtime(&ts.tv_sec)); - minutesdelta = tm->tm_gmtoff / 60; - - tz.tz_minuteswest = -minutesdelta; - tz.tz_dsttime = 0; /* DST_NONE */ - - /* - * If the RTC does not run in UTC but in local time, the very first - * call to settimeofday() will set the kernel's timezone and will warp the - * system clock, so that it runs in UTC instead of the local time we - * have read from the RTC. - */ - if (settimeofday(tv_null, &tz) < 0) - return negative_errno(); - - if (min) - *min = minutesdelta; - return 0; -} - -int clock_reset_timewarp(void) { - const struct timeval *tv_null = NULL; - struct timezone tz; - - tz.tz_minuteswest = 0; - tz.tz_dsttime = 0; /* DST_NONE */ - - /* - * The very first call to settimeofday() does time warp magic. Do a - * dummy call here, so the time warping is sealed and all later calls - * behave as expected. - */ - if (settimeofday(tv_null, &tz) < 0) - return -errno; - - return 0; -} - -#define TIME_EPOCH_USEC ((usec_t) TIME_EPOCH * USEC_PER_SEC) - -int clock_apply_epoch(void) { - struct timespec ts; - - if (now(CLOCK_REALTIME) >= TIME_EPOCH_USEC) - return 0; - - if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, TIME_EPOCH_USEC)) < 0) - return -errno; - - return 1; -} diff --git a/src/basic/clock-util.h b/src/basic/clock-util.h deleted file mode 100644 index 8830cd2f38..0000000000 --- a/src/basic/clock-util.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010-2012 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 <time.h> - -int clock_is_localtime(const char* adjtime_path); -int clock_set_timezone(int *min); -int clock_reset_timewarp(void); -int clock_get_hwclock(struct tm *tm); -int clock_set_hwclock(const struct tm *tm); -int clock_apply_epoch(void); diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c deleted file mode 100644 index c781610e14..0000000000 --- a/src/basic/conf-files.c +++ /dev/null @@ -1,166 +0,0 @@ -/*** - 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 <errno.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "conf-files.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "hashmap.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "path-util.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" - -static int files_add(Hashmap *h, const char *root, const char *path, const char *suffix) { - _cleanup_closedir_ DIR *dir = NULL; - const char *dirpath; - struct dirent *de; - int r; - - assert(path); - assert(suffix); - - dirpath = prefix_roota(root, path); - - dir = opendir(dirpath); - if (!dir) { - if (errno == ENOENT) - return 0; - return -errno; - } - - FOREACH_DIRENT(de, dir, return -errno) { - char *p; - - if (!dirent_is_file_with_suffix(de, suffix)) - continue; - - p = strjoin(dirpath, "/", de->d_name, NULL); - if (!p) - return -ENOMEM; - - r = hashmap_put(h, basename(p), p); - if (r == -EEXIST) { - log_debug("Skipping overridden file: %s.", p); - free(p); - } else if (r < 0) { - free(p); - return r; - } else if (r == 0) { - log_debug("Duplicate file %s", p); - free(p); - } - } - - return 0; -} - -static int base_cmp(const void *a, const void *b) { - const char *s1, *s2; - - s1 = *(char * const *)a; - s2 = *(char * const *)b; - return strcmp(basename(s1), basename(s2)); -} - -static int conf_files_list_strv_internal(char ***strv, const char *suffix, const char *root, char **dirs) { - _cleanup_hashmap_free_ Hashmap *fh = NULL; - char **files, **p; - int r; - - assert(strv); - assert(suffix); - - /* This alters the dirs string array */ - if (!path_strv_resolve_uniq(dirs, root)) - return -ENOMEM; - - fh = hashmap_new(&string_hash_ops); - if (!fh) - return -ENOMEM; - - STRV_FOREACH(p, dirs) { - r = files_add(fh, root, *p, suffix); - if (r == -ENOMEM) - return r; - if (r < 0) - log_debug_errno(r, "Failed to search for files in %s, ignoring: %m", *p); - } - - files = hashmap_get_strv(fh); - if (!files) - return -ENOMEM; - - qsort_safe(files, hashmap_size(fh), sizeof(char *), base_cmp); - *strv = files; - - return 0; -} - -int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs) { - _cleanup_strv_free_ char **copy = NULL; - - assert(strv); - assert(suffix); - - copy = strv_copy((char**) dirs); - if (!copy) - return -ENOMEM; - - return conf_files_list_strv_internal(strv, suffix, root, copy); -} - -int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...) { - _cleanup_strv_free_ char **dirs = NULL; - va_list ap; - - assert(strv); - assert(suffix); - - va_start(ap, dir); - dirs = strv_new_ap(dir, ap); - va_end(ap); - - if (!dirs) - return -ENOMEM; - - return conf_files_list_strv_internal(strv, suffix, root, dirs); -} - -int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *d) { - _cleanup_strv_free_ char **dirs = NULL; - - assert(strv); - assert(suffix); - - dirs = strv_split_nulstr(d); - if (!dirs) - return -ENOMEM; - - return conf_files_list_strv_internal(strv, suffix, root, dirs); -} diff --git a/src/basic/conf-files.h b/src/basic/conf-files.h deleted file mode 100644 index e00e0e81fb..0000000000 --- a/src/basic/conf-files.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010-2012 Lennart Poettering - Copyright 2010-2012 Kay Sievers - - 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/>. -***/ - -int conf_files_list(char ***ret, const char *suffix, const char *root, const char *dir, ...); -int conf_files_list_strv(char ***ret, const char *suffix, const char *root, const char* const* dirs); -int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, const char *dirs); diff --git a/src/basic/copy.c b/src/basic/copy.c deleted file mode 100644 index 9883f5fa31..0000000000 --- a/src/basic/copy.c +++ /dev/null @@ -1,603 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 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 <errno.h> -#include <fcntl.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/sendfile.h> -#include <sys/stat.h> -#include <sys/xattr.h> -#include <time.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "btrfs-util.h" -#include "chattr-util.h" -#include "copy.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "io-util.h" -#include "macro.h" -#include "missing.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" -#include "umask-util.h" -#include "xattr-util.h" - -#define COPY_BUFFER_SIZE (16*1024u) - -static ssize_t try_copy_file_range(int fd_in, loff_t *off_in, - int fd_out, loff_t *off_out, - size_t len, - unsigned int flags) { - static int have = -1; - ssize_t r; - - if (have == false) - return -ENOSYS; - - r = copy_file_range(fd_in, off_in, fd_out, off_out, len, flags); - if (_unlikely_(have < 0)) - have = r >= 0 || errno != ENOSYS; - if (r >= 0) - return r; - else - return -errno; -} - -int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { - bool try_cfr = true, try_sendfile = true, try_splice = true; - int r; - size_t m = SSIZE_MAX; /* that is the maximum that sendfile and c_f_r accept */ - - assert(fdf >= 0); - assert(fdt >= 0); - - /* Try btrfs reflinks first. */ - if (try_reflink && - max_bytes == (uint64_t) -1 && - lseek(fdf, 0, SEEK_CUR) == 0 && - lseek(fdt, 0, SEEK_CUR) == 0) { - - r = btrfs_reflink(fdf, fdt); - if (r >= 0) - return 0; /* we copied the whole thing, hence hit EOF, return 0 */ - } - - for (;;) { - ssize_t n; - - if (max_bytes != (uint64_t) -1) { - if (max_bytes <= 0) - return 1; /* return > 0 if we hit the max_bytes limit */ - - if (m > max_bytes) - m = max_bytes; - } - - /* First try copy_file_range(), unless we already tried */ - if (try_cfr) { - n = try_copy_file_range(fdf, NULL, fdt, NULL, m, 0u); - if (n < 0) { - if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV, -EBADF)) - return n; - - try_cfr = false; - /* use fallback below */ - } else if (n == 0) /* EOF */ - break; - else - /* Success! */ - goto next; - } - - /* First try sendfile(), unless we already tried */ - if (try_sendfile) { - n = sendfile(fdt, fdf, NULL, m); - if (n < 0) { - if (!IN_SET(errno, EINVAL, ENOSYS)) - return -errno; - - try_sendfile = false; - /* use fallback below */ - } else if (n == 0) /* EOF */ - break; - else - /* Success! */ - goto next; - } - - /* Then try splice, unless we already tried */ - if (try_splice) { - n = splice(fdf, NULL, fdt, NULL, m, 0); - if (n < 0) { - if (!IN_SET(errno, EINVAL, ENOSYS)) - return -errno; - - try_splice = false; - /* use fallback below */ - } else if (n == 0) /* EOF */ - break; - else - /* Success! */ - goto next; - } - - /* As a fallback just copy bits by hand */ - { - uint8_t buf[MIN(m, COPY_BUFFER_SIZE)]; - - n = read(fdf, buf, sizeof buf); - if (n < 0) - return -errno; - if (n == 0) /* EOF */ - break; - - r = loop_write(fdt, buf, (size_t) n, false); - if (r < 0) - return r; - } - - next: - if (max_bytes != (uint64_t) -1) { - assert(max_bytes >= (uint64_t) n); - max_bytes -= n; - } - /* sendfile accepts at most SSIZE_MAX-offset bytes to copy, - * so reduce our maximum by the amount we already copied, - * but don't go below our copy buffer size, unless we are - * close the limit of bytes we are allowed to copy. */ - m = MAX(MIN(COPY_BUFFER_SIZE, max_bytes), m - n); - } - - return 0; /* return 0 if we hit EOF earlier than the size limit */ -} - -static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) { - _cleanup_free_ char *target = NULL; - int r; - - assert(from); - assert(st); - assert(to); - - r = readlinkat_malloc(df, from, &target); - if (r < 0) - return r; - - if (symlinkat(target, dt, to) < 0) - return -errno; - - if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0) - return -errno; - - return 0; -} - -static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) { - _cleanup_close_ int fdf = -1, fdt = -1; - struct timespec ts[2]; - int r, q; - - assert(from); - assert(st); - assert(to); - - fdf = openat(df, from, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fdf < 0) - return -errno; - - fdt = openat(dt, to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, st->st_mode & 07777); - if (fdt < 0) - return -errno; - - r = copy_bytes(fdf, fdt, (uint64_t) -1, true); - if (r < 0) { - unlinkat(dt, to, 0); - return r; - } - - if (fchown(fdt, st->st_uid, st->st_gid) < 0) - r = -errno; - - if (fchmod(fdt, st->st_mode & 07777) < 0) - r = -errno; - - ts[0] = st->st_atim; - ts[1] = st->st_mtim; - (void) futimens(fdt, ts); - - (void) copy_xattr(fdf, fdt); - - q = close(fdt); - fdt = -1; - - if (q < 0) { - r = -errno; - unlinkat(dt, to, 0); - } - - return r; -} - -static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) { - int r; - - assert(from); - assert(st); - assert(to); - - r = mkfifoat(dt, to, st->st_mode & 07777); - if (r < 0) - return -errno; - - if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0) - r = -errno; - - if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0) - r = -errno; - - return r; -} - -static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) { - int r; - - assert(from); - assert(st); - assert(to); - - r = mknodat(dt, to, st->st_mode, st->st_rdev); - if (r < 0) - return -errno; - - if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0) - r = -errno; - - if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0) - r = -errno; - - return r; -} - -static int fd_copy_directory( - int df, - const char *from, - const struct stat *st, - int dt, - const char *to, - dev_t original_device, - bool merge) { - - _cleanup_close_ int fdf = -1, fdt = -1; - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - bool created; - int r; - - assert(st); - assert(to); - - if (from) - fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - else - fdf = fcntl(df, F_DUPFD_CLOEXEC, 3); - if (fdf < 0) - return -errno; - - d = fdopendir(fdf); - if (!d) - return -errno; - fdf = -1; - - r = mkdirat(dt, to, st->st_mode & 07777); - if (r >= 0) - created = true; - else if (errno == EEXIST && merge) - created = false; - else - return -errno; - - fdt = openat(dt, to, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fdt < 0) - return -errno; - - r = 0; - - FOREACH_DIRENT_ALL(de, d, return -errno) { - struct stat buf; - int q; - - if (STR_IN_SET(de->d_name, ".", "..")) - continue; - - if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) { - r = -errno; - continue; - } - - if (buf.st_dev != original_device) - continue; - - if (S_ISREG(buf.st_mode)) - q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name); - else if (S_ISDIR(buf.st_mode)) - q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge); - else if (S_ISLNK(buf.st_mode)) - q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name); - else if (S_ISFIFO(buf.st_mode)) - q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name); - else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode) || S_ISSOCK(buf.st_mode)) - q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name); - else - q = -EOPNOTSUPP; - - if (q == -EEXIST && merge) - q = 0; - - if (q < 0) - r = q; - } - - if (created) { - struct timespec ut[2] = { - st->st_atim, - st->st_mtim - }; - - if (fchown(fdt, st->st_uid, st->st_gid) < 0) - r = -errno; - - if (fchmod(fdt, st->st_mode & 07777) < 0) - r = -errno; - - (void) copy_xattr(dirfd(d), fdt); - (void) futimens(fdt, ut); - } - - return r; -} - -int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) { - struct stat st; - - assert(from); - assert(to); - - if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0) - return -errno; - - if (S_ISREG(st.st_mode)) - return fd_copy_regular(fdf, from, &st, fdt, to); - else if (S_ISDIR(st.st_mode)) - return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge); - else if (S_ISLNK(st.st_mode)) - return fd_copy_symlink(fdf, from, &st, fdt, to); - else if (S_ISFIFO(st.st_mode)) - return fd_copy_fifo(fdf, from, &st, fdt, to); - else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode)) - return fd_copy_node(fdf, from, &st, fdt, to); - else - return -EOPNOTSUPP; -} - -int copy_tree(const char *from, const char *to, bool merge) { - return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge); -} - -int copy_directory_fd(int dirfd, const char *to, bool merge) { - struct stat st; - - assert(dirfd >= 0); - assert(to); - - if (fstat(dirfd, &st) < 0) - return -errno; - - if (!S_ISDIR(st.st_mode)) - return -ENOTDIR; - - return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge); -} - -int copy_directory(const char *from, const char *to, bool merge) { - struct stat st; - - assert(from); - assert(to); - - if (lstat(from, &st) < 0) - return -errno; - - if (!S_ISDIR(st.st_mode)) - return -ENOTDIR; - - return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge); -} - -int copy_file_fd(const char *from, int fdt, bool try_reflink) { - _cleanup_close_ int fdf = -1; - int r; - - assert(from); - assert(fdt >= 0); - - fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fdf < 0) - return -errno; - - r = copy_bytes(fdf, fdt, (uint64_t) -1, try_reflink); - - (void) copy_times(fdf, fdt); - (void) copy_xattr(fdf, fdt); - - return r; -} - -int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) { - int fdt = -1, r; - - assert(from); - assert(to); - - RUN_WITH_UMASK(0000) { - fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode); - if (fdt < 0) - return -errno; - } - - if (chattr_flags != 0) - (void) chattr_fd(fdt, chattr_flags, (unsigned) -1); - - r = copy_file_fd(from, fdt, true); - if (r < 0) { - close(fdt); - unlink(to); - return r; - } - - if (close(fdt) < 0) { - unlink_noerrno(to); - return -errno; - } - - return 0; -} - -int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) { - _cleanup_free_ char *t = NULL; - int r; - - assert(from); - assert(to); - - r = tempfn_random(to, NULL, &t); - if (r < 0) - return r; - - r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags); - if (r < 0) - return r; - - if (replace) { - r = renameat(AT_FDCWD, t, AT_FDCWD, to); - if (r < 0) - r = -errno; - } else - r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to); - if (r < 0) { - (void) unlink_noerrno(t); - return r; - } - - return 0; -} - -int copy_times(int fdf, int fdt) { - struct timespec ut[2]; - struct stat st; - usec_t crtime = 0; - - assert(fdf >= 0); - assert(fdt >= 0); - - if (fstat(fdf, &st) < 0) - return -errno; - - ut[0] = st.st_atim; - ut[1] = st.st_mtim; - - if (futimens(fdt, ut) < 0) - return -errno; - - if (fd_getcrtime(fdf, &crtime) >= 0) - (void) fd_setcrtime(fdt, crtime); - - return 0; -} - -int copy_xattr(int fdf, int fdt) { - _cleanup_free_ char *bufa = NULL, *bufb = NULL; - size_t sza = 100, szb = 100; - ssize_t n; - int ret = 0; - const char *p; - - for (;;) { - bufa = malloc(sza); - if (!bufa) - return -ENOMEM; - - n = flistxattr(fdf, bufa, sza); - if (n == 0) - return 0; - if (n > 0) - break; - if (errno != ERANGE) - return -errno; - - sza *= 2; - - bufa = mfree(bufa); - } - - p = bufa; - while (n > 0) { - size_t l; - - l = strlen(p); - assert(l < (size_t) n); - - if (startswith(p, "user.")) { - ssize_t m; - - if (!bufb) { - bufb = malloc(szb); - if (!bufb) - return -ENOMEM; - } - - m = fgetxattr(fdf, p, bufb, szb); - if (m < 0) { - if (errno == ERANGE) { - szb *= 2; - bufb = mfree(bufb); - continue; - } - - return -errno; - } - - if (fsetxattr(fdt, p, bufb, m, 0) < 0) - ret = -errno; - } - - p += l + 1; - n -= l + 1; - } - - return ret; -} diff --git a/src/basic/copy.h b/src/basic/copy.h deleted file mode 100644 index b5d08ebafe..0000000000 --- a/src/basic/copy.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 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 <stdbool.h> -#include <stdint.h> -#include <sys/types.h> - -int copy_file_fd(const char *from, int to, bool try_reflink); -int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags); -int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags); -int copy_tree(const char *from, const char *to, bool merge); -int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge); -int copy_directory_fd(int dirfd, const char *to, bool merge); -int copy_directory(const char *from, const char *to, bool merge); -int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink); -int copy_times(int fdf, int fdt); -int copy_xattr(int fdf, int fdt); diff --git a/src/basic/cpu-set-util.c b/src/basic/cpu-set-util.c deleted file mode 100644 index 95ed6928ff..0000000000 --- a/src/basic/cpu-set-util.c +++ /dev/null @@ -1,114 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010-2015 Lennart Poettering - Copyright 2015 Filipe Brandenburger - - 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 <errno.h> -#include <stddef.h> -#include <syslog.h> - -#include "alloc-util.h" -#include "cpu-set-util.h" -#include "extract-word.h" -#include "log.h" -#include "macro.h" -#include "parse-util.h" -#include "string-util.h" - -cpu_set_t* cpu_set_malloc(unsigned *ncpus) { - cpu_set_t *c; - unsigned n = 1024; - - /* Allocates the cpuset in the right size */ - - for (;;) { - c = CPU_ALLOC(n); - if (!c) - return NULL; - - if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), c) >= 0) { - CPU_ZERO_S(CPU_ALLOC_SIZE(n), c); - - if (ncpus) - *ncpus = n; - - return c; - } - - CPU_FREE(c); - - if (errno != EINVAL) - return NULL; - - n *= 2; - } -} - -int parse_cpu_set_and_warn( - const char *rvalue, - cpu_set_t **cpu_set, - const char *unit, - const char *filename, - unsigned line, - const char *lvalue) { - - const char *whole_rvalue = rvalue; - _cleanup_cpu_free_ cpu_set_t *c = NULL; - unsigned ncpus = 0; - - assert(lvalue); - assert(rvalue); - - for (;;) { - _cleanup_free_ char *word = NULL; - unsigned cpu, cpu_lower, cpu_upper; - int r; - - r = extract_first_word(&rvalue, &word, WHITESPACE ",", EXTRACT_QUOTES); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue); - if (r == 0) - break; - - if (!c) { - c = cpu_set_malloc(&ncpus); - if (!c) - return log_oom(); - } - - r = parse_range(word, &cpu_lower, &cpu_upper); - if (r < 0) - return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU affinity '%s'", word); - if (cpu_lower >= ncpus || cpu_upper >= ncpus) - return log_syntax(unit, LOG_ERR, filename, line, EINVAL, "CPU out of range '%s' ncpus is %u", word, ncpus); - - if (cpu_lower > cpu_upper) - log_syntax(unit, LOG_WARNING, filename, line, 0, "Range '%s' is invalid, %u > %u", word, cpu_lower, cpu_upper); - else - for (cpu = cpu_lower; cpu <= cpu_upper; cpu++) - CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c); - } - - /* On success, sets *cpu_set and returns ncpus for the system. */ - if (c) { - *cpu_set = c; - c = NULL; - } - - return (int) ncpus; -} diff --git a/src/basic/cpu-set-util.h b/src/basic/cpu-set-util.h deleted file mode 100644 index 6f49d9afb0..0000000000 --- a/src/basic/cpu-set-util.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010-2015 Lennart Poettering - Copyright 2015 Filipe Brandenburger - - 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 <sched.h> - -#include "macro.h" - -DEFINE_TRIVIAL_CLEANUP_FUNC(cpu_set_t*, CPU_FREE); -#define _cleanup_cpu_free_ _cleanup_(CPU_FREEp) - -cpu_set_t* cpu_set_malloc(unsigned *ncpus); - -int parse_cpu_set_and_warn(const char *rvalue, cpu_set_t **cpu_set, const char *unit, const char *filename, unsigned line, const char *lvalue); diff --git a/src/basic/def.h b/src/basic/def.h deleted file mode 100644 index 2266eff650..0000000000 --- a/src/basic/def.h +++ /dev/null @@ -1,90 +0,0 @@ -#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 "util.h" - -#define DEFAULT_TIMEOUT_USEC (90*USEC_PER_SEC) -#define DEFAULT_RESTART_USEC (100*USEC_PER_MSEC) -#define DEFAULT_CONFIRM_USEC (30*USEC_PER_SEC) - -#define DEFAULT_START_LIMIT_INTERVAL (10*USEC_PER_SEC) -#define DEFAULT_START_LIMIT_BURST 5 - -/* The default time after which exit-on-idle services exit. This - * should be kept lower than the watchdog timeout, because otherwise - * the watchdog pings will keep the loop busy. */ -#define DEFAULT_EXIT_USEC (30*USEC_PER_SEC) - -/* The default value for the net.unix.max_dgram_qlen sysctl */ -#define DEFAULT_UNIX_MAX_DGRAM_QLEN 512UL - -#define SYSTEMD_CGROUP_CONTROLLER "name=systemd" - -#define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT -#define SIGNALS_IGNORE SIGPIPE - -#ifdef HAVE_SPLIT_USR -#define KBD_KEYMAP_DIRS \ - "/usr/share/keymaps/\0" \ - "/usr/share/kbd/keymaps/\0" \ - "/usr/lib/kbd/keymaps/\0" \ - "/lib/kbd/keymaps/\0" -#else -#define KBD_KEYMAP_DIRS \ - "/usr/share/keymaps/\0" \ - "/usr/share/kbd/keymaps/\0" \ - "/usr/lib/kbd/keymaps/\0" -#endif - -#define UNIX_SYSTEM_BUS_ADDRESS "unix:path=/var/run/dbus/system_bus_socket" -#define KERNEL_SYSTEM_BUS_ADDRESS "kernel:path=/sys/fs/kdbus/0-system/bus" -#define DEFAULT_SYSTEM_BUS_ADDRESS KERNEL_SYSTEM_BUS_ADDRESS ";" UNIX_SYSTEM_BUS_ADDRESS -#define UNIX_USER_BUS_ADDRESS_FMT "unix:path=%s/bus" -#define KERNEL_USER_BUS_ADDRESS_FMT "kernel:path=/sys/fs/kdbus/"UID_FMT"-user/bus" - -#define PLYMOUTH_SOCKET { \ - .un.sun_family = AF_UNIX, \ - .un.sun_path = "\0/org/freedesktop/plymouthd", \ - } - -#ifndef TTY_GID -#define TTY_GID 5 -#endif - -#define NOTIFY_FD_MAX 768 -#define NOTIFY_BUFFER_MAX PIPE_BUF - -#ifdef HAVE_SPLIT_USR -#define _CONF_PATHS_SPLIT_USR(n) "/lib/" n "\0" -#else -#define _CONF_PATHS_SPLIT_USR(n) -#endif - -/* Return a nulstr for a standard cascade of configuration paths, - * suitable to pass to conf_files_list_nulstr() or config_parse_many_nulstr() - * to implement drop-in directories for extending configuration - * files. */ -#define CONF_PATHS_NULSTR(n) \ - "/etc/" n "\0" \ - "/run/" n "\0" \ - "/usr/local/lib/" n "\0" \ - "/usr/lib/" n "\0" \ - _CONF_PATHS_SPLIT_USR(n) diff --git a/src/basic/device-nodes.c b/src/basic/device-nodes.c deleted file mode 100644 index 38c0628a90..0000000000 --- a/src/basic/device-nodes.c +++ /dev/null @@ -1,80 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2011 Kay Sievers - - 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 <errno.h> -#include <stdio.h> -#include <string.h> - -#include "device-nodes.h" -#include "utf8.h" - -int whitelisted_char_for_devnode(char c, const char *white) { - - if ((c >= '0' && c <= '9') || - (c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z') || - strchr("#+-.:=@_", c) != NULL || - (white != NULL && strchr(white, c) != NULL)) - return 1; - - return 0; -} - -int encode_devnode_name(const char *str, char *str_enc, size_t len) { - size_t i, j; - - if (str == NULL || str_enc == NULL) - return -EINVAL; - - for (i = 0, j = 0; str[i] != '\0'; i++) { - int seqlen; - - seqlen = utf8_encoded_valid_unichar(&str[i]); - if (seqlen > 1) { - - if (len-j < (size_t)seqlen) - return -EINVAL; - - memcpy(&str_enc[j], &str[i], seqlen); - j += seqlen; - i += (seqlen-1); - - } else if (str[i] == '\\' || !whitelisted_char_for_devnode(str[i], NULL)) { - - if (len-j < 4) - return -EINVAL; - - sprintf(&str_enc[j], "\\x%02x", (unsigned char) str[i]); - j += 4; - - } else { - if (len-j < 1) - return -EINVAL; - - str_enc[j] = str[i]; - j++; - } - } - - if (len-j < 1) - return -EINVAL; - - str_enc[j] = '\0'; - return 0; -} diff --git a/src/basic/device-nodes.h b/src/basic/device-nodes.h deleted file mode 100644 index 94f385abcb..0000000000 --- a/src/basic/device-nodes.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 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 <sys/types.h> - -int encode_devnode_name(const char *str, char *str_enc, size_t len); -int whitelisted_char_for_devnode(char c, const char *additional); diff --git a/src/basic/dirent-util.c b/src/basic/dirent-util.c deleted file mode 100644 index 59067121b7..0000000000 --- a/src/basic/dirent-util.c +++ /dev/null @@ -1,74 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010-2012 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 <sys/stat.h> - -#include "dirent-util.h" -#include "path-util.h" -#include "string-util.h" - -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; -} - -bool dirent_is_file(const struct dirent *de) { - assert(de); - - if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN)) - return false; - - if (hidden_or_backup_file(de->d_name)) - return false; - - return true; -} - -bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) { - assert(de); - - if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN)) - return false; - - if (de->d_name[0] == '.') - return false; - - return endswith(de->d_name, suffix); -} diff --git a/src/basic/dirent-util.h b/src/basic/dirent-util.h deleted file mode 100644 index b91d04908f..0000000000 --- a/src/basic/dirent-util.h +++ /dev/null @@ -1,52 +0,0 @@ -#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 <errno.h> -#include <stdbool.h> - -#include "macro.h" -#include "path-util.h" - -int dirent_ensure_type(DIR *d, struct dirent *de); - -bool dirent_is_file(const struct dirent *de) _pure_; -bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pure_; - -#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_or_backup_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 diff --git a/src/basic/env-util.c b/src/basic/env-util.c deleted file mode 100644 index b74290d6fd..0000000000 --- a/src/basic/env-util.c +++ /dev/null @@ -1,623 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 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 <errno.h> -#include <limits.h> -#include <stdarg.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "env-util.h" -#include "extract-word.h" -#include "macro.h" -#include "parse-util.h" -#include "string-util.h" -#include "strv.h" -#include "utf8.h" - -#define VALID_CHARS_ENV_NAME \ - DIGITS LETTERS \ - "_" - -#ifndef ARG_MAX -#define ARG_MAX ((size_t) sysconf(_SC_ARG_MAX)) -#endif - -static bool env_name_is_valid_n(const char *e, size_t n) { - const char *p; - - if (!e) - return false; - - if (n <= 0) - return false; - - if (e[0] >= '0' && e[0] <= '9') - return false; - - /* POSIX says the overall size of the environment block cannot - * be > ARG_MAX, an individual assignment hence cannot be - * either. Discounting the equal sign and trailing NUL this - * hence leaves ARG_MAX-2 as longest possible variable - * name. */ - if (n > ARG_MAX - 2) - return false; - - for (p = e; p < e + n; p++) - if (!strchr(VALID_CHARS_ENV_NAME, *p)) - return false; - - return true; -} - -bool env_name_is_valid(const char *e) { - if (!e) - return false; - - return env_name_is_valid_n(e, strlen(e)); -} - -bool env_value_is_valid(const char *e) { - if (!e) - return false; - - if (!utf8_is_valid(e)) - return false; - - /* bash allows tabs in environment variables, and so should - * we */ - if (string_has_cc(e, "\t")) - return false; - - /* POSIX says the overall size of the environment block cannot - * be > ARG_MAX, an individual assignment hence cannot be - * either. Discounting the shortest possible variable name of - * length 1, the equal sign and trailing NUL this hence leaves - * ARG_MAX-3 as longest possible variable value. */ - if (strlen(e) > ARG_MAX - 3) - return false; - - return true; -} - -bool env_assignment_is_valid(const char *e) { - const char *eq; - - eq = strchr(e, '='); - if (!eq) - return false; - - if (!env_name_is_valid_n(e, eq - e)) - return false; - - if (!env_value_is_valid(eq + 1)) - return false; - - /* POSIX says the overall size of the environment block cannot - * be > ARG_MAX, hence the individual variable assignments - * cannot be either, but let's leave room for one trailing NUL - * byte. */ - if (strlen(e) > ARG_MAX - 1) - return false; - - return true; -} - -bool strv_env_is_valid(char **e) { - char **p, **q; - - STRV_FOREACH(p, e) { - size_t k; - - if (!env_assignment_is_valid(*p)) - return false; - - /* Check if there are duplicate assginments */ - k = strcspn(*p, "="); - STRV_FOREACH(q, p + 1) - if (strneq(*p, *q, k) && (*q)[k] == '=') - return false; - } - - return true; -} - -bool strv_env_name_is_valid(char **l) { - char **p, **q; - - STRV_FOREACH(p, l) { - if (!env_name_is_valid(*p)) - return false; - - STRV_FOREACH(q, p + 1) - if (streq(*p, *q)) - return false; - } - - return true; -} - -bool strv_env_name_or_assignment_is_valid(char **l) { - char **p, **q; - - STRV_FOREACH(p, l) { - if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p)) - return false; - - STRV_FOREACH(q, p + 1) - if (streq(*p, *q)) - return false; - } - - return true; -} - -static int env_append(char **r, char ***k, char **a) { - assert(r); - assert(k); - - if (!a) - return 0; - - /* Add the entries of a to *k unless they already exist in *r - * in which case they are overridden instead. This assumes - * there is enough space in the r array. */ - - for (; *a; a++) { - char **j; - size_t n; - - n = strcspn(*a, "="); - - if ((*a)[n] == '=') - n++; - - for (j = r; j < *k; j++) - if (strneq(*j, *a, n)) - break; - - if (j >= *k) - (*k)++; - else - free(*j); - - *j = strdup(*a); - if (!*j) - return -ENOMEM; - } - - return 0; -} - -char **strv_env_merge(unsigned n_lists, ...) { - size_t n = 0; - char **l, **k, **r; - va_list ap; - unsigned i; - - /* Merges an arbitrary number of environment sets */ - - va_start(ap, n_lists); - for (i = 0; i < n_lists; i++) { - l = va_arg(ap, char**); - n += strv_length(l); - } - va_end(ap); - - r = new(char*, n+1); - if (!r) - return NULL; - - k = r; - - va_start(ap, n_lists); - for (i = 0; i < n_lists; i++) { - l = va_arg(ap, char**); - if (env_append(r, &k, l) < 0) - goto fail; - } - va_end(ap); - - *k = NULL; - - return r; - -fail: - va_end(ap); - strv_free(r); - - return NULL; -} - -_pure_ static bool env_match(const char *t, const char *pattern) { - assert(t); - assert(pattern); - - /* pattern a matches string a - * a matches a= - * a matches a=b - * a= matches a= - * a=b matches a=b - * a= does not match a - * a=b does not match a= - * a=b does not match a - * a=b does not match a=c */ - - if (streq(t, pattern)) - return true; - - if (!strchr(pattern, '=')) { - size_t l = strlen(pattern); - - return strneq(t, pattern, l) && t[l] == '='; - } - - return false; -} - -char **strv_env_delete(char **x, unsigned n_lists, ...) { - size_t n, i = 0; - char **k, **r; - va_list ap; - - /* Deletes every entry from x that is mentioned in the other - * string lists */ - - n = strv_length(x); - - r = new(char*, n+1); - if (!r) - return NULL; - - STRV_FOREACH(k, x) { - unsigned v; - - va_start(ap, n_lists); - for (v = 0; v < n_lists; v++) { - char **l, **j; - - l = va_arg(ap, char**); - STRV_FOREACH(j, l) - if (env_match(*k, *j)) - goto skip; - } - va_end(ap); - - r[i] = strdup(*k); - if (!r[i]) { - strv_free(r); - return NULL; - } - - i++; - continue; - - skip: - va_end(ap); - } - - r[i] = NULL; - - assert(i <= n); - - return r; -} - -char **strv_env_unset(char **l, const char *p) { - - char **f, **t; - - if (!l) - return NULL; - - assert(p); - - /* Drops every occurrence of the env var setting p in the - * string list. Edits in-place. */ - - for (f = t = l; *f; f++) { - - if (env_match(*f, p)) { - free(*f); - continue; - } - - *(t++) = *f; - } - - *t = NULL; - return l; -} - -char **strv_env_unset_many(char **l, ...) { - - char **f, **t; - - if (!l) - return NULL; - - /* Like strv_env_unset() but applies many at once. Edits in-place. */ - - for (f = t = l; *f; f++) { - bool found = false; - const char *p; - va_list ap; - - va_start(ap, l); - - while ((p = va_arg(ap, const char*))) { - if (env_match(*f, p)) { - found = true; - break; - } - } - - va_end(ap); - - if (found) { - free(*f); - continue; - } - - *(t++) = *f; - } - - *t = NULL; - return l; -} - -char **strv_env_set(char **x, const char *p) { - - char **k, **r; - char* m[2] = { (char*) p, NULL }; - - /* Overrides the env var setting of p, returns a new copy */ - - r = new(char*, strv_length(x)+2); - if (!r) - return NULL; - - k = r; - if (env_append(r, &k, x) < 0) - goto fail; - - if (env_append(r, &k, m) < 0) - goto fail; - - *k = NULL; - - return r; - -fail: - strv_free(r); - return NULL; -} - -char *strv_env_get_n(char **l, const char *name, size_t k) { - char **i; - - assert(name); - - if (k <= 0) - return NULL; - - STRV_FOREACH(i, l) - if (strneq(*i, name, k) && - (*i)[k] == '=') - return *i + k + 1; - - return NULL; -} - -char *strv_env_get(char **l, const char *name) { - assert(name); - - return strv_env_get_n(l, name, strlen(name)); -} - -char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) { - char **p, **q; - int k = 0; - - STRV_FOREACH(p, e) { - size_t n; - bool duplicate = false; - - if (!env_assignment_is_valid(*p)) { - if (invalid_callback) - invalid_callback(*p, userdata); - free(*p); - continue; - } - - n = strcspn(*p, "="); - STRV_FOREACH(q, p + 1) - if (strneq(*p, *q, n) && (*q)[n] == '=') { - duplicate = true; - break; - } - - if (duplicate) { - free(*p); - continue; - } - - e[k++] = *p; - } - - if (e) - e[k] = NULL; - - return e; -} - -char *replace_env(const char *format, char **env) { - enum { - WORD, - CURLY, - VARIABLE - } state = WORD; - - const char *e, *word = format; - char *r = NULL, *k; - - assert(format); - - for (e = format; *e; e ++) { - - switch (state) { - - case WORD: - if (*e == '$') - state = CURLY; - break; - - case CURLY: - if (*e == '{') { - k = strnappend(r, word, e-word-1); - if (!k) - goto fail; - - free(r); - r = k; - - word = e-1; - state = VARIABLE; - - } else if (*e == '$') { - k = strnappend(r, word, e-word); - if (!k) - goto fail; - - free(r); - r = k; - - word = e+1; - state = WORD; - } else - state = WORD; - break; - - case VARIABLE: - if (*e == '}') { - const char *t; - - t = strempty(strv_env_get_n(env, word+2, e-word-2)); - - k = strappend(r, t); - if (!k) - goto fail; - - free(r); - r = k; - - word = e+1; - state = WORD; - } - break; - } - } - - k = strnappend(r, word, e-word); - if (!k) - goto fail; - - free(r); - return k; - -fail: - return mfree(r); -} - -char **replace_env_argv(char **argv, char **env) { - char **ret, **i; - unsigned k = 0, l = 0; - - l = strv_length(argv); - - ret = new(char*, l+1); - if (!ret) - return NULL; - - STRV_FOREACH(i, argv) { - - /* If $FOO appears as single word, replace it by the split up variable */ - if ((*i)[0] == '$' && (*i)[1] != '{' && (*i)[1] != '$') { - char *e; - char **w, **m = NULL; - unsigned q; - - e = strv_env_get(env, *i+1); - if (e) { - int r; - - r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES); - if (r < 0) { - ret[k] = NULL; - strv_free(ret); - return NULL; - } - } else - m = NULL; - - q = strv_length(m); - l = l + q - 1; - - w = realloc(ret, sizeof(char*) * (l+1)); - if (!w) { - ret[k] = NULL; - strv_free(ret); - strv_free(m); - return NULL; - } - - ret = w; - if (m) { - memcpy(ret + k, m, q * sizeof(char*)); - free(m); - } - - k += q; - continue; - } - - /* If ${FOO} appears as part of a word, replace it by the variable as-is */ - ret[k] = replace_env(*i, env); - if (!ret[k]) { - strv_free(ret); - return NULL; - } - k++; - } - - ret[k] = NULL; - return ret; -} - -int getenv_bool(const char *p) { - const char *e; - - e = getenv(p); - if (!e) - return -ENXIO; - - return parse_boolean(e); -} diff --git a/src/basic/env-util.h b/src/basic/env-util.h deleted file mode 100644 index b1fef704c2..0000000000 --- a/src/basic/env-util.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 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 <stddef.h> - -#include "macro.h" - -bool env_name_is_valid(const char *e); -bool env_value_is_valid(const char *e); -bool env_assignment_is_valid(const char *e); - -char *replace_env(const char *format, char **env); -char **replace_env_argv(char **argv, char **env); - -bool strv_env_is_valid(char **e); -#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL) -char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata); - -bool strv_env_name_is_valid(char **l); -bool strv_env_name_or_assignment_is_valid(char **l); - -char **strv_env_merge(unsigned n_lists, ...); -char **strv_env_delete(char **x, unsigned n_lists, ...); /* New copy */ - -char **strv_env_set(char **x, const char *p); /* New copy ... */ -char **strv_env_unset(char **l, const char *p); /* In place ... */ -char **strv_env_unset_many(char **l, ...) _sentinel_; - -char *strv_env_get_n(char **l, const char *name, size_t k) _pure_; -char *strv_env_get(char **x, const char *n) _pure_; - -int getenv_bool(const char *p); diff --git a/src/basic/errno-list.c b/src/basic/errno-list.c deleted file mode 100644 index 31b66bad5e..0000000000 --- a/src/basic/errno-list.c +++ /dev/null @@ -1,57 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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.h> - -#include "errno-list.h" -#include "macro.h" - -static const struct errno_name* lookup_errno(register const char *str, - register unsigned int len); - -#include "errno-from-name.h" -#include "errno-to-name.h" - -const char *errno_to_name(int id) { - - if (id < 0) - id = -id; - - if (id >= (int) ELEMENTSOF(errno_names)) - return NULL; - - return errno_names[id]; -} - -int errno_from_name(const char *name) { - const struct errno_name *sc; - - assert(name); - - sc = lookup_errno(name, strlen(name)); - if (!sc) - return -EINVAL; - - assert(sc->id > 0); - return sc->id; -} - -int errno_max(void) { - return ELEMENTSOF(errno_names); -} diff --git a/src/basic/errno-list.h b/src/basic/errno-list.h deleted file mode 100644 index 4eec0cc786..0000000000 --- a/src/basic/errno-list.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 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/>. -***/ - -const char *errno_to_name(int id); -int errno_from_name(const char *name); - -int errno_max(void); diff --git a/src/basic/escape.c b/src/basic/escape.c deleted file mode 100644 index 4a1ec4505e..0000000000 --- a/src/basic/escape.c +++ /dev/null @@ -1,502 +0,0 @@ -/*** - 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 <errno.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "escape.h" -#include "hexdecoct.h" -#include "macro.h" -#include "utf8.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_length(const char *s, size_t n) { - const char *f; - char *r, *t; - - assert(s || n == 0); - - /* Does C style string escaping. May be reversed with - * cunescape(). */ - - r = new(char, n*4 + 1); - if (!r) - return NULL; - - for (f = s, t = r; f < s + n; f++) - t += cescape_char(*f, t); - - *t = 0; - - return r; -} - -char *cescape(const char *s) { - assert(s); - - return cescape_length(s, strlen(s)); -} - -int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit) { - int r = 1; - - assert(p); - assert(*p); - assert(ret); - - /* Unescapes C style. Returns the unescaped character in ret. - * Sets *eight_bit to true if the escaped sequence either fits in - * one byte in UTF-8 or is a non-unicode literal byte and should - * instead be copied directly. - */ - - if (length != (size_t) -1 && length < 1) - return -EINVAL; - - switch (p[0]) { - - case 'a': - *ret = '\a'; - break; - case 'b': - *ret = '\b'; - break; - case 'f': - *ret = '\f'; - break; - case 'n': - *ret = '\n'; - break; - case 'r': - *ret = '\r'; - break; - case 't': - *ret = '\t'; - break; - case 'v': - *ret = '\v'; - break; - case '\\': - *ret = '\\'; - break; - case '"': - *ret = '"'; - break; - case '\'': - *ret = '\''; - break; - - case 's': - /* This is an extension of the XDG syntax files */ - *ret = ' '; - break; - - case 'x': { - /* hexadecimal encoding */ - int a, b; - - if (length != (size_t) -1 && length < 3) - return -EINVAL; - - a = unhexchar(p[1]); - if (a < 0) - return -EINVAL; - - b = unhexchar(p[2]); - if (b < 0) - return -EINVAL; - - /* Don't allow NUL bytes */ - if (a == 0 && b == 0) - return -EINVAL; - - *ret = (a << 4U) | b; - *eight_bit = true; - r = 3; - break; - } - - case 'u': { - /* C++11 style 16bit unicode */ - - int a[4]; - 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; - - *ret = c; - r = 5; - break; - } - - case 'U': { - /* C++11 style 32bit unicode */ - - int a[8]; - unsigned i; - char32_t c; - - if (length != (size_t) -1 && length < 9) - return -EINVAL; - - for (i = 0; i < 8; i++) { - a[i] = unhexchar(p[1 + i]); - if (a[i] < 0) - return a[i]; - } - - c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) | - ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] << 8U) | ((uint32_t) a[6] << 4U) | (uint32_t) a[7]; - - /* Don't allow 0 chars */ - if (c == 0) - return -EINVAL; - - /* Don't allow invalid code points */ - if (!unichar_is_valid(c)) - return -EINVAL; - - *ret = c; - r = 9; - break; - } - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': { - /* octal encoding */ - int a, b, c; - char32_t m; - - if (length != (size_t) -1 && length < 3) - return -EINVAL; - - a = unoctchar(p[0]); - if (a < 0) - return -EINVAL; - - b = unoctchar(p[1]); - if (b < 0) - return -EINVAL; - - c = unoctchar(p[2]); - if (c < 0) - return -EINVAL; - - /* don't allow NUL bytes */ - if (a == 0 && b == 0 && c == 0) - return -EINVAL; - - /* Don't allow bytes above 255 */ - m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c; - if (m > 255) - return -EINVAL; - - *ret = m; - *eight_bit = true; - r = 3; - break; - } - - default: - return -EINVAL; - } - - return r; -} - -int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) { - char *r, *t; - const char *f; - size_t pl; - - assert(s); - assert(ret); - - /* Undoes C style string escaping, and optionally prefixes it. */ - - pl = 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; - bool eight_bit = false; - char32_t u; - int k; - - remaining = s + length - f; - assert(remaining > 0); - - if (*f != '\\') { - /* A literal, copy verbatim */ - *(t++) = *f; - continue; - } - - if (remaining == 1) { - if (flags & UNESCAPE_RELAX) { - /* A trailing backslash, copy verbatim */ - *(t++) = *f; - continue; - } - - free(r); - return -EINVAL; - } - - k = cunescape_one(f + 1, remaining - 1, &u, &eight_bit); - if (k < 0) { - if (flags & UNESCAPE_RELAX) { - /* Invalid escape code, let's take it literal then */ - *(t++) = '\\'; - continue; - } - - free(r); - return k; - } - - f += k; - if (eight_bit) - /* One byte? Set directly as specified */ - *(t++) = u; - else - /* Otherwise encode as multi-byte UTF-8 */ - t += utf8_encode_unichar(t, u); - } - - *t = 0; - - *ret = r; - return t - r; -} - -int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) { - return cunescape_length_with_prefix(s, length, NULL, flags, ret); -} - -int cunescape(const char *s, UnescapeFlags flags, char **ret) { - return cunescape_length(s, strlen(s), flags, ret); -} - -char *xescape(const char *s, const char *bad) { - char *r, *t; - const char *f; - - /* Escapes all chars in bad, in addition to \ and all special - * chars, in \xFF style escaping. May be reversed with - * cunescape(). */ - - r = new(char, strlen(s) * 4 + 1); - if (!r) - return NULL; - - for (f = s, t = r; *f; f++) { - - if ((*f < ' ') || (*f >= 127) || - (*f == '\\') || strchr(bad, *f)) { - *(t++) = '\\'; - *(t++) = 'x'; - *(t++) = hexchar(*f >> 4); - *(t++) = hexchar(*f); - } else - *(t++) = *f; - } - - *t = 0; - - return r; -} - -char *octescape(const char *s, size_t len) { - char *r, *t; - const char *f; - - /* Escapes all chars in bad, in addition to \ and " chars, - * in \nnn style escaping. */ - - r = new(char, len * 4 + 1); - if (!r) - return NULL; - - for (f = s, t = r; f < s + len; f++) { - - if (*f < ' ' || *f >= 127 || *f == '\\' || *f == '"') { - *(t++) = '\\'; - *(t++) = '0' + (*f >> 6); - *(t++) = '0' + ((*f >> 3) & 8); - *(t++) = '0' + (*f & 8); - } else - *(t++) = *f; - } - - *t = 0; - - return r; - -} - -static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad) { - 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/basic/escape.h b/src/basic/escape.h deleted file mode 100644 index deaa4def28..0000000000 --- a/src/basic/escape.h +++ /dev/null @@ -1,54 +0,0 @@ -#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 <stddef.h> -#include <stdint.h> -#include <sys/types.h> -#include <uchar.h> - -#include "string-util.h" -#include "missing.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); -char *cescape_length(const char *s, size_t n); -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, char32_t *ret, bool *eight_bit); - -char *xescape(const char *s, const char *bad); -char *octescape(const char *s, size_t len); - -char *shell_escape(const char *s, const char *bad); -char *shell_maybe_quote(const char *s); diff --git a/src/basic/ether-addr-util.c b/src/basic/ether-addr-util.c deleted file mode 100644 index 5697e8d132..0000000000 --- a/src/basic/ether-addr-util.c +++ /dev/null @@ -1,125 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Tom Gundersen - - 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 <stdio.h> -#include <sys/types.h> - -#include "ether-addr-util.h" -#include "macro.h" -#include "string-util.h" - -char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) { - assert(addr); - assert(buffer); - - /* Like ether_ntoa() but uses %02x instead of %x to print - * ethernet addresses, which makes them look less funny. Also, - * doesn't use a static buffer. */ - - sprintf(buffer, "%02x:%02x:%02x:%02x:%02x:%02x", - addr->ether_addr_octet[0], - addr->ether_addr_octet[1], - addr->ether_addr_octet[2], - addr->ether_addr_octet[3], - addr->ether_addr_octet[4], - addr->ether_addr_octet[5]); - - return buffer; -} - -bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) { - assert(a); - assert(b); - - return a->ether_addr_octet[0] == b->ether_addr_octet[0] && - a->ether_addr_octet[1] == b->ether_addr_octet[1] && - a->ether_addr_octet[2] == b->ether_addr_octet[2] && - a->ether_addr_octet[3] == b->ether_addr_octet[3] && - a->ether_addr_octet[4] == b->ether_addr_octet[4] && - a->ether_addr_octet[5] == b->ether_addr_octet[5]; -} - -int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset) { - size_t pos = 0, n, field; - char sep = '\0'; - const char *hex = HEXDIGITS, *hexoff; - size_t x; - bool touched; - -#define parse_fields(v) \ - for (field = 0; field < ELEMENTSOF(v); field++) { \ - touched = false; \ - for (n = 0; n < (2 * sizeof(v[0])); n++) { \ - if (s[pos] == '\0') \ - break; \ - hexoff = strchr(hex, s[pos]); \ - if (hexoff == NULL) \ - break; \ - assert(hexoff >= hex); \ - x = hexoff - hex; \ - if (x >= 16) \ - x -= 6; /* A-F */ \ - assert(x < 16); \ - touched = true; \ - v[field] <<= 4; \ - v[field] += x; \ - pos++; \ - } \ - if (!touched) \ - return -EINVAL; \ - if (field < (ELEMENTSOF(v)-1)) { \ - if (s[pos] != sep) \ - return -EINVAL; \ - else \ - pos++; \ - } \ - } - - assert(s); - assert(ret); - - sep = s[strspn(s, hex)]; - if (sep == '\n') - return -EINVAL; - if (strchr(":.-", sep) == NULL) - return -EINVAL; - - if (sep == '.') { - uint16_t shorts[3] = { 0 }; - - parse_fields(shorts); - - for (n = 0; n < ELEMENTSOF(shorts); n++) { - ret->ether_addr_octet[2*n] = ((shorts[n] & (uint16_t)0xff00) >> 8); - ret->ether_addr_octet[2*n + 1] = (shorts[n] & (uint16_t)0x00ff); - } - } else { - struct ether_addr out = { .ether_addr_octet = { 0 } }; - - parse_fields(out.ether_addr_octet); - - for (n = 0; n < ELEMENTSOF(out.ether_addr_octet); n++) - ret->ether_addr_octet[n] = out.ether_addr_octet[n]; - } - - if (offset) - *offset = pos; - return 0; -} diff --git a/src/basic/ether-addr-util.h b/src/basic/ether-addr-util.h deleted file mode 100644 index 74e125a95f..0000000000 --- a/src/basic/ether-addr-util.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Tom Gundersen - - 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 <stdbool.h> - -#define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X" -#define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5] - -#define ETHER_ADDR_TO_STRING_MAX (3*6) -char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]); - -bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b); - -#define ETHER_ADDR_NULL ((const struct ether_addr){}) - -static inline bool ether_addr_is_null(const struct ether_addr *addr) { - return ether_addr_equal(addr, ÐER_ADDR_NULL); -} - -int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset); diff --git a/src/basic/exit-status.c b/src/basic/exit-status.c deleted file mode 100644 index 59557f8afe..0000000000 --- a/src/basic/exit-status.c +++ /dev/null @@ -1,223 +0,0 @@ -/*** - 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 <signal.h> -#include <stdlib.h> - -#include "exit-status.h" -#include "macro.h" -#include "set.h" - -const char* exit_status_to_string(int status, ExitStatusLevel level) { - - /* We cast to int here, so that -Wenum doesn't complain that - * EXIT_SUCCESS/EXIT_FAILURE aren't in the enum */ - - switch (status) { - - case EXIT_SUCCESS: - return "SUCCESS"; - - case EXIT_FAILURE: - return "FAILURE"; - } - - if (IN_SET(level, EXIT_STATUS_SYSTEMD, EXIT_STATUS_LSB)) { - switch (status) { - - case EXIT_CHDIR: - return "CHDIR"; - - case EXIT_NICE: - return "NICE"; - - case EXIT_FDS: - return "FDS"; - - case EXIT_EXEC: - return "EXEC"; - - case EXIT_MEMORY: - return "MEMORY"; - - case EXIT_LIMITS: - return "LIMITS"; - - case EXIT_OOM_ADJUST: - return "OOM_ADJUST"; - - case EXIT_SIGNAL_MASK: - return "SIGNAL_MASK"; - - case EXIT_STDIN: - return "STDIN"; - - case EXIT_STDOUT: - return "STDOUT"; - - case EXIT_CHROOT: - return "CHROOT"; - - case EXIT_IOPRIO: - return "IOPRIO"; - - case EXIT_TIMERSLACK: - return "TIMERSLACK"; - - case EXIT_SECUREBITS: - return "SECUREBITS"; - - case EXIT_SETSCHEDULER: - return "SETSCHEDULER"; - - case EXIT_CPUAFFINITY: - return "CPUAFFINITY"; - - case EXIT_GROUP: - return "GROUP"; - - case EXIT_USER: - return "USER"; - - case EXIT_CAPABILITIES: - return "CAPABILITIES"; - - case EXIT_CGROUP: - return "CGROUP"; - - case EXIT_SETSID: - return "SETSID"; - - case EXIT_CONFIRM: - return "CONFIRM"; - - case EXIT_STDERR: - return "STDERR"; - - case EXIT_PAM: - return "PAM"; - - case EXIT_NETWORK: - return "NETWORK"; - - case EXIT_NAMESPACE: - return "NAMESPACE"; - - case EXIT_NO_NEW_PRIVILEGES: - return "NO_NEW_PRIVILEGES"; - - case EXIT_SECCOMP: - return "SECCOMP"; - - case EXIT_SELINUX_CONTEXT: - return "SELINUX_CONTEXT"; - - case EXIT_PERSONALITY: - return "PERSONALITY"; - - case EXIT_APPARMOR_PROFILE: - return "APPARMOR"; - - case EXIT_ADDRESS_FAMILIES: - return "ADDRESS_FAMILIES"; - - case EXIT_RUNTIME_DIRECTORY: - return "RUNTIME_DIRECTORY"; - - case EXIT_MAKE_STARTER: - return "MAKE_STARTER"; - - case EXIT_CHOWN: - return "CHOWN"; - - case EXIT_SMACK_PROCESS_LABEL: - return "SMACK_PROCESS_LABEL"; - } - } - - if (level == EXIT_STATUS_LSB) { - switch (status) { - - case EXIT_INVALIDARGUMENT: - return "INVALIDARGUMENT"; - - case EXIT_NOTIMPLEMENTED: - return "NOTIMPLEMENTED"; - - case EXIT_NOPERMISSION: - return "NOPERMISSION"; - - case EXIT_NOTINSTALLED: - return "NOTINSTALLED"; - - case EXIT_NOTCONFIGURED: - return "NOTCONFIGURED"; - - case EXIT_NOTRUNNING: - return "NOTRUNNING"; - } - } - - return NULL; -} - -bool is_clean_exit(int code, int status, ExitClean clean, ExitStatusSet *success_status) { - - if (code == CLD_EXITED) - return status == 0 || - (success_status && - set_contains(success_status->status, INT_TO_PTR(status))); - - /* If a daemon does not implement handlers for some of the signals that's not considered an unclean shutdown */ - if (code == CLD_KILLED) - return - (clean == EXIT_CLEAN_DAEMON && IN_SET(status, SIGHUP, SIGINT, SIGTERM, SIGPIPE)) || - (success_status && - set_contains(success_status->signal, INT_TO_PTR(status))); - - return false; -} - -void exit_status_set_free(ExitStatusSet *x) { - assert(x); - - x->status = set_free(x->status); - x->signal = set_free(x->signal); -} - -bool exit_status_set_is_empty(ExitStatusSet *x) { - if (!x) - return true; - - return set_isempty(x->status) && set_isempty(x->signal); -} - -bool exit_status_set_test(ExitStatusSet *x, int code, int status) { - - if (exit_status_set_is_empty(x)) - return false; - - if (code == CLD_EXITED && set_contains(x->status, INT_TO_PTR(status))) - return true; - - if (IN_SET(code, CLD_KILLED, CLD_DUMPED) && set_contains(x->signal, INT_TO_PTR(status))) - return true; - - return false; -} diff --git a/src/basic/exit-status.h b/src/basic/exit-status.h deleted file mode 100644 index 0cfdfd7891..0000000000 --- a/src/basic/exit-status.h +++ /dev/null @@ -1,110 +0,0 @@ -#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 "hashmap.h" -#include "macro.h" -#include "set.h" - -/* This defines pretty names for the LSB 'start' verb exit codes. Note that they shouldn't be confused with the LSB - * 'status' verb exit codes which are defined very differently. For details see: - * - * https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html - */ - -enum { - /* EXIT_SUCCESS defined by libc */ - /* EXIT_FAILURE defined by libc */ - EXIT_INVALIDARGUMENT = 2, - EXIT_NOTIMPLEMENTED = 3, - EXIT_NOPERMISSION = 4, - EXIT_NOTINSTALLED = 5, - EXIT_NOTCONFIGURED = 6, - EXIT_NOTRUNNING = 7, - - /* The LSB suggests that error codes >= 200 are "reserved". We - * use them here under the assumption that they hence are - * unused by init scripts. */ - - EXIT_CHDIR = 200, - EXIT_NICE, - EXIT_FDS, - EXIT_EXEC, - EXIT_MEMORY, - EXIT_LIMITS, - EXIT_OOM_ADJUST, - EXIT_SIGNAL_MASK, - EXIT_STDIN, - EXIT_STDOUT, - EXIT_CHROOT, /* 210 */ - EXIT_IOPRIO, - EXIT_TIMERSLACK, - EXIT_SECUREBITS, - EXIT_SETSCHEDULER, - EXIT_CPUAFFINITY, - EXIT_GROUP, - EXIT_USER, - EXIT_CAPABILITIES, - EXIT_CGROUP, - EXIT_SETSID, /* 220 */ - EXIT_CONFIRM, - EXIT_STDERR, - _EXIT_RESERVED, /* used to be tcpwrap, don't reuse! */ - EXIT_PAM, - EXIT_NETWORK, - EXIT_NAMESPACE, - EXIT_NO_NEW_PRIVILEGES, - EXIT_SECCOMP, - EXIT_SELINUX_CONTEXT, - EXIT_PERSONALITY, /* 230 */ - EXIT_APPARMOR_PROFILE, - EXIT_ADDRESS_FAMILIES, - EXIT_RUNTIME_DIRECTORY, - EXIT_MAKE_STARTER, - EXIT_CHOWN, - EXIT_SMACK_PROCESS_LABEL, -}; - -typedef enum ExitStatusLevel { - EXIT_STATUS_MINIMAL, /* only cover libc EXIT_STATUS/EXIT_FAILURE */ - EXIT_STATUS_SYSTEMD, /* cover libc and systemd's own exit codes */ - EXIT_STATUS_LSB, /* cover libc, systemd's own and LSB exit codes */ - EXIT_STATUS_FULL = EXIT_STATUS_LSB -} ExitStatusLevel; - -typedef struct ExitStatusSet { - Set *status; - Set *signal; -} ExitStatusSet; - -const char* exit_status_to_string(int status, ExitStatusLevel level) _const_; - -typedef enum ExitClean { - EXIT_CLEAN_DAEMON, - EXIT_CLEAN_COMMAND, -} ExitClean; - -bool is_clean_exit(int code, int status, ExitClean clean, ExitStatusSet *success_status); - -void exit_status_set_free(ExitStatusSet *x); -bool exit_status_set_is_empty(ExitStatusSet *x); -bool exit_status_set_test(ExitStatusSet *x, int code, int status); diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c deleted file mode 100644 index d6c1228463..0000000000 --- a/src/basic/extract-word.c +++ /dev/null @@ -1,298 +0,0 @@ -/*** - 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 <errno.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <syslog.h> - -#include "alloc-util.h" -#include "escape.h" -#include "extract-word.h" -#include "log.h" -#include "macro.h" -#include "string-util.h" -#include "utf8.h" - -int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) { - _cleanup_free_ char *s = NULL; - size_t allocated = 0, sz = 0; - char c; - int r; - - char quote = 0; /* 0 or ' or " */ - bool backslash = false; /* whether we've just seen a backslash */ - - assert(p); - assert(ret); - - /* Bail early if called after last value or with no input */ - if (!*p) - goto finish_force_terminate; - c = **p; - - if (!separators) - separators = WHITESPACE; - - /* Parses the first word of a string, and returns it in - * *ret. Removes all quotes in the process. When parsing fails - * (because of an uneven number of quotes or similar), leaves - * the pointer *p at the first invalid character. */ - - if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) - if (!GREEDY_REALLOC(s, allocated, sz+1)) - return -ENOMEM; - - for (;; (*p)++, c = **p) { - if (c == 0) - goto finish_force_terminate; - else if (strchr(separators, c)) { - if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) { - (*p)++; - goto finish_force_next; - } - } else { - /* We found a non-blank character, so we will always - * want to return a string (even if it is empty), - * allocate it here. */ - if (!GREEDY_REALLOC(s, allocated, sz+1)) - return -ENOMEM; - break; - } - } - - for (;; (*p)++, c = **p) { - if (backslash) { - if (!GREEDY_REALLOC(s, allocated, sz+7)) - return -ENOMEM; - - if (c == 0) { - if ((flags & EXTRACT_CUNESCAPE_RELAX) && - (!quote || flags & EXTRACT_RELAX)) { - /* If we find an unquoted trailing backslash and we're in - * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the - * output. - * - * Unbalanced quotes will only be allowed in EXTRACT_RELAX - * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them. - */ - s[sz++] = '\\'; - goto finish_force_terminate; - } - if (flags & EXTRACT_RELAX) - goto finish_force_terminate; - return -EINVAL; - } - - if (flags & EXTRACT_CUNESCAPE) { - bool eight_bit = false; - char32_t u; - - r = cunescape_one(*p, (size_t) -1, &u, &eight_bit); - if (r < 0) { - if (flags & EXTRACT_CUNESCAPE_RELAX) { - s[sz++] = '\\'; - s[sz++] = c; - } else - return -EINVAL; - } else { - (*p) += r - 1; - - if (eight_bit) - s[sz++] = u; - else - sz += utf8_encode_unichar(s + sz, u); - } - } else - s[sz++] = c; - - backslash = false; - - } else if (quote) { /* inside either single or double quotes */ - for (;; (*p)++, c = **p) { - if (c == 0) { - if (flags & EXTRACT_RELAX) - goto finish_force_terminate; - return -EINVAL; - } else if (c == quote) { /* found the end quote */ - quote = 0; - break; - } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) { - backslash = true; - break; - } else { - if (!GREEDY_REALLOC(s, allocated, sz+2)) - return -ENOMEM; - - s[sz++] = c; - } - } - - } else { - for (;; (*p)++, c = **p) { - if (c == 0) - goto finish_force_terminate; - else if ((c == '\'' || c == '"') && (flags & EXTRACT_QUOTES)) { - quote = c; - break; - } else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) { - backslash = true; - break; - } else if (strchr(separators, c)) { - if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) { - (*p)++; - goto finish_force_next; - } - /* Skip additional coalesced separators. */ - for (;; (*p)++, c = **p) { - if (c == 0) - goto finish_force_terminate; - if (!strchr(separators, c)) - break; - } - goto finish; - - } else { - if (!GREEDY_REALLOC(s, allocated, sz+2)) - return -ENOMEM; - - s[sz++] = c; - } - } - } - } - -finish_force_terminate: - *p = NULL; -finish: - if (!s) { - *p = NULL; - *ret = NULL; - return 0; - } - -finish_force_next: - s[sz] = 0; - *ret = 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) - return r; - - if (r == -EINVAL && !(flags & EXTRACT_CUNESCAPE_RELAX)) { - - /* Retry it with EXTRACT_CUNESCAPE_RELAX. */ - *p = save; - r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX); - if (r >= 0) { - /* It worked this time, hence it must have been an invalid escape sequence we could correct. */ - log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Invalid escape sequences in line, correcting: \"%s\"", rvalue); - return r; - } - - /* If it's still EINVAL; then it must be unbalanced quoting, report this. */ - if (r == -EINVAL) - return log_syntax(unit, LOG_ERR, filename, line, r, "Unbalanced quoting, ignoring: \"%s\"", rvalue); - } - - /* Can be any error, report it */ - return log_syntax(unit, LOG_ERR, filename, line, r, "Unable to decode word \"%s\", ignoring: %m", rvalue); -} - -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; -} diff --git a/src/basic/extract-word.h b/src/basic/extract-word.h deleted file mode 100644 index 21db5ef33f..0000000000 --- a/src/basic/extract-word.h +++ /dev/null @@ -1,35 +0,0 @@ -#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 "macro.h" - -typedef enum ExtractFlags { - EXTRACT_RELAX = 1, - EXTRACT_CUNESCAPE = 2, - EXTRACT_CUNESCAPE_RELAX = 4, - EXTRACT_QUOTES = 8, - EXTRACT_DONT_COALESCE_SEPARATORS = 16, - EXTRACT_RETAIN_ESCAPE = 32, -} ExtractFlags; - -int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags); -int extract_first_word_and_warn(const char **p, char **ret, const char *separators, ExtractFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue); -int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) _sentinel_; diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c deleted file mode 100644 index 5c820332a5..0000000000 --- a/src/basic/fd-util.c +++ /dev/null @@ -1,380 +0,0 @@ -/*** - 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 <errno.h> -#include <fcntl.h> -#include <sys/resource.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <unistd.h> - -#include "fd-util.h" -#include "fs-util.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "path-util.h" -#include "socket-util.h" -#include "stdio-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; -} - -void stdio_unset_cloexec(void) { - fd_cloexec(STDIN_FILENO, false); - fd_cloexec(STDOUT_FILENO, false); - fd_cloexec(STDERR_FILENO, false); -} - -_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_or_backup_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; -} - -int fd_get_path(int fd, char **ret) { - char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - int r; - - xsprintf(procfs_path, "/proc/self/fd/%i", fd); - - r = readlink_malloc(procfs_path, ret); - - if (r == -ENOENT) /* If the file doesn't exist the fd is invalid */ - return -EBADF; - - return r; -} diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h deleted file mode 100644 index 34b98d4aec..0000000000 --- a/src/basic/fd-util.h +++ /dev/null @@ -1,80 +0,0 @@ -#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); -void stdio_unset_cloexec(void); - -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); - -int fd_get_path(int fd, char **ret); - -/* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */ -#define ERRNO_IS_DISCONNECT(r) \ - IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH) diff --git a/src/basic/fileio-label.c b/src/basic/fileio-label.c deleted file mode 100644 index 66dbc0fe1e..0000000000 --- a/src/basic/fileio-label.c +++ /dev/null @@ -1,68 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2010 Harald Hoyer - - 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 <sys/stat.h> - -#include "fileio-label.h" -#include "fileio.h" -#include "selinux-util.h" - -int write_string_file_atomic_label(const char *fn, const char *line) { - int r; - - r = mac_selinux_create_file_prepare(fn, S_IFREG); - if (r < 0) - return r; - - r = write_string_file(fn, line, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); - - mac_selinux_create_file_clear(); - - return r; -} - -int write_env_file_label(const char *fname, char **l) { - int r; - - r = mac_selinux_create_file_prepare(fname, S_IFREG); - if (r < 0) - return r; - - r = write_env_file(fname, l); - - mac_selinux_create_file_clear(); - - return r; -} - -int fopen_temporary_label(const char *target, - const char *path, FILE **f, char **temp_path) { - int r; - - r = mac_selinux_create_file_prepare(target, S_IFREG); - if (r < 0) - return r; - - r = fopen_temporary(path, f, temp_path); - - mac_selinux_create_file_clear(); - - return r; -} diff --git a/src/basic/fileio-label.h b/src/basic/fileio-label.h deleted file mode 100644 index fe7543013d..0000000000 --- a/src/basic/fileio-label.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2010 Harald Hoyer - - 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 <stdio.h> - -#include "fileio.h" - -int write_string_file_atomic_label(const char *fn, const char *line); -int write_env_file_label(const char *fname, char **l); -int fopen_temporary_label(const char *target, - const char *path, FILE **f, char **temp_path); diff --git a/src/basic/fileio.c b/src/basic/fileio.c deleted file mode 100644 index 1cfb7a98f5..0000000000 --- a/src/basic/fileio.c +++ /dev/null @@ -1,1411 +0,0 @@ -/*** - 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 <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <stdarg.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.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 "log.h" -#include "macro.h" -#include "missing.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 "time-util.h" -#include "umask-util.h" -#include "utf8.h" - -#define READ_FULL_BYTES_MAX (4U*1024U*1024U) - -int write_string_stream(FILE *f, const char *line, bool enforce_newline) { - - assert(f); - assert(line); - - fputs(line, f); - if (enforce_newline && !endswith(line, "\n")) - fputc('\n', f); - - return fflush_and_check(f); -} - -static int write_string_file_atomic(const char *fn, const char *line, bool enforce_newline) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *p = NULL; - int r; - - assert(fn); - assert(line); - - r = fopen_temporary(fn, &f, &p); - if (r < 0) - return r; - - (void) fchmod_umask(fileno(f), 0644); - - r = write_string_stream(f, line, enforce_newline); - if (r >= 0) { - if (rename(p, fn) < 0) - r = -errno; - } - - if (r < 0) - (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); - - if (flags & WRITE_STRING_FILE_ATOMIC) { - assert(flags & WRITE_STRING_FILE_CREATE); - - 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) { - 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) { - r = -errno; - goto fail; - } - - f = fdopen(fd, "we"); - if (!f) { - r = -errno; - safe_close(fd); - goto fail; - } - } - - 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) { - _cleanup_fclose_ FILE *f = NULL; - char t[LINE_MAX], *c; - - assert(fn); - assert(line); - - f = fopen(fn, "re"); - if (!f) - return -errno; - - if (!fgets(t, sizeof(t), f)) { - - if (ferror(f)) - return errno > 0 ? -errno : -EIO; - - t[0] = 0; - } - - c = strdup(t); - if (!c) - return -ENOMEM; - truncate_nl(c); - - *line = c; - return 0; -} - -int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *buf = NULL; - size_t l, k; - - assert(fn); - assert(blob); - - l = strlen(blob); - - if (accept_extra_nl && endswith(blob, "\n")) - accept_extra_nl = false; - - buf = malloc(l + accept_extra_nl + 1); - if (!buf) - return -ENOMEM; - - f = fopen(fn, "re"); - if (!f) - return -errno; - - /* 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) { - size_t n, l; - _cleanup_free_ char *buf = NULL; - struct stat st; - - assert(f); - assert(contents); - - if (fstat(fileno(f), &st) < 0) - return -errno; - - n = LINE_MAX; - - if (S_ISREG(st.st_mode)) { - - /* Safety check */ - if (st.st_size > READ_FULL_BYTES_MAX) - return -E2BIG; - - /* Start with the right file size, but be prepared for - * files from /proc which generally report a file size - * of 0 */ - if (st.st_size > 0) - n = st.st_size; - } - - l = 0; - for (;;) { - char *t; - size_t k; - - t = realloc(buf, n + 1); - if (!t) - return -ENOMEM; - - buf = t; - k = fread(buf + l, 1, n - l, f); - if (k > 0) - l += k; - - if (ferror(f)) - return -errno; - - if (feof(f)) - break; - - /* We aren't expecting fread() to return a short read outside - * of (error && eof), assert buffer is full and enlarge buffer. - */ - assert(l == n); - - /* Safety check */ - if (n >= READ_FULL_BYTES_MAX) - return -E2BIG; - - n = MIN(n * 2, READ_FULL_BYTES_MAX); - } - - buf[l] = 0; - *contents = buf; - buf = NULL; /* do not free */ - - if (size) - *size = l; - - return 0; -} - -int read_full_file(const char *fn, char **contents, size_t *size) { - _cleanup_fclose_ FILE *f = NULL; - - assert(fn); - assert(contents); - - f = fopen(fn, "re"); - if (!f) - return -errno; - - return read_full_stream(f, contents, size); -} - -static int parse_env_file_internal( - FILE *f, - const char *fname, - const char *newline, - int (*push) (const char *filename, unsigned line, - const char *key, char *value, void *userdata, int *n_pushed), - void *userdata, - int *n_pushed) { - - _cleanup_free_ char *contents = NULL, *key = NULL; - size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1; - char *p, *value = NULL; - int r; - unsigned line = 1; - - enum { - PRE_KEY, - KEY, - PRE_VALUE, - VALUE, - VALUE_ESCAPE, - SINGLE_QUOTE_VALUE, - SINGLE_QUOTE_VALUE_ESCAPE, - DOUBLE_QUOTE_VALUE, - DOUBLE_QUOTE_VALUE_ESCAPE, - COMMENT, - COMMENT_ESCAPE - } state = PRE_KEY; - - assert(newline); - - if (f) - r = read_full_stream(f, &contents, NULL); - else - r = read_full_file(fname, &contents, NULL); - if (r < 0) - return r; - - for (p = contents; *p; p++) { - char c = *p; - - switch (state) { - - case PRE_KEY: - if (strchr(COMMENTS, c)) - state = COMMENT; - else if (!strchr(WHITESPACE, c)) { - state = KEY; - last_key_whitespace = (size_t) -1; - - if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) { - r = -ENOMEM; - goto fail; - } - - key[n_key++] = c; - } - break; - - case KEY: - if (strchr(newline, c)) { - state = PRE_KEY; - line++; - n_key = 0; - } else if (c == '=') { - state = PRE_VALUE; - last_value_whitespace = (size_t) -1; - } else { - if (!strchr(WHITESPACE, c)) - last_key_whitespace = (size_t) -1; - else if (last_key_whitespace == (size_t) -1) - last_key_whitespace = n_key; - - if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) { - r = -ENOMEM; - goto fail; - } - - key[n_key++] = c; - } - - break; - - case PRE_VALUE: - if (strchr(newline, c)) { - state = PRE_KEY; - line++; - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - goto fail; - - n_key = 0; - value = NULL; - value_alloc = n_value = 0; - - } else if (c == '\'') - state = SINGLE_QUOTE_VALUE; - else if (c == '\"') - state = DOUBLE_QUOTE_VALUE; - else if (c == '\\') - state = VALUE_ESCAPE; - else if (!strchr(WHITESPACE, c)) { - state = VALUE; - - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } - - value[n_value++] = c; - } - - break; - - case VALUE: - if (strchr(newline, c)) { - state = PRE_KEY; - line++; - - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - /* Chomp off trailing whitespace from value */ - if (last_value_whitespace != (size_t) -1) - value[last_value_whitespace] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - goto fail; - - n_key = 0; - value = NULL; - value_alloc = n_value = 0; - - } else if (c == '\\') { - state = VALUE_ESCAPE; - last_value_whitespace = (size_t) -1; - } else { - if (!strchr(WHITESPACE, c)) - last_value_whitespace = (size_t) -1; - else if (last_value_whitespace == (size_t) -1) - last_value_whitespace = n_value; - - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } - - value[n_value++] = c; - } - - break; - - case VALUE_ESCAPE: - state = VALUE; - - if (!strchr(newline, c)) { - /* Escaped newlines we eat up entirely */ - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } - - value[n_value++] = c; - } - break; - - case SINGLE_QUOTE_VALUE: - if (c == '\'') - state = PRE_VALUE; - else if (c == '\\') - state = SINGLE_QUOTE_VALUE_ESCAPE; - else { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } - - value[n_value++] = c; - } - - break; - - case SINGLE_QUOTE_VALUE_ESCAPE: - state = SINGLE_QUOTE_VALUE; - - if (!strchr(newline, c)) { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } - - value[n_value++] = c; - } - break; - - case DOUBLE_QUOTE_VALUE: - if (c == '\"') - state = PRE_VALUE; - else if (c == '\\') - state = DOUBLE_QUOTE_VALUE_ESCAPE; - else { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } - - value[n_value++] = c; - } - - break; - - case DOUBLE_QUOTE_VALUE_ESCAPE: - state = DOUBLE_QUOTE_VALUE; - - if (!strchr(newline, c)) { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) { - r = -ENOMEM; - goto fail; - } - - value[n_value++] = c; - } - break; - - case COMMENT: - if (c == '\\') - state = COMMENT_ESCAPE; - else if (strchr(newline, c)) { - state = PRE_KEY; - line++; - } - break; - - case COMMENT_ESCAPE: - state = COMMENT; - break; - } - } - - if (state == PRE_VALUE || - state == VALUE || - state == VALUE_ESCAPE || - state == SINGLE_QUOTE_VALUE || - state == SINGLE_QUOTE_VALUE_ESCAPE || - state == DOUBLE_QUOTE_VALUE || - state == DOUBLE_QUOTE_VALUE_ESCAPE) { - - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - if (state == VALUE) - if (last_value_whitespace != (size_t) -1) - value[last_value_whitespace] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - goto fail; - } - - return 0; - -fail: - free(value); - return r; -} - -static int parse_env_file_push( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - - const char *k; - va_list aq, *ap = userdata; - - if (!utf8_is_valid(key)) { - _cleanup_free_ char *p = NULL; - - p = utf8_escape_invalid(key); - log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p); - return -EINVAL; - } - - if (value && !utf8_is_valid(value)) { - _cleanup_free_ char *p = NULL; - - p = utf8_escape_invalid(value); - log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p); - return -EINVAL; - } - - va_copy(aq, *ap); - - while ((k = va_arg(aq, const char *))) { - char **v; - - v = va_arg(aq, char **); - - if (streq(key, k)) { - va_end(aq); - free(*v); - *v = value; - - if (n_pushed) - (*n_pushed)++; - - return 1; - } - } - - va_end(aq); - free(value); - - return 0; -} - -int parse_env_file( - const char *fname, - const char *newline, ...) { - - va_list ap; - int r, n_pushed = 0; - - if (!newline) - newline = NEWLINE; - - va_start(ap, newline); - r = parse_env_file_internal(NULL, fname, newline, parse_env_file_push, &ap, &n_pushed); - va_end(ap); - - return r < 0 ? r : n_pushed; -} - -static int load_env_file_push( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - char ***m = userdata; - char *p; - int r; - - if (!utf8_is_valid(key)) { - _cleanup_free_ char *t = utf8_escape_invalid(key); - - log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t); - return -EINVAL; - } - - if (value && !utf8_is_valid(value)) { - _cleanup_free_ char *t = utf8_escape_invalid(value); - - log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t); - return -EINVAL; - } - - p = strjoin(key, "=", strempty(value), NULL); - if (!p) - return -ENOMEM; - - r = strv_consume(m, p); - if (r < 0) - return r; - - if (n_pushed) - (*n_pushed)++; - - free(value); - return 0; -} - -int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) { - char **m = NULL; - int r; - - if (!newline) - newline = NEWLINE; - - r = parse_env_file_internal(f, fname, newline, load_env_file_push, &m, NULL); - if (r < 0) { - strv_free(m); - return r; - } - - *rl = m; - return 0; -} - -static int load_env_file_push_pairs( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - char ***m = userdata; - int r; - - if (!utf8_is_valid(key)) { - _cleanup_free_ char *t = utf8_escape_invalid(key); - - log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t); - return -EINVAL; - } - - if (value && !utf8_is_valid(value)) { - _cleanup_free_ char *t = utf8_escape_invalid(value); - - log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t); - return -EINVAL; - } - - r = strv_extend(m, key); - if (r < 0) - return -ENOMEM; - - if (!value) { - r = strv_extend(m, ""); - if (r < 0) - return -ENOMEM; - } else { - r = strv_push(m, value); - if (r < 0) - return r; - } - - if (n_pushed) - (*n_pushed)++; - - return 0; -} - -int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char ***rl) { - char **m = NULL; - int r; - - if (!newline) - newline = NEWLINE; - - r = parse_env_file_internal(f, fname, newline, load_env_file_push_pairs, &m, NULL); - if (r < 0) { - strv_free(m); - return r; - } - - *rl = m; - return 0; -} - -static void write_env_var(FILE *f, const char *v) { - const char *p; - - p = strchr(v, '='); - if (!p) { - /* Fallback */ - fputs(v, f); - fputc('\n', f); - return; - } - - p++; - fwrite(v, 1, p-v, f); - - if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) { - fputc('\"', f); - - for (; *p; p++) { - if (strchr(SHELL_NEED_ESCAPE, *p)) - fputc('\\', f); - - fputc(*p, f); - } - - fputc('\"', f); - } else - fputs(p, f); - - fputc('\n', f); -} - -int write_env_file(const char *fname, char **l) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *p = NULL; - char **i; - int r; - - assert(fname); - - r = fopen_temporary(fname, &f, &p); - if (r < 0) - return r; - - fchmod_umask(fileno(f), 0644); - - STRV_FOREACH(i, l) - write_env_var(f, *i); - - r = fflush_and_check(f); - if (r >= 0) { - if (rename(p, fname) >= 0) - return 0; - - r = -errno; - } - - unlink(p); - return r; -} - -int executable_is_script(const char *path, char **interpreter) { - int r; - _cleanup_free_ char *line = NULL; - int len; - char *ans; - - assert(path); - - r = read_one_line_file(path, &line); - if (r < 0) - return r; - - if (!startswith(line, "#!")) - return 0; - - ans = strstrip(line + 2); - len = strcspn(ans, " \t"); - - if (len == 0) - return 0; - - ans = strndup(ans, len); - if (!ans) - return -ENOMEM; - - *interpreter = ans; - return 1; -} - -/** - * Retrieve one field from a file like /proc/self/status. pattern - * should not include whitespace or the delimiter (':'). pattern matches only - * the beginning of a line. Whitespace before ':' is skipped. Whitespace and - * zeros after the ':' will be skipped. field must be freed afterwards. - * terminator specifies the terminating characters of the field value (not - * included in the value). - */ -int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) { - _cleanup_free_ char *status = NULL; - char *t, *f; - size_t len; - int r; - - assert(terminator); - assert(filename); - assert(pattern); - assert(field); - - r = read_full_file(filename, &status, NULL); - if (r < 0) - return r; - - t = status; - - do { - bool pattern_ok; - - do { - t = strstr(t, pattern); - if (!t) - return -ENOENT; - - /* Check that pattern occurs in beginning of line. */ - pattern_ok = (t == status || t[-1] == '\n'); - - t += strlen(pattern); - - } while (!pattern_ok); - - t += strspn(t, " \t"); - if (!*t) - return -ENOENT; - - } while (*t != ':'); - - t++; - - if (*t) { - t += strspn(t, " \t"); - - /* Also skip zeros, because when this is used for - * capabilities, we don't want the zeros. This way the - * same capability set always maps to the same string, - * irrespective of the total capability set size. For - * other numbers it shouldn't matter. */ - t += strspn(t, "0"); - /* Back off one char if there's nothing but whitespace - and zeros */ - if (!*t || isspace(*t)) - t--; - } - - len = strcspn(t, terminator); - - f = strndup(t, len); - if (!f) - return -ENOMEM; - - *field = f; - return 0; -} - -DIR *xopendirat(int fd, const char *name, int flags) { - int nfd; - DIR *d; - - assert(!(flags & O_CREAT)); - - nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0); - if (nfd < 0) - return NULL; - - d = fdopendir(nfd); - if (!d) { - safe_close(nfd); - return NULL; - } - - return d; -} - -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); - 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 > 0 ? -errno : -EIO; - - return 0; -} - -/* This is much like mkostemp() but is subject to umask(). */ -int mkostemp_safe(char *pattern) { - _cleanup_umask_ mode_t u = 0; - int fd; - - assert(pattern); - - u = umask(077); - - fd = mkostemp(pattern, O_CLOEXEC); - if (fd < 0) - return -errno; - - return fd; -} - -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; - int r; - - assert(ret); - - /* Turns this: - * /foo/bar/waldo - * Into this: - * /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0 - */ - - if (!p) { - r = tmp_dir(&p); - if (r < 0) - return r; - } - - 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; -} - -int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) { - int r; - - assert(s); - - /* Outputs the specified string with fputs(), but optionally prefixes it with a separator. The *space parameter - * when specified shall initially point to a boolean variable initialized to false. It is set to true after the - * first invocation. This call is supposed to be use in loops, where a separator shall be inserted between each - * element, but not before the first one. */ - - if (!f) - f = stdout; - - if (space) { - if (!separator) - separator = " "; - - if (*space) { - r = fputs(separator, f); - if (r < 0) - return r; - } - - *space = true; - } - - return fputs(s, f); -} - -int open_tmpfile_unlinkable(const char *directory, int flags) { - char *p; - int fd, r; - - if (!directory) { - r = tmp_dir(&directory); - if (r < 0) - return r; - } - - /* Returns an unlinked temporary file that cannot be linked into the file system anymore */ - - /* Try O_TMPFILE first, if it is supported */ - fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR); - if (fd >= 0) - return fd; - - /* Fall back to unguessable name + unlinking */ - p = strjoina(directory, "/systemd-tmp-XXXXXX"); - - fd = mkostemp_safe(p); - if (fd < 0) - return fd; - - (void) unlink(p); - - return fd; -} - -int open_tmpfile_linkable(const char *target, int flags, char **ret_path) { - _cleanup_free_ char *tmp = NULL; - int r, fd; - - assert(target); - assert(ret_path); - - /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */ - assert((flags & O_EXCL) == 0); - - /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in - * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in - * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */ - - { - _cleanup_free_ char *dn = NULL; - - dn = dirname_malloc(target); - if (!dn) - return -ENOMEM; - - fd = open(dn, O_TMPFILE|flags, 0640); - if (fd >= 0) { - *ret_path = NULL; - return fd; - } - - log_debug_errno(errno, "Failed to use O_TMPFILE on %s: %m", dn); - } - - r = tempfn_random(target, NULL, &tmp); - if (r < 0) - return r; - - fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640); - if (fd < 0) - return -errno; - - *ret_path = tmp; - tmp = NULL; - - return fd; -} - -int link_tmpfile(int fd, const char *path, const char *target) { - - assert(fd >= 0); - assert(target); - - /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd - * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported - * on the directory, and renameat2() is used instead. - * - * Note that in both cases we will not replace existing files. This is because linkat() does not support this - * operation currently (renameat2() does), and there is no nice way to emulate this. */ - - if (path) { - if (rename_noreplace(AT_FDCWD, path, AT_FDCWD, target) < 0) - return -errno; - } else { - char proc_fd_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; - - xsprintf(proc_fd_path, "/proc/self/fd/%i", fd); - - if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0) - return -errno; - } - - return 0; -} - -int read_nul_string(FILE *f, char **ret) { - _cleanup_free_ char *x = NULL; - size_t allocated = 0, n = 0; - - assert(f); - assert(ret); - - /* Reads a NUL-terminated string from the specified file. */ - - for (;;) { - int c; - - if (!GREEDY_REALLOC(x, allocated, n+2)) - return -ENOMEM; - - c = fgetc(f); - if (c == 0) /* Terminate at NUL byte */ - break; - if (c == EOF) { - if (ferror(f)) - return -errno; - break; /* Terminate at EOF */ - } - - x[n++] = (char) c; - } - - if (x) - x[n] = 0; - else { - x = new0(char, 1); - if (!x) - return -ENOMEM; - } - - *ret = x; - x = NULL; - - return 0; -} diff --git a/src/basic/fileio.h b/src/basic/fileio.h deleted file mode 100644 index b58c83e64a..0000000000 --- a/src/basic/fileio.h +++ /dev/null @@ -1,90 +0,0 @@ -#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 <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); -int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags); - -int read_one_line_file(const char *fn, char **line); -int read_full_file(const char *fn, char **contents, size_t *size); -int read_full_stream(FILE *f, char **contents, size_t *size); - -int verify_file(const char *fn, const char *blob, bool accept_extra_nl); - -int parse_env_file(const char *fname, const char *separator, ...) _sentinel_; -int load_env_file(FILE *f, const char *fname, const char *separator, char ***l); -int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l); - -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 tempfn_xxxxxx(const char *p, const char *extra, char **ret); -int tempfn_random(const char *p, const char *extra, char **ret); -int tempfn_random_child(const char *p, const char *extra, char **ret); - -int write_timestamp_file_atomic(const char *fn, usec_t n); -int read_timestamp_file(const char *fn, usec_t *ret); - -int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space); - -int open_tmpfile_unlinkable(const char *directory, int flags); -int open_tmpfile_linkable(const char *target, int flags, char **ret_path); - -int link_tmpfile(int fd, const char *path, const char *target); - -int read_nul_string(FILE *f, char **ret); diff --git a/src/basic/formats-util.h b/src/basic/formats-util.h deleted file mode 100644 index 39a185f59b..0000000000 --- a/src/basic/formats-util.h +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2015 Ronny Chevalier - - 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> - -#if SIZEOF_PID_T == 4 -# define PID_PRI PRIi32 -#elif SIZEOF_PID_T == 2 -# define PID_PRI PRIi16 -#else -# error Unknown pid_t size -#endif -#define PID_FMT "%" PID_PRI - -#if SIZEOF_UID_T == 4 -# define UID_FMT "%" PRIu32 -#elif SIZEOF_UID_T == 2 -# define UID_FMT "%" PRIu16 -#else -# error Unknown uid_t size -#endif - -#if SIZEOF_GID_T == 4 -# define GID_FMT "%" PRIu32 -#elif SIZEOF_GID_T == 2 -# define GID_FMT "%" PRIu16 -#else -# error Unknown gid_t size -#endif - -#if SIZEOF_TIME_T == 8 -# define PRI_TIME PRIi64 -#elif SIZEOF_TIME_T == 4 -# define PRI_TIME "li" -#else -# error Unknown time_t size -#endif - -#if SIZEOF_RLIM_T == 8 -# define RLIM_FMT "%" PRIu64 -#elif SIZEOF_RLIM_T == 4 -# define RLIM_FMT "%" PRIu32 -#else -# error Unknown rlim_t size -#endif - -#if SIZEOF_DEV_T == 8 -# define DEV_FMT "%" PRIu64 -#elif SIZEOF_DEV_T == 4 -# define DEV_FMT "%" PRIu32 -#else -# error Unknown dev_t size -#endif - -#if SIZEOF_INO_T == 8 -# define INO_FMT "%" PRIu64 -#elif SIZEOF_INO_T == 4 -# define INO_FMT "%" PRIu32 -#else -# error Unknown ino_t size -#endif diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c deleted file mode 100644 index 48952a1c26..0000000000 --- a/src/basic/fs-util.c +++ /dev/null @@ -1,782 +0,0 @@ -/*** - 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 <errno.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <time.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "mkdir.h" -#include "parse-util.h" -#include "path-util.h" -#include "stat-util.h" -#include "stdio-util.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.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_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 == MODE_INVALID) ? 0644 : mode); - 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; -} - -static int getenv_tmp_dir(const char **ret_path) { - const char *n; - int r, ret = 0; - - assert(ret_path); - - /* We use the same order of environment variables python uses in tempfile.gettempdir(): - * https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir */ - FOREACH_STRING(n, "TMPDIR", "TEMP", "TMP") { - const char *e; - - e = secure_getenv(n); - if (!e) - continue; - if (!path_is_absolute(e)) { - r = -ENOTDIR; - goto next; - } - if (!path_is_safe(e)) { - r = -EPERM; - goto next; - } - - r = is_dir(e, true); - if (r < 0) - goto next; - if (r == 0) { - r = -ENOTDIR; - goto next; - } - - *ret_path = e; - return 1; - - next: - /* Remember first error, to make this more debuggable */ - if (ret >= 0) - ret = r; - } - - if (ret < 0) - return ret; - - *ret_path = NULL; - return ret; -} - -static int tmp_dir_internal(const char *def, const char **ret) { - const char *e; - int r, k; - - assert(def); - assert(ret); - - r = getenv_tmp_dir(&e); - if (r > 0) { - *ret = e; - return 0; - } - - k = is_dir(def, true); - if (k == 0) - k = -ENOTDIR; - if (k < 0) - return r < 0 ? r : k; - - *ret = def; - return 0; -} - -int var_tmp_dir(const char **ret) { - - /* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus - * even might survive a boot: /var/tmp. If $TMPDIR (or related environment variables) are set, its value is - * returned preferably however. Note that both this function and tmp_dir() below are affected by $TMPDIR, - * making it a variable that overrides all temporary file storage locations. */ - - return tmp_dir_internal("/var/tmp", ret); -} - -int tmp_dir(const char **ret) { - - /* Similar to var_tmp_dir() above, but returns the location for "smaller" temporary files, which is usually - * backed by an in-memory file system: /tmp. */ - - return tmp_dir_internal("/tmp", ret); -} - -int inotify_add_watch_fd(int fd, int what, uint32_t mask) { - char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; - int r; - - /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ - xsprintf(path, "/proc/self/fd/%i", what); - - r = inotify_add_watch(fd, path, mask); - if (r < 0) - return -errno; - - return r; -} - -int chase_symlinks(const char *path, const char *_root, char **ret) { - _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL; - _cleanup_close_ int fd = -1; - unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */ - char *todo; - int r; - - assert(path); - - /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following - * symlinks relative to a root directory, instead of the root of the host. - * - * Note that "root" matters only if we encounter an absolute symlink, it's unused otherwise. Most importantly - * this means the path parameter passed in is not prefixed by it. - * - * Algorithmically this operates on two path buffers: "done" are the components of the path we already - * processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to - * process. On each iteration, we move one component from "todo" to "done", processing it's special meaning - * each time. The "todo" path always starts with at least one slash, the "done" path always ends in no - * slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races - * at a minimum. */ - - r = path_make_absolute_cwd(path, &buffer); - if (r < 0) - return r; - - if (_root) { - r = path_make_absolute_cwd(_root, &root); - if (r < 0) - return r; - } - - fd = open("/", O_CLOEXEC|O_NOFOLLOW|O_PATH); - if (fd < 0) - return -errno; - - todo = buffer; - for (;;) { - _cleanup_free_ char *first = NULL; - _cleanup_close_ int child = -1; - struct stat st; - size_t n, m; - - /* Determine length of first component in the path */ - n = strspn(todo, "/"); /* The slashes */ - m = n + strcspn(todo + n, "/"); /* The entire length of the component */ - - /* Extract the first component. */ - first = strndup(todo, m); - if (!first) - return -ENOMEM; - - todo += m; - - /* Just a single slash? Then we reached the end. */ - if (isempty(first) || path_equal(first, "/")) - break; - - /* Just a dot? Then let's eat this up. */ - if (path_equal(first, "/.")) - continue; - - /* Two dots? Then chop off the last bit of what we already found out. */ - if (path_equal(first, "/..")) { - _cleanup_free_ char *parent = NULL; - int fd_parent = -1; - - if (isempty(done) || path_equal(done, "/")) - return -EINVAL; - - parent = dirname_malloc(done); - if (!parent) - return -ENOMEM; - - /* Don't allow this to leave the root dir */ - if (root && - path_startswith(done, root) && - !path_startswith(parent, root)) - return -EINVAL; - - free_and_replace(done, parent); - - fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH); - if (fd_parent < 0) - return -errno; - - safe_close(fd); - fd = fd_parent; - - continue; - } - - /* Otherwise let's see what this is. */ - child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH); - if (child < 0) - return -errno; - - if (fstat(child, &st) < 0) - return -errno; - - if (S_ISLNK(st.st_mode)) { - _cleanup_free_ char *destination = NULL; - - /* This is a symlink, in this case read the destination. But let's make sure we don't follow - * symlinks without bounds. */ - if (--max_follow <= 0) - return -ELOOP; - - r = readlinkat_malloc(fd, first + n, &destination); - if (r < 0) - return r; - if (isempty(destination)) - return -EINVAL; - - if (path_is_absolute(destination)) { - - /* An absolute destination. Start the loop from the beginning, but use the root - * directory as base. */ - - safe_close(fd); - fd = open(root ?: "/", O_CLOEXEC|O_NOFOLLOW|O_PATH); - if (fd < 0) - return -errno; - - free_and_replace(buffer, destination); - - todo = buffer; - free(done); - - /* Note that we do not revalidate the root, we take it as is. */ - if (isempty(root)) - done = NULL; - else { - done = strdup(root); - if (!done) - return -ENOMEM; - } - - } else { - char *joined; - - /* A relative destination. If so, this is what we'll prefix what's left to do with what - * we just read, and start the loop again, but remain in the current directory. */ - - joined = strjoin("/", destination, todo, NULL); - if (!joined) - return -ENOMEM; - - free(buffer); - todo = buffer = joined; - } - - continue; - } - - /* If this is not a symlink, then let's just add the name we read to what we already verified. */ - if (!done) { - done = first; - first = NULL; - } else { - if (!strextend(&done, first, NULL)) - return -ENOMEM; - } - - /* And iterate again, but go one directory further down. */ - safe_close(fd); - fd = child; - child = -1; - } - - if (!done) { - /* Special case, turn the empty string into "/", to indicate the root directory. */ - done = strdup("/"); - if (!done) - return -ENOMEM; - } - - *ret = done; - done = NULL; - - return 0; -} diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h deleted file mode 100644 index 31df47cf1e..0000000000 --- a/src/basic/fs-util.h +++ /dev/null @@ -1,81 +0,0 @@ -#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 <stdbool.h> -#include <stdint.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_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); - -int tmp_dir(const char **ret); -int var_tmp_dir(const char **ret); - -#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1) - -#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \ - for ((e) = &buffer.ev; \ - (uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \ - (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len)) - -union inotify_event_buffer { - struct inotify_event ev; - uint8_t raw[INOTIFY_EVENT_MAX]; -}; - -int inotify_add_watch_fd(int fd, int what, uint32_t mask); - -int chase_symlinks(const char *path, const char *_root, char **ret); diff --git a/src/basic/glob-util.c b/src/basic/glob-util.c deleted file mode 100644 index 007198c269..0000000000 --- a/src/basic/glob-util.c +++ /dev/null @@ -1,70 +0,0 @@ -/*** - 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 <errno.h> -#include <glob.h> - -#include "glob-util.h" -#include "macro.h" -#include "strv.h" - -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; - if (k == GLOB_NOSPACE) - return -ENOMEM; - if (k != 0) - return errno > 0 ? -errno : -EIO; - - return !strv_isempty(g.gl_pathv); -} - -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; - if (k == GLOB_NOSPACE) - return -ENOMEM; - if (k != 0) - return errno > 0 ? -errno : -EIO; - if (strv_isempty(g.gl_pathv)) - return -ENOENT; - - STRV_FOREACH(p, g.gl_pathv) { - k = strv_extend(strv, *p); - if (k < 0) - return k; - } - - return 0; -} diff --git a/src/basic/glob-util.h b/src/basic/glob-util.h deleted file mode 100644 index 5d8fb47a26..0000000000 --- a/src/basic/glob-util.h +++ /dev/null @@ -1,36 +0,0 @@ -#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" -#include "string-util.h" - -int glob_exists(const char *path); -int glob_extend(char ***strv, const char *path); - -#define _cleanup_globfree_ _cleanup_(globfree) - -_pure_ static inline bool string_is_glob(const char *p) { - /* Check if a string contains any glob patterns. */ - return !!strpbrk(p, GLOB_CHARS); -} diff --git a/src/basic/gunicode.c b/src/basic/gunicode.c deleted file mode 100644 index e6ac0545a4..0000000000 --- a/src/basic/gunicode.c +++ /dev/null @@ -1,112 +0,0 @@ -/* gunicode.c - Unicode manipulation functions - * - * Copyright (C) 1999, 2000 Tom Tromey - * Copyright 2000, 2005 Red Hat, Inc. - */ - -#include <stdlib.h> - -#include "gunicode.h" - -#define unichar uint32_t - -/** - * g_utf8_prev_char: - * @p: a pointer to a position within a UTF-8 encoded string - * - * Finds the previous UTF-8 character in the string before @p. - * - * @p does not have to be at the beginning of a UTF-8 character. No check - * is made to see if the character found is actually valid other than - * it starts with an appropriate byte. If @p might be the first - * character of the string, you must use g_utf8_find_prev_char() instead. - * - * Return value: a pointer to the found character. - **/ -char * -utf8_prev_char (const char *p) -{ - for (;;) - { - p--; - if ((*p & 0xc0) != 0x80) - return (char *)p; - } -} - -struct Interval -{ - unichar start, end; -}; - -static int -interval_compare (const void *key, const void *elt) -{ - unichar c = (unichar) (long) (key); - struct Interval *interval = (struct Interval *)elt; - - if (c < interval->start) - return -1; - if (c > interval->end) - return +1; - - return 0; -} - -/* - * NOTE: - * - * The tables for g_unichar_iswide() and g_unichar_iswide_cjk() are - * generated from the Unicode Character Database's file - * extracted/DerivedEastAsianWidth.txt using the gen-iswide-table.py - * in this way: - * - * ./gen-iswide-table.py < path/to/ucd/extracted/DerivedEastAsianWidth.txt | fmt - * - * Last update for Unicode 6.0. - */ - -/** - * g_unichar_iswide: - * @c: a Unicode character - * - * Determines if a character is typically rendered in a double-width - * cell. - * - * Return value: %TRUE if the character is wide - **/ -bool -unichar_iswide (unichar c) -{ - /* See NOTE earlier for how to update this table. */ - static const struct Interval wide[] = { - {0x1100, 0x115F}, {0x2329, 0x232A}, {0x2E80, 0x2E99}, {0x2E9B, 0x2EF3}, - {0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB}, {0x3000, 0x303E}, {0x3041, 0x3096}, - {0x3099, 0x30FF}, {0x3105, 0x312D}, {0x3131, 0x318E}, {0x3190, 0x31BA}, - {0x31C0, 0x31E3}, {0x31F0, 0x321E}, {0x3220, 0x3247}, {0x3250, 0x32FE}, - {0x3300, 0x4DBF}, {0x4E00, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C}, - {0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52}, - {0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6}, - {0x1B000, 0x1B001}, {0x1F200, 0x1F202}, {0x1F210, 0x1F23A}, - {0x1F240, 0x1F248}, {0x1F250, 0x1F251}, - {0x1F300, 0x1F567}, /* Miscellaneous Symbols and Pictographs */ - {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD}, - }; - - if (bsearch ((void *)(uintptr_t)c, wide, (sizeof (wide) / sizeof ((wide)[0])), sizeof wide[0], - interval_compare)) - return true; - - return false; -} - -const char utf8_skip_data[256] = { - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 -}; diff --git a/src/basic/gunicode.h b/src/basic/gunicode.h deleted file mode 100644 index 5975bc8fc9..0000000000 --- a/src/basic/gunicode.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -/* gunicode.h - Unicode manipulation functions - * - * Copyright (C) 1999, 2000 Tom Tromey - * Copyright 2000, 2005 Red Hat, Inc. - */ - -#include <stdbool.h> -#include <stdint.h> -#include <stdlib.h> - -char *utf8_prev_char (const char *p); - -extern const char utf8_skip_data[256]; - -/** - * g_utf8_next_char: - * @p: Pointer to the start of a valid UTF-8 character - * - * Skips to the next character in a UTF-8 string. The string must be - * valid; this macro is as fast as possible, and has no error-checking. - * You would use this macro to iterate over a string character by - * character. The macro returns the start of the next UTF-8 character. - * Before using this macro, use g_utf8_validate() to validate strings - * that may contain invalid UTF-8. - */ -#define utf8_next_char(p) (char *)((p) + utf8_skip_data[*(const unsigned char *)(p)]) - -bool unichar_iswide (uint32_t c); diff --git a/src/basic/hash-funcs.c b/src/basic/hash-funcs.c deleted file mode 100644 index c3a4a011b5..0000000000 --- a/src/basic/hash-funcs.c +++ /dev/null @@ -1,81 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2014 Michal Schmidt - - 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 "hash-funcs.h" - -void string_hash_func(const void *p, struct siphash *state) { - siphash24_compress(p, strlen(p) + 1, state); -} - -int string_compare_func(const void *a, const void *b) { - return strcmp(a, b); -} - -const struct hash_ops string_hash_ops = { - .hash = string_hash_func, - .compare = string_compare_func -}; - -void trivial_hash_func(const void *p, struct siphash *state) { - siphash24_compress(&p, sizeof(p), state); -} - -int trivial_compare_func(const void *a, const void *b) { - return a < b ? -1 : (a > b ? 1 : 0); -} - -const struct hash_ops trivial_hash_ops = { - .hash = trivial_hash_func, - .compare = trivial_compare_func -}; - -void uint64_hash_func(const void *p, struct siphash *state) { - siphash24_compress(p, sizeof(uint64_t), state); -} - -int uint64_compare_func(const void *_a, const void *_b) { - uint64_t a, b; - a = *(const uint64_t*) _a; - b = *(const uint64_t*) _b; - return a < b ? -1 : (a > b ? 1 : 0); -} - -const struct hash_ops uint64_hash_ops = { - .hash = uint64_hash_func, - .compare = uint64_compare_func -}; - -#if SIZEOF_DEV_T != 8 -void devt_hash_func(const void *p, struct siphash *state) { - siphash24_compress(p, sizeof(dev_t), state); -} - -int devt_compare_func(const void *_a, const void *_b) { - dev_t a, b; - a = *(const dev_t*) _a; - b = *(const dev_t*) _b; - return a < b ? -1 : (a > b ? 1 : 0); -} - -const struct hash_ops devt_hash_ops = { - .hash = devt_hash_func, - .compare = devt_compare_func -}; -#endif diff --git a/src/basic/hash-funcs.h b/src/basic/hash-funcs.h deleted file mode 100644 index 299189d143..0000000000 --- a/src/basic/hash-funcs.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2014 Michal Schmidt - - 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 "macro.h" -#include "siphash24.h" - -typedef void (*hash_func_t)(const void *p, struct siphash *state); -typedef int (*compare_func_t)(const void *a, const void *b); - -struct hash_ops { - hash_func_t hash; - compare_func_t compare; -}; - -void string_hash_func(const void *p, struct siphash *state); -int string_compare_func(const void *a, const void *b) _pure_; -extern const struct hash_ops string_hash_ops; - -/* This will compare the passed pointers directly, and will not - * dereference them. This is hence not useful for strings or - * suchlike. */ -void trivial_hash_func(const void *p, struct siphash *state); -int trivial_compare_func(const void *a, const void *b) _const_; -extern const struct hash_ops trivial_hash_ops; - -/* 32bit values we can always just embed in the pointer itself, but - * in order to support 32bit archs we need store 64bit values - * indirectly, since they don't fit in a pointer. */ -void uint64_hash_func(const void *p, struct siphash *state); -int uint64_compare_func(const void *a, const void *b) _pure_; -extern const struct hash_ops uint64_hash_ops; - -/* On some archs dev_t is 32bit, and on others 64bit. And sometimes - * it's 64bit on 32bit archs, and sometimes 32bit on 64bit archs. Yuck! */ -#if SIZEOF_DEV_T != 8 -void devt_hash_func(const void *p, struct siphash *state) _pure_; -int devt_compare_func(const void *a, const void *b) _pure_; -extern const struct hash_ops devt_hash_ops = { - .hash = devt_hash_func, - .compare = devt_compare_func -}; -#else -#define devt_hash_func uint64_hash_func -#define devt_compare_func uint64_compare_func -#define devt_hash_ops uint64_hash_ops -#endif diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c deleted file mode 100644 index 50fefb0b54..0000000000 --- a/src/basic/hashmap.c +++ /dev/null @@ -1,1828 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2014 Michal Schmidt - - 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 <errno.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "hashmap.h" -#include "macro.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 <pthread.h> -#include "list.h" -#endif - -/* - * Implementation of hashmaps. - * Addressing: open - * - uses less RAM compared to closed addressing (chaining), because - * our entries are small (especially in Sets, which tend to contain - * the majority of entries in systemd). - * Collision resolution: Robin Hood - * - tends to equalize displacement of entries from their optimal buckets. - * Probe sequence: linear - * - though theoretically worse than random probing/uniform hashing/double - * hashing, it is good for cache locality. - * - * References: - * Celis, P. 1986. Robin Hood Hashing. - * Ph.D. Dissertation. University of Waterloo, Waterloo, Ont., Canada, Canada. - * https://cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf - * - The results are derived for random probing. Suggests deletion with - * tombstones and two mean-centered search methods. None of that works - * well for linear probing. - * - * Janson, S. 2005. Individual displacements for linear probing hashing with different insertion policies. - * ACM Trans. Algorithms 1, 2 (October 2005), 177-213. - * DOI=10.1145/1103963.1103964 http://doi.acm.org/10.1145/1103963.1103964 - * http://www.math.uu.se/~svante/papers/sj157.pdf - * - Applies to Robin Hood with linear probing. Contains remarks on - * the unsuitability of mean-centered search with linear probing. - * - * Viola, A. 2005. Exact distribution of individual displacements in linear probing hashing. - * ACM Trans. Algorithms 1, 2 (October 2005), 214-242. - * DOI=10.1145/1103963.1103965 http://doi.acm.org/10.1145/1103963.1103965 - * - Similar to Janson. Note that Viola writes about C_{m,n} (number of probes - * in a successful search), and Janson writes about displacement. C = d + 1. - * - * Goossaert, E. 2013. Robin Hood hashing: backward shift deletion. - * http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/ - * - Explanation of backward shift deletion with pictures. - * - * Khuong, P. 2013. The Other Robin Hood Hashing. - * http://www.pvk.ca/Blog/2013/11/26/the-other-robin-hood-hashing/ - * - Short summary of random vs. linear probing, and tombstones vs. backward shift. - */ - -/* - * XXX Ideas for improvement: - * For unordered hashmaps, randomize iteration order, similarly to Perl: - * http://blog.booking.com/hardening-perls-hash-function.html - */ - -/* INV_KEEP_FREE = 1 / (1 - max_load_factor) - * e.g. 1 / (1 - 0.8) = 5 ... keep one fifth of the buckets free. */ -#define INV_KEEP_FREE 5U - -/* Fields common to entries of all hashmap/set types */ -struct hashmap_base_entry { - const void *key; -}; - -/* Entry types for specific hashmap/set types - * hashmap_base_entry must be at the beginning of each entry struct. */ - -struct plain_hashmap_entry { - struct hashmap_base_entry b; - void *value; -}; - -struct ordered_hashmap_entry { - struct plain_hashmap_entry p; - unsigned iterate_next, iterate_previous; -}; - -struct set_entry { - struct hashmap_base_entry b; -}; - -/* In several functions it is advantageous to have the hash table extended - * virtually by a couple of additional buckets. We reserve special index values - * for these "swap" buckets. */ -#define _IDX_SWAP_BEGIN (UINT_MAX - 3) -#define IDX_PUT (_IDX_SWAP_BEGIN + 0) -#define IDX_TMP (_IDX_SWAP_BEGIN + 1) -#define _IDX_SWAP_END (_IDX_SWAP_BEGIN + 2) - -#define IDX_FIRST (UINT_MAX - 1) /* special index for freshly initialized iterators */ -#define IDX_NIL UINT_MAX /* special index value meaning "none" or "end" */ - -assert_cc(IDX_FIRST == _IDX_SWAP_END); -assert_cc(IDX_FIRST == _IDX_ITERATOR_FIRST); - -/* Storage space for the "swap" buckets. - * All entry types can fit into a ordered_hashmap_entry. */ -struct swap_entries { - struct ordered_hashmap_entry e[_IDX_SWAP_END - _IDX_SWAP_BEGIN]; -}; - -/* Distance from Initial Bucket */ -typedef uint8_t dib_raw_t; -#define DIB_RAW_OVERFLOW ((dib_raw_t)0xfdU) /* indicates DIB value is greater than representable */ -#define DIB_RAW_REHASH ((dib_raw_t)0xfeU) /* entry yet to be rehashed during in-place resize */ -#define DIB_RAW_FREE ((dib_raw_t)0xffU) /* a free bucket */ -#define DIB_RAW_INIT ((char)DIB_RAW_FREE) /* a byte to memset a DIB store with when initializing */ - -#define DIB_FREE UINT_MAX - -#ifdef ENABLE_DEBUG_HASHMAP -struct hashmap_debug_info { - LIST_FIELDS(struct hashmap_debug_info, debug_list); - unsigned max_entries; /* high watermark of n_entries */ - - /* who allocated this hashmap */ - int line; - const char *file; - const char *func; - - /* fields to detect modification while iterating */ - unsigned put_count; /* counts puts into the hashmap */ - unsigned rem_count; /* counts removals from hashmap */ - unsigned last_rem_idx; /* remembers last removal index */ -}; - -/* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */ -static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list); -static pthread_mutex_t hashmap_debug_list_mutex = PTHREAD_MUTEX_INITIALIZER; - -#define HASHMAP_DEBUG_FIELDS struct hashmap_debug_info debug; - -#else /* !ENABLE_DEBUG_HASHMAP */ -#define HASHMAP_DEBUG_FIELDS -#endif /* ENABLE_DEBUG_HASHMAP */ - -enum HashmapType { - HASHMAP_TYPE_PLAIN, - HASHMAP_TYPE_ORDERED, - HASHMAP_TYPE_SET, - _HASHMAP_TYPE_MAX -}; - -struct _packed_ indirect_storage { - void *storage; /* where buckets and DIBs are stored */ - uint8_t hash_key[HASH_KEY_SIZE]; /* hash key; changes during resize */ - - unsigned n_entries; /* number of stored entries */ - unsigned n_buckets; /* number of buckets */ - - unsigned idx_lowest_entry; /* Index below which all buckets are free. - Makes "while(hashmap_steal_first())" loops - O(n) instead of O(n^2) for unordered hashmaps. */ - uint8_t _pad[3]; /* padding for the whole HashmapBase */ - /* The bitfields in HashmapBase complete the alignment of the whole thing. */ -}; - -struct direct_storage { - /* This gives us 39 bytes on 64bit, or 35 bytes on 32bit. - * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit, - * or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */ - uint8_t storage[sizeof(struct indirect_storage)]; -}; - -#define DIRECT_BUCKETS(entry_t) \ - (sizeof(struct direct_storage) / (sizeof(entry_t) + sizeof(dib_raw_t))) - -/* We should be able to store at least one entry directly. */ -assert_cc(DIRECT_BUCKETS(struct ordered_hashmap_entry) >= 1); - -/* We have 3 bits for n_direct_entries. */ -assert_cc(DIRECT_BUCKETS(struct set_entry) < (1 << 3)); - -/* Hashmaps with directly stored entries all use this shared hash key. - * It's no big deal if the key is guessed, because there can be only - * a handful of directly stored entries in a hashmap. When a hashmap - * outgrows direct storage, it gets its own key for indirect storage. */ -static uint8_t shared_hash_key[HASH_KEY_SIZE]; -static bool shared_hash_key_initialized; - -/* Fields that all hashmap/set types must have */ -struct HashmapBase { - const struct hash_ops *hash_ops; /* hash and compare ops to use */ - - union _packed_ { - struct indirect_storage indirect; /* if has_indirect */ - struct direct_storage direct; /* if !has_indirect */ - }; - - enum HashmapType type:2; /* HASHMAP_TYPE_* */ - bool has_indirect:1; /* whether indirect storage is used */ - unsigned n_direct_entries:3; /* Number of entries in direct storage. - * Only valid if !has_indirect. */ - bool from_pool:1; /* whether was allocated from mempool */ - HASHMAP_DEBUG_FIELDS /* optional hashmap_debug_info */ -}; - -/* Specific hash types - * HashmapBase must be at the beginning of each hashmap struct. */ - -struct Hashmap { - struct HashmapBase b; -}; - -struct OrderedHashmap { - struct HashmapBase b; - unsigned iterate_list_head, iterate_list_tail; -}; - -struct Set { - struct HashmapBase b; -}; - -DEFINE_MEMPOOL(hashmap_pool, Hashmap, 8); -DEFINE_MEMPOOL(ordered_hashmap_pool, OrderedHashmap, 8); -/* No need for a separate Set pool */ -assert_cc(sizeof(Hashmap) == sizeof(Set)); - -struct hashmap_type_info { - size_t head_size; - size_t entry_size; - struct mempool *mempool; - unsigned n_direct_buckets; -}; - -static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = { - [HASHMAP_TYPE_PLAIN] = { - .head_size = sizeof(Hashmap), - .entry_size = sizeof(struct plain_hashmap_entry), - .mempool = &hashmap_pool, - .n_direct_buckets = DIRECT_BUCKETS(struct plain_hashmap_entry), - }, - [HASHMAP_TYPE_ORDERED] = { - .head_size = sizeof(OrderedHashmap), - .entry_size = sizeof(struct ordered_hashmap_entry), - .mempool = &ordered_hashmap_pool, - .n_direct_buckets = DIRECT_BUCKETS(struct ordered_hashmap_entry), - }, - [HASHMAP_TYPE_SET] = { - .head_size = sizeof(Set), - .entry_size = sizeof(struct set_entry), - .mempool = &hashmap_pool, - .n_direct_buckets = DIRECT_BUCKETS(struct set_entry), - }, -}; - -static unsigned n_buckets(HashmapBase *h) { - return h->has_indirect ? h->indirect.n_buckets - : hashmap_type_info[h->type].n_direct_buckets; -} - -static unsigned n_entries(HashmapBase *h) { - return h->has_indirect ? h->indirect.n_entries - : h->n_direct_entries; -} - -static void n_entries_inc(HashmapBase *h) { - if (h->has_indirect) - h->indirect.n_entries++; - else - h->n_direct_entries++; -} - -static void n_entries_dec(HashmapBase *h) { - if (h->has_indirect) - h->indirect.n_entries--; - else - h->n_direct_entries--; -} - -static void *storage_ptr(HashmapBase *h) { - return h->has_indirect ? h->indirect.storage - : h->direct.storage; -} - -static uint8_t *hash_key(HashmapBase *h) { - return h->has_indirect ? h->indirect.hash_key - : shared_hash_key; -} - -static unsigned base_bucket_hash(HashmapBase *h, const void *p) { - struct siphash state; - uint64_t hash; - - siphash24_init(&state, hash_key(h)); - - h->hash_ops->hash(p, &state); - - hash = siphash24_finalize(&state); - - return (unsigned) (hash % n_buckets(h)); -} -#define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p) - -static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) { - static uint8_t current[HASH_KEY_SIZE]; - static bool current_initialized = false; - - /* Returns a hash function key to use. In order to keep things - * fast we will not generate a new key each time we allocate a - * new hash table. Instead, we'll just reuse the most recently - * generated one, except if we never generated one or when we - * are rehashing an entire hash table because we reached a - * fill level */ - - if (!current_initialized || !reuse_is_ok) { - random_bytes(current, sizeof(current)); - current_initialized = true; - } - - memcpy(hash_key, current, sizeof(current)); -} - -static struct hashmap_base_entry *bucket_at(HashmapBase *h, unsigned idx) { - return (struct hashmap_base_entry*) - ((uint8_t*) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size); -} - -static struct plain_hashmap_entry *plain_bucket_at(Hashmap *h, unsigned idx) { - return (struct plain_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx); -} - -static struct ordered_hashmap_entry *ordered_bucket_at(OrderedHashmap *h, unsigned idx) { - return (struct ordered_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx); -} - -static struct set_entry *set_bucket_at(Set *h, unsigned idx) { - return (struct set_entry*) bucket_at(HASHMAP_BASE(h), idx); -} - -static struct ordered_hashmap_entry *bucket_at_swap(struct swap_entries *swap, unsigned idx) { - return &swap->e[idx - _IDX_SWAP_BEGIN]; -} - -/* Returns a pointer to the bucket at index idx. - * Understands real indexes and swap indexes, hence "_virtual". */ -static struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_entries *swap, - unsigned idx) { - if (idx < _IDX_SWAP_BEGIN) - return bucket_at(h, idx); - - if (idx < _IDX_SWAP_END) - return &bucket_at_swap(swap, idx)->p.b; - - assert_not_reached("Invalid index"); -} - -static dib_raw_t *dib_raw_ptr(HashmapBase *h) { - return (dib_raw_t*) - ((uint8_t*) storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h)); -} - -static unsigned bucket_distance(HashmapBase *h, unsigned idx, unsigned from) { - return idx >= from ? idx - from - : n_buckets(h) + idx - from; -} - -static unsigned bucket_calculate_dib(HashmapBase *h, unsigned idx, dib_raw_t raw_dib) { - unsigned initial_bucket; - - if (raw_dib == DIB_RAW_FREE) - return DIB_FREE; - - if (_likely_(raw_dib < DIB_RAW_OVERFLOW)) - return raw_dib; - - /* - * Having an overflow DIB value is very unlikely. The hash function - * would have to be bad. For example, in a table of size 2^24 filled - * to load factor 0.9 the maximum observed DIB is only about 60. - * In theory (assuming I used Maxima correctly), for an infinite size - * hash table with load factor 0.8 the probability of a given entry - * having DIB > 40 is 1.9e-8. - * This returns the correct DIB value by recomputing the hash value in - * the unlikely case. XXX Hitting this case could be a hint to rehash. - */ - initial_bucket = bucket_hash(h, bucket_at(h, idx)->key); - return bucket_distance(h, idx, initial_bucket); -} - -static void bucket_set_dib(HashmapBase *h, unsigned idx, unsigned dib) { - dib_raw_ptr(h)[idx] = dib != DIB_FREE ? MIN(dib, DIB_RAW_OVERFLOW) : DIB_RAW_FREE; -} - -static unsigned skip_free_buckets(HashmapBase *h, unsigned idx) { - dib_raw_t *dibs; - - dibs = dib_raw_ptr(h); - - for ( ; idx < n_buckets(h); idx++) - if (dibs[idx] != DIB_RAW_FREE) - return idx; - - return IDX_NIL; -} - -static void bucket_mark_free(HashmapBase *h, unsigned idx) { - memzero(bucket_at(h, idx), hashmap_type_info[h->type].entry_size); - bucket_set_dib(h, idx, DIB_FREE); -} - -static void bucket_move_entry(HashmapBase *h, struct swap_entries *swap, - unsigned from, unsigned to) { - struct hashmap_base_entry *e_from, *e_to; - - assert(from != to); - - e_from = bucket_at_virtual(h, swap, from); - e_to = bucket_at_virtual(h, swap, to); - - memcpy(e_to, e_from, hashmap_type_info[h->type].entry_size); - - if (h->type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*) h; - struct ordered_hashmap_entry *le, *le_to; - - le_to = (struct ordered_hashmap_entry*) e_to; - - if (le_to->iterate_next != IDX_NIL) { - le = (struct ordered_hashmap_entry*) - bucket_at_virtual(h, swap, le_to->iterate_next); - le->iterate_previous = to; - } - - if (le_to->iterate_previous != IDX_NIL) { - le = (struct ordered_hashmap_entry*) - bucket_at_virtual(h, swap, le_to->iterate_previous); - le->iterate_next = to; - } - - if (lh->iterate_list_head == from) - lh->iterate_list_head = to; - if (lh->iterate_list_tail == from) - lh->iterate_list_tail = to; - } -} - -static unsigned next_idx(HashmapBase *h, unsigned idx) { - return (idx + 1U) % n_buckets(h); -} - -static unsigned prev_idx(HashmapBase *h, unsigned idx) { - return (n_buckets(h) + idx - 1U) % n_buckets(h); -} - -static void *entry_value(HashmapBase *h, struct hashmap_base_entry *e) { - switch (h->type) { - - case HASHMAP_TYPE_PLAIN: - case HASHMAP_TYPE_ORDERED: - return ((struct plain_hashmap_entry*)e)->value; - - case HASHMAP_TYPE_SET: - return (void*) e->key; - - default: - assert_not_reached("Unknown hashmap type"); - } -} - -static void base_remove_entry(HashmapBase *h, unsigned idx) { - unsigned left, right, prev, dib; - dib_raw_t raw_dib, *dibs; - - dibs = dib_raw_ptr(h); - assert(dibs[idx] != DIB_RAW_FREE); - -#ifdef ENABLE_DEBUG_HASHMAP - h->debug.rem_count++; - h->debug.last_rem_idx = idx; -#endif - - left = idx; - /* Find the stop bucket ("right"). It is either free or has DIB == 0. */ - for (right = next_idx(h, left); ; right = next_idx(h, right)) { - raw_dib = dibs[right]; - if (raw_dib == 0 || raw_dib == DIB_RAW_FREE) - break; - - /* The buckets are not supposed to be all occupied and with DIB > 0. - * That would mean we could make everyone better off by shifting them - * backward. This scenario is impossible. */ - assert(left != right); - } - - if (h->type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*) h; - struct ordered_hashmap_entry *le = ordered_bucket_at(lh, idx); - - if (le->iterate_next != IDX_NIL) - ordered_bucket_at(lh, le->iterate_next)->iterate_previous = le->iterate_previous; - else - lh->iterate_list_tail = le->iterate_previous; - - if (le->iterate_previous != IDX_NIL) - ordered_bucket_at(lh, le->iterate_previous)->iterate_next = le->iterate_next; - else - lh->iterate_list_head = le->iterate_next; - } - - /* Now shift all buckets in the interval (left, right) one step backwards */ - for (prev = left, left = next_idx(h, left); left != right; - prev = left, left = next_idx(h, left)) { - dib = bucket_calculate_dib(h, left, dibs[left]); - assert(dib != 0); - bucket_move_entry(h, NULL, left, prev); - bucket_set_dib(h, prev, dib - 1); - } - - bucket_mark_free(h, prev); - n_entries_dec(h); -} -#define remove_entry(h, idx) base_remove_entry(HASHMAP_BASE(h), idx) - -static unsigned hashmap_iterate_in_insertion_order(OrderedHashmap *h, Iterator *i) { - struct ordered_hashmap_entry *e; - unsigned idx; - - assert(h); - assert(i); - - if (i->idx == IDX_NIL) - goto at_end; - - if (i->idx == IDX_FIRST && h->iterate_list_head == IDX_NIL) - goto at_end; - - if (i->idx == IDX_FIRST) { - idx = h->iterate_list_head; - e = ordered_bucket_at(h, idx); - } else { - idx = i->idx; - e = ordered_bucket_at(h, idx); - /* - * We allow removing the current entry while iterating, but removal may cause - * a backward shift. The next entry may thus move one bucket to the left. - * To detect when it happens, we remember the key pointer of the entry we were - * going to iterate next. If it does not match, there was a backward shift. - */ - if (e->p.b.key != i->next_key) { - idx = prev_idx(HASHMAP_BASE(h), idx); - e = ordered_bucket_at(h, idx); - } - assert(e->p.b.key == i->next_key); - } - -#ifdef ENABLE_DEBUG_HASHMAP - i->prev_idx = idx; -#endif - - if (e->iterate_next != IDX_NIL) { - struct ordered_hashmap_entry *n; - i->idx = e->iterate_next; - n = ordered_bucket_at(h, i->idx); - i->next_key = n->p.b.key; - } else - i->idx = IDX_NIL; - - return idx; - -at_end: - i->idx = IDX_NIL; - return IDX_NIL; -} - -static unsigned hashmap_iterate_in_internal_order(HashmapBase *h, Iterator *i) { - unsigned idx; - - assert(h); - assert(i); - - if (i->idx == IDX_NIL) - goto at_end; - - if (i->idx == IDX_FIRST) { - /* fast forward to the first occupied bucket */ - if (h->has_indirect) { - i->idx = skip_free_buckets(h, h->indirect.idx_lowest_entry); - h->indirect.idx_lowest_entry = i->idx; - } else - i->idx = skip_free_buckets(h, 0); - - if (i->idx == IDX_NIL) - goto at_end; - } else { - struct hashmap_base_entry *e; - - assert(i->idx > 0); - - e = bucket_at(h, i->idx); - /* - * We allow removing the current entry while iterating, but removal may cause - * a backward shift. The next entry may thus move one bucket to the left. - * To detect when it happens, we remember the key pointer of the entry we were - * going to iterate next. If it does not match, there was a backward shift. - */ - if (e->key != i->next_key) - e = bucket_at(h, --i->idx); - - assert(e->key == i->next_key); - } - - idx = i->idx; -#ifdef ENABLE_DEBUG_HASHMAP - i->prev_idx = idx; -#endif - - i->idx = skip_free_buckets(h, i->idx + 1); - if (i->idx != IDX_NIL) - i->next_key = bucket_at(h, i->idx)->key; - else - i->idx = IDX_NIL; - - return idx; - -at_end: - i->idx = IDX_NIL; - return IDX_NIL; -} - -static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) { - if (!h) { - i->idx = IDX_NIL; - return IDX_NIL; - } - -#ifdef ENABLE_DEBUG_HASHMAP - if (i->idx == IDX_FIRST) { - i->put_count = h->debug.put_count; - i->rem_count = h->debug.rem_count; - } else { - /* While iterating, must not add any new entries */ - assert(i->put_count == h->debug.put_count); - /* ... or remove entries other than the current one */ - assert(i->rem_count == h->debug.rem_count || - (i->rem_count == h->debug.rem_count - 1 && - i->prev_idx == h->debug.last_rem_idx)); - /* Reset our removals counter */ - i->rem_count = h->debug.rem_count; - } -#endif - - return h->type == HASHMAP_TYPE_ORDERED ? hashmap_iterate_in_insertion_order((OrderedHashmap*) h, i) - : hashmap_iterate_in_internal_order(h, i); -} - -bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key) { - struct hashmap_base_entry *e; - void *data; - unsigned idx; - - idx = hashmap_iterate_entry(h, i); - if (idx == IDX_NIL) { - if (value) - *value = NULL; - if (key) - *key = NULL; - - return false; - } - - e = bucket_at(h, idx); - data = entry_value(h, e); - if (value) - *value = data; - if (key) - *key = e->key; - - return true; -} - -bool set_iterate(Set *s, Iterator *i, void **value) { - return internal_hashmap_iterate(HASHMAP_BASE(s), i, value, NULL); -} - -#define HASHMAP_FOREACH_IDX(idx, h, i) \ - for ((i) = ITERATOR_FIRST, (idx) = hashmap_iterate_entry((h), &(i)); \ - (idx != IDX_NIL); \ - (idx) = hashmap_iterate_entry((h), &(i))) - -static void reset_direct_storage(HashmapBase *h) { - const struct hashmap_type_info *hi = &hashmap_type_info[h->type]; - void *p; - - assert(!h->has_indirect); - - p = mempset(h->direct.storage, 0, hi->entry_size * hi->n_direct_buckets); - memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets); -} - -static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) { - HashmapBase *h; - const struct hashmap_type_info *hi = &hashmap_type_info[type]; - bool use_pool; - - use_pool = is_main_thread(); - - h = use_pool ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size); - - if (!h) - return NULL; - - h->type = type; - h->from_pool = use_pool; - h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops; - - if (type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*)h; - lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL; - } - - reset_direct_storage(h); - - if (!shared_hash_key_initialized) { - random_bytes(shared_hash_key, sizeof(shared_hash_key)); - shared_hash_key_initialized= true; - } - -#ifdef ENABLE_DEBUG_HASHMAP - h->debug.func = func; - h->debug.file = file; - h->debug.line = line; - assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0); - LIST_PREPEND(debug_list, hashmap_debug_list, &h->debug); - assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0); -#endif - - return h; -} - -Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); -} - -OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); -} - -Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); -} - -static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops, - enum HashmapType type HASHMAP_DEBUG_PARAMS) { - HashmapBase *q; - - assert(h); - - if (*h) - return 0; - - q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS); - if (!q) - return -ENOMEM; - - *h = q; - return 0; -} - -int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); -} - -int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); -} - -int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { - return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); -} - -static void hashmap_free_no_clear(HashmapBase *h) { - assert(!h->has_indirect); - assert(!h->n_direct_entries); - -#ifdef ENABLE_DEBUG_HASHMAP - assert_se(pthread_mutex_lock(&hashmap_debug_list_mutex) == 0); - LIST_REMOVE(debug_list, hashmap_debug_list, &h->debug); - assert_se(pthread_mutex_unlock(&hashmap_debug_list_mutex) == 0); -#endif - - if (h->from_pool) - mempool_free_tile(hashmap_type_info[h->type].mempool, h); - else - free(h); -} - -HashmapBase *internal_hashmap_free(HashmapBase *h) { - - /* Free the hashmap, but nothing in it */ - - if (h) { - internal_hashmap_clear(h); - hashmap_free_no_clear(h); - } - - return NULL; -} - -HashmapBase *internal_hashmap_free_free(HashmapBase *h) { - - /* Free the hashmap and all data objects in it, but not the - * keys */ - - if (h) { - internal_hashmap_clear_free(h); - hashmap_free_no_clear(h); - } - - return NULL; -} - -Hashmap *hashmap_free_free_free(Hashmap *h) { - - /* Free the hashmap and all data and key objects in it */ - - if (h) { - hashmap_clear_free_free(h); - hashmap_free_no_clear(HASHMAP_BASE(h)); - } - - return NULL; -} - -void internal_hashmap_clear(HashmapBase *h) { - if (!h) - return; - - if (h->has_indirect) { - free(h->indirect.storage); - h->has_indirect = false; - } - - h->n_direct_entries = 0; - reset_direct_storage(h); - - if (h->type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*) h; - lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL; - } -} - -void internal_hashmap_clear_free(HashmapBase *h) { - unsigned idx; - - if (!h) - return; - - for (idx = skip_free_buckets(h, 0); idx != IDX_NIL; - idx = skip_free_buckets(h, idx + 1)) - free(entry_value(h, bucket_at(h, idx))); - - internal_hashmap_clear(h); -} - -void hashmap_clear_free_free(Hashmap *h) { - unsigned idx; - - if (!h) - return; - - for (idx = skip_free_buckets(HASHMAP_BASE(h), 0); idx != IDX_NIL; - idx = skip_free_buckets(HASHMAP_BASE(h), idx + 1)) { - struct plain_hashmap_entry *e = plain_bucket_at(h, idx); - free((void*)e->b.key); - free(e->value); - } - - internal_hashmap_clear(HASHMAP_BASE(h)); -} - -static int resize_buckets(HashmapBase *h, unsigned entries_add); - -/* - * Finds an empty bucket to put an entry into, starting the scan at 'idx'. - * Performs Robin Hood swaps as it goes. The entry to put must be placed - * by the caller into swap slot IDX_PUT. - * If used for in-place resizing, may leave a displaced entry in swap slot - * IDX_PUT. Caller must rehash it next. - * Returns: true if it left a displaced entry to rehash next in IDX_PUT, - * false otherwise. - */ -static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx, - struct swap_entries *swap) { - dib_raw_t raw_dib, *dibs; - unsigned dib, distance; - -#ifdef ENABLE_DEBUG_HASHMAP - h->debug.put_count++; -#endif - - dibs = dib_raw_ptr(h); - - for (distance = 0; ; distance++) { - raw_dib = dibs[idx]; - if (raw_dib == DIB_RAW_FREE || raw_dib == DIB_RAW_REHASH) { - if (raw_dib == DIB_RAW_REHASH) - bucket_move_entry(h, swap, idx, IDX_TMP); - - if (h->has_indirect && h->indirect.idx_lowest_entry > idx) - h->indirect.idx_lowest_entry = idx; - - bucket_set_dib(h, idx, distance); - bucket_move_entry(h, swap, IDX_PUT, idx); - if (raw_dib == DIB_RAW_REHASH) { - bucket_move_entry(h, swap, IDX_TMP, IDX_PUT); - return true; - } - - return false; - } - - dib = bucket_calculate_dib(h, idx, raw_dib); - - if (dib < distance) { - /* Found a wealthier entry. Go Robin Hood! */ - bucket_set_dib(h, idx, distance); - - /* swap the entries */ - bucket_move_entry(h, swap, idx, IDX_TMP); - bucket_move_entry(h, swap, IDX_PUT, idx); - bucket_move_entry(h, swap, IDX_TMP, IDX_PUT); - - distance = dib; - } - - idx = next_idx(h, idx); - } -} - -/* - * Puts an entry into a hashmap, boldly - no check whether key already exists. - * The caller must place the entry (only its key and value, not link indexes) - * in swap slot IDX_PUT. - * Caller must ensure: the key does not exist yet in the hashmap. - * that resize is not needed if !may_resize. - * Returns: 1 if entry was put successfully. - * -ENOMEM if may_resize==true and resize failed with -ENOMEM. - * Cannot return -ENOMEM if !may_resize. - */ -static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx, - struct swap_entries *swap, bool may_resize) { - struct ordered_hashmap_entry *new_entry; - int r; - - assert(idx < n_buckets(h)); - - new_entry = bucket_at_swap(swap, IDX_PUT); - - if (may_resize) { - r = resize_buckets(h, 1); - if (r < 0) - return r; - if (r > 0) - idx = bucket_hash(h, new_entry->p.b.key); - } - assert(n_entries(h) < n_buckets(h)); - - if (h->type == HASHMAP_TYPE_ORDERED) { - OrderedHashmap *lh = (OrderedHashmap*) h; - - new_entry->iterate_next = IDX_NIL; - new_entry->iterate_previous = lh->iterate_list_tail; - - if (lh->iterate_list_tail != IDX_NIL) { - struct ordered_hashmap_entry *old_tail; - - old_tail = ordered_bucket_at(lh, lh->iterate_list_tail); - assert(old_tail->iterate_next == IDX_NIL); - old_tail->iterate_next = IDX_PUT; - } - - lh->iterate_list_tail = IDX_PUT; - if (lh->iterate_list_head == IDX_NIL) - lh->iterate_list_head = IDX_PUT; - } - - assert_se(hashmap_put_robin_hood(h, idx, swap) == false); - - n_entries_inc(h); -#ifdef ENABLE_DEBUG_HASHMAP - h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h)); -#endif - - return 1; -} -#define hashmap_put_boldly(h, idx, swap, may_resize) \ - hashmap_base_put_boldly(HASHMAP_BASE(h), idx, swap, may_resize) - -/* - * Returns 0 if resize is not needed. - * 1 if successfully resized. - * -ENOMEM on allocation failure. - */ -static int resize_buckets(HashmapBase *h, unsigned entries_add) { - struct swap_entries swap; - void *new_storage; - dib_raw_t *old_dibs, *new_dibs; - const struct hashmap_type_info *hi; - unsigned idx, optimal_idx; - unsigned old_n_buckets, new_n_buckets, n_rehashed, new_n_entries; - uint8_t new_shift; - bool rehash_next; - - assert(h); - - hi = &hashmap_type_info[h->type]; - new_n_entries = n_entries(h) + entries_add; - - /* overflow? */ - if (_unlikely_(new_n_entries < entries_add)) - return -ENOMEM; - - /* For direct storage we allow 100% load, because it's tiny. */ - if (!h->has_indirect && new_n_entries <= hi->n_direct_buckets) - return 0; - - /* - * Load factor = n/m = 1 - (1/INV_KEEP_FREE). - * From it follows: m = n + n/(INV_KEEP_FREE - 1) - */ - new_n_buckets = new_n_entries + new_n_entries / (INV_KEEP_FREE - 1); - /* overflow? */ - if (_unlikely_(new_n_buckets < new_n_entries)) - return -ENOMEM; - - if (_unlikely_(new_n_buckets > UINT_MAX / (hi->entry_size + sizeof(dib_raw_t)))) - return -ENOMEM; - - old_n_buckets = n_buckets(h); - - if (_likely_(new_n_buckets <= old_n_buckets)) - return 0; - - new_shift = log2u_round_up(MAX( - new_n_buckets * (hi->entry_size + sizeof(dib_raw_t)), - 2 * sizeof(struct direct_storage))); - - /* Realloc storage (buckets and DIB array). */ - new_storage = realloc(h->has_indirect ? h->indirect.storage : NULL, - 1U << new_shift); - if (!new_storage) - return -ENOMEM; - - /* Must upgrade direct to indirect storage. */ - if (!h->has_indirect) { - memcpy(new_storage, h->direct.storage, - old_n_buckets * (hi->entry_size + sizeof(dib_raw_t))); - h->indirect.n_entries = h->n_direct_entries; - h->indirect.idx_lowest_entry = 0; - h->n_direct_entries = 0; - } - - /* Get a new hash key. If we've just upgraded to indirect storage, - * allow reusing a previously generated key. It's still a different key - * from the shared one that we used for direct storage. */ - get_hash_key(h->indirect.hash_key, !h->has_indirect); - - h->has_indirect = true; - h->indirect.storage = new_storage; - h->indirect.n_buckets = (1U << new_shift) / - (hi->entry_size + sizeof(dib_raw_t)); - - old_dibs = (dib_raw_t*)((uint8_t*) new_storage + hi->entry_size * old_n_buckets); - new_dibs = dib_raw_ptr(h); - - /* - * Move the DIB array to the new place, replacing valid DIB values with - * DIB_RAW_REHASH to indicate all of the used buckets need rehashing. - * Note: Overlap is not possible, because we have at least doubled the - * number of buckets and dib_raw_t is smaller than any entry type. - */ - for (idx = 0; idx < old_n_buckets; idx++) { - assert(old_dibs[idx] != DIB_RAW_REHASH); - new_dibs[idx] = old_dibs[idx] == DIB_RAW_FREE ? DIB_RAW_FREE - : DIB_RAW_REHASH; - } - - /* Zero the area of newly added entries (including the old DIB area) */ - memzero(bucket_at(h, old_n_buckets), - (n_buckets(h) - old_n_buckets) * hi->entry_size); - - /* The upper half of the new DIB array needs initialization */ - memset(&new_dibs[old_n_buckets], DIB_RAW_INIT, - (n_buckets(h) - old_n_buckets) * sizeof(dib_raw_t)); - - /* Rehash entries that need it */ - n_rehashed = 0; - for (idx = 0; idx < old_n_buckets; idx++) { - if (new_dibs[idx] != DIB_RAW_REHASH) - continue; - - optimal_idx = bucket_hash(h, bucket_at(h, idx)->key); - - /* - * Not much to do if by luck the entry hashes to its current - * location. Just set its DIB. - */ - if (optimal_idx == idx) { - new_dibs[idx] = 0; - n_rehashed++; - continue; - } - - new_dibs[idx] = DIB_RAW_FREE; - bucket_move_entry(h, &swap, idx, IDX_PUT); - /* bucket_move_entry does not clear the source */ - memzero(bucket_at(h, idx), hi->entry_size); - - do { - /* - * Find the new bucket for the current entry. This may make - * another entry homeless and load it into IDX_PUT. - */ - rehash_next = hashmap_put_robin_hood(h, optimal_idx, &swap); - n_rehashed++; - - /* Did the current entry displace another one? */ - if (rehash_next) - optimal_idx = bucket_hash(h, bucket_at_swap(&swap, IDX_PUT)->p.b.key); - } while (rehash_next); - } - - assert(n_rehashed == n_entries(h)); - - return 1; -} - -/* - * Finds an entry with a matching key - * Returns: index of the found entry, or IDX_NIL if not found. - */ -static unsigned base_bucket_scan(HashmapBase *h, unsigned idx, const void *key) { - struct hashmap_base_entry *e; - unsigned dib, distance; - dib_raw_t *dibs = dib_raw_ptr(h); - - assert(idx < n_buckets(h)); - - for (distance = 0; ; distance++) { - if (dibs[idx] == DIB_RAW_FREE) - return IDX_NIL; - - dib = bucket_calculate_dib(h, idx, dibs[idx]); - - if (dib < distance) - return IDX_NIL; - if (dib == distance) { - e = bucket_at(h, idx); - if (h->hash_ops->compare(e->key, key) == 0) - return idx; - } - - idx = next_idx(h, idx); - } -} -#define bucket_scan(h, idx, key) base_bucket_scan(HASHMAP_BASE(h), idx, key) - -int hashmap_put(Hashmap *h, const void *key, void *value) { - struct swap_entries swap; - struct plain_hashmap_entry *e; - unsigned hash, idx; - - assert(h); - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx != IDX_NIL) { - e = plain_bucket_at(h, idx); - if (e->value == value) - return 0; - return -EEXIST; - } - - e = &bucket_at_swap(&swap, IDX_PUT)->p; - e->b.key = key; - e->value = value; - return hashmap_put_boldly(h, hash, &swap, true); -} - -int set_put(Set *s, const void *key) { - struct swap_entries swap; - struct hashmap_base_entry *e; - unsigned hash, idx; - - assert(s); - - hash = bucket_hash(s, key); - idx = bucket_scan(s, hash, key); - if (idx != IDX_NIL) - return 0; - - e = &bucket_at_swap(&swap, IDX_PUT)->p.b; - e->key = key; - return hashmap_put_boldly(s, hash, &swap, true); -} - -int hashmap_replace(Hashmap *h, const void *key, void *value) { - struct swap_entries swap; - struct plain_hashmap_entry *e; - unsigned hash, idx; - - assert(h); - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx != IDX_NIL) { - e = plain_bucket_at(h, idx); -#ifdef ENABLE_DEBUG_HASHMAP - /* Although the key is equal, the key pointer may have changed, - * and this would break our assumption for iterating. So count - * this operation as incompatible with iteration. */ - if (e->b.key != key) { - h->b.debug.put_count++; - h->b.debug.rem_count++; - h->b.debug.last_rem_idx = idx; - } -#endif - e->b.key = key; - e->value = value; - return 0; - } - - e = &bucket_at_swap(&swap, IDX_PUT)->p; - e->b.key = key; - e->value = value; - return hashmap_put_boldly(h, hash, &swap, true); -} - -int hashmap_update(Hashmap *h, const void *key, void *value) { - struct plain_hashmap_entry *e; - unsigned hash, idx; - - assert(h); - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return -ENOENT; - - e = plain_bucket_at(h, idx); - e->value = value; - return 0; -} - -void *internal_hashmap_get(HashmapBase *h, const void *key) { - struct hashmap_base_entry *e; - unsigned hash, idx; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - return entry_value(h, e); -} - -void *hashmap_get2(Hashmap *h, const void *key, void **key2) { - struct plain_hashmap_entry *e; - unsigned hash, idx; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = plain_bucket_at(h, idx); - if (key2) - *key2 = (void*) e->b.key; - - return e->value; -} - -bool internal_hashmap_contains(HashmapBase *h, const void *key) { - unsigned hash; - - if (!h) - return false; - - hash = bucket_hash(h, key); - return bucket_scan(h, hash, key) != IDX_NIL; -} - -void *internal_hashmap_remove(HashmapBase *h, const void *key) { - struct hashmap_base_entry *e; - unsigned hash, idx; - void *data; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - data = entry_value(h, e); - remove_entry(h, idx); - - return data; -} - -void *hashmap_remove2(Hashmap *h, const void *key, void **rkey) { - struct plain_hashmap_entry *e; - unsigned hash, idx; - void *data; - - if (!h) { - if (rkey) - *rkey = NULL; - return NULL; - } - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) { - if (rkey) - *rkey = NULL; - return NULL; - } - - e = plain_bucket_at(h, idx); - data = e->value; - if (rkey) - *rkey = (void*) e->b.key; - - remove_entry(h, idx); - - return data; -} - -int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) { - struct swap_entries swap; - struct plain_hashmap_entry *e; - unsigned old_hash, new_hash, idx; - - if (!h) - return -ENOENT; - - old_hash = bucket_hash(h, old_key); - idx = bucket_scan(h, old_hash, old_key); - if (idx == IDX_NIL) - return -ENOENT; - - new_hash = bucket_hash(h, new_key); - if (bucket_scan(h, new_hash, new_key) != IDX_NIL) - return -EEXIST; - - remove_entry(h, idx); - - e = &bucket_at_swap(&swap, IDX_PUT)->p; - e->b.key = new_key; - e->value = value; - assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1); - - return 0; -} - -int set_remove_and_put(Set *s, const void *old_key, const void *new_key) { - struct swap_entries swap; - struct hashmap_base_entry *e; - unsigned old_hash, new_hash, idx; - - if (!s) - return -ENOENT; - - old_hash = bucket_hash(s, old_key); - idx = bucket_scan(s, old_hash, old_key); - if (idx == IDX_NIL) - return -ENOENT; - - new_hash = bucket_hash(s, new_key); - if (bucket_scan(s, new_hash, new_key) != IDX_NIL) - return -EEXIST; - - remove_entry(s, idx); - - e = &bucket_at_swap(&swap, IDX_PUT)->p.b; - e->key = new_key; - assert_se(hashmap_put_boldly(s, new_hash, &swap, false) == 1); - - return 0; -} - -int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) { - struct swap_entries swap; - struct plain_hashmap_entry *e; - unsigned old_hash, new_hash, idx_old, idx_new; - - if (!h) - return -ENOENT; - - old_hash = bucket_hash(h, old_key); - idx_old = bucket_scan(h, old_hash, old_key); - if (idx_old == IDX_NIL) - return -ENOENT; - - old_key = bucket_at(HASHMAP_BASE(h), idx_old)->key; - - new_hash = bucket_hash(h, new_key); - idx_new = bucket_scan(h, new_hash, new_key); - if (idx_new != IDX_NIL) - if (idx_old != idx_new) { - remove_entry(h, idx_new); - /* Compensate for a possible backward shift. */ - if (old_key != bucket_at(HASHMAP_BASE(h), idx_old)->key) - idx_old = prev_idx(HASHMAP_BASE(h), idx_old); - assert(old_key == bucket_at(HASHMAP_BASE(h), idx_old)->key); - } - - remove_entry(h, idx_old); - - e = &bucket_at_swap(&swap, IDX_PUT)->p; - e->b.key = new_key; - e->value = value; - assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1); - - return 0; -} - -void *hashmap_remove_value(Hashmap *h, const void *key, void *value) { - struct plain_hashmap_entry *e; - unsigned hash, idx; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = plain_bucket_at(h, idx); - if (e->value != value) - return NULL; - - remove_entry(h, idx); - - return value; -} - -static unsigned find_first_entry(HashmapBase *h) { - Iterator i = ITERATOR_FIRST; - - if (!h || !n_entries(h)) - return IDX_NIL; - - return hashmap_iterate_entry(h, &i); -} - -void *internal_hashmap_first(HashmapBase *h) { - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - return entry_value(h, bucket_at(h, idx)); -} - -void *internal_hashmap_first_key(HashmapBase *h) { - struct hashmap_base_entry *e; - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - return (void*) e->key; -} - -void *internal_hashmap_steal_first(HashmapBase *h) { - struct hashmap_base_entry *e; - void *data; - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - data = entry_value(h, e); - remove_entry(h, idx); - - return data; -} - -void *internal_hashmap_steal_first_key(HashmapBase *h) { - struct hashmap_base_entry *e; - void *key; - unsigned idx; - - idx = find_first_entry(h); - if (idx == IDX_NIL) - return NULL; - - e = bucket_at(h, idx); - key = (void*) e->key; - remove_entry(h, idx); - - return key; -} - -unsigned internal_hashmap_size(HashmapBase *h) { - - if (!h) - return 0; - - return n_entries(h); -} - -unsigned internal_hashmap_buckets(HashmapBase *h) { - - if (!h) - return 0; - - return n_buckets(h); -} - -int internal_hashmap_merge(Hashmap *h, Hashmap *other) { - Iterator i; - unsigned idx; - - assert(h); - - HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) { - struct plain_hashmap_entry *pe = plain_bucket_at(other, idx); - int r; - - r = hashmap_put(h, pe->b.key, pe->value); - if (r < 0 && r != -EEXIST) - return r; - } - - return 0; -} - -int set_merge(Set *s, Set *other) { - Iterator i; - unsigned idx; - - assert(s); - - HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) { - struct set_entry *se = set_bucket_at(other, idx); - int r; - - r = set_put(s, se->b.key); - if (r < 0) - return r; - } - - return 0; -} - -int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) { - int r; - - assert(h); - - r = resize_buckets(h, entries_add); - if (r < 0) - return r; - - return 0; -} - -/* - * The same as hashmap_merge(), but every new item from other is moved to h. - * Keys already in h are skipped and stay in other. - * Returns: 0 on success. - * -ENOMEM on alloc failure, in which case no move has been done. - */ -int internal_hashmap_move(HashmapBase *h, HashmapBase *other) { - struct swap_entries swap; - struct hashmap_base_entry *e, *n; - Iterator i; - unsigned idx; - int r; - - assert(h); - - if (!other) - return 0; - - assert(other->type == h->type); - - /* - * This reserves buckets for the worst case, where none of other's - * entries are yet present in h. This is preferable to risking - * an allocation failure in the middle of the moving and having to - * rollback or return a partial result. - */ - r = resize_buckets(h, n_entries(other)); - if (r < 0) - return r; - - HASHMAP_FOREACH_IDX(idx, other, i) { - unsigned h_hash; - - e = bucket_at(other, idx); - h_hash = bucket_hash(h, e->key); - if (bucket_scan(h, h_hash, e->key) != IDX_NIL) - continue; - - n = &bucket_at_swap(&swap, IDX_PUT)->p.b; - n->key = e->key; - if (h->type != HASHMAP_TYPE_SET) - ((struct plain_hashmap_entry*) n)->value = - ((struct plain_hashmap_entry*) e)->value; - assert_se(hashmap_put_boldly(h, h_hash, &swap, false) == 1); - - remove_entry(other, idx); - } - - return 0; -} - -int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) { - struct swap_entries swap; - unsigned h_hash, other_hash, idx; - struct hashmap_base_entry *e, *n; - int r; - - assert(h); - - h_hash = bucket_hash(h, key); - if (bucket_scan(h, h_hash, key) != IDX_NIL) - return -EEXIST; - - if (!other) - return -ENOENT; - - assert(other->type == h->type); - - other_hash = bucket_hash(other, key); - idx = bucket_scan(other, other_hash, key); - if (idx == IDX_NIL) - return -ENOENT; - - e = bucket_at(other, idx); - - n = &bucket_at_swap(&swap, IDX_PUT)->p.b; - n->key = e->key; - if (h->type != HASHMAP_TYPE_SET) - ((struct plain_hashmap_entry*) n)->value = - ((struct plain_hashmap_entry*) e)->value; - r = hashmap_put_boldly(h, h_hash, &swap, true); - if (r < 0) - return r; - - remove_entry(other, idx); - return 0; -} - -HashmapBase *internal_hashmap_copy(HashmapBase *h) { - HashmapBase *copy; - int r; - - assert(h); - - copy = hashmap_base_new(h->hash_ops, h->type HASHMAP_DEBUG_SRC_ARGS); - if (!copy) - return NULL; - - switch (h->type) { - case HASHMAP_TYPE_PLAIN: - case HASHMAP_TYPE_ORDERED: - r = hashmap_merge((Hashmap*)copy, (Hashmap*)h); - break; - case HASHMAP_TYPE_SET: - r = set_merge((Set*)copy, (Set*)h); - break; - default: - assert_not_reached("Unknown hashmap type"); - } - - if (r < 0) { - internal_hashmap_free(copy); - return NULL; - } - - return copy; -} - -char **internal_hashmap_get_strv(HashmapBase *h) { - char **sv; - Iterator i; - unsigned idx, n; - - sv = new(char*, n_entries(h)+1); - if (!sv) - return NULL; - - n = 0; - HASHMAP_FOREACH_IDX(idx, h, i) - sv[n++] = entry_value(h, bucket_at(h, idx)); - sv[n] = NULL; - - return sv; -} - -void *ordered_hashmap_next(OrderedHashmap *h, const void *key) { - struct ordered_hashmap_entry *e; - unsigned hash, idx; - - if (!h) - return NULL; - - hash = bucket_hash(h, key); - idx = bucket_scan(h, hash, key); - if (idx == IDX_NIL) - return NULL; - - e = ordered_bucket_at(h, idx); - if (e->iterate_next == IDX_NIL) - return NULL; - return ordered_bucket_at(h, e->iterate_next)->p.value; -} - -int set_consume(Set *s, void *value) { - int r; - - assert(s); - assert(value); - - r = set_put(s, value); - if (r <= 0) - free(value); - - return r; -} - -int set_put_strdup(Set *s, const char *p) { - char *c; - - assert(s); - assert(p); - - if (set_contains(s, (char*) p)) - return 0; - - c = strdup(p); - if (!c) - return -ENOMEM; - - return set_consume(s, c); -} - -int set_put_strdupv(Set *s, char **l) { - int n = 0, r; - char **i; - - assert(s); - - STRV_FOREACH(i, l) { - r = set_put_strdup(s, *i); - if (r < 0) - return r; - - n += r; - } - - return n; -} - -int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags) { - const char *p = v; - int r; - - assert(s); - assert(v); - - for (;;) { - char *word; - - r = extract_first_word(&p, &word, separators, flags); - if (r <= 0) - return r; - - r = set_consume(s, word); - if (r < 0) - return r; - } -} diff --git a/src/basic/hashmap.h b/src/basic/hashmap.h deleted file mode 100644 index 6d1ae48b21..0000000000 --- a/src/basic/hashmap.h +++ /dev/null @@ -1,372 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2014 Michal Schmidt - - 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 <limits.h> -#include <stdbool.h> -#include <stddef.h> - -#include "hash-funcs.h" -#include "macro.h" -#include "util.h" - -/* - * A hash table implementation. As a minor optimization a NULL hashmap object - * will be treated as empty hashmap for all read operations. That way it is not - * necessary to instantiate an object for each Hashmap use. - * - * If ENABLE_DEBUG_HASHMAP is defined (by configuring with --enable-debug=hashmap), - * the implemention will: - * - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py) - * - perform extra checks for invalid use of iterators - */ - -#define HASH_KEY_SIZE 16 - -/* The base type for all hashmap and set types. Many functions in the - * implementation take (HashmapBase*) parameters and are run-time polymorphic, - * though the API is not meant to be polymorphic (do not call functions - * internal_*() directly). */ -typedef struct HashmapBase HashmapBase; - -/* Specific hashmap/set types */ -typedef struct Hashmap Hashmap; /* Maps keys to values */ -typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */ -typedef struct Set Set; /* Stores just keys */ - -/* Ideally the Iterator would be an opaque struct, but it is instantiated - * by hashmap users, so the definition has to be here. Do not use its fields - * directly. */ -typedef struct { - unsigned idx; /* index of an entry to be iterated next */ - const void *next_key; /* expected value of that entry's key pointer */ -#ifdef ENABLE_DEBUG_HASHMAP - unsigned put_count; /* hashmap's put_count recorded at start of iteration */ - unsigned rem_count; /* hashmap's rem_count in previous iteration */ - unsigned prev_idx; /* idx in previous iteration */ -#endif -} Iterator; - -#define _IDX_ITERATOR_FIRST (UINT_MAX - 1) -#define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL }) - -/* Macros for type checking */ -#define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \ - (__builtin_types_compatible_p(typeof(h), HashmapBase*) || \ - __builtin_types_compatible_p(typeof(h), Hashmap*) || \ - __builtin_types_compatible_p(typeof(h), OrderedHashmap*) || \ - __builtin_types_compatible_p(typeof(h), Set*)) - -#define PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h) \ - (__builtin_types_compatible_p(typeof(h), Hashmap*) || \ - __builtin_types_compatible_p(typeof(h), OrderedHashmap*)) \ - -#define HASHMAP_BASE(h) \ - __builtin_choose_expr(PTR_COMPATIBLE_WITH_HASHMAP_BASE(h), \ - (HashmapBase*)(h), \ - (void)0) - -#define PLAIN_HASHMAP(h) \ - __builtin_choose_expr(PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h), \ - (Hashmap*)(h), \ - (void)0) - -#ifdef ENABLE_DEBUG_HASHMAP -# define HASHMAP_DEBUG_PARAMS , const char *func, const char *file, int line -# define HASHMAP_DEBUG_SRC_ARGS , __func__, __FILE__, __LINE__ -# define HASHMAP_DEBUG_PASS_ARGS , func, file, line -#else -# define HASHMAP_DEBUG_PARAMS -# define HASHMAP_DEBUG_SRC_ARGS -# define HASHMAP_DEBUG_PASS_ARGS -#endif - -Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) -#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) - -HashmapBase *internal_hashmap_free(HashmapBase *h); -static inline Hashmap *hashmap_free(Hashmap *h) { - return (void*)internal_hashmap_free(HASHMAP_BASE(h)); -} -static inline OrderedHashmap *ordered_hashmap_free(OrderedHashmap *h) { - return (void*)internal_hashmap_free(HASHMAP_BASE(h)); -} - -HashmapBase *internal_hashmap_free_free(HashmapBase *h); -static inline Hashmap *hashmap_free_free(Hashmap *h) { - return (void*)internal_hashmap_free_free(HASHMAP_BASE(h)); -} -static inline OrderedHashmap *ordered_hashmap_free_free(OrderedHashmap *h) { - return (void*)internal_hashmap_free_free(HASHMAP_BASE(h)); -} - -Hashmap *hashmap_free_free_free(Hashmap *h); -static inline OrderedHashmap *ordered_hashmap_free_free_free(OrderedHashmap *h) { - return (void*)hashmap_free_free_free(PLAIN_HASHMAP(h)); -} - -HashmapBase *internal_hashmap_copy(HashmapBase *h); -static inline Hashmap *hashmap_copy(Hashmap *h) { - return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); -} -static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) { - return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); -} - -int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) -#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) - -int hashmap_put(Hashmap *h, const void *key, void *value); -static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) { - return hashmap_put(PLAIN_HASHMAP(h), key, value); -} - -int hashmap_update(Hashmap *h, const void *key, void *value); -static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) { - return hashmap_update(PLAIN_HASHMAP(h), key, value); -} - -int hashmap_replace(Hashmap *h, const void *key, void *value); -static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, void *value) { - return hashmap_replace(PLAIN_HASHMAP(h), key, value); -} - -void *internal_hashmap_get(HashmapBase *h, const void *key); -static inline void *hashmap_get(Hashmap *h, const void *key) { - return internal_hashmap_get(HASHMAP_BASE(h), key); -} -static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) { - return internal_hashmap_get(HASHMAP_BASE(h), key); -} - -void *hashmap_get2(Hashmap *h, const void *key, void **rkey); -static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, void **rkey) { - return hashmap_get2(PLAIN_HASHMAP(h), key, rkey); -} - -bool internal_hashmap_contains(HashmapBase *h, const void *key); -static inline bool hashmap_contains(Hashmap *h, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE(h), key); -} -static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE(h), key); -} - -void *internal_hashmap_remove(HashmapBase *h, const void *key); -static inline void *hashmap_remove(Hashmap *h, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(h), key); -} -static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(h), key); -} - -void *hashmap_remove2(Hashmap *h, const void *key, void **rkey); -static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, void **rkey) { - return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey); -} - -void *hashmap_remove_value(Hashmap *h, const void *key, void *value); -static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) { - return hashmap_remove_value(PLAIN_HASHMAP(h), key, value); -} - -int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value); -static inline int ordered_hashmap_remove_and_put(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) { - return hashmap_remove_and_put(PLAIN_HASHMAP(h), old_key, new_key, value); -} - -int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value); -static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) { - return hashmap_remove_and_replace(PLAIN_HASHMAP(h), old_key, new_key, value); -} - -/* Since merging data from a OrderedHashmap into a Hashmap or vice-versa - * should just work, allow this by having looser type-checking here. */ -int internal_hashmap_merge(Hashmap *h, Hashmap *other); -#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other)) -#define ordered_hashmap_merge(h, other) hashmap_merge(h, other) - -int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add); -static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); -} -static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); -} - -int internal_hashmap_move(HashmapBase *h, HashmapBase *other); -/* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */ -static inline int hashmap_move(Hashmap *h, Hashmap *other) { - return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); -} -static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) { - return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); -} - -int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key); -static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); -} -static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); -} - -unsigned internal_hashmap_size(HashmapBase *h) _pure_; -static inline unsigned hashmap_size(Hashmap *h) { - return internal_hashmap_size(HASHMAP_BASE(h)); -} -static inline unsigned ordered_hashmap_size(OrderedHashmap *h) { - return internal_hashmap_size(HASHMAP_BASE(h)); -} - -static inline bool hashmap_isempty(Hashmap *h) { - return hashmap_size(h) == 0; -} -static inline bool ordered_hashmap_isempty(OrderedHashmap *h) { - return ordered_hashmap_size(h) == 0; -} - -unsigned internal_hashmap_buckets(HashmapBase *h) _pure_; -static inline unsigned hashmap_buckets(Hashmap *h) { - return internal_hashmap_buckets(HASHMAP_BASE(h)); -} -static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) { - return internal_hashmap_buckets(HASHMAP_BASE(h)); -} - -bool internal_hashmap_iterate(HashmapBase *h, Iterator *i, void **value, const void **key); -static inline bool hashmap_iterate(Hashmap *h, Iterator *i, void **value, const void **key) { - return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); -} -static inline bool ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, void **value, const void **key) { - return internal_hashmap_iterate(HASHMAP_BASE(h), i, value, key); -} - -void internal_hashmap_clear(HashmapBase *h); -static inline void hashmap_clear(Hashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h)); -} -static inline void ordered_hashmap_clear(OrderedHashmap *h) { - internal_hashmap_clear(HASHMAP_BASE(h)); -} - -void internal_hashmap_clear_free(HashmapBase *h); -static inline void hashmap_clear_free(Hashmap *h) { - internal_hashmap_clear_free(HASHMAP_BASE(h)); -} -static inline void ordered_hashmap_clear_free(OrderedHashmap *h) { - internal_hashmap_clear_free(HASHMAP_BASE(h)); -} - -void hashmap_clear_free_free(Hashmap *h); -static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { - hashmap_clear_free_free(PLAIN_HASHMAP(h)); -} - -/* - * Note about all *_first*() functions - * - * For plain Hashmaps and Sets the order of entries is undefined. - * The functions find whatever entry is first in the implementation - * internal order. - * - * Only for OrderedHashmaps the order is well defined and finding - * the first entry is O(1). - */ - -void *internal_hashmap_steal_first(HashmapBase *h); -static inline void *hashmap_steal_first(Hashmap *h) { - return internal_hashmap_steal_first(HASHMAP_BASE(h)); -} -static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) { - return internal_hashmap_steal_first(HASHMAP_BASE(h)); -} - -void *internal_hashmap_steal_first_key(HashmapBase *h); -static inline void *hashmap_steal_first_key(Hashmap *h) { - return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); -} -static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) { - return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); -} - -void *internal_hashmap_first_key(HashmapBase *h) _pure_; -static inline void *hashmap_first_key(Hashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h)); -} -static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { - return internal_hashmap_first_key(HASHMAP_BASE(h)); -} - -void *internal_hashmap_first(HashmapBase *h) _pure_; -static inline void *hashmap_first(Hashmap *h) { - return internal_hashmap_first(HASHMAP_BASE(h)); -} -static inline void *ordered_hashmap_first(OrderedHashmap *h) { - return internal_hashmap_first(HASHMAP_BASE(h)); -} - -/* no hashmap_next */ -void *ordered_hashmap_next(OrderedHashmap *h, const void *key); - -char **internal_hashmap_get_strv(HashmapBase *h); -static inline char **hashmap_get_strv(Hashmap *h) { - return internal_hashmap_get_strv(HASHMAP_BASE(h)); -} -static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) { - return internal_hashmap_get_strv(HASHMAP_BASE(h)); -} - -/* - * Hashmaps are iterated in unpredictable order. - * OrderedHashmaps are an exception to this. They are iterated in the order - * the entries were inserted. - * It is safe to remove the current entry. - */ -#define HASHMAP_FOREACH(e, h, i) \ - for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), NULL); ) - -#define ORDERED_HASHMAP_FOREACH(e, h, i) \ - for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), NULL); ) - -#define HASHMAP_FOREACH_KEY(e, k, h, i) \ - for ((i) = ITERATOR_FIRST; hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); ) - -#define ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \ - for ((i) = ITERATOR_FIRST; ordered_hashmap_iterate((h), &(i), (void**)&(e), (const void**) &(k)); ) - -DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free); - -#define _cleanup_hashmap_free_ _cleanup_(hashmap_freep) -#define _cleanup_hashmap_free_free_ _cleanup_(hashmap_free_freep) -#define _cleanup_hashmap_free_free_free_ _cleanup_(hashmap_free_free_freep) -#define _cleanup_ordered_hashmap_free_ _cleanup_(ordered_hashmap_freep) -#define _cleanup_ordered_hashmap_free_free_ _cleanup_(ordered_hashmap_free_freep) -#define _cleanup_ordered_hashmap_free_free_free_ _cleanup_(ordered_hashmap_free_free_freep) diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c deleted file mode 100644 index c5bda6c4d6..0000000000 --- a/src/basic/hexdecoct.c +++ /dev/null @@ -1,754 +0,0 @@ -/*** - 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 <errno.h> -#include <stdint.h> -#include <stdlib.h> - -#include "alloc-util.h" -#include "hexdecoct.h" -#include "macro.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; -} - -ssize_t base64mem(const void *p, size_t l, char **out) { - 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 -ENOMEM; - - for (x = p; x < (const uint8_t*) p + (l / 3) * 3; x += 3) { - /* x[0] == XXXXXXXX; x[1] == YYYYYYYY; x[2] == ZZZZZZZZ */ - *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ - *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */ - *(z++) = base64char((x[1] & 15) << 2 | x[2] >> 6); /* 00YYYYZZ */ - *(z++) = base64char(x[2] & 63); /* 00ZZZZZZ */ - } - - switch (l % 3) { - case 2: - *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ - *(z++) = base64char((x[0] & 3) << 4 | x[1] >> 4); /* 00XXYYYY */ - *(z++) = base64char((x[1] & 15) << 2); /* 00YYYY00 */ - *(z++) = '='; - - break; - case 1: - *(z++) = base64char(x[0] >> 2); /* 00XXXXXX */ - *(z++) = base64char((x[0] & 3) << 4); /* 00XX0000 */ - *(z++) = '='; - *(z++) = '='; - - break; - } - - *z = 0; - *out = r; - return z - r; -} - -static int base64_append_width(char **prefix, int plen, - const char *sep, int indent, - const void *p, size_t l, - int width) { - - _cleanup_free_ char *x = NULL; - char *t, *s; - ssize_t slen, len, avail; - int line, lines; - - len = base64mem(p, l, &x); - if (len <= 0) - return len; - - lines = (len + width - 1) / width; - - slen = sep ? strlen(sep) : 0; - t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines); - if (!t) - return -ENOMEM; - - memcpy_safe(t + plen, sep, slen); - - for (line = 0, s = t + plen + slen, avail = len; line < lines; line++) { - int act = MIN(width, avail); - - if (line > 0 || sep) { - memset(s, ' ', indent); - s += indent; - } - - memcpy(s, x + width * line, act); - s += act; - *(s++) = line < lines - 1 ? '\n' : '\0'; - avail -= act; - } - assert(avail == 0); - - *prefix = t; - return 0; -} - -int base64_append(char **prefix, int plen, - const void *p, size_t l, - int indent, int width) { - if (plen > width / 2 || plen + indent > width) - /* leave indent on the left, keep last column free */ - return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1); - else - /* leave plen on the left, keep last column free */ - return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1); -}; - - -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/basic/hexdecoct.h b/src/basic/hexdecoct.h deleted file mode 100644 index 1ba2f69ebd..0000000000 --- a/src/basic/hexdecoct.h +++ /dev/null @@ -1,56 +0,0 @@ -#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 <stddef.h> -#include <stdio.h> -#include <sys/types.h> - -#include "macro.h" - -char octchar(int x) _const_; -int unoctchar(char c) _const_; - -char decchar(int x) _const_; -int undecchar(char c) _const_; - -char hexchar(int x) _const_; -int unhexchar(char c) _const_; - -char *hexmem(const void *p, size_t l); -int unhexmem(const char *p, size_t l, void **mem, size_t *len); - -char base32hexchar(int x) _const_; -int unbase32hexchar(char c) _const_; - -char base64char(int x) _const_; -int unbase64char(char c) _const_; - -char *base32hexmem(const void *p, size_t l, bool padding); -int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *len); - -ssize_t base64mem(const void *p, size_t l, char **out); -int base64_append(char **prefix, int plen, - const void *p, size_t l, - int margin, int width); -int unbase64mem(const char *p, size_t l, void **mem, size_t *len); - -void hexdump(FILE *f, const void *p, size_t s); diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c deleted file mode 100644 index e44a357287..0000000000 --- a/src/basic/hostname-util.c +++ /dev/null @@ -1,251 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 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 <errno.h> -#include <limits.h> -#include <stdio.h> -#include <string.h> -#include <sys/utsname.h> -#include <unistd.h> - -#include "fd-util.h" -#include "fileio.h" -#include "hostname-util.h" -#include "macro.h" -#include "string-util.h" - -bool hostname_is_set(void) { - struct utsname u; - - assert_se(uname(&u) >= 0); - - if (isempty(u.nodename)) - return false; - - /* This is the built-in kernel default host name */ - if (streq(u.nodename, "(none)")) - return false; - - return true; -} - -char* gethostname_malloc(void) { - struct utsname u; - - /* This call tries to return something useful, either the actual hostname - * or it makes something up. The only reason it might fail is OOM. - * It might even return "localhost" if that's set. */ - - assert_se(uname(&u) >= 0); - - if (isempty(u.nodename) || streq(u.nodename, "(none)")) - return strdup(u.sysname); - - return strdup(u.nodename); -} - -int gethostname_strict(char **ret) { - struct utsname u; - char *k; - - /* This call will rather fail than make up a name. It will not return "localhost" either. */ - - assert_se(uname(&u) >= 0); - - if (isempty(u.nodename)) - return -ENXIO; - - if (streq(u.nodename, "(none)")) - return -ENXIO; - - if (is_localhost(u.nodename)) - return -ENXIO; - - k = strdup(u.nodename); - if (!k) - return -ENOMEM; - - *ret = k; - return 0; -} - -static bool hostname_valid_char(char c) { - return - (c >= 'a' && c <= 'z') || - (c >= 'A' && c <= 'Z') || - (c >= '0' && c <= '9') || - c == '-' || - c == '_' || - c == '.'; -} - -/** - * Check if s looks like a valid host name or FQDN. This does not do - * full DNS validation, but only checks if the name is composed of - * allowed characters and the length is not above the maximum allowed - * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if - * allow_trailing_dot is true and at least two components are present - * in the name. Note that due to the restricted charset and length - * this call is substantially more conservative than - * dns_name_is_valid(). - */ -bool hostname_is_valid(const char *s, bool allow_trailing_dot) { - unsigned n_dots = 0; - const char *p; - bool dot; - - if (isempty(s)) - return false; - - /* Doesn't accept empty hostnames, hostnames with - * leading dots, and hostnames with multiple dots in a - * sequence. Also ensures that the length stays below - * HOST_NAME_MAX. */ - - for (p = s, dot = true; *p; p++) { - if (*p == '.') { - if (dot) - return false; - - dot = true; - n_dots++; - } else { - if (!hostname_valid_char(*p)) - return false; - - dot = false; - } - } - - if (dot && (n_dots < 2 || !allow_trailing_dot)) - return false; - - if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on - * Linux, but DNS allows domain names - * up to 255 characters */ - return false; - - return true; -} - -char* hostname_cleanup(char *s) { - char *p, *d; - bool dot; - - assert(s); - - strshorten(s, HOST_NAME_MAX); - - for (p = s, d = s, dot = true; *p; p++) { - if (*p == '.') { - if (dot) - continue; - - *(d++) = '.'; - dot = true; - } else if (hostname_valid_char(*p)) { - *(d++) = *p; - dot = false; - } - } - - if (dot && d > s) - d[-1] = 0; - else - *d = 0; - - return s; -} - -bool is_localhost(const char *hostname) { - assert(hostname); - - /* This tries to identify local host and domain names - * described in RFC6761 plus the redhatism of localdomain */ - - return strcaseeq(hostname, "localhost") || - strcaseeq(hostname, "localhost.") || - strcaseeq(hostname, "localhost.localdomain") || - strcaseeq(hostname, "localhost.localdomain.") || - endswith_no_case(hostname, ".localhost") || - endswith_no_case(hostname, ".localhost.") || - endswith_no_case(hostname, ".localhost.localdomain") || - endswith_no_case(hostname, ".localhost.localdomain."); -} - -bool is_gateway_hostname(const char *hostname) { - assert(hostname); - - /* This tries to identify the valid syntaxes for the our - * synthetic "gateway" host. */ - - return - strcaseeq(hostname, "gateway") || - strcaseeq(hostname, "gateway."); -} - -int sethostname_idempotent(const char *s) { - char buf[HOST_NAME_MAX + 1] = {}; - - assert(s); - - if (gethostname(buf, sizeof(buf)) < 0) - return -errno; - - if (streq(buf, s)) - return 0; - - if (sethostname(s, strlen(s)) < 0) - return -errno; - - return 1; -} - -int read_hostname_config(const char *path, char **hostname) { - _cleanup_fclose_ FILE *f = NULL; - char l[LINE_MAX]; - char *name = NULL; - - assert(path); - assert(hostname); - - f = fopen(path, "re"); - if (!f) - return -errno; - - /* may have comments, ignore them */ - FOREACH_LINE(l, f, return -errno) { - truncate_nl(l); - if (l[0] != '\0' && l[0] != '#') { - /* found line with value */ - name = hostname_cleanup(l); - name = strdup(name); - if (!name) - return -ENOMEM; - break; - } - } - - if (!name) - /* no non-empty line found */ - return -ENOENT; - - *hostname = name; - return 0; -} diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h deleted file mode 100644 index 7af4e6c7ec..0000000000 --- a/src/basic/hostname-util.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010-2015 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 "macro.h" - -bool hostname_is_set(void); - -char* gethostname_malloc(void); -int gethostname_strict(char **ret); - -bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_; -char* hostname_cleanup(char *s); - -#define machine_name_is_valid(s) hostname_is_valid(s, false) - -bool is_localhost(const char *hostname); -bool is_gateway_hostname(const char *hostname); - -int sethostname_idempotent(const char *s); - -int read_hostname_config(const char *path, char **hostname); diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c deleted file mode 100644 index aa7ccd1afd..0000000000 --- a/src/basic/in-addr-util.c +++ /dev/null @@ -1,449 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 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 <arpa/inet.h> -#include <endian.h> -#include <errno.h> -#include <net/if.h> -#include <stdint.h> -#include <stdlib.h> - -#include "alloc-util.h" -#include "in-addr-util.h" -#include "macro.h" -#include "parse-util.h" -#include "util.h" - -bool in4_addr_is_null(const struct in_addr *a) { - return a->s_addr == 0; -} - -bool in6_addr_is_null(const struct in6_addr *a) { - return - a->s6_addr32[0] == 0 && - a->s6_addr32[1] == 0 && - a->s6_addr32[2] == 0 && - a->s6_addr32[3] == 0; -} - -int in_addr_is_null(int family, const union in_addr_union *u) { - assert(u); - - if (family == AF_INET) - return in4_addr_is_null(&u->in); - - if (family == AF_INET6) - return in6_addr_is_null(&u->in6); - - return -EAFNOSUPPORT; -} - -int in_addr_is_link_local(int family, const union in_addr_union *u) { - assert(u); - - if (family == AF_INET) - return (be32toh(u->in.s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16); - - if (family == AF_INET6) - return IN6_IS_ADDR_LINKLOCAL(&u->in6); - - return -EAFNOSUPPORT; -} - -int in_addr_is_localhost(int family, const union in_addr_union *u) { - assert(u); - - if (family == AF_INET) - /* All of 127.x.x.x is localhost. */ - return (be32toh(u->in.s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24; - - if (family == AF_INET6) - return IN6_IS_ADDR_LOOPBACK(&u->in6); - - return -EAFNOSUPPORT; -} - -int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b) { - assert(a); - assert(b); - - if (family == AF_INET) - return a->in.s_addr == b->in.s_addr; - - if (family == AF_INET6) - return - a->in6.s6_addr32[0] == b->in6.s6_addr32[0] && - a->in6.s6_addr32[1] == b->in6.s6_addr32[1] && - a->in6.s6_addr32[2] == b->in6.s6_addr32[2] && - a->in6.s6_addr32[3] == b->in6.s6_addr32[3]; - - return -EAFNOSUPPORT; -} - -int in_addr_prefix_intersect( - int family, - const union in_addr_union *a, - unsigned aprefixlen, - const union in_addr_union *b, - unsigned bprefixlen) { - - unsigned m; - - assert(a); - assert(b); - - /* Checks whether there are any addresses that are in both - * networks */ - - m = MIN(aprefixlen, bprefixlen); - - if (family == AF_INET) { - uint32_t x, nm; - - x = be32toh(a->in.s_addr ^ b->in.s_addr); - nm = (m == 0) ? 0 : 0xFFFFFFFFUL << (32 - m); - - return (x & nm) == 0; - } - - if (family == AF_INET6) { - unsigned i; - - if (m > 128) - m = 128; - - for (i = 0; i < 16; i++) { - uint8_t x, nm; - - x = a->in6.s6_addr[i] ^ b->in6.s6_addr[i]; - - if (m < 8) - nm = 0xFF << (8 - m); - else - nm = 0xFF; - - if ((x & nm) != 0) - return 0; - - if (m > 8) - m -= 8; - else - m = 0; - } - - return 1; - } - - return -EAFNOSUPPORT; -} - -int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) { - assert(u); - - /* Increases the network part of an address by one. Returns - * positive it that succeeds, or 0 if this overflows. */ - - if (prefixlen <= 0) - return 0; - - if (family == AF_INET) { - uint32_t c, n; - - if (prefixlen > 32) - prefixlen = 32; - - c = be32toh(u->in.s_addr); - n = c + (1UL << (32 - prefixlen)); - if (n < c) - return 0; - n &= 0xFFFFFFFFUL << (32 - prefixlen); - - u->in.s_addr = htobe32(n); - return 1; - } - - if (family == AF_INET6) { - struct in6_addr add = {}, result; - uint8_t overflow = 0; - unsigned i; - - if (prefixlen > 128) - prefixlen = 128; - - /* First calculate what we have to add */ - add.s6_addr[(prefixlen-1) / 8] = 1 << (7 - (prefixlen-1) % 8); - - for (i = 16; i > 0; i--) { - unsigned j = i - 1; - - result.s6_addr[j] = u->in6.s6_addr[j] + add.s6_addr[j] + overflow; - overflow = (result.s6_addr[j] < u->in6.s6_addr[j]); - } - - if (overflow) - return 0; - - u->in6 = result; - return 1; - } - - return -EAFNOSUPPORT; -} - -int in_addr_to_string(int family, const union in_addr_union *u, char **ret) { - char *x; - size_t l; - - assert(u); - assert(ret); - - if (family == AF_INET) - l = INET_ADDRSTRLEN; - else if (family == AF_INET6) - l = INET6_ADDRSTRLEN; - else - return -EAFNOSUPPORT; - - x = new(char, l); - if (!x) - return -ENOMEM; - - errno = 0; - if (!inet_ntop(family, u, x, l)) { - free(x); - return errno > 0 ? -errno : -EINVAL; - } - - *ret = x; - return 0; -} - -int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) { - size_t l; - char *x; - int r; - - assert(u); - assert(ret); - - /* Much like in_addr_to_string(), but optionally appends the zone interface index to the address, to properly - * handle IPv6 link-local addresses. */ - - if (family != AF_INET6) - goto fallback; - if (ifindex <= 0) - goto fallback; - - r = in_addr_is_link_local(family, u); - if (r < 0) - return r; - if (r == 0) - goto fallback; - - l = INET6_ADDRSTRLEN + 1 + DECIMAL_STR_MAX(ifindex) + 1; - x = new(char, l); - if (!x) - return -ENOMEM; - - errno = 0; - if (!inet_ntop(family, u, x, l)) { - free(x); - return errno > 0 ? -errno : -EINVAL; - } - - sprintf(strchr(x, 0), "%%%i", ifindex); - *ret = x; - - return 0; - -fallback: - return in_addr_to_string(family, u, ret); -} - -int in_addr_from_string(int family, const char *s, union in_addr_union *ret) { - - assert(s); - assert(ret); - - if (!IN_SET(family, AF_INET, AF_INET6)) - return -EAFNOSUPPORT; - - errno = 0; - if (inet_pton(family, s, ret) <= 0) - return errno > 0 ? -errno : -EINVAL; - - return 0; -} - -int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret) { - int r; - - assert(s); - assert(family); - assert(ret); - - r = in_addr_from_string(AF_INET, s, ret); - if (r >= 0) { - *family = AF_INET; - return 0; - } - - r = in_addr_from_string(AF_INET6, s, ret); - if (r >= 0) { - *family = AF_INET6; - return 0; - } - - return -EINVAL; -} - -int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex) { - const char *suffix; - int r, ifi = 0; - - assert(s); - assert(family); - assert(ret); - - /* Similar to in_addr_from_string_auto() but also parses an optionally appended IPv6 zone suffix ("scope id") - * if one is found. */ - - suffix = strchr(s, '%'); - if (suffix) { - - if (ifindex) { - /* If we shall return the interface index, try to parse it */ - r = parse_ifindex(suffix + 1, &ifi); - if (r < 0) { - unsigned u; - - u = if_nametoindex(suffix + 1); - if (u <= 0) - return -errno; - - ifi = (int) u; - } - } - - s = strndupa(s, suffix - s); - } - - r = in_addr_from_string_auto(s, family, ret); - if (r < 0) - return r; - - if (ifindex) - *ifindex = ifi; - - return r; -} - -unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr) { - assert(addr); - - return 32 - u32ctz(be32toh(addr->s_addr)); -} - -struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen) { - assert(addr); - assert(prefixlen <= 32); - - /* Shifting beyond 32 is not defined, handle this specially. */ - if (prefixlen == 0) - addr->s_addr = 0; - else - addr->s_addr = htobe32((0xffffffff << (32 - prefixlen)) & 0xffffffff); - - return addr; -} - -int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) { - uint8_t msb_octet = *(uint8_t*) addr; - - /* addr may not be aligned, so make sure we only access it byte-wise */ - - assert(addr); - assert(prefixlen); - - if (msb_octet < 128) - /* class A, leading bits: 0 */ - *prefixlen = 8; - else if (msb_octet < 192) - /* class B, leading bits 10 */ - *prefixlen = 16; - else if (msb_octet < 224) - /* class C, leading bits 110 */ - *prefixlen = 24; - else - /* class D or E, no default prefixlen */ - return -ERANGE; - - return 0; -} - -int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask) { - unsigned char prefixlen; - int r; - - assert(addr); - assert(mask); - - r = in_addr_default_prefixlen(addr, &prefixlen); - if (r < 0) - return r; - - in_addr_prefixlen_to_netmask(mask, prefixlen); - return 0; -} - -int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) { - assert(addr); - - if (family == AF_INET) { - struct in_addr mask; - - if (!in_addr_prefixlen_to_netmask(&mask, prefixlen)) - return -EINVAL; - - addr->in.s_addr &= mask.s_addr; - return 0; - } - - if (family == AF_INET6) { - unsigned i; - - for (i = 0; i < 16; i++) { - uint8_t mask; - - if (prefixlen >= 8) { - mask = 0xFF; - prefixlen -= 8; - } else { - mask = 0xFF << (8 - prefixlen); - prefixlen = 0; - } - - addr->in6.s6_addr[i] &= mask; - } - - return 0; - } - - return -EAFNOSUPPORT; -} diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h deleted file mode 100644 index d60064aef8..0000000000 --- a/src/basic/in-addr-util.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 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 <netinet/in.h> -#include <stddef.h> -#include <sys/socket.h> - -#include "macro.h" -#include "util.h" - -union in_addr_union { - struct in_addr in; - struct in6_addr in6; -}; - -struct in_addr_data { - int family; - union in_addr_union address; -}; - -bool in4_addr_is_null(const struct in_addr *a); -bool in6_addr_is_null(const struct in6_addr *a); - -int in_addr_is_null(int family, const union in_addr_union *u); -int in_addr_is_link_local(int family, const union in_addr_union *u); -int in_addr_is_localhost(int family, const union in_addr_union *u); -int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b); -int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen); -int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen); -int in_addr_to_string(int family, const union in_addr_union *u, char **ret); -int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret); -int in_addr_from_string(int family, const char *s, union in_addr_union *ret); -int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *ret); -int in_addr_ifindex_from_string_auto(const char *s, int *family, union in_addr_union *ret, int *ifindex); -unsigned char in_addr_netmask_to_prefixlen(const struct in_addr *addr); -struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char prefixlen); -int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen); -int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask); -int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen); - -static inline size_t FAMILY_ADDRESS_SIZE(int family) { - assert(family == AF_INET || family == AF_INET6); - return family == AF_INET6 ? 16 : 4; -} - -#define IN_ADDR_NULL ((union in_addr_union) {}) diff --git a/src/basic/io-util.c b/src/basic/io-util.c deleted file mode 100644 index cc6dfa8c1b..0000000000 --- a/src/basic/io-util.c +++ /dev/null @@ -1,269 +0,0 @@ -/*** - 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 <errno.h> -#include <limits.h> -#include <poll.h> -#include <stdio.h> -#include <time.h> -#include <unistd.h> - -#include "io-util.h" -#include "time-util.h" - -int flush_fd(int fd) { - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN, - }; - - /* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything - * read. Note that some file descriptors (notable IP sockets) will trigger POLLIN even when no data can be read - * (due to IP packet checksum mismatches), hence this function is only safe to be non-blocking if the fd used - * was set to non-blocking too. */ - - for (;;) { - char buf[LINE_MAX]; - ssize_t l; - int r; - - r = poll(&pollfd, 1, 0); - if (r < 0) { - if (errno == EINTR) - continue; - - return -errno; - - } else if (r == 0) - return 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/basic/io-util.h b/src/basic/io-util.h deleted file mode 100644 index 4684ed3bfc..0000000000 --- a/src/basic/io-util.h +++ /dev/null @@ -1,95 +0,0 @@ -#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 <stddef.h> -#include <stdint.h> -#include <sys/types.h> -#include <sys/uio.h> - -#include "macro.h" -#include "time-util.h" - -int flush_fd(int fd); - -ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll); -int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll); -int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll); - -int pipe_eof(int fd); - -int fd_wait_for_event(int fd, int event, usec_t timeout); - -ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length); - -#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; -} - -static inline bool FILE_SIZE_VALID(uint64_t l) { - /* ftruncate() and friends take an unsigned file size, but actually cannot deal with file sizes larger than - * 2^63 since the kernel internally handles it as signed value. This call allows checking for this early. */ - - return (l >> 63) == 0; -} - -static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) { - - /* Same as above, but allows one extra value: -1 as indication for infinity. */ - - if (l == (uint64_t) -1) - return true; - - return FILE_SIZE_VALID(l); - -} diff --git a/src/basic/ioprio.h b/src/basic/ioprio.h deleted file mode 100644 index d8bb6eb497..0000000000 --- a/src/basic/ioprio.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef IOPRIO_H -#define IOPRIO_H - -/* This is minimal version of Linux' linux/ioprio.h header file, which - * is licensed GPL2 */ - -#include <sys/syscall.h> -#include <unistd.h> - -/* - * Gives us 8 prio classes with 13-bits of data for each class - */ -#define IOPRIO_BITS (16) -#define IOPRIO_CLASS_SHIFT (13) -#define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1) - -#define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT) -#define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK) -#define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data) - -#define ioprio_valid(mask) (IOPRIO_PRIO_CLASS((mask)) != IOPRIO_CLASS_NONE) - -/* - * These are the io priority groups as implemented by CFQ. RT is the realtime - * class, it always gets premium service. BE is the best-effort scheduling - * class, the default for any process. IDLE is the idle scheduling class, it - * is only served when no one else is using the disk. - */ -enum { - IOPRIO_CLASS_NONE, - IOPRIO_CLASS_RT, - IOPRIO_CLASS_BE, - IOPRIO_CLASS_IDLE, -}; - -/* - * 8 best effort priority levels are supported - */ -#define IOPRIO_BE_NR (8) - -enum { - IOPRIO_WHO_PROCESS = 1, - IOPRIO_WHO_PGRP, - IOPRIO_WHO_USER, -}; - -static inline int ioprio_set(int which, int who, int ioprio) { - return syscall(__NR_ioprio_set, which, who, ioprio); -} - -static inline int ioprio_get(int which, int who) { - return syscall(__NR_ioprio_get, which, who); -} - -#endif diff --git a/src/basic/label.c b/src/basic/label.c deleted file mode 100644 index f5ab855d32..0000000000 --- a/src/basic/label.c +++ /dev/null @@ -1,82 +0,0 @@ -/*** - 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 <errno.h> -#include <sys/stat.h> -#include <unistd.h> - -#include "label.h" -#include "macro.h" -#include "selinux-util.h" -#include "smack-util.h" - -int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { - int r, q; - - r = mac_selinux_fix(path, ignore_enoent, ignore_erofs); - q = mac_smack_fix(path, ignore_enoent, ignore_erofs); - - if (r < 0) - return r; - if (q < 0) - return q; - - return 0; -} - -int mkdir_label(const char *path, mode_t mode) { - int r; - - assert(path); - - r = mac_selinux_create_file_prepare(path, S_IFDIR); - if (r < 0) - return r; - - if (mkdir(path, mode) < 0) - r = -errno; - - mac_selinux_create_file_clear(); - - if (r < 0) - return r; - - return mac_smack_fix(path, false, false); -} - -int symlink_label(const char *old_path, const char *new_path) { - int r; - - assert(old_path); - assert(new_path); - - r = mac_selinux_create_file_prepare(new_path, S_IFLNK); - if (r < 0) - return r; - - if (symlink(old_path, new_path) < 0) - r = -errno; - - mac_selinux_create_file_clear(); - - if (r < 0) - return r; - - return mac_smack_fix(new_path, false, false); -} diff --git a/src/basic/label.h b/src/basic/label.h deleted file mode 100644 index 3e9251aa71..0000000000 --- a/src/basic/label.h +++ /dev/null @@ -1,28 +0,0 @@ -#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> - -int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs); - -int mkdir_label(const char *path, mode_t mode); -int symlink_label(const char *old_path, const char *new_path); diff --git a/src/basic/list.h b/src/basic/list.h deleted file mode 100644 index c3771a177f..0000000000 --- a/src/basic/list.h +++ /dev/null @@ -1,184 +0,0 @@ -#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/>. -***/ - -/* The head of the linked list. Use this in the structure that shall - * contain the head of the linked list */ -#define LIST_HEAD(t,name) \ - t *name - -/* The pointers in the linked list's items. Use this in the item structure */ -#define LIST_FIELDS(t,name) \ - t *name##_next, *name##_prev - -/* Initialize the list's head */ -#define LIST_HEAD_INIT(head) \ - do { \ - (head) = NULL; } \ - while (false) - -/* Initialize a list item */ -#define LIST_INIT(name,item) \ - do { \ - typeof(*(item)) *_item = (item); \ - assert(_item); \ - _item->name##_prev = _item->name##_next = NULL; \ - } while (false) - -/* Prepend an item to the list */ -#define LIST_PREPEND(name,head,item) \ - do { \ - typeof(*(head)) **_head = &(head), *_item = (item); \ - assert(_item); \ - if ((_item->name##_next = *_head)) \ - _item->name##_next->name##_prev = _item; \ - _item->name##_prev = NULL; \ - *_head = _item; \ - } while (false) - -/* Append an item to the list */ -#define LIST_APPEND(name,head,item) \ - do { \ - typeof(*(head)) *_tail; \ - LIST_FIND_TAIL(name,head,_tail); \ - LIST_INSERT_AFTER(name,head,_tail,item); \ - } while (false) - -/* Remove an item from the list */ -#define LIST_REMOVE(name,head,item) \ - do { \ - typeof(*(head)) **_head = &(head), *_item = (item); \ - assert(_item); \ - if (_item->name##_next) \ - _item->name##_next->name##_prev = _item->name##_prev; \ - if (_item->name##_prev) \ - _item->name##_prev->name##_next = _item->name##_next; \ - else { \ - assert(*_head == _item); \ - *_head = _item->name##_next; \ - } \ - _item->name##_next = _item->name##_prev = NULL; \ - } while (false) - -/* Find the head of the list */ -#define LIST_FIND_HEAD(name,item,head) \ - do { \ - typeof(*(item)) *_item = (item); \ - if (!_item) \ - (head) = NULL; \ - else { \ - while (_item->name##_prev) \ - _item = _item->name##_prev; \ - (head) = _item; \ - } \ - } while (false) - -/* Find the tail of the list */ -#define LIST_FIND_TAIL(name,item,tail) \ - do { \ - typeof(*(item)) *_item = (item); \ - if (!_item) \ - (tail) = NULL; \ - else { \ - while (_item->name##_next) \ - _item = _item->name##_next; \ - (tail) = _item; \ - } \ - } while (false) - -/* Insert an item after another one (a = where, b = what) */ -#define LIST_INSERT_AFTER(name,head,a,b) \ - do { \ - typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \ - assert(_b); \ - if (!_a) { \ - if ((_b->name##_next = *_head)) \ - _b->name##_next->name##_prev = _b; \ - _b->name##_prev = NULL; \ - *_head = _b; \ - } else { \ - if ((_b->name##_next = _a->name##_next)) \ - _b->name##_next->name##_prev = _b; \ - _b->name##_prev = _a; \ - _a->name##_next = _b; \ - } \ - } while (false) - -/* Insert an item before another one (a = where, b = what) */ -#define LIST_INSERT_BEFORE(name,head,a,b) \ - do { \ - typeof(*(head)) **_head = &(head), *_a = (a), *_b = (b); \ - assert(_b); \ - if (!_a) { \ - if (!*_head) { \ - _b->name##_next = NULL; \ - _b->name##_prev = NULL; \ - *_head = _b; \ - } else { \ - typeof(*(head)) *_tail = (head); \ - while (_tail->name##_next) \ - _tail = _tail->name##_next; \ - _b->name##_next = NULL; \ - _b->name##_prev = _tail; \ - _tail->name##_next = _b; \ - } \ - } else { \ - if ((_b->name##_prev = _a->name##_prev)) \ - _b->name##_prev->name##_next = _b; \ - else \ - *_head = _b; \ - _b->name##_next = _a; \ - _a->name##_prev = _b; \ - } \ - } while (false) - -#define LIST_JUST_US(name,item) \ - (!(item)->name##_prev && !(item)->name##_next) \ - -#define LIST_FOREACH(name,i,head) \ - for ((i) = (head); (i); (i) = (i)->name##_next) - -#define LIST_FOREACH_SAFE(name,i,n,head) \ - for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n)) - -#define LIST_FOREACH_BEFORE(name,i,p) \ - for ((i) = (p)->name##_prev; (i); (i) = (i)->name##_prev) - -#define LIST_FOREACH_AFTER(name,i,p) \ - for ((i) = (p)->name##_next; (i); (i) = (i)->name##_next) - -/* Iterate through all the members of the list p is included in, but skip over p */ -#define LIST_FOREACH_OTHERS(name,i,p) \ - for (({ \ - (i) = (p); \ - while ((i) && (i)->name##_prev) \ - (i) = (i)->name##_prev; \ - if ((i) == (p)) \ - (i) = (p)->name##_next; \ - }); \ - (i); \ - (i) = (i)->name##_next == (p) ? (p)->name##_next : (i)->name##_next) - -/* Loop starting from p->next until p->prev. - p can be adjusted meanwhile. */ -#define LIST_LOOP_BUT_ONE(name,i,head,p) \ - for ((i) = (p)->name##_next ? (p)->name##_next : (head); \ - (i) != (p); \ - (i) = (i)->name##_next ? (i)->name##_next : (head)) diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c deleted file mode 100644 index ada0a28cd8..0000000000 --- a/src/basic/locale-util.c +++ /dev/null @@ -1,322 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 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 <errno.h> -#include <fcntl.h> -#include <langinfo.h> -#include <libintl.h> -#include <locale.h> -#include <stddef.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/stat.h> - -#include "dirent-util.h" -#include "fd-util.h" -#include "hashmap.h" -#include "locale-util.h" -#include "path-util.h" -#include "set.h" -#include "string-table.h" -#include "string-util.h" -#include "strv.h" -#include "utf8.h" - -static int add_locales_from_archive(Set *locales) { - /* Stolen from glibc... */ - - struct locarhead { - uint32_t magic; - /* Serial number. */ - uint32_t serial; - /* Name hash table. */ - uint32_t namehash_offset; - uint32_t namehash_used; - uint32_t namehash_size; - /* String table. */ - uint32_t string_offset; - uint32_t string_used; - uint32_t string_size; - /* Table with locale records. */ - uint32_t locrectab_offset; - uint32_t locrectab_used; - uint32_t locrectab_size; - /* MD5 sum hash table. */ - uint32_t sumhash_offset; - uint32_t sumhash_used; - uint32_t sumhash_size; - }; - - struct namehashent { - /* Hash value of the name. */ - uint32_t hashval; - /* Offset of the name in the string table. */ - uint32_t name_offset; - /* Offset of the locale record. */ - uint32_t locrec_offset; - }; - - const struct locarhead *h; - const struct namehashent *e; - const void *p = MAP_FAILED; - _cleanup_close_ int fd = -1; - size_t sz = 0; - struct stat st; - unsigned i; - int r; - - fd = open("/usr/lib/locale/locale-archive", O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return errno == ENOENT ? 0 : -errno; - - if (fstat(fd, &st) < 0) - return -errno; - - if (!S_ISREG(st.st_mode)) - return -EBADMSG; - - if (st.st_size < (off_t) sizeof(struct locarhead)) - return -EBADMSG; - - p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); - if (p == MAP_FAILED) - return -errno; - - h = (const struct locarhead *) p; - if (h->magic != 0xde020109 || - h->namehash_offset + h->namehash_size > st.st_size || - h->string_offset + h->string_size > st.st_size || - h->locrectab_offset + h->locrectab_size > st.st_size || - h->sumhash_offset + h->sumhash_size > st.st_size) { - r = -EBADMSG; - goto finish; - } - - e = (const struct namehashent*) ((const uint8_t*) p + h->namehash_offset); - for (i = 0; i < h->namehash_size; i++) { - char *z; - - if (e[i].locrec_offset == 0) - continue; - - if (!utf8_is_valid((char*) p + e[i].name_offset)) - continue; - - z = strdup((char*) p + e[i].name_offset); - if (!z) { - r = -ENOMEM; - goto finish; - } - - r = set_consume(locales, z); - if (r < 0) - goto finish; - } - - r = 0; - - finish: - if (p != MAP_FAILED) - munmap((void*) p, sz); - - return r; -} - -static int add_locales_from_libdir (Set *locales) { - _cleanup_closedir_ DIR *dir = NULL; - struct dirent *entry; - int r; - - dir = opendir("/usr/lib/locale"); - if (!dir) - return errno == ENOENT ? 0 : -errno; - - FOREACH_DIRENT(entry, dir, return -errno) { - char *z; - - dirent_ensure_type(dir, entry); - - if (entry->d_type != DT_DIR) - continue; - - z = strdup(entry->d_name); - if (!z) - return -ENOMEM; - - r = set_consume(locales, z); - if (r < 0 && r != -EEXIST) - return r; - } - - return 0; -} - -int get_locales(char ***ret) { - _cleanup_set_free_ Set *locales = NULL; - _cleanup_strv_free_ char **l = NULL; - int r; - - locales = set_new(&string_hash_ops); - if (!locales) - return -ENOMEM; - - r = add_locales_from_archive(locales); - if (r < 0 && r != -ENOENT) - return r; - - r = add_locales_from_libdir(locales); - if (r < 0) - return r; - - l = set_get_strv(locales); - if (!l) - return -ENOMEM; - - strv_sort(l); - - *ret = l; - l = NULL; - - return 0; -} - -bool locale_is_valid(const char *name) { - - if (isempty(name)) - return false; - - if (strlen(name) >= 128) - return false; - - if (!utf8_is_valid(name)) - return false; - - if (!filename_is_valid(name)) - return false; - - if (!string_is_safe(name)) - return false; - - return true; -} - -void init_gettext(void) { - setlocale(LC_ALL, ""); - textdomain(GETTEXT_PACKAGE); -} - -bool is_locale_utf8(void) { - const char *set; - static int cached_answer = -1; - - /* Note that we default to 'true' here, since today UTF8 is - * pretty much supported everywhere. */ - - 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 *special_glyph(SpecialGlyph code) { - - static const char* const draw_table[2][_SPECIAL_GLYPH_MAX] = { - /* ASCII fallback */ - [false] = { - [TREE_VERTICAL] = "| ", - [TREE_BRANCH] = "|-", - [TREE_RIGHT] = "`-", - [TREE_SPACE] = " ", - [TRIANGULAR_BULLET] = ">", - [BLACK_CIRCLE] = "*", - [ARROW] = "->", - [MDASH] = "-", - }, - - /* UTF-8 */ - [ true ] = { - [TREE_VERTICAL] = "\342\224\202 ", /* │ */ - [TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */ - [TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */ - [TREE_SPACE] = " ", /* */ - [TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */ - [BLACK_CIRCLE] = "\342\227\217", /* ● */ - [ARROW] = "\342\206\222", /* → */ - [MDASH] = "\342\200\223", /* – */ - }, - }; - - return draw_table[is_locale_utf8()][code]; -} - -static const char * const locale_variable_table[_VARIABLE_LC_MAX] = { - [VARIABLE_LANG] = "LANG", - [VARIABLE_LANGUAGE] = "LANGUAGE", - [VARIABLE_LC_CTYPE] = "LC_CTYPE", - [VARIABLE_LC_NUMERIC] = "LC_NUMERIC", - [VARIABLE_LC_TIME] = "LC_TIME", - [VARIABLE_LC_COLLATE] = "LC_COLLATE", - [VARIABLE_LC_MONETARY] = "LC_MONETARY", - [VARIABLE_LC_MESSAGES] = "LC_MESSAGES", - [VARIABLE_LC_PAPER] = "LC_PAPER", - [VARIABLE_LC_NAME] = "LC_NAME", - [VARIABLE_LC_ADDRESS] = "LC_ADDRESS", - [VARIABLE_LC_TELEPHONE] = "LC_TELEPHONE", - [VARIABLE_LC_MEASUREMENT] = "LC_MEASUREMENT", - [VARIABLE_LC_IDENTIFICATION] = "LC_IDENTIFICATION" -}; - -DEFINE_STRING_TABLE_LOOKUP(locale_variable, LocaleVariable); diff --git a/src/basic/locale-util.h b/src/basic/locale-util.h deleted file mode 100644 index 0630a034ab..0000000000 --- a/src/basic/locale-util.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 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 <libintl.h> -#include <stdbool.h> - -#include "macro.h" - -typedef enum LocaleVariable { - /* We don't list LC_ALL here on purpose. People should be - * using LANG instead. */ - - VARIABLE_LANG, - VARIABLE_LANGUAGE, - VARIABLE_LC_CTYPE, - VARIABLE_LC_NUMERIC, - VARIABLE_LC_TIME, - VARIABLE_LC_COLLATE, - VARIABLE_LC_MONETARY, - VARIABLE_LC_MESSAGES, - VARIABLE_LC_PAPER, - VARIABLE_LC_NAME, - VARIABLE_LC_ADDRESS, - VARIABLE_LC_TELEPHONE, - VARIABLE_LC_MEASUREMENT, - VARIABLE_LC_IDENTIFICATION, - _VARIABLE_LC_MAX, - _VARIABLE_LC_INVALID = -1 -} LocaleVariable; - -int get_locales(char ***l); -bool locale_is_valid(const char *name); - -#define _(String) gettext(String) -#define N_(String) String -void init_gettext(void); - -bool is_locale_utf8(void); - -typedef enum { - TREE_VERTICAL, - TREE_BRANCH, - TREE_RIGHT, - TREE_SPACE, - TRIANGULAR_BULLET, - BLACK_CIRCLE, - ARROW, - MDASH, - _SPECIAL_GLYPH_MAX -} SpecialGlyph; - -const char *special_glyph(SpecialGlyph code) _const_; - -const char* locale_variable_to_string(LocaleVariable i) _const_; -LocaleVariable locale_variable_from_string(const char *s) _pure_; diff --git a/src/basic/lockfile-util.c b/src/basic/lockfile-util.c deleted file mode 100644 index 3ee4191e4d..0000000000 --- a/src/basic/lockfile-util.c +++ /dev/null @@ -1,153 +0,0 @@ -/*** - 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 <errno.h> -#include <fcntl.h> -#include <stdio.h> -#include <string.h> -#include <sys/file.h> -#include <sys/stat.h> - -#include "alloc-util.h" -#include "fd-util.h" -#include "fs-util.h" -#include "lockfile-util.h" -#include "macro.h" -#include "path-util.h" - -int make_lock_file(const char *p, int operation, LockFile *ret) { - _cleanup_close_ int fd = -1; - _cleanup_free_ char *t = NULL; - int r; - - /* - * We use UNPOSIX locks if they are available. They have nice - * semantics, and are mostly compatible with NFS. However, - * they are only available on new kernels. When we detect we - * are running on an older kernel, then we fall back to good - * old BSD locks. They also have nice semantics, but are - * slightly problematic on NFS, where they are upgraded to - * POSIX locks, even though locally they are orthogonal to - * POSIX locks. - */ - - t = strdup(p); - if (!t) - return -ENOMEM; - - for (;;) { - struct flock fl = { - .l_type = (operation & ~LOCK_NB) == LOCK_EX ? F_WRLCK : F_RDLCK, - .l_whence = SEEK_SET, - }; - struct stat st; - - fd = open(p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600); - if (fd < 0) - return -errno; - - r = fcntl(fd, (operation & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl); - if (r < 0) { - - /* If the kernel is too old, use good old BSD locks */ - if (errno == EINVAL) - r = flock(fd, operation); - - if (r < 0) - return errno == EAGAIN ? -EBUSY : -errno; - } - - /* If we acquired the lock, let's check if the file - * still exists in the file system. If not, then the - * previous exclusive owner removed it and then closed - * it. In such a case our acquired lock is worthless, - * hence try again. */ - - r = fstat(fd, &st); - if (r < 0) - return -errno; - if (st.st_nlink > 0) - break; - - fd = safe_close(fd); - } - - ret->path = t; - ret->fd = fd; - ret->operation = operation; - - fd = -1; - t = NULL; - - return r; -} - -int make_lock_file_for(const char *p, int operation, LockFile *ret) { - const char *fn; - char *t; - - assert(p); - assert(ret); - - fn = basename(p); - if (!filename_is_valid(fn)) - return -EINVAL; - - t = newa(char, strlen(p) + 2 + 4 + 1); - stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), ".lck"); - - return make_lock_file(t, operation, ret); -} - -void release_lock_file(LockFile *f) { - int r; - - if (!f) - return; - - if (f->path) { - - /* If we are the exclusive owner we can safely delete - * the lock file itself. If we are not the exclusive - * owner, we can try becoming it. */ - - if (f->fd >= 0 && - (f->operation & ~LOCK_NB) == LOCK_SH) { - static const struct flock fl = { - .l_type = F_WRLCK, - .l_whence = SEEK_SET, - }; - - r = fcntl(f->fd, F_OFD_SETLK, &fl); - if (r < 0 && errno == EINVAL) - r = flock(f->fd, LOCK_EX|LOCK_NB); - - if (r >= 0) - f->operation = LOCK_EX|LOCK_NB; - } - - if ((f->operation & ~LOCK_NB) == LOCK_EX) - unlink_noerrno(f->path); - - f->path = mfree(f->path); - } - - f->fd = safe_close(f->fd); - f->operation = 0; -} diff --git a/src/basic/lockfile-util.h b/src/basic/lockfile-util.h deleted file mode 100644 index 22491ee8e1..0000000000 --- a/src/basic/lockfile-util.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 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 "macro.h" -#include "missing.h" - -typedef struct LockFile { - char *path; - int fd; - int operation; -} LockFile; - -int make_lock_file(const char *p, int operation, LockFile *ret); -int make_lock_file_for(const char *p, int operation, LockFile *ret); -void release_lock_file(LockFile *f); - -#define _cleanup_release_lock_file_ _cleanup_(release_lock_file) - -#define LOCK_FILE_INIT { .fd = -1, .path = NULL } diff --git a/src/basic/log.c b/src/basic/log.c deleted file mode 100644 index 4919d175da..0000000000 --- a/src/basic/log.c +++ /dev/null @@ -1,1177 +0,0 @@ -/*** - 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 <errno.h> -#include <fcntl.h> -#include <inttypes.h> -#include <limits.h> -#include <stdarg.h> -#include <stddef.h> -#include <stdio.h> -#include <string.h> -#include <sys/signalfd.h> -#include <sys/socket.h> -#include <sys/time.h> -#include <sys/uio.h> -#include <sys/un.h> -#include <time.h> -#include <unistd.h> - -#include "sd-messages.h" - -#include "alloc-util.h" -#include "fd-util.h" -#include "formats-util.h" -#include "io-util.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "proc-cmdline.h" -#include "process-util.h" -#include "signal-util.h" -#include "socket-util.h" -#include "stdio-util.h" -#include "string-table.h" -#include "string-util.h" -#include "syslog-util.h" -#include "terminal-util.h" -#include "time-util.h" -#include "util.h" - -#define SNDBUF_SIZE (8*1024*1024) - -static LogTarget log_target = LOG_TARGET_CONSOLE; -static int log_max_level = LOG_INFO; -static int log_facility = LOG_DAEMON; - -static int console_fd = STDERR_FILENO; -static int syslog_fd = -1; -static int kmsg_fd = -1; -static int journal_fd = -1; - -static bool syslog_is_stream = false; - -static bool show_color = false; -static bool show_location = false; - -static bool upgrade_syslog_to_journal = false; - -/* Akin to glibc's __abort_msg; which is private and we hence cannot - * use here. */ -static char *log_abort_msg = NULL; - -void log_close_console(void) { - - if (console_fd < 0) - return; - - if (getpid() == 1) { - if (console_fd >= 3) - safe_close(console_fd); - - console_fd = -1; - } -} - -static int log_open_console(void) { - - if (console_fd >= 0) - return 0; - - if (getpid() == 1) { - console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); - if (console_fd < 0) - return console_fd; - } else - console_fd = STDERR_FILENO; - - return 0; -} - -void log_close_kmsg(void) { - kmsg_fd = safe_close(kmsg_fd); -} - -static int log_open_kmsg(void) { - - if (kmsg_fd >= 0) - return 0; - - kmsg_fd = open("/dev/kmsg", O_WRONLY|O_NOCTTY|O_CLOEXEC); - if (kmsg_fd < 0) - return -errno; - - return 0; -} - -void log_close_syslog(void) { - syslog_fd = safe_close(syslog_fd); -} - -static int create_log_socket(int type) { - struct timeval tv; - int fd; - - fd = socket(AF_UNIX, type|SOCK_CLOEXEC, 0); - if (fd < 0) - return -errno; - - (void) fd_inc_sndbuf(fd, SNDBUF_SIZE); - - /* We need a blocking fd here since we'd otherwise lose - messages way too early. However, let's not hang forever in the - unlikely case of a deadlock. */ - if (getpid() == 1) - timeval_store(&tv, 10 * USEC_PER_MSEC); - else - timeval_store(&tv, 10 * USEC_PER_SEC); - (void) setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); - - return fd; -} - -static int log_open_syslog(void) { - - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/dev/log", - }; - - int r; - - if (syslog_fd >= 0) - return 0; - - syslog_fd = create_log_socket(SOCK_DGRAM); - if (syslog_fd < 0) { - r = syslog_fd; - goto fail; - } - - if (connect(syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { - safe_close(syslog_fd); - - /* Some legacy syslog systems still use stream - * sockets. They really shouldn't. But what can we - * do... */ - syslog_fd = create_log_socket(SOCK_STREAM); - if (syslog_fd < 0) { - r = syslog_fd; - goto fail; - } - - if (connect(syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { - r = -errno; - goto fail; - } - - syslog_is_stream = true; - } else - syslog_is_stream = false; - - return 0; - -fail: - log_close_syslog(); - return r; -} - -void log_close_journal(void) { - journal_fd = safe_close(journal_fd); -} - -static int log_open_journal(void) { - - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/journal/socket", - }; - - int r; - - if (journal_fd >= 0) - return 0; - - journal_fd = create_log_socket(SOCK_DGRAM); - if (journal_fd < 0) { - r = journal_fd; - goto fail; - } - - if (connect(journal_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { - r = -errno; - goto fail; - } - - return 0; - -fail: - log_close_journal(); - return r; -} - -int log_open(void) { - int r; - - /* If we don't use the console we close it here, to not get - * killed by SAK. If we don't use syslog we close it here so - * that we are not confused by somebody deleting the socket in - * the fs. If we don't use /dev/kmsg we still keep it open, - * because there is no reason to close it. */ - - if (log_target == LOG_TARGET_NULL) { - log_close_journal(); - log_close_syslog(); - log_close_console(); - return 0; - } - - if ((log_target != LOG_TARGET_AUTO && log_target != LOG_TARGET_SAFE) || - getpid() == 1 || - isatty(STDERR_FILENO) <= 0) { - - if (log_target == LOG_TARGET_AUTO || - log_target == LOG_TARGET_JOURNAL_OR_KMSG || - log_target == LOG_TARGET_JOURNAL) { - r = log_open_journal(); - if (r >= 0) { - log_close_syslog(); - log_close_console(); - return r; - } - } - - if (log_target == LOG_TARGET_SYSLOG_OR_KMSG || - log_target == LOG_TARGET_SYSLOG) { - r = log_open_syslog(); - if (r >= 0) { - log_close_journal(); - log_close_console(); - return r; - } - } - - if (log_target == LOG_TARGET_AUTO || - log_target == LOG_TARGET_SAFE || - log_target == LOG_TARGET_JOURNAL_OR_KMSG || - log_target == LOG_TARGET_SYSLOG_OR_KMSG || - log_target == LOG_TARGET_KMSG) { - r = log_open_kmsg(); - if (r >= 0) { - log_close_journal(); - log_close_syslog(); - log_close_console(); - return r; - } - } - } - - log_close_journal(); - log_close_syslog(); - - return log_open_console(); -} - -void log_set_target(LogTarget target) { - assert(target >= 0); - assert(target < _LOG_TARGET_MAX); - - if (upgrade_syslog_to_journal) { - if (target == LOG_TARGET_SYSLOG) - target = LOG_TARGET_JOURNAL; - else if (target == LOG_TARGET_SYSLOG_OR_KMSG) - target = LOG_TARGET_JOURNAL_OR_KMSG; - } - - log_target = target; -} - -void log_close(void) { - log_close_journal(); - log_close_syslog(); - log_close_kmsg(); - log_close_console(); -} - -void log_forget_fds(void) { - console_fd = kmsg_fd = syslog_fd = journal_fd = -1; -} - -void log_set_max_level(int level) { - assert((level & LOG_PRIMASK) == level); - - log_max_level = level; -} - -void log_set_facility(int facility) { - log_facility = facility; -} - -static int write_to_console( - int level, - int error, - const char *file, - int line, - const char *func, - const char *buffer) { - - char location[256], prefix[1 + DECIMAL_STR_MAX(int) + 2]; - struct iovec iovec[6] = {}; - unsigned n = 0; - bool highlight; - - if (console_fd < 0) - return 0; - - if (log_target == LOG_TARGET_CONSOLE_PREFIXED) { - xsprintf(prefix, "<%i>", level); - IOVEC_SET_STRING(iovec[n++], prefix); - } - - highlight = LOG_PRI(level) <= LOG_ERR && show_color; - - if (show_location) { - snprintf(location, sizeof(location), "(%s:%i) ", file, line); - IOVEC_SET_STRING(iovec[n++], location); - } - - if (highlight) - IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_RED); - IOVEC_SET_STRING(iovec[n++], buffer); - if (highlight) - IOVEC_SET_STRING(iovec[n++], ANSI_NORMAL); - IOVEC_SET_STRING(iovec[n++], "\n"); - - if (writev(console_fd, iovec, n) < 0) { - - if (errno == EIO && getpid() == 1) { - - /* If somebody tried to kick us from our - * console tty (via vhangup() or suchlike), - * try to reconnect */ - - log_close_console(); - log_open_console(); - - if (console_fd < 0) - return 0; - - if (writev(console_fd, iovec, n) < 0) - return -errno; - } else - return -errno; - } - - return 1; -} - -static int write_to_syslog( - int level, - int error, - const char *file, - int line, - const char *func, - const char *buffer) { - - char header_priority[2 + DECIMAL_STR_MAX(int) + 1], - header_time[64], - header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1]; - struct iovec iovec[5] = {}; - struct msghdr msghdr = { - .msg_iov = iovec, - .msg_iovlen = ELEMENTSOF(iovec), - }; - time_t t; - struct tm *tm; - - if (syslog_fd < 0) - return 0; - - xsprintf(header_priority, "<%i>", level); - - t = (time_t) (now(CLOCK_REALTIME) / USEC_PER_SEC); - tm = localtime(&t); - if (!tm) - return -EINVAL; - - if (strftime(header_time, sizeof(header_time), "%h %e %T ", tm) <= 0) - return -EINVAL; - - xsprintf(header_pid, "["PID_FMT"]: ", getpid()); - - IOVEC_SET_STRING(iovec[0], header_priority); - IOVEC_SET_STRING(iovec[1], header_time); - IOVEC_SET_STRING(iovec[2], program_invocation_short_name); - IOVEC_SET_STRING(iovec[3], header_pid); - IOVEC_SET_STRING(iovec[4], buffer); - - /* When using syslog via SOCK_STREAM separate the messages by NUL chars */ - if (syslog_is_stream) - iovec[4].iov_len++; - - for (;;) { - ssize_t n; - - n = sendmsg(syslog_fd, &msghdr, MSG_NOSIGNAL); - if (n < 0) - return -errno; - - if (!syslog_is_stream || - (size_t) n >= IOVEC_TOTAL_SIZE(iovec, ELEMENTSOF(iovec))) - break; - - IOVEC_INCREMENT(iovec, ELEMENTSOF(iovec), n); - } - - return 1; -} - -static int write_to_kmsg( - int level, - int error, - const char *file, - int line, - const char *func, - const char *buffer) { - - char header_priority[2 + DECIMAL_STR_MAX(int) + 1], - header_pid[4 + DECIMAL_STR_MAX(pid_t) + 1]; - struct iovec iovec[5] = {}; - - if (kmsg_fd < 0) - return 0; - - xsprintf(header_priority, "<%i>", level); - xsprintf(header_pid, "["PID_FMT"]: ", getpid()); - - IOVEC_SET_STRING(iovec[0], header_priority); - IOVEC_SET_STRING(iovec[1], program_invocation_short_name); - IOVEC_SET_STRING(iovec[2], header_pid); - IOVEC_SET_STRING(iovec[3], buffer); - IOVEC_SET_STRING(iovec[4], "\n"); - - if (writev(kmsg_fd, iovec, ELEMENTSOF(iovec)) < 0) - return -errno; - - return 1; -} - -static int log_do_header( - char *header, - size_t size, - int level, - int error, - const char *file, int line, const char *func, - const char *object_field, const char *object, - const char *extra_field, const char *extra) { - - snprintf(header, size, - "PRIORITY=%i\n" - "SYSLOG_FACILITY=%i\n" - "%s%s%s" - "%s%.*i%s" - "%s%s%s" - "%s%.*i%s" - "%s%s%s" - "%s%s%s" - "SYSLOG_IDENTIFIER=%s\n", - LOG_PRI(level), - LOG_FAC(level), - isempty(file) ? "" : "CODE_FILE=", - isempty(file) ? "" : file, - isempty(file) ? "" : "\n", - line ? "CODE_LINE=" : "", - line ? 1 : 0, line, /* %.0d means no output too, special case for 0 */ - line ? "\n" : "", - isempty(func) ? "" : "CODE_FUNCTION=", - isempty(func) ? "" : func, - isempty(func) ? "" : "\n", - error ? "ERRNO=" : "", - error ? 1 : 0, error, - error ? "\n" : "", - isempty(object) ? "" : object_field, - isempty(object) ? "" : object, - isempty(object) ? "" : "\n", - isempty(extra) ? "" : extra_field, - isempty(extra) ? "" : extra, - isempty(extra) ? "" : "\n", - program_invocation_short_name); - - return 0; -} - -static int write_to_journal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *extra_field, - const char *extra, - const char *buffer) { - - char header[LINE_MAX]; - struct iovec iovec[4] = {}; - struct msghdr mh = {}; - - if (journal_fd < 0) - return 0; - - log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object, extra_field, extra); - - IOVEC_SET_STRING(iovec[0], header); - IOVEC_SET_STRING(iovec[1], "MESSAGE="); - IOVEC_SET_STRING(iovec[2], buffer); - IOVEC_SET_STRING(iovec[3], "\n"); - - mh.msg_iov = iovec; - mh.msg_iovlen = ELEMENTSOF(iovec); - - if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 0) - return -errno; - - return 1; -} - -static int log_dispatch( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *extra, - const char *extra_field, - char *buffer) { - - assert(buffer); - - if (error < 0) - error = -error; - - if (log_target == LOG_TARGET_NULL) - return -error; - - /* Patch in LOG_DAEMON facility if necessary */ - if ((level & LOG_FACMASK) == 0) - level = log_facility | LOG_PRI(level); - - do { - char *e; - int k = 0; - - buffer += strspn(buffer, NEWLINE); - - if (buffer[0] == 0) - break; - - if ((e = strpbrk(buffer, NEWLINE))) - *(e++) = 0; - - if (log_target == LOG_TARGET_AUTO || - log_target == LOG_TARGET_JOURNAL_OR_KMSG || - log_target == LOG_TARGET_JOURNAL) { - - k = write_to_journal(level, error, file, line, func, object_field, object, extra_field, extra, buffer); - if (k < 0) { - if (k != -EAGAIN) - log_close_journal(); - log_open_kmsg(); - } - } - - if (log_target == LOG_TARGET_SYSLOG_OR_KMSG || - log_target == LOG_TARGET_SYSLOG) { - - k = write_to_syslog(level, error, file, line, func, buffer); - if (k < 0) { - if (k != -EAGAIN) - log_close_syslog(); - log_open_kmsg(); - } - } - - if (k <= 0 && - (log_target == LOG_TARGET_AUTO || - log_target == LOG_TARGET_SAFE || - log_target == LOG_TARGET_SYSLOG_OR_KMSG || - log_target == LOG_TARGET_JOURNAL_OR_KMSG || - log_target == LOG_TARGET_KMSG)) { - - k = write_to_kmsg(level, error, file, line, func, buffer); - if (k < 0) { - log_close_kmsg(); - log_open_console(); - } - } - - if (k <= 0) - (void) write_to_console(level, error, file, line, func, buffer); - - buffer = e; - } while (buffer); - - return -error; -} - -int log_dump_internal( - int level, - int error, - const char *file, - int line, - const char *func, - char *buffer) { - - PROTECT_ERRNO; - - /* This modifies the buffer... */ - - if (error < 0) - error = -error; - - if (_likely_(LOG_PRI(level) > log_max_level)) - return -error; - - return log_dispatch(level, error, file, line, func, NULL, NULL, NULL, NULL, buffer); -} - -int log_internalv( - int level, - int error, - const char *file, - int line, - const char *func, - const char *format, - va_list ap) { - - PROTECT_ERRNO; - char buffer[LINE_MAX]; - - if (error < 0) - error = -error; - - if (_likely_(LOG_PRI(level) > log_max_level)) - return -error; - - /* Make sure that %m maps to the specified error */ - if (error != 0) - errno = error; - - vsnprintf(buffer, sizeof(buffer), format, ap); - - return log_dispatch(level, error, file, line, func, NULL, NULL, NULL, NULL, buffer); -} - -int log_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *format, ...) { - - va_list ap; - int r; - - va_start(ap, format); - r = log_internalv(level, error, file, line, func, format, ap); - va_end(ap); - - return r; -} - -int log_object_internalv( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *extra_field, - const char *extra, - const char *format, - va_list ap) { - - PROTECT_ERRNO; - char *buffer, *b; - size_t l; - - if (error < 0) - error = -error; - - if (_likely_(LOG_PRI(level) > log_max_level)) - return -error; - - /* Make sure that %m maps to the specified error */ - if (error != 0) - errno = error; - - /* Prepend the object name before the message */ - if (object) { - size_t n; - - n = strlen(object); - l = n + 2 + LINE_MAX; - - buffer = newa(char, l); - b = stpcpy(stpcpy(buffer, object), ": "); - } else { - l = LINE_MAX; - b = buffer = newa(char, l); - } - - vsnprintf(b, l, format, ap); - - return log_dispatch(level, error, file, line, func, object_field, object, extra_field, extra, buffer); -} - -int log_object_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *extra_field, - const char *extra, - const char *format, ...) { - - va_list ap; - int r; - - va_start(ap, format); - r = log_object_internalv(level, error, file, line, func, object_field, object, extra_field, extra, format, ap); - va_end(ap); - - return r; -} - -static void log_assert( - int level, - const char *text, - const char *file, - int line, - const char *func, - const char *format) { - - static char buffer[LINE_MAX]; - - if (_likely_(LOG_PRI(level) > log_max_level)) - return; - - DISABLE_WARNING_FORMAT_NONLITERAL; - snprintf(buffer, sizeof buffer, format, text, file, line, func); - REENABLE_WARNING; - - log_abort_msg = buffer; - - log_dispatch(level, 0, file, line, func, NULL, NULL, NULL, NULL, buffer); -} - -noreturn void log_assert_failed(const char *text, const char *file, int line, const char *func) { - log_assert(LOG_CRIT, text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting."); - abort(); -} - -noreturn void log_assert_failed_unreachable(const char *text, const char *file, int line, const char *func) { - log_assert(LOG_CRIT, text, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting."); - abort(); -} - -void log_assert_failed_return(const char *text, const char *file, int line, const char *func) { - PROTECT_ERRNO; - log_assert(LOG_DEBUG, text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Ignoring."); -} - -int log_oom_internal(const char *file, int line, const char *func) { - log_internal(LOG_ERR, ENOMEM, file, line, func, "Out of memory."); - return -ENOMEM; -} - -int log_format_iovec( - struct iovec *iovec, - unsigned iovec_len, - unsigned *n, - bool newline_separator, - int error, - const char *format, - va_list ap) { - - static const char nl = '\n'; - - while (format && *n + 1 < iovec_len) { - va_list aq; - char *m; - int r; - - /* We need to copy the va_list structure, - * since vasprintf() leaves it afterwards at - * an undefined location */ - - if (error != 0) - errno = error; - - va_copy(aq, ap); - r = vasprintf(&m, format, aq); - va_end(aq); - if (r < 0) - return -EINVAL; - - /* Now, jump enough ahead, so that we point to - * the next format string */ - VA_FORMAT_ADVANCE(format, ap); - - IOVEC_SET_STRING(iovec[(*n)++], m); - - if (newline_separator) { - iovec[*n].iov_base = (char*) &nl; - iovec[*n].iov_len = 1; - (*n)++; - } - - format = va_arg(ap, char *); - } - return 0; -} - -int log_struct_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *format, ...) { - - char buf[LINE_MAX]; - bool found = false; - PROTECT_ERRNO; - va_list ap; - - if (error < 0) - error = -error; - - if (_likely_(LOG_PRI(level) > log_max_level)) - return -error; - - if (log_target == LOG_TARGET_NULL) - return -error; - - if ((level & LOG_FACMASK) == 0) - level = log_facility | LOG_PRI(level); - - if ((log_target == LOG_TARGET_AUTO || - log_target == LOG_TARGET_JOURNAL_OR_KMSG || - log_target == LOG_TARGET_JOURNAL) && - journal_fd >= 0) { - char header[LINE_MAX]; - struct iovec iovec[17] = {}; - unsigned n = 0, i; - int r; - struct msghdr mh = { - .msg_iov = iovec, - }; - bool fallback = false; - - /* If the journal is available do structured logging */ - log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL, NULL, NULL); - IOVEC_SET_STRING(iovec[n++], header); - - va_start(ap, format); - r = log_format_iovec(iovec, ELEMENTSOF(iovec), &n, true, error, format, ap); - if (r < 0) - fallback = true; - else { - mh.msg_iovlen = n; - (void) sendmsg(journal_fd, &mh, MSG_NOSIGNAL); - } - - va_end(ap); - for (i = 1; i < n; i += 2) - free(iovec[i].iov_base); - - if (!fallback) - return -error; - } - - /* Fallback if journal logging is not available or didn't work. */ - - va_start(ap, format); - while (format) { - va_list aq; - - if (error != 0) - errno = error; - - va_copy(aq, ap); - vsnprintf(buf, sizeof(buf), format, aq); - va_end(aq); - - if (startswith(buf, "MESSAGE=")) { - found = true; - break; - } - - VA_FORMAT_ADVANCE(format, ap); - - format = va_arg(ap, char *); - } - va_end(ap); - - if (!found) - return -error; - - return log_dispatch(level, error, file, line, func, NULL, NULL, NULL, NULL, buf + 8); -} - -int log_set_target_from_string(const char *e) { - LogTarget t; - - t = log_target_from_string(e); - if (t < 0) - return -EINVAL; - - log_set_target(t); - return 0; -} - -int log_set_max_level_from_string(const char *e) { - int t; - - t = log_level_from_string(e); - if (t < 0) - return -EINVAL; - - log_set_max_level(t); - return 0; -} - -static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { - - /* - * The systemd.log_xyz= settings are parsed by all tools, and - * so is "debug". - * - * However, "quiet" is only parsed by PID 1, and only turns of - * status output to /dev/console, but does not alter the log - * level. - */ - - if (streq(key, "debug") && !value) - log_set_max_level(LOG_DEBUG); - - else if (streq(key, "systemd.log_target") && value) { - - if (log_set_target_from_string(value) < 0) - log_warning("Failed to parse log target '%s'. Ignoring.", value); - - } else if (streq(key, "systemd.log_level") && value) { - - if (log_set_max_level_from_string(value) < 0) - log_warning("Failed to parse log level '%s'. Ignoring.", value); - - } else if (streq(key, "systemd.log_color") && value) { - - if (log_show_color_from_string(value) < 0) - log_warning("Failed to parse log color setting '%s'. Ignoring.", value); - - } else if (streq(key, "systemd.log_location") && value) { - - if (log_show_location_from_string(value) < 0) - log_warning("Failed to parse log location setting '%s'. Ignoring.", value); - } - - return 0; -} - -void log_parse_environment(void) { - const char *e; - - if (get_ctty_devnr(0, NULL) < 0) - /* Only try to read the command line in daemons. - We assume that anything that has a controlling - tty is user stuff. */ - (void) parse_proc_cmdline(parse_proc_cmdline_item, NULL, true); - - e = secure_getenv("SYSTEMD_LOG_TARGET"); - if (e && log_set_target_from_string(e) < 0) - log_warning("Failed to parse log target '%s'. Ignoring.", e); - - e = secure_getenv("SYSTEMD_LOG_LEVEL"); - if (e && log_set_max_level_from_string(e) < 0) - log_warning("Failed to parse log level '%s'. Ignoring.", e); - - e = secure_getenv("SYSTEMD_LOG_COLOR"); - if (e && log_show_color_from_string(e) < 0) - log_warning("Failed to parse bool '%s'. Ignoring.", e); - - e = secure_getenv("SYSTEMD_LOG_LOCATION"); - if (e && log_show_location_from_string(e) < 0) - log_warning("Failed to parse bool '%s'. Ignoring.", e); -} - -LogTarget log_get_target(void) { - return log_target; -} - -int log_get_max_level(void) { - return log_max_level; -} - -void log_show_color(bool b) { - show_color = b; -} - -bool log_get_show_color(void) { - return show_color; -} - -void log_show_location(bool b) { - show_location = b; -} - -bool log_get_show_location(void) { - return show_location; -} - -int log_show_color_from_string(const char *e) { - int t; - - t = parse_boolean(e); - if (t < 0) - return t; - - log_show_color(t); - return 0; -} - -int log_show_location_from_string(const char *e) { - int t; - - t = parse_boolean(e); - if (t < 0) - return t; - - log_show_location(t); - return 0; -} - -bool log_on_console(void) { - if (log_target == LOG_TARGET_CONSOLE || - log_target == LOG_TARGET_CONSOLE_PREFIXED) - return true; - - return syslog_fd < 0 && kmsg_fd < 0 && journal_fd < 0; -} - -static const char *const log_target_table[_LOG_TARGET_MAX] = { - [LOG_TARGET_CONSOLE] = "console", - [LOG_TARGET_CONSOLE_PREFIXED] = "console-prefixed", - [LOG_TARGET_KMSG] = "kmsg", - [LOG_TARGET_JOURNAL] = "journal", - [LOG_TARGET_JOURNAL_OR_KMSG] = "journal-or-kmsg", - [LOG_TARGET_SYSLOG] = "syslog", - [LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg", - [LOG_TARGET_AUTO] = "auto", - [LOG_TARGET_SAFE] = "safe", - [LOG_TARGET_NULL] = "null" -}; - -DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget); - -void log_received_signal(int level, const struct signalfd_siginfo *si) { - if (si->ssi_pid > 0) { - _cleanup_free_ char *p = NULL; - - get_process_comm(si->ssi_pid, &p); - - log_full(level, - "Received SIG%s from PID %"PRIu32" (%s).", - signal_to_string(si->ssi_signo), - si->ssi_pid, strna(p)); - } else - log_full(level, - "Received SIG%s.", - signal_to_string(si->ssi_signo)); - -} - -void log_set_upgrade_syslog_to_journal(bool b) { - upgrade_syslog_to_journal = b; -} - -int log_syntax_internal( - const char *unit, - int level, - const char *config_file, - unsigned config_line, - int error, - const char *file, - int line, - const char *func, - const char *format, ...) { - - PROTECT_ERRNO; - char buffer[LINE_MAX]; - int r; - va_list ap; - - if (error < 0) - error = -error; - - if (_likely_(LOG_PRI(level) > log_max_level)) - return -error; - - if (log_target == LOG_TARGET_NULL) - return -error; - - if (error != 0) - errno = error; - - va_start(ap, format); - vsnprintf(buffer, sizeof(buffer), format, ap); - va_end(ap); - - if (unit) - r = log_struct_internal( - level, error, - file, line, func, - getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit, - LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION), - "CONFIG_FILE=%s", config_file, - "CONFIG_LINE=%u", config_line, - LOG_MESSAGE("[%s:%u] %s", config_file, config_line, buffer), - NULL); - else - r = log_struct_internal( - level, error, - file, line, func, - LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION), - "CONFIG_FILE=%s", config_file, - "CONFIG_LINE=%u", config_line, - LOG_MESSAGE("[%s:%u] %s", config_file, config_line, buffer), - NULL); - - return r; -} diff --git a/src/basic/log.h b/src/basic/log.h deleted file mode 100644 index 2afee20bb5..0000000000 --- a/src/basic/log.h +++ /dev/null @@ -1,253 +0,0 @@ -#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 <errno.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stdlib.h> -#include <sys/signalfd.h> -#include <sys/socket.h> -#include <syslog.h> - -#include "sd-id128.h" - -#include "macro.h" - -typedef enum LogTarget{ - LOG_TARGET_CONSOLE, - LOG_TARGET_CONSOLE_PREFIXED, - LOG_TARGET_KMSG, - LOG_TARGET_JOURNAL, - LOG_TARGET_JOURNAL_OR_KMSG, - LOG_TARGET_SYSLOG, - LOG_TARGET_SYSLOG_OR_KMSG, - LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */ - LOG_TARGET_SAFE, /* console if stderr is tty, KMSG otherwise */ - LOG_TARGET_NULL, - _LOG_TARGET_MAX, - _LOG_TARGET_INVALID = -1 -} LogTarget; - -void log_set_target(LogTarget target); -void log_set_max_level(int level); -void log_set_facility(int facility); - -int log_set_target_from_string(const char *e); -int log_set_max_level_from_string(const char *e); - -void log_show_color(bool b); -bool log_get_show_color(void) _pure_; -void log_show_location(bool b); -bool log_get_show_location(void) _pure_; - -int log_show_color_from_string(const char *e); -int log_show_location_from_string(const char *e); - -LogTarget log_get_target(void) _pure_; -int log_get_max_level(void) _pure_; - -int log_open(void); -void log_close(void); -void log_forget_fds(void); - -void log_close_syslog(void); -void log_close_journal(void); -void log_close_kmsg(void); -void log_close_console(void); - -void log_parse_environment(void); - -int log_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *format, ...) _printf_(6,7); - -int log_internalv( - int level, - int error, - const char *file, - int line, - const char *func, - const char *format, - va_list ap) _printf_(6,0); - -int log_object_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *extra_field, - const char *extra, - const char *format, ...) _printf_(10,11); - -int log_object_internalv( - int level, - int error, - const char *file, - int line, - const char *func, - const char *object_field, - const char *object, - const char *extra_field, - const char *extra, - const char *format, - va_list ap) _printf_(9,0); - -int log_struct_internal( - int level, - int error, - const char *file, - int line, - const char *func, - const char *format, ...) _printf_(6,0) _sentinel_; - -int log_oom_internal( - const char *file, - int line, - const char *func); - -int log_format_iovec( - struct iovec *iovec, - unsigned iovec_len, - unsigned *n, - bool newline_separator, - int error, - const char *format, - va_list ap); - -/* This modifies the buffer passed! */ -int log_dump_internal( - int level, - int error, - const char *file, - int line, - const char *func, - char *buffer); - -/* Logging for various assertions */ -noreturn void log_assert_failed( - const char *text, - const char *file, - int line, - const char *func); - -noreturn void log_assert_failed_unreachable( - const char *text, - const char *file, - int line, - const char *func); - -void log_assert_failed_return( - const char *text, - const char *file, - int line, - const char *func); - -/* Logging with level */ -#define log_full_errno(level, error, ...) \ - ({ \ - int _level = (level), _e = (error); \ - (log_get_max_level() >= LOG_PRI(_level)) \ - ? log_internal(_level, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \ - : -abs(_e); \ - }) - -#define log_full(level, ...) log_full_errno(level, 0, __VA_ARGS__) - -/* Normal logging */ -#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__) -#define log_info(...) log_full(LOG_INFO, __VA_ARGS__) -#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__) -#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__) -#define log_error(...) log_full(LOG_ERR, __VA_ARGS__) -#define log_emergency(...) log_full(getpid() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__) - -/* Logging triggered by an errno-like error */ -#define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__) -#define log_info_errno(error, ...) log_full_errno(LOG_INFO, error, __VA_ARGS__) -#define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__) -#define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__) -#define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__) -#define log_emergency_errno(error, ...) log_full_errno(getpid() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__) - -#ifdef LOG_TRACE -# define log_trace(...) log_debug(__VA_ARGS__) -#else -# define log_trace(...) do {} while (0) -#endif - -/* Structured logging */ -#define log_struct(level, ...) log_struct_internal(level, 0, __FILE__, __LINE__, __func__, __VA_ARGS__) -#define log_struct_errno(level, error, ...) log_struct_internal(level, error, __FILE__, __LINE__, __func__, __VA_ARGS__) - -/* This modifies the buffer passed! */ -#define log_dump(level, buffer) log_dump_internal(level, 0, __FILE__, __LINE__, __func__, buffer) - -#define log_oom() log_oom_internal(__FILE__, __LINE__, __func__) - -bool log_on_console(void) _pure_; - -const char *log_target_to_string(LogTarget target) _const_; -LogTarget log_target_from_string(const char *s) _pure_; - -/* Helpers to prepare various fields for structured logging */ -#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__ -#define LOG_MESSAGE_ID(x) "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(x) - -void log_received_signal(int level, const struct signalfd_siginfo *si); - -void log_set_upgrade_syslog_to_journal(bool b); - -int log_syntax_internal( - const char *unit, - int level, - const char *config_file, - unsigned config_line, - int error, - const char *file, - int line, - const char *func, - const char *format, ...) _printf_(9, 10); - -#define log_syntax(unit, level, config_file, config_line, error, ...) \ - ({ \ - int _level = (level), _e = (error); \ - (log_get_max_level() >= LOG_PRI(_level)) \ - ? log_syntax_internal(unit, _level, config_file, config_line, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \ - : -abs(_e); \ - }) - -#define log_syntax_invalid_utf8(unit, level, config_file, config_line, rvalue) \ - ({ \ - int _level = (level); \ - if (log_get_max_level() >= LOG_PRI(_level)) { \ - _cleanup_free_ char *_p = NULL; \ - _p = utf8_escape_invalid(rvalue); \ - log_syntax_internal(unit, _level, config_file, config_line, 0, __FILE__, __LINE__, __func__, \ - "String is not UTF-8 clean, ignoring assignment: %s", strna(_p)); \ - } \ - }) diff --git a/src/basic/login-util.c b/src/basic/login-util.c deleted file mode 100644 index 339e94f12d..0000000000 --- a/src/basic/login-util.c +++ /dev/null @@ -1,31 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - 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.h> - -#include "login-util.h" -#include "string-util.h" - -bool session_id_valid(const char *id) { - - if (isempty(id)) - return false; - - return id[strspn(id, LETTERS DIGITS)] == '\0'; -} diff --git a/src/basic/login-util.h b/src/basic/login-util.h deleted file mode 100644 index b01ee25c88..0000000000 --- a/src/basic/login-util.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - 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 <unistd.h> - -bool session_id_valid(const char *id); - -static inline bool logind_running(void) { - return access("/run/systemd/seats/", F_OK) >= 0; -} diff --git a/src/basic/macro.h b/src/basic/macro.h deleted file mode 100644 index 6b2aeb933f..0000000000 --- a/src/basic/macro.h +++ /dev/null @@ -1,415 +0,0 @@ -#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 <assert.h> -#include <inttypes.h> -#include <stdbool.h> -#include <sys/param.h> -#include <sys/sysmacros.h> -#include <sys/types.h> - -#define _printf_(a,b) __attribute__ ((format (printf, a, b))) -#ifdef __clang__ -# define _alloc_(...) -#else -# define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__))) -#endif -#define _sentinel_ __attribute__ ((sentinel)) -#define _unused_ __attribute__ ((unused)) -#define _destructor_ __attribute__ ((destructor)) -#define _pure_ __attribute__ ((pure)) -#define _const_ __attribute__ ((const)) -#define _deprecated_ __attribute__ ((deprecated)) -#define _packed_ __attribute__ ((packed)) -#define _malloc_ __attribute__ ((malloc)) -#define _weak_ __attribute__ ((weak)) -#define _likely_(x) (__builtin_expect(!!(x),1)) -#define _unlikely_(x) (__builtin_expect(!!(x),0)) -#define _public_ __attribute__ ((visibility("default"))) -#define _hidden_ __attribute__ ((visibility("hidden"))) -#define _weakref_(x) __attribute__((weakref(#x))) -#define _alignas_(x) __attribute__((aligned(__alignof(x)))) -#define _cleanup_(x) __attribute__((cleanup(x))) - -/* Temporarily disable some warnings */ -#define DISABLE_WARNING_DECLARATION_AFTER_STATEMENT \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wdeclaration-after-statement\"") - -#define DISABLE_WARNING_FORMAT_NONLITERAL \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wformat-nonliteral\"") - -#define DISABLE_WARNING_MISSING_PROTOTYPES \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"") - -#define DISABLE_WARNING_NONNULL \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wnonnull\"") - -#define DISABLE_WARNING_SHADOW \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wshadow\"") - -#define DISABLE_WARNING_INCOMPATIBLE_POINTER_TYPES \ - _Pragma("GCC diagnostic push"); \ - _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"") - -#define REENABLE_WARNING \ - _Pragma("GCC diagnostic pop") - -/* automake test harness */ -#define EXIT_TEST_SKIP 77 - -#define XSTRINGIFY(x) #x -#define STRINGIFY(x) XSTRINGIFY(x) - -#define XCONCATENATE(x, y) x ## y -#define CONCATENATE(x, y) XCONCATENATE(x, y) - -#define UNIQ_T(x, uniq) CONCATENATE(__unique_prefix_, CONCATENATE(x, uniq)) -#define UNIQ __COUNTER__ - -/* builtins */ -#if __SIZEOF_INT__ == 4 -#define BUILTIN_FFS_U32(x) __builtin_ffs(x); -#elif __SIZEOF_LONG__ == 4 -#define BUILTIN_FFS_U32(x) __builtin_ffsl(x); -#else -#error "neither int nor long are four bytes long?!?" -#endif - -/* Rounds up */ - -#define ALIGN4(l) (((l) + 3) & ~3) -#define ALIGN8(l) (((l) + 7) & ~7) - -#if __SIZEOF_POINTER__ == 8 -#define ALIGN(l) ALIGN8(l) -#elif __SIZEOF_POINTER__ == 4 -#define ALIGN(l) ALIGN4(l) -#else -#error "Wut? Pointers are neither 4 nor 8 bytes long?" -#endif - -#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) (p))) -#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) (p))) -#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p))) - -static inline size_t ALIGN_TO(size_t l, size_t ali) { - return ((l + ali - 1) & ~(ali - 1)); -} - -#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) (p), (ali))) - -/* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */ -static inline unsigned long ALIGN_POWER2(unsigned long u) { - /* clz(0) is undefined */ - if (u == 1) - return 1; - - /* left-shift overflow is undefined */ - if (__builtin_clzl(u - 1UL) < 1) - return 0; - - return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL)); -} - -#define ELEMENTSOF(x) \ - __extension__ (__builtin_choose_expr( \ - !__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \ - sizeof(x)/sizeof((x)[0]), \ - (void)0)) -/* - * container_of - cast a member of a structure out to the containing structure - * @ptr: the pointer to the member. - * @type: the type of the container struct this is embedded in. - * @member: the name of the member within the struct. - */ -#define container_of(ptr, type, member) __container_of(UNIQ, (ptr), type, member) -#define __container_of(uniq, ptr, type, member) \ - __extension__ ({ \ - const typeof( ((type*)0)->member ) *UNIQ_T(A, uniq) = (ptr); \ - (type*)( (char *)UNIQ_T(A, uniq) - offsetof(type,member) ); \ - }) - -#undef MAX -#define MAX(a, b) __MAX(UNIQ, (a), UNIQ, (b)) -#define __MAX(aq, a, bq, b) \ - __extension__ ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A,aq) > UNIQ_T(B,bq) ? UNIQ_T(A,aq) : UNIQ_T(B,bq); \ - }) - -/* evaluates to (void) if _A or _B are not constant or of different types */ -#define CONST_MAX(_A, _B) \ - __extension__ (__builtin_choose_expr( \ - __builtin_constant_p(_A) && \ - __builtin_constant_p(_B) && \ - __builtin_types_compatible_p(typeof(_A), typeof(_B)), \ - ((_A) > (_B)) ? (_A) : (_B), \ - (void)0)) - -/* takes two types and returns the size of the larger one */ -#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; })) - -#define MAX3(x,y,z) \ - __extension__ ({ \ - const typeof(x) _c = MAX(x,y); \ - MAX(_c, z); \ - }) - -#undef MIN -#define MIN(a, b) __MIN(UNIQ, (a), UNIQ, (b)) -#define __MIN(aq, a, bq, b) \ - __extension__ ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A,aq) < UNIQ_T(B,bq) ? UNIQ_T(A,aq) : UNIQ_T(B,bq); \ - }) - -#define MIN3(x,y,z) \ - __extension__ ({ \ - const typeof(x) _c = MIN(x,y); \ - MIN(_c, z); \ - }) - -#define LESS_BY(a, b) __LESS_BY(UNIQ, (a), UNIQ, (b)) -#define __LESS_BY(aq, a, bq, b) \ - __extension__ ({ \ - const typeof(a) UNIQ_T(A, aq) = (a); \ - const typeof(b) UNIQ_T(B, bq) = (b); \ - UNIQ_T(A,aq) > UNIQ_T(B,bq) ? UNIQ_T(A,aq) - UNIQ_T(B,bq) : 0; \ - }) - -#undef CLAMP -#define CLAMP(x, low, high) __CLAMP(UNIQ, (x), UNIQ, (low), UNIQ, (high)) -#define __CLAMP(xq, x, lowq, low, highq, high) \ - __extension__ ({ \ - const typeof(x) UNIQ_T(X,xq) = (x); \ - const typeof(low) UNIQ_T(LOW,lowq) = (low); \ - const typeof(high) UNIQ_T(HIGH,highq) = (high); \ - UNIQ_T(X,xq) > UNIQ_T(HIGH,highq) ? \ - UNIQ_T(HIGH,highq) : \ - UNIQ_T(X,xq) < UNIQ_T(LOW,lowq) ? \ - UNIQ_T(LOW,lowq) : \ - UNIQ_T(X,xq); \ - }) - -/* [(x + y - 1) / y] suffers from an integer overflow, even though the - * computation should be possible in the given type. Therefore, we use - * [x / y + !!(x % y)]. Note that on "Real CPUs" a division returns both the - * quotient and the remainder, so both should be equally fast. */ -#define DIV_ROUND_UP(_x, _y) \ - __extension__ ({ \ - const typeof(_x) __x = (_x); \ - const typeof(_y) __y = (_y); \ - (__x / __y + !!(__x % __y)); \ - }) - -#define assert_message_se(expr, message) \ - do { \ - if (_unlikely_(!(expr))) \ - log_assert_failed(message, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ - } while (false) - -#define assert_se(expr) assert_message_se(expr, #expr) - -/* We override the glibc assert() here. */ -#undef assert -#ifdef NDEBUG -#define assert(expr) do {} while (false) -#else -#define assert(expr) assert_message_se(expr, #expr) -#endif - -#define assert_not_reached(t) \ - do { \ - log_assert_failed_unreachable(t, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ - } while (false) - -#if defined(static_assert) -/* static_assert() is sometimes defined in a way that trips up - * -Wdeclaration-after-statement, hence let's temporarily turn off - * this warning around it. */ -#define assert_cc(expr) \ - DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \ - static_assert(expr, #expr); \ - REENABLE_WARNING -#else -#define assert_cc(expr) \ - DISABLE_WARNING_DECLARATION_AFTER_STATEMENT; \ - struct CONCATENATE(_assert_struct_, __COUNTER__) { \ - char x[(expr) ? 0 : -1]; \ - }; \ - REENABLE_WARNING -#endif - -#define assert_log(expr, message) ((_likely_(expr)) \ - ? (true) \ - : (log_assert_failed_return(message, __FILE__, __LINE__, __PRETTY_FUNCTION__), false)) - -#define assert_return(expr, r) \ - do { \ - if (!assert_log(expr, #expr)) \ - return (r); \ - } while (false) - -#define assert_return_errno(expr, r, err) \ - do { \ - if (!assert_log(expr, #expr)) { \ - errno = err; \ - return (r); \ - } \ - } while (false) - -#define PTR_TO_INT(p) ((int) ((intptr_t) (p))) -#define INT_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p))) -#define UINT_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_LONG(p) ((long) ((intptr_t) (p))) -#define LONG_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_ULONG(p) ((unsigned long) ((uintptr_t) (p))) -#define ULONG_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_INT32(p) ((int32_t) ((intptr_t) (p))) -#define INT32_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_UINT32(p) ((uint32_t) ((uintptr_t) (p))) -#define UINT32_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_INT64(p) ((int64_t) ((intptr_t) (p))) -#define INT64_TO_PTR(u) ((void *) ((intptr_t) (u))) -#define PTR_TO_UINT64(p) ((uint64_t) ((uintptr_t) (p))) -#define UINT64_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p))) -#define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u))) - -#define CHAR_TO_STR(x) ((char[2]) { x, 0 }) - -#define char_array_0(x) x[sizeof(x)-1] = 0; - -/* Returns the number of chars needed to format variables of the - * specified type as a decimal string. Adds in extra space for a - * negative '-' prefix (hence works correctly on signed - * types). Includes space for the trailing NUL. */ -#define DECIMAL_STR_MAX(type) \ - (2+(sizeof(type) <= 1 ? 3 : \ - sizeof(type) <= 2 ? 5 : \ - sizeof(type) <= 4 ? 10 : \ - sizeof(type) <= 8 ? 20 : sizeof(int[-2*(sizeof(type) > 8)]))) - -#define DECIMAL_STR_WIDTH(x) \ - ({ \ - typeof(x) _x_ = (x); \ - unsigned ans = 1; \ - while (_x_ /= 10) \ - ans++; \ - ans; \ - }) - -#define SET_FLAG(v, flag, b) \ - (v) = (b) ? ((v) | (flag)) : ((v) & ~(flag)) - -#define CASE_F(X) case X: -#define CASE_F_1(CASE, X) CASE_F(X) -#define CASE_F_2(CASE, X, ...) CASE(X) CASE_F_1(CASE, __VA_ARGS__) -#define CASE_F_3(CASE, X, ...) CASE(X) CASE_F_2(CASE, __VA_ARGS__) -#define CASE_F_4(CASE, X, ...) CASE(X) CASE_F_3(CASE, __VA_ARGS__) -#define CASE_F_5(CASE, X, ...) CASE(X) CASE_F_4(CASE, __VA_ARGS__) -#define CASE_F_6(CASE, X, ...) CASE(X) CASE_F_5(CASE, __VA_ARGS__) -#define CASE_F_7(CASE, X, ...) CASE(X) CASE_F_6(CASE, __VA_ARGS__) -#define CASE_F_8(CASE, X, ...) CASE(X) CASE_F_7(CASE, __VA_ARGS__) -#define CASE_F_9(CASE, X, ...) CASE(X) CASE_F_8(CASE, __VA_ARGS__) -#define CASE_F_10(CASE, X, ...) CASE(X) CASE_F_9(CASE, __VA_ARGS__) -#define CASE_F_11(CASE, X, ...) CASE(X) CASE_F_10(CASE, __VA_ARGS__) -#define CASE_F_12(CASE, X, ...) CASE(X) CASE_F_11(CASE, __VA_ARGS__) -#define CASE_F_13(CASE, X, ...) CASE(X) CASE_F_12(CASE, __VA_ARGS__) -#define CASE_F_14(CASE, X, ...) CASE(X) CASE_F_13(CASE, __VA_ARGS__) -#define CASE_F_15(CASE, X, ...) CASE(X) CASE_F_14(CASE, __VA_ARGS__) -#define CASE_F_16(CASE, X, ...) CASE(X) CASE_F_15(CASE, __VA_ARGS__) -#define CASE_F_17(CASE, X, ...) CASE(X) CASE_F_16(CASE, __VA_ARGS__) -#define CASE_F_18(CASE, X, ...) CASE(X) CASE_F_17(CASE, __VA_ARGS__) -#define CASE_F_19(CASE, X, ...) CASE(X) CASE_F_18(CASE, __VA_ARGS__) -#define CASE_F_20(CASE, X, ...) CASE(X) CASE_F_19(CASE, __VA_ARGS__) - -#define GET_CASE_F(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,NAME,...) NAME -#define FOR_EACH_MAKE_CASE(...) \ - GET_CASE_F(__VA_ARGS__,CASE_F_20,CASE_F_19,CASE_F_18,CASE_F_17,CASE_F_16,CASE_F_15,CASE_F_14,CASE_F_13,CASE_F_12,CASE_F_11, \ - CASE_F_10,CASE_F_9,CASE_F_8,CASE_F_7,CASE_F_6,CASE_F_5,CASE_F_4,CASE_F_3,CASE_F_2,CASE_F_1) \ - (CASE_F,__VA_ARGS__) - -#define IN_SET(x, ...) \ - ({ \ - bool _found = false; \ - /* If the build breaks in the line below, you need to extend the case macros */ \ - static _unused_ char _static_assert__macros_need_to_be_extended[20 - sizeof((int[]){__VA_ARGS__})/sizeof(int)]; \ - switch(x) { \ - FOR_EACH_MAKE_CASE(__VA_ARGS__) \ - _found = true; \ - break; \ - default: \ - break; \ - } \ - _found; \ - }) - -#define SWAP_TWO(x, y) do { \ - typeof(x) _t = (x); \ - (x) = (y); \ - (y) = (_t); \ - } while (false) - -/* Define C11 thread_local attribute even on older gcc compiler - * version */ -#ifndef thread_local -/* - * Don't break on glibc < 2.16 that doesn't define __STDC_NO_THREADS__ - * see http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53769 - */ -#if __STDC_VERSION__ >= 201112L && !(defined(__STDC_NO_THREADS__) || (defined(__GNU_LIBRARY__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 16)) -#define thread_local _Thread_local -#else -#define thread_local __thread -#endif -#endif - -/* Define C11 noreturn without <stdnoreturn.h> and even on older gcc - * compiler versions */ -#ifndef noreturn -#if __STDC_VERSION__ >= 201112L -#define noreturn _Noreturn -#else -#define noreturn __attribute__((noreturn)) -#endif -#endif - -#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \ - static inline void func##p(type *p) { \ - if (*p) \ - func(*p); \ - } \ - struct __useless_struct_to_allow_trailing_semicolon__ - -#include "log.h" diff --git a/src/basic/memfd-util.c b/src/basic/memfd-util.c deleted file mode 100644 index 8c8cc78ebf..0000000000 --- a/src/basic/memfd-util.c +++ /dev/null @@ -1,174 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 <errno.h> -#include <fcntl.h> -#include <sys/stat.h> -#include <unistd.h> -#ifdef HAVE_LINUX_MEMFD_H -#include <linux/memfd.h> -#endif -#include <stdio.h> -#include <sys/mman.h> -#include <sys/prctl.h> - -#include "alloc-util.h" -#include "fd-util.h" -#include "macro.h" -#include "memfd-util.h" -#include "missing.h" -#include "string-util.h" -#include "utf8.h" - -int memfd_new(const char *name) { - _cleanup_free_ char *g = NULL; - int fd; - - if (!name) { - char pr[17] = {}; - - /* If no name is specified we generate one. We include - * a hint indicating our library implementation, and - * add the thread name to it */ - - assert_se(prctl(PR_GET_NAME, (unsigned long) pr) >= 0); - - if (isempty(pr)) - name = "sd"; - else { - _cleanup_free_ char *e = NULL; - - e = utf8_escape_invalid(pr); - if (!e) - return -ENOMEM; - - g = strappend("sd-", e); - if (!g) - return -ENOMEM; - - name = g; - } - } - - fd = memfd_create(name, MFD_ALLOW_SEALING | MFD_CLOEXEC); - if (fd < 0) - return -errno; - - return fd; -} - -int memfd_map(int fd, uint64_t offset, size_t size, void **p) { - void *q; - int sealed; - - assert(fd >= 0); - assert(size > 0); - assert(p); - - sealed = memfd_get_sealed(fd); - if (sealed < 0) - return sealed; - - if (sealed) - q = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, offset); - else - q = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset); - - if (q == MAP_FAILED) - return -errno; - - *p = q; - return 0; -} - -int memfd_set_sealed(int fd) { - int r; - - assert(fd >= 0); - - r = fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL); - if (r < 0) - return -errno; - - return 0; -} - -int memfd_get_sealed(int fd) { - int r; - - assert(fd >= 0); - - r = fcntl(fd, F_GET_SEALS); - if (r < 0) - return -errno; - - return r == (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL); -} - -int memfd_get_size(int fd, uint64_t *sz) { - struct stat stat; - int r; - - assert(fd >= 0); - assert(sz); - - r = fstat(fd, &stat); - if (r < 0) - return -errno; - - *sz = stat.st_size; - return 0; -} - -int memfd_set_size(int fd, uint64_t sz) { - int r; - - assert(fd >= 0); - - r = ftruncate(fd, sz); - if (r < 0) - return -errno; - - return 0; -} - -int memfd_new_and_map(const char *name, size_t sz, void **p) { - _cleanup_close_ int fd = -1; - int r; - - assert(sz > 0); - assert(p); - - fd = memfd_new(name); - if (fd < 0) - return fd; - - r = memfd_set_size(fd, sz); - if (r < 0) - return r; - - r = memfd_map(fd, 0, sz, p); - if (r < 0) - return r; - - r = fd; - fd = -1; - - return r; -} diff --git a/src/basic/memfd-util.h b/src/basic/memfd-util.h deleted file mode 100644 index 46d4989e4c..0000000000 --- a/src/basic/memfd-util.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 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 <stddef.h> -#include <stdint.h> -#include <sys/types.h> - -int memfd_new(const char *name); -int memfd_new_and_map(const char *name, size_t sz, void **p); - -int memfd_map(int fd, uint64_t offset, size_t size, void **p); - -int memfd_set_sealed(int fd); -int memfd_get_sealed(int fd); - -int memfd_get_size(int fd, uint64_t *sz); -int memfd_set_size(int fd, uint64_t sz); diff --git a/src/basic/mempool.c b/src/basic/mempool.c deleted file mode 100644 index f95e2beb0f..0000000000 --- a/src/basic/mempool.c +++ /dev/null @@ -1,104 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010-2014 Lennart Poettering - Copyright 2014 Michal Schmidt - - 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 <stdint.h> -#include <stdlib.h> - -#include "macro.h" -#include "mempool.h" -#include "util.h" - -struct pool { - struct pool *next; - unsigned n_tiles; - unsigned n_used; -}; - -void* mempool_alloc_tile(struct mempool *mp) { - unsigned i; - - /* When a tile is released we add it to the list and simply - * place the next pointer at its offset 0. */ - - assert(mp->tile_size >= sizeof(void*)); - assert(mp->at_least > 0); - - if (mp->freelist) { - void *r; - - r = mp->freelist; - mp->freelist = * (void**) mp->freelist; - return r; - } - - if (_unlikely_(!mp->first_pool) || - _unlikely_(mp->first_pool->n_used >= mp->first_pool->n_tiles)) { - unsigned n; - size_t size; - struct pool *p; - - n = mp->first_pool ? mp->first_pool->n_tiles : 0; - n = MAX(mp->at_least, n * 2); - size = PAGE_ALIGN(ALIGN(sizeof(struct pool)) + n*mp->tile_size); - n = (size - ALIGN(sizeof(struct pool))) / mp->tile_size; - - p = malloc(size); - if (!p) - return NULL; - - p->next = mp->first_pool; - p->n_tiles = n; - p->n_used = 0; - - mp->first_pool = p; - } - - i = mp->first_pool->n_used++; - - return ((uint8_t*) mp->first_pool) + ALIGN(sizeof(struct pool)) + i*mp->tile_size; -} - -void* mempool_alloc0_tile(struct mempool *mp) { - void *p; - - p = mempool_alloc_tile(mp); - if (p) - memzero(p, mp->tile_size); - return p; -} - -void mempool_free_tile(struct mempool *mp, void *p) { - * (void**) p = mp->freelist; - mp->freelist = p; -} - -#ifdef VALGRIND - -void mempool_drop(struct mempool *mp) { - struct pool *p = mp->first_pool; - while (p) { - struct pool *n; - n = p->next; - free(p); - p = n; - } -} - -#endif diff --git a/src/basic/mempool.h b/src/basic/mempool.h deleted file mode 100644 index 0618b8dd22..0000000000 --- a/src/basic/mempool.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011-2014 Lennart Poettering - Copyright 2014 Michal Schmidt - - 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> - -struct pool; - -struct mempool { - struct pool *first_pool; - void *freelist; - size_t tile_size; - unsigned at_least; -}; - -void* mempool_alloc_tile(struct mempool *mp); -void* mempool_alloc0_tile(struct mempool *mp); -void mempool_free_tile(struct mempool *mp, void *p); - -#define DEFINE_MEMPOOL(pool_name, tile_type, alloc_at_least) \ -static struct mempool pool_name = { \ - .tile_size = sizeof(tile_type), \ - .at_least = alloc_at_least, \ -} - - -#ifdef VALGRIND -void mempool_drop(struct mempool *mp); -#endif diff --git a/src/basic/missing.h b/src/basic/missing.h deleted file mode 100644 index 4c013be608..0000000000 --- a/src/basic/missing.h +++ /dev/null @@ -1,1081 +0,0 @@ -#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/>. -***/ - -/* Missing glibc definitions to access certain kernel APIs */ - -#include <errno.h> -#include <fcntl.h> -#include <linux/audit.h> -#include <linux/capability.h> -#include <linux/if_link.h> -#include <linux/input.h> -#include <linux/loop.h> -#include <linux/neighbour.h> -#include <linux/oom.h> -#include <linux/rtnetlink.h> -#include <net/ethernet.h> -#include <stdlib.h> -#include <sys/resource.h> -#include <sys/syscall.h> -#include <uchar.h> -#include <unistd.h> - -#ifdef HAVE_AUDIT -#include <libaudit.h> -#endif - -#ifdef ARCH_MIPS -#include <asm/sgidefs.h> -#endif - -#ifdef HAVE_LINUX_BTRFS_H -#include <linux/btrfs.h> -#endif - -#include "macro.h" - -#ifndef RLIMIT_RTTIME -#define RLIMIT_RTTIME 15 -#endif - -/* If RLIMIT_RTTIME is not defined, then we cannot use RLIMIT_NLIMITS as is */ -#define _RLIMIT_MAX (RLIMIT_RTTIME+1 > RLIMIT_NLIMITS ? RLIMIT_RTTIME+1 : RLIMIT_NLIMITS) - -#ifndef F_LINUX_SPECIFIC_BASE -#define F_LINUX_SPECIFIC_BASE 1024 -#endif - -#ifndef F_SETPIPE_SZ -#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7) -#endif - -#ifndef F_GETPIPE_SZ -#define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8) -#endif - -#ifndef F_ADD_SEALS -#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9) -#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10) - -#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */ -#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */ -#define F_SEAL_GROW 0x0004 /* prevent file from growing */ -#define F_SEAL_WRITE 0x0008 /* prevent writes */ -#endif - -#ifndef F_OFD_GETLK -#define F_OFD_GETLK 36 -#define F_OFD_SETLK 37 -#define F_OFD_SETLKW 38 -#endif - -#ifndef MFD_ALLOW_SEALING -#define MFD_ALLOW_SEALING 0x0002U -#endif - -#ifndef MFD_CLOEXEC -#define MFD_CLOEXEC 0x0001U -#endif - -#ifndef IP_FREEBIND -#define IP_FREEBIND 15 -#endif - -#ifndef OOM_SCORE_ADJ_MIN -#define OOM_SCORE_ADJ_MIN (-1000) -#endif - -#ifndef OOM_SCORE_ADJ_MAX -#define OOM_SCORE_ADJ_MAX 1000 -#endif - -#ifndef AUDIT_SERVICE_START -#define AUDIT_SERVICE_START 1130 /* Service (daemon) start */ -#endif - -#ifndef AUDIT_SERVICE_STOP -#define AUDIT_SERVICE_STOP 1131 /* Service (daemon) stop */ -#endif - -#ifndef TIOCVHANGUP -#define TIOCVHANGUP 0x5437 -#endif - -#ifndef IP_TRANSPARENT -#define IP_TRANSPARENT 19 -#endif - -#ifndef SOL_NETLINK -#define SOL_NETLINK 270 -#endif - -#ifndef NETLINK_LIST_MEMBERSHIPS -#define NETLINK_LIST_MEMBERSHIPS 9 -#endif - -#ifndef SOL_SCTP -#define SOL_SCTP 132 -#endif - -#ifndef GRND_NONBLOCK -#define GRND_NONBLOCK 0x0001 -#endif - -#ifndef GRND_RANDOM -#define GRND_RANDOM 0x0002 -#endif - -#ifndef BTRFS_IOCTL_MAGIC -#define BTRFS_IOCTL_MAGIC 0x94 -#endif - -#ifndef BTRFS_PATH_NAME_MAX -#define BTRFS_PATH_NAME_MAX 4087 -#endif - -#ifndef BTRFS_DEVICE_PATH_NAME_MAX -#define BTRFS_DEVICE_PATH_NAME_MAX 1024 -#endif - -#ifndef BTRFS_FSID_SIZE -#define BTRFS_FSID_SIZE 16 -#endif - -#ifndef BTRFS_UUID_SIZE -#define BTRFS_UUID_SIZE 16 -#endif - -#ifndef BTRFS_SUBVOL_RDONLY -#define BTRFS_SUBVOL_RDONLY (1ULL << 1) -#endif - -#ifndef BTRFS_SUBVOL_NAME_MAX -#define BTRFS_SUBVOL_NAME_MAX 4039 -#endif - -#ifndef BTRFS_INO_LOOKUP_PATH_MAX -#define BTRFS_INO_LOOKUP_PATH_MAX 4080 -#endif - -#ifndef BTRFS_SEARCH_ARGS_BUFSIZE -#define BTRFS_SEARCH_ARGS_BUFSIZE (4096 - sizeof(struct btrfs_ioctl_search_key)) -#endif - -#ifndef BTRFS_QGROUP_LEVEL_SHIFT -#define BTRFS_QGROUP_LEVEL_SHIFT 48 -#endif - -#ifndef HAVE_LINUX_BTRFS_H -struct btrfs_ioctl_vol_args { - int64_t fd; - char name[BTRFS_PATH_NAME_MAX + 1]; -}; - -struct btrfs_qgroup_limit { - __u64 flags; - __u64 max_rfer; - __u64 max_excl; - __u64 rsv_rfer; - __u64 rsv_excl; -}; - -struct btrfs_qgroup_inherit { - __u64 flags; - __u64 num_qgroups; - __u64 num_ref_copies; - __u64 num_excl_copies; - struct btrfs_qgroup_limit lim; - __u64 qgroups[0]; -}; - -struct btrfs_ioctl_qgroup_limit_args { - __u64 qgroupid; - struct btrfs_qgroup_limit lim; -}; - -struct btrfs_ioctl_vol_args_v2 { - __s64 fd; - __u64 transid; - __u64 flags; - union { - struct { - __u64 size; - struct btrfs_qgroup_inherit *qgroup_inherit; - }; - __u64 unused[4]; - }; - char name[BTRFS_SUBVOL_NAME_MAX + 1]; -}; - -struct btrfs_ioctl_dev_info_args { - uint64_t devid; /* in/out */ - uint8_t uuid[BTRFS_UUID_SIZE]; /* in/out */ - uint64_t bytes_used; /* out */ - uint64_t total_bytes; /* out */ - uint64_t unused[379]; /* pad to 4k */ - char path[BTRFS_DEVICE_PATH_NAME_MAX]; /* out */ -}; - -struct btrfs_ioctl_fs_info_args { - uint64_t max_id; /* out */ - uint64_t num_devices; /* out */ - uint8_t fsid[BTRFS_FSID_SIZE]; /* out */ - uint64_t reserved[124]; /* pad to 1k */ -}; - -struct btrfs_ioctl_ino_lookup_args { - __u64 treeid; - __u64 objectid; - char name[BTRFS_INO_LOOKUP_PATH_MAX]; -}; - -struct btrfs_ioctl_search_key { - /* which root are we searching. 0 is the tree of tree roots */ - __u64 tree_id; - - /* keys returned will be >= min and <= max */ - __u64 min_objectid; - __u64 max_objectid; - - /* keys returned will be >= min and <= max */ - __u64 min_offset; - __u64 max_offset; - - /* max and min transids to search for */ - __u64 min_transid; - __u64 max_transid; - - /* keys returned will be >= min and <= max */ - __u32 min_type; - __u32 max_type; - - /* - * how many items did userland ask for, and how many are we - * returning - */ - __u32 nr_items; - - /* align to 64 bits */ - __u32 unused; - - /* some extra for later */ - __u64 unused1; - __u64 unused2; - __u64 unused3; - __u64 unused4; -}; - -struct btrfs_ioctl_search_header { - __u64 transid; - __u64 objectid; - __u64 offset; - __u32 type; - __u32 len; -}; - - -struct btrfs_ioctl_search_args { - struct btrfs_ioctl_search_key key; - char buf[BTRFS_SEARCH_ARGS_BUFSIZE]; -}; - -struct btrfs_ioctl_clone_range_args { - __s64 src_fd; - __u64 src_offset, src_length; - __u64 dest_offset; -}; - -#define BTRFS_QUOTA_CTL_ENABLE 1 -#define BTRFS_QUOTA_CTL_DISABLE 2 -#define BTRFS_QUOTA_CTL_RESCAN__NOTUSED 3 -struct btrfs_ioctl_quota_ctl_args { - __u64 cmd; - __u64 status; -}; -#endif - -#ifndef BTRFS_IOC_DEFRAG -#define BTRFS_IOC_DEFRAG _IOW(BTRFS_IOCTL_MAGIC, 2, \ - struct btrfs_ioctl_vol_args) -#endif - -#ifndef BTRFS_IOC_RESIZE -#define BTRFS_IOC_RESIZE _IOW(BTRFS_IOCTL_MAGIC, 3, \ - struct btrfs_ioctl_vol_args) -#endif - -#ifndef BTRFS_IOC_CLONE -#define BTRFS_IOC_CLONE _IOW(BTRFS_IOCTL_MAGIC, 9, int) -#endif - -#ifndef BTRFS_IOC_CLONE_RANGE -#define BTRFS_IOC_CLONE_RANGE _IOW(BTRFS_IOCTL_MAGIC, 13, \ - struct btrfs_ioctl_clone_range_args) -#endif - -#ifndef BTRFS_IOC_SUBVOL_CREATE -#define BTRFS_IOC_SUBVOL_CREATE _IOW(BTRFS_IOCTL_MAGIC, 14, \ - struct btrfs_ioctl_vol_args) -#endif - -#ifndef BTRFS_IOC_SNAP_DESTROY -#define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \ - struct btrfs_ioctl_vol_args) -#endif - -#ifndef BTRFS_IOC_TREE_SEARCH -#define BTRFS_IOC_TREE_SEARCH _IOWR(BTRFS_IOCTL_MAGIC, 17, \ - struct btrfs_ioctl_search_args) -#endif - -#ifndef BTRFS_IOC_INO_LOOKUP -#define BTRFS_IOC_INO_LOOKUP _IOWR(BTRFS_IOCTL_MAGIC, 18, \ - struct btrfs_ioctl_ino_lookup_args) -#endif - -#ifndef BTRFS_IOC_SNAP_CREATE_V2 -#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \ - struct btrfs_ioctl_vol_args_v2) -#endif - -#ifndef BTRFS_IOC_SUBVOL_GETFLAGS -#define BTRFS_IOC_SUBVOL_GETFLAGS _IOR(BTRFS_IOCTL_MAGIC, 25, __u64) -#endif - -#ifndef BTRFS_IOC_SUBVOL_SETFLAGS -#define BTRFS_IOC_SUBVOL_SETFLAGS _IOW(BTRFS_IOCTL_MAGIC, 26, __u64) -#endif - -#ifndef BTRFS_IOC_DEV_INFO -#define BTRFS_IOC_DEV_INFO _IOWR(BTRFS_IOCTL_MAGIC, 30, \ - struct btrfs_ioctl_dev_info_args) -#endif - -#ifndef BTRFS_IOC_FS_INFO -#define BTRFS_IOC_FS_INFO _IOR(BTRFS_IOCTL_MAGIC, 31, \ - struct btrfs_ioctl_fs_info_args) -#endif - -#ifndef BTRFS_IOC_DEVICES_READY -#define BTRFS_IOC_DEVICES_READY _IOR(BTRFS_IOCTL_MAGIC, 39, \ - struct btrfs_ioctl_vol_args) -#endif - -#ifndef BTRFS_IOC_QUOTA_CTL -#define BTRFS_IOC_QUOTA_CTL _IOWR(BTRFS_IOCTL_MAGIC, 40, \ - struct btrfs_ioctl_quota_ctl_args) -#endif - -#ifndef BTRFS_IOC_QGROUP_LIMIT -#define BTRFS_IOC_QGROUP_LIMIT _IOR(BTRFS_IOCTL_MAGIC, 43, \ - struct btrfs_ioctl_qgroup_limit_args) -#endif - -#ifndef BTRFS_IOC_QUOTA_RESCAN_WAIT -#define BTRFS_IOC_QUOTA_RESCAN_WAIT _IO(BTRFS_IOCTL_MAGIC, 46) -#endif - -#ifndef BTRFS_FIRST_FREE_OBJECTID -#define BTRFS_FIRST_FREE_OBJECTID 256 -#endif - -#ifndef BTRFS_LAST_FREE_OBJECTID -#define BTRFS_LAST_FREE_OBJECTID -256ULL -#endif - -#ifndef BTRFS_ROOT_TREE_OBJECTID -#define BTRFS_ROOT_TREE_OBJECTID 1 -#endif - -#ifndef BTRFS_QUOTA_TREE_OBJECTID -#define BTRFS_QUOTA_TREE_OBJECTID 8ULL -#endif - -#ifndef BTRFS_ROOT_ITEM_KEY -#define BTRFS_ROOT_ITEM_KEY 132 -#endif - -#ifndef BTRFS_QGROUP_STATUS_KEY -#define BTRFS_QGROUP_STATUS_KEY 240 -#endif - -#ifndef BTRFS_QGROUP_INFO_KEY -#define BTRFS_QGROUP_INFO_KEY 242 -#endif - -#ifndef BTRFS_QGROUP_LIMIT_KEY -#define BTRFS_QGROUP_LIMIT_KEY 244 -#endif - -#ifndef BTRFS_QGROUP_RELATION_KEY -#define BTRFS_QGROUP_RELATION_KEY 246 -#endif - -#ifndef BTRFS_ROOT_BACKREF_KEY -#define BTRFS_ROOT_BACKREF_KEY 144 -#endif - -#ifndef BTRFS_SUPER_MAGIC -#define BTRFS_SUPER_MAGIC 0x9123683E -#endif - -#ifndef CGROUP_SUPER_MAGIC -#define CGROUP_SUPER_MAGIC 0x27e0eb -#endif - -#ifndef CGROUP2_SUPER_MAGIC -#define CGROUP2_SUPER_MAGIC 0x63677270 -#endif - -#ifndef CLONE_NEWCGROUP -#define CLONE_NEWCGROUP 0x02000000 -#endif - -#ifndef TMPFS_MAGIC -#define TMPFS_MAGIC 0x01021994 -#endif - -#ifndef MQUEUE_MAGIC -#define MQUEUE_MAGIC 0x19800202 -#endif - -#ifndef SECURITYFS_MAGIC -#define SECURITYFS_MAGIC 0x73636673 -#endif - -#ifndef TRACEFS_MAGIC -#define TRACEFS_MAGIC 0x74726163 -#endif - -#ifndef BPF_FS_MAGIC -#define BPF_FS_MAGIC 0xcafe4a11 -#endif - -#ifndef MS_MOVE -#define MS_MOVE 8192 -#endif - -#ifndef MS_REC -#define MS_REC 16384 -#endif - -#ifndef MS_PRIVATE -#define MS_PRIVATE (1<<18) -#endif - -#ifndef MS_REC -#define MS_REC (1<<19) -#endif - -#ifndef MS_SHARED -#define MS_SHARED (1<<20) -#endif - -#ifndef MS_RELATIME -#define MS_RELATIME (1<<21) -#endif - -#ifndef MS_KERNMOUNT -#define MS_KERNMOUNT (1<<22) -#endif - -#ifndef MS_I_VERSION -#define MS_I_VERSION (1<<23) -#endif - -#ifndef MS_STRICTATIME -#define MS_STRICTATIME (1<<24) -#endif - -#ifndef MS_LAZYTIME -#define MS_LAZYTIME (1<<25) -#endif - -#ifndef SCM_SECURITY -#define SCM_SECURITY 0x03 -#endif - -#ifndef PR_SET_NO_NEW_PRIVS -#define PR_SET_NO_NEW_PRIVS 38 -#endif - -#ifndef PR_SET_CHILD_SUBREAPER -#define PR_SET_CHILD_SUBREAPER 36 -#endif - -#ifndef MAX_HANDLE_SZ -#define MAX_HANDLE_SZ 128 -#endif - -#ifndef HAVE_SECURE_GETENV -# ifdef HAVE___SECURE_GETENV -# define secure_getenv __secure_getenv -# else -# error "neither secure_getenv nor __secure_getenv are available" -# endif -#endif - -#ifndef CIFS_MAGIC_NUMBER -# define CIFS_MAGIC_NUMBER 0xFF534D42 -#endif - -#ifndef TFD_TIMER_CANCEL_ON_SET -# define TFD_TIMER_CANCEL_ON_SET (1 << 1) -#endif - -#ifndef SO_REUSEPORT -# define SO_REUSEPORT 15 -#endif - -#ifndef EVIOCREVOKE -# define EVIOCREVOKE _IOW('E', 0x91, int) -#endif - -#ifndef DRM_IOCTL_SET_MASTER -# define DRM_IOCTL_SET_MASTER _IO('d', 0x1e) -#endif - -#ifndef DRM_IOCTL_DROP_MASTER -# define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f) -#endif - -/* The precise definition of __O_TMPFILE is arch specific; use the - * values defined by the kernel (note: some are hexa, some are octal, - * duplicated as-is from the kernel definitions): - * - alpha, parisc, sparc: each has a specific value; - * - others: they use the "generic" value. - */ - -#ifndef __O_TMPFILE -#if defined(__alpha__) -#define __O_TMPFILE 0100000000 -#elif defined(__parisc__) || defined(__hppa__) -#define __O_TMPFILE 0400000000 -#elif defined(__sparc__) || defined(__sparc64__) -#define __O_TMPFILE 0x2000000 -#else -#define __O_TMPFILE 020000000 -#endif - -/* a horrid kludge trying to make sure that this will fail on old kernels */ -#ifndef O_TMPFILE -#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) -#endif - -#endif - -#if !HAVE_DECL_LO_FLAGS_PARTSCAN -#define LO_FLAGS_PARTSCAN 8 -#endif - -#ifndef LOOP_CTL_REMOVE -#define LOOP_CTL_REMOVE 0x4C81 -#endif - -#ifndef LOOP_CTL_GET_FREE -#define LOOP_CTL_GET_FREE 0x4C82 -#endif - -#if !HAVE_DECL_IFLA_INET6_ADDR_GEN_MODE -#define IFLA_INET6_UNSPEC 0 -#define IFLA_INET6_FLAGS 1 -#define IFLA_INET6_CONF 2 -#define IFLA_INET6_STATS 3 -#define IFLA_INET6_MCAST 4 -#define IFLA_INET6_CACHEINFO 5 -#define IFLA_INET6_ICMP6STATS 6 -#define IFLA_INET6_TOKEN 7 -#define IFLA_INET6_ADDR_GEN_MODE 8 -#define __IFLA_INET6_MAX 9 - -#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1) - -#define IN6_ADDR_GEN_MODE_EUI64 0 -#define IN6_ADDR_GEN_MODE_NONE 1 -#endif - -#if !HAVE_DECL_IN6_ADDR_GEN_MODE_STABLE_PRIVACY -#define IN6_ADDR_GEN_MODE_STABLE_PRIVACY 2 -#endif - -#if !HAVE_DECL_IFLA_MACVLAN_FLAGS -#define IFLA_MACVLAN_UNSPEC 0 -#define IFLA_MACVLAN_MODE 1 -#define IFLA_MACVLAN_FLAGS 2 -#define __IFLA_MACVLAN_MAX 3 - -#define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1) -#endif - -#if !HAVE_DECL_IFLA_IPVLAN_MODE -#define IFLA_IPVLAN_UNSPEC 0 -#define IFLA_IPVLAN_MODE 1 -#define __IFLA_IPVLAN_MAX 2 - -#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1) - -#define IPVLAN_MODE_L2 0 -#define IPVLAN_MODE_L3 1 -#define IPVLAN_MAX 2 -#endif - -#if !HAVE_DECL_IFLA_VTI_REMOTE -#define IFLA_VTI_UNSPEC 0 -#define IFLA_VTI_LINK 1 -#define IFLA_VTI_IKEY 2 -#define IFLA_VTI_OKEY 3 -#define IFLA_VTI_LOCAL 4 -#define IFLA_VTI_REMOTE 5 -#define __IFLA_VTI_MAX 6 - -#define IFLA_VTI_MAX (__IFLA_VTI_MAX - 1) -#endif - -#if !HAVE_DECL_IFLA_PHYS_PORT_ID -#define IFLA_EXT_MASK 29 -#undef IFLA_PROMISCUITY -#define IFLA_PROMISCUITY 30 -#define IFLA_NUM_TX_QUEUES 31 -#define IFLA_NUM_RX_QUEUES 32 -#define IFLA_CARRIER 33 -#define IFLA_PHYS_PORT_ID 34 -#define __IFLA_MAX 35 - -#define IFLA_MAX (__IFLA_MAX - 1) -#endif - -#if !HAVE_DECL_IFLA_BOND_AD_INFO -#define IFLA_BOND_UNSPEC 0 -#define IFLA_BOND_MODE 1 -#define IFLA_BOND_ACTIVE_SLAVE 2 -#define IFLA_BOND_MIIMON 3 -#define IFLA_BOND_UPDELAY 4 -#define IFLA_BOND_DOWNDELAY 5 -#define IFLA_BOND_USE_CARRIER 6 -#define IFLA_BOND_ARP_INTERVAL 7 -#define IFLA_BOND_ARP_IP_TARGET 8 -#define IFLA_BOND_ARP_VALIDATE 9 -#define IFLA_BOND_ARP_ALL_TARGETS 10 -#define IFLA_BOND_PRIMARY 11 -#define IFLA_BOND_PRIMARY_RESELECT 12 -#define IFLA_BOND_FAIL_OVER_MAC 13 -#define IFLA_BOND_XMIT_HASH_POLICY 14 -#define IFLA_BOND_RESEND_IGMP 15 -#define IFLA_BOND_NUM_PEER_NOTIF 16 -#define IFLA_BOND_ALL_SLAVES_ACTIVE 17 -#define IFLA_BOND_MIN_LINKS 18 -#define IFLA_BOND_LP_INTERVAL 19 -#define IFLA_BOND_PACKETS_PER_SLAVE 20 -#define IFLA_BOND_AD_LACP_RATE 21 -#define IFLA_BOND_AD_SELECT 22 -#define IFLA_BOND_AD_INFO 23 -#define __IFLA_BOND_MAX 24 - -#define IFLA_BOND_MAX (__IFLA_BOND_MAX - 1) -#endif - -#if !HAVE_DECL_IFLA_VLAN_PROTOCOL -#define IFLA_VLAN_UNSPEC 0 -#define IFLA_VLAN_ID 1 -#define IFLA_VLAN_FLAGS 2 -#define IFLA_VLAN_EGRESS_QOS 3 -#define IFLA_VLAN_INGRESS_QOS 4 -#define IFLA_VLAN_PROTOCOL 5 -#define __IFLA_VLAN_MAX 6 - -#define IFLA_VLAN_MAX (__IFLA_VLAN_MAX - 1) -#endif - -#if !HAVE_DECL_IFLA_VXLAN_REMCSUM_NOPARTIAL -#define IFLA_VXLAN_UNSPEC 0 -#define IFLA_VXLAN_ID 1 -#define IFLA_VXLAN_GROUP 2 -#define IFLA_VXLAN_LINK 3 -#define IFLA_VXLAN_LOCAL 4 -#define IFLA_VXLAN_TTL 5 -#define IFLA_VXLAN_TOS 6 -#define IFLA_VXLAN_LEARNING 7 -#define IFLA_VXLAN_AGEING 8 -#define IFLA_VXLAN_LIMIT 9 -#define IFLA_VXLAN_PORT_RANGE 10 -#define IFLA_VXLAN_PROXY 11 -#define IFLA_VXLAN_RSC 12 -#define IFLA_VXLAN_L2MISS 13 -#define IFLA_VXLAN_L3MISS 14 -#define IFLA_VXLAN_PORT 15 -#define IFLA_VXLAN_GROUP6 16 -#define IFLA_VXLAN_LOCAL6 17 -#define IFLA_VXLAN_UDP_CSUM 18 -#define IFLA_VXLAN_UDP_ZERO_CSUM6_TX 19 -#define IFLA_VXLAN_UDP_ZERO_CSUM6_RX 20 -#define IFLA_VXLAN_REMCSUM_TX 21 -#define IFLA_VXLAN_REMCSUM_RX 22 -#define IFLA_VXLAN_GBP 23 -#define IFLA_VXLAN_REMCSUM_NOPARTIAL 24 -#define __IFLA_VXLAN_MAX 25 - -#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1) -#endif - -#if !HAVE_DECL_IFLA_IPTUN_ENCAP_DPORT -#define IFLA_IPTUN_UNSPEC 0 -#define IFLA_IPTUN_LINK 1 -#define IFLA_IPTUN_LOCAL 2 -#define IFLA_IPTUN_REMOTE 3 -#define IFLA_IPTUN_TTL 4 -#define IFLA_IPTUN_TOS 5 -#define IFLA_IPTUN_ENCAP_LIMIT 6 -#define IFLA_IPTUN_FLOWINFO 7 -#define IFLA_IPTUN_FLAGS 8 -#define IFLA_IPTUN_PROTO 9 -#define IFLA_IPTUN_PMTUDISC 10 -#define IFLA_IPTUN_6RD_PREFIX 11 -#define IFLA_IPTUN_6RD_RELAY_PREFIX 12 -#define IFLA_IPTUN_6RD_PREFIXLEN 13 -#define IFLA_IPTUN_6RD_RELAY_PREFIXLEN 14 -#define IFLA_IPTUN_ENCAP_TYPE 15 -#define IFLA_IPTUN_ENCAP_FLAGS 16 -#define IFLA_IPTUN_ENCAP_SPORT 17 -#define IFLA_IPTUN_ENCAP_DPORT 18 - -#define __IFLA_IPTUN_MAX 19 - -#define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1) -#endif - -#if !HAVE_DECL_IFLA_GRE_ENCAP_DPORT -#define IFLA_GRE_UNSPEC 0 -#define IFLA_GRE_LINK 1 -#define IFLA_GRE_IFLAGS 2 -#define IFLA_GRE_OFLAGS 3 -#define IFLA_GRE_IKEY 4 -#define IFLA_GRE_OKEY 5 -#define IFLA_GRE_LOCAL 6 -#define IFLA_GRE_REMOTE 7 -#define IFLA_GRE_TTL 8 -#define IFLA_GRE_TOS 9 -#define IFLA_GRE_PMTUDISC 10 -#define IFLA_GRE_ENCAP_LIMIT 11 -#define IFLA_GRE_FLOWINFO 12 -#define IFLA_GRE_FLAGS 13 -#define IFLA_GRE_ENCAP_TYPE 14 -#define IFLA_GRE_ENCAP_FLAGS 15 -#define IFLA_GRE_ENCAP_SPORT 16 -#define IFLA_GRE_ENCAP_DPORT 17 - -#define __IFLA_GRE_MAX 18 - -#define IFLA_GRE_MAX (__IFLA_GRE_MAX - 1) -#endif - -#if !HAVE_DECL_IFLA_BRIDGE_VLAN_INFO -#define IFLA_BRIDGE_FLAGS 0 -#define IFLA_BRIDGE_MODE 1 -#define IFLA_BRIDGE_VLAN_INFO 2 -#define __IFLA_BRIDGE_MAX 3 - -#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1) -#endif - -#ifndef BRIDGE_VLAN_INFO_RANGE_BEGIN -#define BRIDGE_VLAN_INFO_RANGE_BEGIN (1<<3) /* VLAN is start of vlan range */ -#endif - -#ifndef BRIDGE_VLAN_INFO_RANGE_END -#define BRIDGE_VLAN_INFO_RANGE_END (1<<4) /* VLAN is end of vlan range */ -#endif - -#if !HAVE_DECL_IFLA_BR_VLAN_DEFAULT_PVID -#define IFLA_BR_UNSPEC 0 -#define IFLA_BR_FORWARD_DELAY 1 -#define IFLA_BR_HELLO_TIME 2 -#define IFLA_BR_MAX_AGE 3 -#define IFLA_BR_AGEING_TIME 4 -#define IFLA_BR_STP_STATE 5 -#define IFLA_BR_PRIORITY 6 -#define IFLA_BR_VLAN_FILTERING 7 -#define IFLA_BR_VLAN_PROTOCOL 8 -#define IFLA_BR_GROUP_FWD_MASK 9 -#define IFLA_BR_ROOT_ID 10 -#define IFLA_BR_BRIDGE_ID 11 -#define IFLA_BR_ROOT_PORT 12 -#define IFLA_BR_ROOT_PATH_COST 13 -#define IFLA_BR_TOPOLOGY_CHANGE 14 -#define IFLA_BR_TOPOLOGY_CHANGE_DETECTED 15 -#define IFLA_BR_HELLO_TIMER 16 -#define IFLA_BR_TCN_TIMER 17 -#define IFLA_BR_TOPOLOGY_CHANGE_TIMER 18 -#define IFLA_BR_GC_TIMER 19 -#define IFLA_BR_GROUP_ADDR 20 -#define IFLA_BR_FDB_FLUSH 21 -#define IFLA_BR_MCAST_ROUTER 22 -#define IFLA_BR_MCAST_SNOOPING 23 -#define IFLA_BR_MCAST_QUERY_USE_IFADDR 24 -#define IFLA_BR_MCAST_QUERIER 25 -#define IFLA_BR_MCAST_HASH_ELASTICITY 26 -#define IFLA_BR_MCAST_HASH_MAX 27 -#define IFLA_BR_MCAST_LAST_MEMBER_CNT 28 -#define IFLA_BR_MCAST_STARTUP_QUERY_CNT 29 -#define IFLA_BR_MCAST_LAST_MEMBER_INTVL 30 -#define IFLA_BR_MCAST_MEMBERSHIP_INTVL 31 -#define IFLA_BR_MCAST_QUERIER_INTVL 32 -#define IFLA_BR_MCAST_QUERY_INTVL 33 -#define IFLA_BR_MCAST_QUERY_RESPONSE_INTVL 34 -#define IFLA_BR_MCAST_STARTUP_QUERY_INTVL 35 -#define IFLA_BR_NF_CALL_IPTABLES 36 -#define IFLA_BR_NF_CALL_IP6TABLES 37 -#define IFLA_BR_NF_CALL_ARPTABLES 38 -#define IFLA_BR_VLAN_DEFAULT_PVID 39 -#define __IFLA_BR_MAX 40 - -#define IFLA_BR_MAX (__IFLA_BR_MAX - 1) -#endif - -#if !HAVE_DECL_IFLA_BRPORT_LEARNING_SYNC -#define IFLA_BRPORT_UNSPEC 0 -#define IFLA_BRPORT_STATE 1 -#define IFLA_BRPORT_PRIORITY 2 -#define IFLA_BRPORT_COST 3 -#define IFLA_BRPORT_MODE 4 -#define IFLA_BRPORT_GUARD 5 -#define IFLA_BRPORT_PROTECT 6 -#define IFLA_BRPORT_FAST_LEAVE 7 -#define IFLA_BRPORT_LEARNING 8 -#define IFLA_BRPORT_UNICAST_FLOOD 9 -#define IFLA_BRPORT_LEARNING_SYNC 11 -#define __IFLA_BRPORT_MAX 12 - -#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) -#endif - -#if !HAVE_DECL_IFLA_BRPORT_PROXYARP -#define IFLA_BRPORT_PROXYARP 10 -#endif - -#if !HAVE_DECL_IFLA_VRF_TABLE -#define IFLA_VRF_TABLE 1 -#endif - -#if !HAVE_DECL_NDA_IFINDEX -#define NDA_UNSPEC 0 -#define NDA_DST 1 -#define NDA_LLADDR 2 -#define NDA_CACHEINFO 3 -#define NDA_PROBES 4 -#define NDA_VLAN 5 -#define NDA_PORT 6 -#define NDA_VNI 7 -#define NDA_IFINDEX 8 -#define __NDA_MAX 9 - -#define NDA_MAX (__NDA_MAX - 1) -#endif - -#ifndef RTA_PREF -#define RTA_PREF 20 -#endif - -#ifndef IPV6_UNICAST_IF -#define IPV6_UNICAST_IF 76 -#endif - -#ifndef IPV6_MIN_MTU -#define IPV6_MIN_MTU 1280 -#endif - -#ifndef IFF_MULTI_QUEUE -#define IFF_MULTI_QUEUE 0x100 -#endif - -#ifndef IFF_LOWER_UP -#define IFF_LOWER_UP 0x10000 -#endif - -#ifndef IFF_DORMANT -#define IFF_DORMANT 0x20000 -#endif - -#ifndef BOND_XMIT_POLICY_ENCAP23 -#define BOND_XMIT_POLICY_ENCAP23 3 -#endif - -#ifndef BOND_XMIT_POLICY_ENCAP34 -#define BOND_XMIT_POLICY_ENCAP34 4 -#endif - -#ifndef NET_ADDR_RANDOM -# define NET_ADDR_RANDOM 1 -#endif - -#ifndef NET_NAME_UNKNOWN -# define NET_NAME_UNKNOWN 0 -#endif - -#ifndef NET_NAME_ENUM -# define NET_NAME_ENUM 1 -#endif - -#ifndef NET_NAME_PREDICTABLE -# define NET_NAME_PREDICTABLE 2 -#endif - -#ifndef NET_NAME_USER -# define NET_NAME_USER 3 -#endif - -#ifndef NET_NAME_RENAMED -# define NET_NAME_RENAMED 4 -#endif - -#ifndef BPF_XOR -# define BPF_XOR 0xa0 -#endif - -/* Note that LOOPBACK_IFINDEX is currently not exported by the - * kernel/glibc, but hardcoded internally by the kernel. However, as - * it is exported to userspace indirectly via rtnetlink and the - * ioctls, and made use of widely we define it here too, in a way that - * is compatible with the kernel's internal definition. */ -#ifndef LOOPBACK_IFINDEX -#define LOOPBACK_IFINDEX 1 -#endif - -#if !HAVE_DECL_IFA_FLAGS -#define IFA_FLAGS 8 -#endif - -#ifndef IFA_F_MANAGETEMPADDR -#define IFA_F_MANAGETEMPADDR 0x100 -#endif - -#ifndef IFA_F_NOPREFIXROUTE -#define IFA_F_NOPREFIXROUTE 0x200 -#endif - -#ifndef MAX_AUDIT_MESSAGE_LENGTH -#define MAX_AUDIT_MESSAGE_LENGTH 8970 -#endif - -#ifndef AUDIT_NLGRP_MAX -#define AUDIT_NLGRP_READLOG 1 -#endif - -#ifndef CAP_MAC_OVERRIDE -#define CAP_MAC_OVERRIDE 32 -#endif - -#ifndef CAP_MAC_ADMIN -#define CAP_MAC_ADMIN 33 -#endif - -#ifndef CAP_SYSLOG -#define CAP_SYSLOG 34 -#endif - -#ifndef CAP_WAKE_ALARM -#define CAP_WAKE_ALARM 35 -#endif - -#ifndef CAP_BLOCK_SUSPEND -#define CAP_BLOCK_SUSPEND 36 -#endif - -#ifndef CAP_AUDIT_READ -#define CAP_AUDIT_READ 37 -#endif - -#ifndef RENAME_NOREPLACE -#define RENAME_NOREPLACE (1 << 0) -#endif - -#ifndef KCMP_FILE -#define KCMP_FILE 0 -#endif - -#ifndef INPUT_PROP_POINTING_STICK -#define INPUT_PROP_POINTING_STICK 0x05 -#endif - -#ifndef INPUT_PROP_ACCELEROMETER -#define INPUT_PROP_ACCELEROMETER 0x06 -#endif - -#ifndef HAVE_KEY_SERIAL_T -typedef int32_t key_serial_t; -#endif - -#ifndef KEYCTL_READ -#define KEYCTL_READ 11 -#endif - -#ifndef KEYCTL_SET_TIMEOUT -#define KEYCTL_SET_TIMEOUT 15 -#endif - -#ifndef KEY_SPEC_USER_KEYRING -#define KEY_SPEC_USER_KEYRING -4 -#endif - -#ifndef PR_CAP_AMBIENT -#define PR_CAP_AMBIENT 47 -#endif - -#ifndef PR_CAP_AMBIENT_IS_SET -#define PR_CAP_AMBIENT_IS_SET 1 -#endif - -#ifndef PR_CAP_AMBIENT_RAISE -#define PR_CAP_AMBIENT_RAISE 2 -#endif - -#ifndef PR_CAP_AMBIENT_CLEAR_ALL -#define PR_CAP_AMBIENT_CLEAR_ALL 4 -#endif - -/* The following two defines are actually available in the kernel headers for longer, but we define them here anyway, - * since that makes it easier to use them in conjunction with the glibc net/if.h header which conflicts with - * linux/if.h. */ -#ifndef IF_OPER_UNKNOWN -#define IF_OPER_UNKNOWN 0 -#endif - -#ifndef IF_OPER_UP -#define IF_OPER_UP 6 - -#ifndef HAVE_CHAR32_T -#define char32_t uint32_t -#endif - -#ifndef HAVE_CHAR16_T -#define char16_t uint16_t -#endif - -#ifndef ETHERTYPE_LLDP -#define ETHERTYPE_LLDP 0x88cc -#endif - -#ifndef IFA_F_MCAUTOJOIN -#define IFA_F_MCAUTOJOIN 0x400 -#endif - -#endif - -#include "missing_syscall.h" diff --git a/src/basic/missing_syscall.h b/src/basic/missing_syscall.h deleted file mode 100644 index e6fd67cb9d..0000000000 --- a/src/basic/missing_syscall.h +++ /dev/null @@ -1,300 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2016 Zbigniew Jędrzejewski-Szmek - - 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/>. -***/ - -/* Missing glibc definitions to access certain kernel APIs */ - -#if !HAVE_DECL_PIVOT_ROOT -static inline int pivot_root(const char *new_root, const char *put_old) { - return syscall(SYS_pivot_root, new_root, put_old); -} -#endif - -/* ======================================================================= */ - -#if !HAVE_DECL_MEMFD_CREATE -# ifndef __NR_memfd_create -# if defined __x86_64__ -# define __NR_memfd_create 319 -# elif defined __arm__ -# define __NR_memfd_create 385 -# elif defined __aarch64__ -# define __NR_memfd_create 279 -# elif defined __s390__ -# define __NR_memfd_create 350 -# elif defined _MIPS_SIM -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define __NR_memfd_create 4354 -# endif -# if _MIPS_SIM == _MIPS_SIM_NABI32 -# define __NR_memfd_create 6318 -# endif -# if _MIPS_SIM == _MIPS_SIM_ABI64 -# define __NR_memfd_create 5314 -# endif -# elif defined __i386__ -# define __NR_memfd_create 356 -# else -# warning "__NR_memfd_create unknown for your architecture" -# endif -# endif - -static inline int memfd_create(const char *name, unsigned int flags) { -# ifdef __NR_memfd_create - return syscall(__NR_memfd_create, name, flags); -# else - errno = ENOSYS; - return -1; -# endif -} -#endif - -/* ======================================================================= */ - -#if !HAVE_DECL_GETRANDOM -# ifndef __NR_getrandom -# if defined __x86_64__ -# define __NR_getrandom 318 -# elif defined(__i386__) -# define __NR_getrandom 355 -# elif defined(__arm__) -# define __NR_getrandom 384 -# elif defined(__aarch64__) -# define __NR_getrandom 278 -# elif defined(__ia64__) -# define __NR_getrandom 1339 -# elif defined(__m68k__) -# define __NR_getrandom 352 -# elif defined(__s390x__) -# define __NR_getrandom 349 -# elif defined(__powerpc__) -# define __NR_getrandom 359 -# elif defined _MIPS_SIM -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define __NR_getrandom 4353 -# endif -# if _MIPS_SIM == _MIPS_SIM_NABI32 -# define __NR_getrandom 6317 -# endif -# if _MIPS_SIM == _MIPS_SIM_ABI64 -# define __NR_getrandom 5313 -# endif -# else -# warning "__NR_getrandom unknown for your architecture" -# endif -# endif - -static inline int getrandom(void *buffer, size_t count, unsigned flags) { -# ifdef __NR_getrandom - return syscall(__NR_getrandom, buffer, count, flags); -# else - errno = ENOSYS; - return -1; -# endif -} -#endif - -/* ======================================================================= */ - -#if !HAVE_DECL_GETTID -static inline pid_t gettid(void) { - return (pid_t) syscall(SYS_gettid); -} -#endif - -/* ======================================================================= */ - -#if !HAVE_DECL_NAME_TO_HANDLE_AT -# ifndef __NR_name_to_handle_at -# if defined(__x86_64__) -# define __NR_name_to_handle_at 303 -# elif defined(__i386__) -# define __NR_name_to_handle_at 341 -# elif defined(__arm__) -# define __NR_name_to_handle_at 370 -# elif defined(__powerpc__) -# define __NR_name_to_handle_at 345 -# else -# error "__NR_name_to_handle_at is not defined" -# endif -# endif - -struct file_handle { - unsigned int handle_bytes; - int handle_type; - unsigned char f_handle[0]; -}; - -static inline int name_to_handle_at(int fd, const char *name, struct file_handle *handle, int *mnt_id, int flags) { -# ifdef __NR_name_to_handle_at - return syscall(__NR_name_to_handle_at, fd, name, handle, mnt_id, flags); -# else - errno = ENOSYS; - return -1; -# endif -} -#endif - -/* ======================================================================= */ - -#if !HAVE_DECL_SETNS -# ifndef __NR_setns -# if defined(__x86_64__) -# define __NR_setns 308 -# elif defined(__i386__) -# define __NR_setns 346 -# else -# error "__NR_setns is not defined" -# endif -# endif - -static inline int setns(int fd, int nstype) { -# ifdef __NR_setns - return syscall(__NR_setns, fd, nstype); -# else - errno = ENOSYS; - return -1; -# endif -} -#endif - -/* ======================================================================= */ - -static inline pid_t raw_getpid(void) { -#if defined(__alpha__) - return (pid_t) syscall(__NR_getxpid); -#else - return (pid_t) syscall(__NR_getpid); -#endif -} - -/* ======================================================================= */ - -#if !HAVE_DECL_RENAMEAT2 -# ifndef __NR_renameat2 -# if defined __x86_64__ -# define __NR_renameat2 316 -# elif defined __arm__ -# define __NR_renameat2 382 -# elif defined _MIPS_SIM -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define __NR_renameat2 4351 -# endif -# if _MIPS_SIM == _MIPS_SIM_NABI32 -# define __NR_renameat2 6315 -# endif -# if _MIPS_SIM == _MIPS_SIM_ABI64 -# define __NR_renameat2 5311 -# endif -# elif defined __i386__ -# define __NR_renameat2 353 -# else -# warning "__NR_renameat2 unknown for your architecture" -# endif -# endif - -static inline int renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) { -# ifdef __NR_renameat2 - return syscall(__NR_renameat2, oldfd, oldname, newfd, newname, flags); -# else - errno = ENOSYS; - return -1; -# endif -} -#endif - -/* ======================================================================= */ - -#if !HAVE_DECL_KCMP -static inline int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) { -# ifdef __NR_kcmp - return syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2); -# else - errno = ENOSYS; - return -1; -# endif -} -#endif - -/* ======================================================================= */ - -#if !HAVE_DECL_KEYCTL -static inline long keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4,unsigned long arg5) { -# ifdef __NR_keyctl - return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5); -# else - errno = ENOSYS; - return -1; -# endif -} - -static inline key_serial_t add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) { -# ifdef __NR_add_key - return syscall(__NR_add_key, type, description, payload, plen, ringid); -# else - errno = ENOSYS; - return -1; -# endif -} - -static inline key_serial_t request_key(const char *type, const char *description, const char * callout_info, key_serial_t destringid) { -# ifdef __NR_request_key - return syscall(__NR_request_key, type, description, callout_info, destringid); -# else - errno = ENOSYS; - return -1; -# endif -} -#endif - -/* ======================================================================= */ - -#if !HAVE_DECL_COPY_FILE_RANGE -# ifndef __NR_copy_file_range -# if defined(__x86_64__) -# define __NR_copy_file_range 326 -# elif defined(__i386__) -# define __NR_copy_file_range 377 -# elif defined __s390__ -# define __NR_copy_file_range 375 -# elif defined __arm__ -# define __NR_copy_file_range 391 -# elif defined __aarch64__ -# define __NR_copy_file_range 285 -# elif defined __powerpc__ -# define __NR_copy_file_range 379 -# else -# warning "__NR_copy_file_range not defined for your architecture" -# endif -# endif - -static inline ssize_t copy_file_range(int fd_in, loff_t *off_in, - int fd_out, loff_t *off_out, - size_t len, - unsigned int flags) { -# ifdef __NR_copy_file_range - return syscall(__NR_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags); -# else - errno = ENOSYS; - return -1; -# endif -} -#endif diff --git a/src/basic/mkdir-label.c b/src/basic/mkdir-label.c deleted file mode 100644 index aa6878cdf0..0000000000 --- a/src/basic/mkdir-label.c +++ /dev/null @@ -1,38 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2013 Kay Sievers - - 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 <stdio.h> -#include <sys/types.h> -#include <unistd.h> - -#include "label.h" -#include "mkdir.h" - -int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid) { - return mkdir_safe_internal(path, mode, uid, gid, mkdir_label); -} - -int mkdir_parents_label(const char *path, mode_t mode) { - return mkdir_parents_internal(NULL, path, mode, mkdir_label); -} - -int mkdir_p_label(const char *path, mode_t mode) { - return mkdir_p_internal(NULL, path, mode, mkdir_label); -} diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c deleted file mode 100644 index 6b1a98402c..0000000000 --- a/src/basic/mkdir.c +++ /dev/null @@ -1,128 +0,0 @@ -/*** - 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 <errno.h> -#include <stdbool.h> -#include <string.h> -#include <sys/stat.h> - -#include "fs-util.h" -#include "macro.h" -#include "mkdir.h" -#include "path-util.h" -#include "stat-util.h" -#include "user-util.h" - -int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkdir_func_t _mkdir) { - struct stat st; - - if (_mkdir(path, mode) >= 0) - if (chmod_and_chown(path, mode, uid, gid) < 0) - return -errno; - - if (lstat(path, &st) < 0) - return -errno; - - if ((st.st_mode & 0007) > (mode & 0007) || - (st.st_mode & 0070) > (mode & 0070) || - (st.st_mode & 0700) > (mode & 0700) || - (uid != UID_INVALID && st.st_uid != uid) || - (gid != GID_INVALID && st.st_gid != gid) || - !S_ISDIR(st.st_mode)) - return -EEXIST; - - return 0; -} - -int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid) { - return mkdir_safe_internal(path, mode, uid, gid, mkdir); -} - -int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) { - const char *p, *e; - int r; - - assert(path); - - if (prefix && !path_startswith(path, prefix)) - return -ENOTDIR; - - /* return immediately if directory exists */ - e = strrchr(path, '/'); - if (!e) - return -EINVAL; - - if (e == path) - return 0; - - p = strndupa(path, e - path); - r = is_dir(p, true); - if (r > 0) - return 0; - if (r == 0) - return -ENOTDIR; - - /* create every parent directory in the path, except the last component */ - p = path + strspn(path, "/"); - for (;;) { - char t[strlen(path) + 1]; - - e = p + strcspn(p, "/"); - p = e + strspn(e, "/"); - - /* Is this the last component? If so, then we're - * done */ - if (*p == 0) - return 0; - - memcpy(t, path, e - path); - t[e-path] = 0; - - if (prefix && path_startswith(prefix, t)) - continue; - - r = _mkdir(t, mode); - if (r < 0 && errno != EEXIST) - return -errno; - } -} - -int mkdir_parents(const char *path, mode_t mode) { - return mkdir_parents_internal(NULL, path, mode, mkdir); -} - -int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) { - int r; - - /* Like mkdir -p */ - - r = mkdir_parents_internal(prefix, path, mode, _mkdir); - if (r < 0) - return r; - - r = _mkdir(path, mode); - if (r < 0 && (errno != EEXIST || is_dir(path, true) <= 0)) - return -errno; - - return 0; -} - -int mkdir_p(const char *path, mode_t mode) { - return mkdir_p_internal(NULL, path, mode, mkdir); -} diff --git a/src/basic/mkdir.h b/src/basic/mkdir.h deleted file mode 100644 index d564a3547f..0000000000 --- a/src/basic/mkdir.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2013 Kay Sievers - - 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 <sys/types.h> - -int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid); -int mkdir_parents(const char *path, mode_t mode); -int mkdir_p(const char *path, mode_t mode); - -/* mandatory access control(MAC) versions */ -int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid); -int mkdir_parents_label(const char *path, mode_t mode); -int mkdir_p_label(const char *path, mode_t mode); - -/* internally used */ -typedef int (*mkdir_func_t)(const char *pathname, mode_t mode); -int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkdir_func_t _mkdir); -int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir); -int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir); diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c deleted file mode 100644 index c8f8022578..0000000000 --- a/src/basic/mount-util.c +++ /dev/null @@ -1,689 +0,0 @@ -/*** - 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 <errno.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mount.h> -#include <sys/stat.h> -#include <sys/statvfs.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "escape.h" -#include "fd-util.h" -#include "fileio.h" -#include "hashmap.h" -#include "mount-util.h" -#include "parse-util.h" -#include "path-util.h" -#include "set.h" -#include "stdio-util.h" -#include "string-util.h" -#include "strv.h" - -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_CLOEXEC|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; - - 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 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; - } - - 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 - 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 (IN_SET(r, -EOPNOTSUPP, -EACCES)) - 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; - - 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; - - t = canonical; - } - - parent = dirname_malloc(t); - if (!parent) - return -ENOMEM; - - fd = openat(AT_FDCWD, parent, O_DIRECTORY|O_CLOEXEC|O_PATH); - if (fd < 0) - return -errno; - - return fd_is_mount_point(fd, basename(t), flags); -} - -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 = log_debug_errno(errno, "Failed to umount %s: %m", p); - continue; - } - - log_debug("Successfully unmounted %s", p); - - 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, char **blacklist) { - _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. - * - * If the "blacklist" parameter is specified it may contain a list of subtrees to exclude from the - * remount operation. Note that we'll ignore the blacklist for the top-level path. */ - - 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; - - if (!path_startswith(p, cleaned)) - continue; - - /* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount we shall - * operate on. */ - if (!path_equal(cleaned, p)) { - bool blacklisted = false; - char **i; - - STRV_FOREACH(i, blacklist) { - - if (path_equal(*i, cleaned)) - continue; - - if (!path_startswith(*i, cleaned)) - continue; - - if (path_startswith(p, *i)) { - blacklisted = true; - log_debug("Not remounting %s, because blacklisted by %s, called for %s", p, *i, cleaned); - break; - } - } - if (blacklisted) - continue; - } - - /* 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 (!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; - - log_debug("Made top-level directory %s a mount point.", prefix); - - 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; - - /* Deal with mount points that are obstructed by a later mount */ - r = path_is_mount_point(x, 0); - if (r == -ENOENT || r == 0) - continue; - if (r < 0) - return r; - - /* Try to reuse the original flag set */ - 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) - return -errno; - - log_debug("Remounted %s read-only.", x); - } - } -} - -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; -} - -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" - "pvfs2\0" /* OrangeFS */ - "ocfs2\0" - "lustre\0" - ; - - const char *x; - - x = startswith(fstype, "fuse."); - if (x) - fstype = x; - - return nulstr_contains(table, fstype); -} - -int repeat_unmount(const char *path, int flags) { - bool done = false; - - assert(path); - - /* If there are multiple mounts on a mount point, this - * removes them all */ - - for (;;) { - if (umount2(path, flags) < 0) { - - if (errno == EINVAL) - return done; - - return -errno; - } - - done = true; - } -} - -const char* mode_to_inaccessible_node(mode_t mode) { - /* This function maps a node type to the correspondent inaccessible node type. - * Character and block inaccessible devices may not be created (because major=0 and minor=0), - * in such case we map character and block devices to the inaccessible node type socket. */ - switch(mode & S_IFMT) { - case S_IFREG: - return "/run/systemd/inaccessible/reg"; - case S_IFDIR: - return "/run/systemd/inaccessible/dir"; - case S_IFCHR: - if (access("/run/systemd/inaccessible/chr", F_OK) == 0) - return "/run/systemd/inaccessible/chr"; - return "/run/systemd/inaccessible/sock"; - case S_IFBLK: - if (access("/run/systemd/inaccessible/blk", F_OK) == 0) - return "/run/systemd/inaccessible/blk"; - return "/run/systemd/inaccessible/sock"; - case S_IFIFO: - return "/run/systemd/inaccessible/fifo"; - case S_IFSOCK: - return "/run/systemd/inaccessible/sock"; - } - return NULL; -} - -#define FLAG(name) (flags & name ? STRINGIFY(name) "|" : "") -static char* mount_flags_to_string(long unsigned flags) { - char *x; - _cleanup_free_ char *y = NULL; - long unsigned overflow; - - overflow = flags & ~(MS_RDONLY | - MS_NOSUID | - MS_NODEV | - MS_NOEXEC | - MS_SYNCHRONOUS | - MS_REMOUNT | - MS_MANDLOCK | - MS_DIRSYNC | - MS_NOATIME | - MS_NODIRATIME | - MS_BIND | - MS_MOVE | - MS_REC | - MS_SILENT | - MS_POSIXACL | - MS_UNBINDABLE | - MS_PRIVATE | - MS_SLAVE | - MS_SHARED | - MS_RELATIME | - MS_KERNMOUNT | - MS_I_VERSION | - MS_STRICTATIME | - MS_LAZYTIME); - - if (flags == 0 || overflow != 0) - if (asprintf(&y, "%lx", overflow) < 0) - return NULL; - - x = strjoin(FLAG(MS_RDONLY), - FLAG(MS_NOSUID), - FLAG(MS_NODEV), - FLAG(MS_NOEXEC), - FLAG(MS_SYNCHRONOUS), - FLAG(MS_REMOUNT), - FLAG(MS_MANDLOCK), - FLAG(MS_DIRSYNC), - FLAG(MS_NOATIME), - FLAG(MS_NODIRATIME), - FLAG(MS_BIND), - FLAG(MS_MOVE), - FLAG(MS_REC), - FLAG(MS_SILENT), - FLAG(MS_POSIXACL), - FLAG(MS_UNBINDABLE), - FLAG(MS_PRIVATE), - FLAG(MS_SLAVE), - FLAG(MS_SHARED), - FLAG(MS_RELATIME), - FLAG(MS_KERNMOUNT), - FLAG(MS_I_VERSION), - FLAG(MS_STRICTATIME), - FLAG(MS_LAZYTIME), - y, NULL); - if (!x) - return NULL; - if (!y) - x[strlen(x) - 1] = '\0'; /* truncate the last | */ - return x; -} - -int mount_verbose( - int error_log_level, - const char *what, - const char *where, - const char *type, - unsigned long flags, - const char *options) { - - _cleanup_free_ char *fl = NULL; - - fl = mount_flags_to_string(flags); - - if ((flags & MS_REMOUNT) && !what && !type) - log_debug("Remounting %s (%s \"%s\")...", - where, strnull(fl), strempty(options)); - else if (!what && !type) - log_debug("Mounting %s (%s \"%s\")...", - where, strnull(fl), strempty(options)); - else if ((flags & MS_BIND) && !type) - log_debug("Bind-mounting %s on %s (%s \"%s\")...", - what, where, strnull(fl), strempty(options)); - else - log_debug("Mounting %s on %s (%s \"%s\")...", - strna(type), where, strnull(fl), strempty(options)); - if (mount(what, where, type, flags, options) < 0) - return log_full_errno(error_log_level, errno, - "Failed to mount %s on %s (%s \"%s\"): %m", - strna(type), where, strnull(fl), strempty(options)); - return 0; -} - -int umount_verbose(const char *what) { - log_debug("Umounting %s...", what); - if (umount(what) < 0) - return log_error_errno(errno, "Failed to unmount %s: %m", what); - return 0; -} diff --git a/src/basic/mount-util.h b/src/basic/mount-util.h deleted file mode 100644 index 4f305df19f..0000000000 --- a/src/basic/mount-util.h +++ /dev/null @@ -1,63 +0,0 @@ -#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 <mntent.h> -#include <stdbool.h> -#include <stdio.h> -#include <sys/stat.h> -#include <sys/types.h> - -#include "macro.h" -#include "missing.h" - -int fd_is_mount_point(int fd, const char *filename, int flags); -int path_is_mount_point(const char *path, int flags); - -int repeat_unmount(const char *path, int flags); - -int umount_recursive(const char *target, int flags); -int bind_remount_recursive(const char *prefix, bool ro, char **blacklist); - -int mount_move_root(const char *path); - -DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, endmntent); -#define _cleanup_endmntent_ _cleanup_(endmntentp) - -bool fstype_is_network(const char *fstype); - -union file_handle_union { - struct file_handle handle; - char padding[sizeof(struct file_handle) + MAX_HANDLE_SZ]; -}; - -const char* mode_to_inaccessible_node(mode_t mode); - -#define FILE_HANDLE_INIT { .handle.handle_bytes = MAX_HANDLE_SZ } - -int mount_verbose( - int error_log_level, - const char *what, - const char *where, - const char *type, - unsigned long flags, - const char *options); -int umount_verbose(const char *where); diff --git a/src/basic/nss-util.h b/src/basic/nss-util.h deleted file mode 100644 index e7844fff96..0000000000 --- a/src/basic/nss-util.h +++ /dev/null @@ -1,199 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 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 <grp.h> -#include <netdb.h> -#include <nss.h> -#include <pwd.h> -#include <resolv.h> - -#define NSS_SIGNALS_BLOCK SIGALRM,SIGVTALRM,SIGPIPE,SIGCHLD,SIGTSTP,SIGIO,SIGHUP,SIGUSR1,SIGUSR2,SIGPROF,SIGURG,SIGWINCH - -#define NSS_GETHOSTBYNAME_PROTOTYPES(module) \ -enum nss_status _nss_##module##_gethostbyname4_r( \ - const char *name, \ - struct gaih_addrtuple **pat, \ - char *buffer, size_t buflen, \ - int *errnop, int *h_errnop, \ - int32_t *ttlp) _public_; \ -enum nss_status _nss_##module##_gethostbyname3_r( \ - const char *name, \ - int af, \ - struct hostent *host, \ - char *buffer, size_t buflen, \ - int *errnop, int *h_errnop, \ - int32_t *ttlp, \ - char **canonp) _public_; \ -enum nss_status _nss_##module##_gethostbyname2_r( \ - const char *name, \ - int af, \ - struct hostent *host, \ - char *buffer, size_t buflen, \ - int *errnop, int *h_errnop) _public_; \ -enum nss_status _nss_##module##_gethostbyname_r( \ - const char *name, \ - struct hostent *host, \ - char *buffer, size_t buflen, \ - int *errnop, int *h_errnop) _public_ - -#define NSS_GETHOSTBYADDR_PROTOTYPES(module) \ -enum nss_status _nss_##module##_gethostbyaddr2_r( \ - const void* addr, socklen_t len, \ - int af, \ - struct hostent *host, \ - char *buffer, size_t buflen, \ - int *errnop, int *h_errnop, \ - int32_t *ttlp) _public_; \ -enum nss_status _nss_##module##_gethostbyaddr_r( \ - const void* addr, socklen_t len, \ - int af, \ - struct hostent *host, \ - char *buffer, size_t buflen, \ - int *errnop, int *h_errnop) _public_ - -#define NSS_GETHOSTBYNAME_FALLBACKS(module) \ -enum nss_status _nss_##module##_gethostbyname2_r( \ - const char *name, \ - int af, \ - struct hostent *host, \ - char *buffer, size_t buflen, \ - int *errnop, int *h_errnop) { \ - return _nss_##module##_gethostbyname3_r( \ - name, \ - af, \ - host, \ - buffer, buflen, \ - errnop, h_errnop, \ - NULL, \ - NULL); \ -} \ -enum nss_status _nss_##module##_gethostbyname_r( \ - const char *name, \ - struct hostent *host, \ - char *buffer, size_t buflen, \ - int *errnop, int *h_errnop) { \ - enum nss_status ret = NSS_STATUS_NOTFOUND; \ - \ - if (_res.options & RES_USE_INET6) \ - ret = _nss_##module##_gethostbyname3_r( \ - name, \ - AF_INET6, \ - host, \ - buffer, buflen, \ - errnop, h_errnop, \ - NULL, \ - NULL); \ - if (ret == NSS_STATUS_NOTFOUND) \ - ret = _nss_##module##_gethostbyname3_r( \ - name, \ - AF_INET, \ - host, \ - buffer, buflen, \ - errnop, h_errnop, \ - NULL, \ - NULL); \ - return ret; \ -} \ -struct __useless_struct_to_allow_trailing_semicolon__ - -#define NSS_GETHOSTBYADDR_FALLBACKS(module) \ -enum nss_status _nss_##module##_gethostbyaddr_r( \ - const void* addr, socklen_t len, \ - int af, \ - struct hostent *host, \ - char *buffer, size_t buflen, \ - int *errnop, int *h_errnop) { \ - return _nss_##module##_gethostbyaddr2_r( \ - addr, len, \ - af, \ - host, \ - buffer, buflen, \ - errnop, h_errnop, \ - NULL); \ -} \ -struct __useless_struct_to_allow_trailing_semicolon__ - -#define NSS_GETPW_PROTOTYPES(module) \ -enum nss_status _nss_##module##_getpwnam_r( \ - const char *name, \ - struct passwd *pwd, \ - char *buffer, size_t buflen, \ - int *errnop) _public_; \ -enum nss_status _nss_##module##_getpwuid_r( \ - uid_t uid, \ - struct passwd *pwd, \ - char *buffer, size_t buflen, \ - int *errnop) _public_ - -#define NSS_GETGR_PROTOTYPES(module) \ -enum nss_status _nss_##module##_getgrnam_r( \ - const char *name, \ - struct group *gr, \ - char *buffer, size_t buflen, \ - int *errnop) _public_; \ -enum nss_status _nss_##module##_getgrgid_r( \ - gid_t gid, \ - struct group *gr, \ - char *buffer, size_t buflen, \ - int *errnop) _public_ - -typedef enum nss_status (*_nss_gethostbyname4_r_t)( - const char *name, - struct gaih_addrtuple **pat, - char *buffer, size_t buflen, - int *errnop, int *h_errnop, - int32_t *ttlp); - -typedef enum nss_status (*_nss_gethostbyname3_r_t)( - const char *name, - int af, - struct hostent *result, - char *buffer, size_t buflen, - int *errnop, int *h_errnop, - int32_t *ttlp, - char **canonp); - -typedef enum nss_status (*_nss_gethostbyname2_r_t)( - const char *name, - int af, - struct hostent *result, - char *buffer, size_t buflen, - int *errnop, int *h_errnop); - -typedef enum nss_status (*_nss_gethostbyname_r_t)( - const char *name, - struct hostent *result, - char *buffer, size_t buflen, - int *errnop, int *h_errnop); - -typedef enum nss_status (*_nss_gethostbyaddr2_r_t)( - const void* addr, socklen_t len, - int af, - struct hostent *result, - char *buffer, size_t buflen, - int *errnop, int *h_errnop, - int32_t *ttlp); -typedef enum nss_status (*_nss_gethostbyaddr_r_t)( - const void* addr, socklen_t len, - int af, - struct hostent *host, - char *buffer, size_t buflen, - int *errnop, int *h_errnop); diff --git a/src/basic/ordered-set.c b/src/basic/ordered-set.c deleted file mode 100644 index 2e0bdf6488..0000000000 --- a/src/basic/ordered-set.c +++ /dev/null @@ -1,64 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2016 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 "ordered-set.h" -#include "strv.h" - -int ordered_set_consume(OrderedSet *s, void *p) { - int r; - - r = ordered_set_put(s, p); - if (r <= 0) - free(p); - - return r; -} - -int ordered_set_put_strdup(OrderedSet *s, const char *p) { - char *c; - int r; - - assert(s); - assert(p); - - c = strdup(p); - if (!c) - return -ENOMEM; - - r = ordered_set_consume(s, c); - if (r == -EEXIST) - return 0; - - return r; -} - -int ordered_set_put_strdupv(OrderedSet *s, char **l) { - int n = 0, r; - char **i; - - STRV_FOREACH(i, l) { - r = ordered_set_put_strdup(s, *i); - if (r < 0) - return r; - - n += r; - } - - return n; -} diff --git a/src/basic/ordered-set.h b/src/basic/ordered-set.h deleted file mode 100644 index e1dfc86380..0000000000 --- a/src/basic/ordered-set.h +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2015 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 "hashmap.h" - -typedef struct OrderedSet OrderedSet; - -static inline OrderedSet* ordered_set_new(const struct hash_ops *ops) { - return (OrderedSet*) ordered_hashmap_new(ops); -} - -static inline int ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops) { - if (*s) - return 0; - - *s = ordered_set_new(ops); - if (!*s) - return -ENOMEM; - - return 0; -} - -static inline OrderedSet* ordered_set_free(OrderedSet *s) { - ordered_hashmap_free((OrderedHashmap*) s); - return NULL; -} - -static inline OrderedSet* ordered_set_free_free(OrderedSet *s) { - ordered_hashmap_free_free((OrderedHashmap*) s); - return NULL; -} - -static inline int ordered_set_put(OrderedSet *s, void *p) { - return ordered_hashmap_put((OrderedHashmap*) s, p, p); -} - -static inline bool ordered_set_isempty(OrderedSet *s) { - return ordered_hashmap_isempty((OrderedHashmap*) s); -} - -static inline bool ordered_set_iterate(OrderedSet *s, Iterator *i, void **value) { - return ordered_hashmap_iterate((OrderedHashmap*) s, i, value, NULL); -} - -int ordered_set_consume(OrderedSet *s, void *p); -int ordered_set_put_strdup(OrderedSet *s, const char *p); -int ordered_set_put_strdupv(OrderedSet *s, char **l); - -#define ORDERED_SET_FOREACH(e, s, i) \ - for ((i) = ITERATOR_FIRST; ordered_set_iterate((s), &(i), (void**)&(e)); ) - -DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedSet*, ordered_set_free_free); - -#define _cleanup_ordered_set_free_ _cleanup_(ordered_set_freep) -#define _cleanup_ordered_set_free_free_ _cleanup_(ordered_set_free_freep) diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c deleted file mode 100644 index c98815b9bc..0000000000 --- a/src/basic/parse-util.c +++ /dev/null @@ -1,576 +0,0 @@ -/*** - 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 <errno.h> -#include <inttypes.h> -#include <locale.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <xlocale.h> - -#include "alloc-util.h" -#include "extract-word.h" -#include "macro.h" -#include "parse-util.h" -#include "process-util.h" -#include "string-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; -} - -int parse_percent_unbounded(const char *p) { - const char *pc, *n; - unsigned v; - int r; - - pc = endswith(p, "%"); - if (!pc) - return -EINVAL; - - n = strndupa(p, pc - p); - r = safe_atou(n, &v); - if (r < 0) - return r; - - return (int) v; -} - -int parse_percent(const char *p) { - int v; - - v = parse_percent_unbounded(p); - if (v > 100) - return -ERANGE; - - return v; -} - -int parse_nice(const char *p, int *ret) { - int n, r; - - r = safe_atoi(p, &n); - if (r < 0) - return r; - - if (!nice_is_valid(n)) - return -ERANGE; - - *ret = n; - return 0; -} diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h deleted file mode 100644 index 461e1cd4d8..0000000000 --- a/src/basic/parse-util.h +++ /dev/null @@ -1,112 +0,0 @@ -#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 <limits.h> -#include <stddef.h> -#include <stdint.h> -#include <sys/types.h> - -#include "macro.h" - -#define MODE_INVALID ((mode_t) -1) - -int parse_boolean(const char *v) _pure_; -int parse_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 - -#if SIZE_MAX == UINT_MAX -static inline int safe_atozu(const char *s, size_t *ret_u) { - assert_cc(sizeof(size_t) == sizeof(unsigned)); - return safe_atou(s, (unsigned *) ret_u); -} -#else -static inline int safe_atozu(const char *s, size_t *ret_u) { - assert_cc(sizeof(size_t) == sizeof(long unsigned)); - return safe_atolu(s, ret_u); -} -#endif - -int safe_atod(const char *s, double *ret_d); - -int parse_fractional_part_u(const char **s, size_t digits, unsigned *res); - -int parse_percent_unbounded(const char *p); -int parse_percent(const char *p); - -int parse_nice(const char *p, int *ret); diff --git a/src/basic/path-util.c b/src/basic/path-util.c deleted file mode 100644 index fd38f51c4c..0000000000 --- a/src/basic/path-util.c +++ /dev/null @@ -1,897 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010-2012 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 <errno.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <unistd.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 "extract-word.h" -#include "fs-util.h" -#include "glob-util.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "path-util.h" -#include "stat-util.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" - -bool path_is_absolute(const char *p) { - return p[0] == '/'; -} - -bool is_path(const char *p) { - return !!strchr(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 -ENOMEM; - - r = path_strv_make_absolute_cwd(l); - if (r < 0) { - strv_free(l); - return r; - } - - *ret = l; - return r; -} - -char *path_make_absolute(const char *p, const char *prefix) { - assert(p); - - /* Makes every item in the list an absolute path by prepending - * the prefix, if specified and necessary */ - - if (path_is_absolute(p) || !prefix) - return strdup(p); - - return strjoin(prefix, "/", p, 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)) - c = strdup(p); - else { - _cleanup_free_ char *cwd = NULL; - - cwd = get_current_dir_name(); - if (!cwd) - return negative_errno(); - - c = strjoin(cwd, "/", p, NULL); - } - if (!c) - return -ENOMEM; - - *ret = c; - return 0; -} - -int path_make_relative(const char *from_dir, const char *to_path, char **_r) { - char *r, *p; - unsigned n_parents; - - assert(from_dir); - assert(to_path); - assert(_r); - - /* Strips the common part, and adds ".." elements as necessary. */ - - if (!path_is_absolute(from_dir)) - return -EINVAL; - - if (!path_is_absolute(to_path)) - return -EINVAL; - - /* Skip the common part. */ - for (;;) { - size_t a; - size_t b; - - from_dir += strspn(from_dir, "/"); - to_path += strspn(to_path, "/"); - - if (!*from_dir) { - if (!*to_path) - /* from_dir equals to_path. */ - r = strdup("."); - else - /* from_dir is a parent directory of to_path. */ - r = strdup(to_path); - - if (!r) - return -ENOMEM; - - path_kill_slashes(r); - - *_r = r; - return 0; - } - - if (!*to_path) - break; - - a = strcspn(from_dir, "/"); - b = strcspn(to_path, "/"); - - if (a != b) - break; - - if (memcmp(from_dir, to_path, a) != 0) - break; - - from_dir += a; - to_path += b; - } - - /* If we're here, then "from_dir" has one or more elements that need to - * be replaced with "..". */ - - /* Count the number of necessary ".." elements. */ - for (n_parents = 0;;) { - from_dir += strspn(from_dir, "/"); - - if (!*from_dir) - break; - - from_dir += strcspn(from_dir, "/"); - n_parents++; - } - - r = malloc(n_parents * 3 + strlen(to_path) + 1); - if (!r) - return -ENOMEM; - - for (p = r; n_parents > 0; n_parents--, p += 3) - memcpy(p, "../", 3); - - strcpy(p, to_path); - path_kill_slashes(r); - - *_r = r; - return 0; -} - -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 - * changes on failure. */ - - STRV_FOREACH(s, l) { - char *t; - - r = path_make_absolute_cwd(*s, &t); - if (r < 0) - return r; - - free(*s); - *s = t; - } - - return 0; -} - -char **path_strv_resolve(char **l, const char *prefix) { - char **s; - unsigned k = 0; - bool enomem = false; - - if (strv_isempty(l)) - return l; - - /* Goes through every item in the string list and canonicalize - * the path. This works in place and won't rollback any - * changes on failure. */ - - STRV_FOREACH(s, l) { - char *t, *u; - _cleanup_free_ char *orig = NULL; - - if (!path_is_absolute(*s)) { - free(*s); - continue; - } - - if (prefix) { - orig = *s; - t = strappend(prefix, orig); - if (!t) { - enomem = true; - continue; - } - } else - t = *s; - - errno = 0; - u = canonicalize_file_name(t); - if (!u) { - if (errno == ENOENT) { - if (prefix) { - u = orig; - orig = NULL; - free(t); - } else - u = t; - } else { - free(t); - if (errno == ENOMEM || errno == 0) - enomem = true; - - continue; - } - } else if (prefix) { - char *x; - - free(t); - x = path_startswith(u, prefix); - if (x) { - /* restore the slash if it was lost */ - if (!startswith(x, "/")) - *(--x) = '/'; - - t = strdup(x); - free(u); - if (!t) { - enomem = true; - continue; - } - u = t; - } else { - /* canonicalized path goes outside of - * prefix, keep the original path instead */ - free_and_replace(u, orig); - } - } else - free(t); - - l[k++] = u; - } - - l[k] = NULL; - - if (enomem) - return NULL; - - return l; -} - -char **path_strv_resolve_uniq(char **l, const char *prefix) { - - if (strv_isempty(l)) - return l; - - if (!path_strv_resolve(l, prefix)) - return NULL; - - return strv_uniq(l); -} - -char *path_kill_slashes(char *path) { - char *f, *t; - bool slash = false; - - /* Removes redundant inner and trailing slashes. Modifies the - * passed string in-place. - * - * ///foo///bar/ becomes /foo/bar - */ - - for (f = path, t = path; *f; f++) { - - if (*f == '/') { - slash = true; - continue; - } - - if (slash) { - slash = false; - *(t++) = '/'; - } - - *(t++) = *f; - } - - /* Special rule, if we are talking of the root directory, a - trailing slash is good */ - - if (t == path && slash) - *(t++) = '/'; - - *t = 0; - return path; -} - -char* path_startswith(const char *path, const char *prefix) { - assert(path); - assert(prefix); - - /* Returns a pointer to the start of the first component after the parts matched by - * the prefix, iff - * - both paths are absolute or both paths are relative, - * and - * - each component in prefix in turn matches a component in path at the same position. - * An empty string will be returned when the prefix and path are equivalent. - * - * Returns NULL otherwise. - */ - - if ((path[0] == '/') != (prefix[0] == '/')) - return NULL; - - for (;;) { - size_t a, b; - - path += strspn(path, "/"); - prefix += strspn(prefix, "/"); - - if (*prefix == 0) - return (char*) path; - - if (*path == 0) - return NULL; - - a = strcspn(path, "/"); - b = strcspn(prefix, "/"); - - if (a != b) - return NULL; - - if (memcmp(path, prefix, a) != 0) - return NULL; - - path += a; - prefix += b; - } -} - -int path_compare(const char *a, const char *b) { - int d; - - assert(a); - assert(b); - - /* A relative path and an abolute path must not compare as equal. - * 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 != 0) - return d; - - for (;;) { - size_t j, k; - - a += strspn(a, "/"); - b += strspn(b, "/"); - - if (*a == 0 && *b == 0) - return 0; - - /* Order prefixes first: "/foo" before "/foo/bar" */ - if (*a == 0) - return -1; - if (*b == 0) - return 1; - - j = strcspn(a, "/"); - k = strcspn(b, "/"); - - /* Alphabetical sort: "/foo/aaa" before "/foo/b" */ - d = memcmp(a, b, MIN(j, k)); - 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 != 0) - return d; - - a += j; - b += k; - } -} - -bool path_equal(const char *a, const char *b) { - return path_compare(a, b) == 0; -} - -bool path_equal_or_files_same(const char *a, const char *b) { - return path_equal(a, b) || files_same(a, b) > 0; -} - -char* path_join(const char *root, const char *path, const char *rest) { - assert(path); - - if (!isempty(root)) - return strjoin(root, endswith(root, "/") ? "" : "/", - path[0] == '/' ? path+1 : path, - rest ? (endswith(path, "/") ? "" : "/") : NULL, - rest && rest[0] == '/' ? rest+1 : rest, - NULL); - else - return strjoin(path, - rest ? (endswith(path, "/") ? "" : "/") : NULL, - rest && rest[0] == '/' ? rest+1 : rest, - NULL); -} - -int find_binary(const char *name, char **ret) { - int last_error, r; - const char *p; - - assert(name); - - if (is_path(name)) { - if (access(name, X_OK) < 0) - return -errno; - - if (ret) { - r = path_make_absolute_cwd(name, ret); - if (r < 0) - return r; - } - - return 0; - } - - /** - * 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; - - last_error = -ENOENT; - - for (;;) { - _cleanup_free_ char *j = NULL, *element = NULL; - - r = extract_first_word(&p, &element, ":", EXTRACT_RELAX|EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return r; - if (r == 0) - break; - - if (!path_is_absolute(element)) - continue; - - j = strjoin(element, "/", name, NULL); - if (!j) - return -ENOMEM; - - if (access(j, X_OK) >= 0) { - /* Found it! */ - - if (ret) { - *ret = path_kill_slashes(j); - j = NULL; - } - - return 0; - } - - last_error = -errno; - } - - return last_error; -} - -bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) { - bool changed = false; - const char* const* i; - - assert(timestamp); - - if (paths == NULL) - return false; - - STRV_FOREACH(i, paths) { - struct stat stats; - usec_t u; - - if (stat(*i, &stats) < 0) - continue; - - u = timespec_load(&stats.st_mtim); - - /* first check */ - if (*timestamp >= u) - continue; - - log_debug("timestamp of '%s' changed", *i); - - /* update timestamp */ - if (update) { - *timestamp = u; - changed = true; - } else - return true; - } - - return changed; -} - -static int binary_is_good(const char *binary) { - _cleanup_free_ char *p = NULL, *d = NULL; - int r; - - r = find_binary(binary, &p); - if (r == -ENOENT) - return 0; - if (r < 0) - return r; - - /* An fsck that is linked to /bin/true is a non-existent - * fsck */ - - r = readlink_malloc(p, &d); - if (r == -EINVAL) /* not a symlink */ - return 1; - if (r < 0) - return r; - - return !PATH_IN_SET(d, "true" - "/bin/true", - "/usr/bin/true", - "/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) { - char *n, *p; - size_t l; - - /* If root is passed, prefixes path with it. Otherwise returns - * it as is. */ - - assert(path); - - /* First, drop duplicate prefixing slashes from the path */ - while (path[0] == '/' && path[1] == '/') - path++; - - if (isempty(root) || path_equal(root, "/")) - return strdup(path); - - l = strlen(root) + 1 + strlen(path) + 1; - - n = new(char, l); - if (!n) - return NULL; - - p = stpcpy(n, root); - - while (p > n && p[-1] == '/') - p--; - - if (path[0] != '/') - *(p++) = '/'; - - strcpy(p, path); - return n; -} - -int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) { - char *p; - int r; - - /* - * 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_or_backup_file(const char *filename) { - const char *p; - - assert(filename); - - if (filename[0] == '.' || - streq(filename, "lost+found") || - streq(filename, "aquota.user") || - streq(filename, "aquota.group") || - endswith(filename, "~")) - return true; - - p = strrchr(filename, '.'); - if (!p) - return false; - - /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up - * with always new suffixes and that everybody else should just adjust to that, then it really should be on - * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt - * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional - * string. Specifically: there's now: - * - * The generic suffixes "~" and ".bak" for backup files - * The generic prefix "." for hidden files - * - * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist" - * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead. - */ - - return STR_IN_SET(p + 1, - "rpmnew", - "rpmsave", - "rpmorig", - "dpkg-old", - "dpkg-new", - "dpkg-tmp", - "dpkg-dist", - "dpkg-bak", - "dpkg-backup", - "dpkg-remove", - "ucf-new", - "ucf-old", - "ucf-dist", - "swp", - "bak", - "old", - "new"); -} - -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/"); -} - -bool is_deviceallow_pattern(const char *path) { - return path_startswith(path, "/dev/") || - startswith(path, "block-") || - startswith(path, "char-"); -} - -int systemd_installation_has_version(const char *root, unsigned minimal_version) { - const char *pattern; - int r; - - /* Try to guess if systemd installation is later than the specified version. This - * is hacky and likely to yield false negatives, particularly if the installation - * is non-standard. False positives should be relatively rare. - */ - - NULSTR_FOREACH(pattern, - /* /lib works for systems without usr-merge, and for systems with a sane - * usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessary - * for Gentoo which does a merge without making /lib a symlink. - */ - "lib/systemd/libsystemd-shared-*.so\0" - "usr/lib/systemd/libsystemd-shared-*.so\0") { - - _cleanup_strv_free_ char **names = NULL; - _cleanup_free_ char *path = NULL; - char *c, **name; - - path = prefix_root(root, pattern); - if (!path) - return -ENOMEM; - - r = glob_extend(&names, path); - if (r == -ENOENT) - continue; - if (r < 0) - return r; - - assert_se((c = endswith(path, "*.so"))); - *c = '\0'; /* truncate the glob part */ - - STRV_FOREACH(name, names) { - /* This is most likely to run only once, hence let's not optimize anything. */ - char *t, *t2; - unsigned version; - - t = startswith(*name, path); - if (!t) - continue; - - t2 = endswith(t, ".so"); - if (!t2) - continue; - - t2[0] = '\0'; /* truncate the suffix */ - - r = safe_atou(t, &version); - if (r < 0) { - log_debug_errno(r, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m", *name); - continue; - } - - log_debug("Found libsystemd shared at \"%s.so\", version %u (%s).", - *name, version, - version >= minimal_version ? "OK" : "too old"); - if (version >= minimal_version) - return true; - } - } - - return false; -} diff --git a/src/basic/path-util.h b/src/basic/path-util.h deleted file mode 100644 index 66545f52d9..0000000000 --- a/src/basic/path-util.h +++ /dev/null @@ -1,130 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010-2012 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 <stdbool.h> -#include <stddef.h> - -#include "macro.h" -#include "time-util.h" - -#define DEFAULT_PATH_NORMAL "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin" -#define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":/sbin:/bin" - -#ifdef HAVE_SPLIT_USR -# define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR -#else -# define DEFAULT_PATH DEFAULT_PATH_NORMAL -#endif - -bool is_path(const char *p) _pure_; -int path_split_and_make_absolute(const char *p, char ***ret); -bool path_is_absolute(const char *p) _pure_; -char* path_make_absolute(const char *p, const char *prefix); -int 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_; -int path_compare(const char *a, const char *b) _pure_; -bool path_equal(const char *a, const char *b) _pure_; -bool path_equal_or_files_same(const char *a, const char *b); -char* path_join(const char *root, const char *path, const char *rest); - -static inline bool path_equal_ptr(const char *a, const char *b) { - return !!a == !!b && (!a || path_equal(a, b)); -} - -/* Note: the search terminates on the first NULL item. */ -#define PATH_IN_SET(p, ...) \ - ({ \ - char **s; \ - bool _found = false; \ - STRV_FOREACH(s, STRV_MAKE(__VA_ARGS__)) \ - if (path_equal(p, *s)) { \ - _found = true; \ - break; \ - } \ - _found; \ - }) - -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 find_binary(const char *name, char **filename); - -bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update); - -int fsck_exists(const char *fstype); -int mkfs_exists(const char *fstype); - -/* Iterates through the path prefixes of the specified path, going up - * the tree, to root. Also returns "" (and not "/"!) for the root - * directory. Excludes the specified directory itself */ -#define PATH_FOREACH_PREFIX(prefix, path) \ - for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); streq(prefix, "/") ? NULL : strrchr(prefix, '/'); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) - -/* Same as PATH_FOREACH_PREFIX but also includes the specified path itself */ -#define PATH_FOREACH_PREFIX_MORE(prefix, path) \ - for (char *_slash = ({ path_kill_slashes(strcpy(prefix, path)); if (streq(prefix, "/")) prefix[0] = 0; strrchr(prefix, 0); }); _slash && ((*_slash = 0), true); _slash = strrchr((prefix), '/')) - -char *prefix_root(const char *root, const char *path); - -/* Similar to prefix_root(), but returns an alloca() buffer, or - * possibly a const pointer into the path parameter */ -#define prefix_roota(root, path) \ - ({ \ - const char* _path = (path), *_root = (root), *_ret; \ - char *_p, *_n; \ - size_t _l; \ - while (_path[0] == '/' && _path[1] == '/') \ - _path ++; \ - if (isempty(_root) || path_equal(_root, "/")) \ - _ret = _path; \ - else { \ - _l = strlen(_root) + 1 + strlen(_path) + 1; \ - _n = alloca(_l); \ - _p = stpcpy(_n, _root); \ - while (_p > _n && _p[-1] == '/') \ - _p--; \ - if (_path[0] != '/') \ - *(_p++) = '/'; \ - strcpy(_p, _path); \ - _ret = _n; \ - } \ - _ret; \ - }) - -int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg); - -char* dirname_malloc(const char *path); - -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_or_backup_file(const char *filename) _pure_; - -bool is_device_path(const char *path); -bool is_deviceallow_pattern(const char *path); - -int systemd_installation_has_version(const char *root, unsigned minimal_version); diff --git a/src/basic/prioq.c b/src/basic/prioq.c deleted file mode 100644 index 4570b8e4ba..0000000000 --- a/src/basic/prioq.c +++ /dev/null @@ -1,318 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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/>. -***/ - -/* - * Priority Queue - * The prioq object implements a priority queue. That is, it orders objects by - * their priority and allows O(1) access to the object with the highest - * priority. Insertion and removal are Θ(log n). Optionally, the caller can - * provide a pointer to an index which will be kept up-to-date by the prioq. - * - * The underlying algorithm used in this implementation is a Heap. - */ - -#include <errno.h> -#include <stdlib.h> - -#include "alloc-util.h" -#include "hashmap.h" -#include "prioq.h" - -struct prioq_item { - void *data; - unsigned *idx; -}; - -struct Prioq { - compare_func_t compare_func; - unsigned n_items, n_allocated; - - struct prioq_item *items; -}; - -Prioq *prioq_new(compare_func_t compare_func) { - Prioq *q; - - q = new0(Prioq, 1); - if (!q) - return q; - - q->compare_func = compare_func; - return q; -} - -Prioq* prioq_free(Prioq *q) { - if (!q) - return NULL; - - free(q->items); - return mfree(q); -} - -int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) { - assert(q); - - if (*q) - return 0; - - *q = prioq_new(compare_func); - if (!*q) - return -ENOMEM; - - return 0; -} - -static void swap(Prioq *q, unsigned j, unsigned k) { - void *saved_data; - unsigned *saved_idx; - - assert(q); - assert(j < q->n_items); - assert(k < q->n_items); - - assert(!q->items[j].idx || *(q->items[j].idx) == j); - assert(!q->items[k].idx || *(q->items[k].idx) == k); - - saved_data = q->items[j].data; - saved_idx = q->items[j].idx; - q->items[j].data = q->items[k].data; - q->items[j].idx = q->items[k].idx; - q->items[k].data = saved_data; - q->items[k].idx = saved_idx; - - if (q->items[j].idx) - *q->items[j].idx = j; - - if (q->items[k].idx) - *q->items[k].idx = k; -} - -static unsigned shuffle_up(Prioq *q, unsigned idx) { - assert(q); - - while (idx > 0) { - unsigned k; - - k = (idx-1)/2; - - if (q->compare_func(q->items[k].data, q->items[idx].data) <= 0) - break; - - swap(q, idx, k); - idx = k; - } - - return idx; -} - -static unsigned shuffle_down(Prioq *q, unsigned idx) { - assert(q); - - for (;;) { - unsigned j, k, s; - - k = (idx+1)*2; /* right child */ - j = k-1; /* left child */ - - if (j >= q->n_items) - break; - - if (q->compare_func(q->items[j].data, q->items[idx].data) < 0) - - /* So our left child is smaller than we are, let's - * remember this fact */ - s = j; - else - s = idx; - - if (k < q->n_items && - q->compare_func(q->items[k].data, q->items[s].data) < 0) - - /* So our right child is smaller than we are, let's - * remember this fact */ - s = k; - - /* s now points to the smallest of the three items */ - - if (s == idx) - /* No swap necessary, we're done */ - break; - - swap(q, idx, s); - idx = s; - } - - return idx; -} - -int prioq_put(Prioq *q, void *data, unsigned *idx) { - struct prioq_item *i; - unsigned k; - - assert(q); - - if (q->n_items >= q->n_allocated) { - unsigned n; - struct prioq_item *j; - - n = MAX((q->n_items+1) * 2, 16u); - j = realloc(q->items, sizeof(struct prioq_item) * n); - if (!j) - return -ENOMEM; - - q->items = j; - q->n_allocated = n; - } - - k = q->n_items++; - i = q->items + k; - i->data = data; - i->idx = idx; - - if (idx) - *idx = k; - - shuffle_up(q, k); - - return 0; -} - -static void remove_item(Prioq *q, struct prioq_item *i) { - struct prioq_item *l; - - assert(q); - assert(i); - - l = q->items + q->n_items - 1; - - if (i == l) - /* Last entry, let's just remove it */ - q->n_items--; - else { - unsigned k; - - /* Not last entry, let's replace the last entry with - * this one, and reshuffle */ - - k = i - q->items; - - i->data = l->data; - i->idx = l->idx; - if (i->idx) - *i->idx = k; - q->n_items--; - - k = shuffle_down(q, k); - shuffle_up(q, k); - } -} - -_pure_ static struct prioq_item* find_item(Prioq *q, void *data, unsigned *idx) { - struct prioq_item *i; - - assert(q); - - if (idx) { - if (*idx == PRIOQ_IDX_NULL || - *idx > q->n_items) - return NULL; - - i = q->items + *idx; - if (i->data != data) - return NULL; - - return i; - } else { - for (i = q->items; i < q->items + q->n_items; i++) - if (i->data == data) - return i; - return NULL; - } -} - -int prioq_remove(Prioq *q, void *data, unsigned *idx) { - struct prioq_item *i; - - if (!q) - return 0; - - i = find_item(q, data, idx); - if (!i) - return 0; - - remove_item(q, i); - return 1; -} - -int prioq_reshuffle(Prioq *q, void *data, unsigned *idx) { - struct prioq_item *i; - unsigned k; - - assert(q); - - i = find_item(q, data, idx); - if (!i) - return 0; - - k = i - q->items; - k = shuffle_down(q, k); - shuffle_up(q, k); - return 1; -} - -void *prioq_peek(Prioq *q) { - - if (!q) - return NULL; - - if (q->n_items <= 0) - return NULL; - - return q->items[0].data; -} - -void *prioq_pop(Prioq *q) { - void *data; - - if (!q) - return NULL; - - if (q->n_items <= 0) - return NULL; - - data = q->items[0].data; - remove_item(q, q->items); - return data; -} - -unsigned prioq_size(Prioq *q) { - - if (!q) - return 0; - - return q->n_items; -} - -bool prioq_isempty(Prioq *q) { - - if (!q) - return true; - - return q->n_items <= 0; -} diff --git a/src/basic/prioq.h b/src/basic/prioq.h deleted file mode 100644 index 113c73d040..0000000000 --- a/src/basic/prioq.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 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 "hashmap.h" -#include "macro.h" - -typedef struct Prioq Prioq; - -#define PRIOQ_IDX_NULL ((unsigned) -1) - -Prioq *prioq_new(compare_func_t compare); -Prioq *prioq_free(Prioq *q); -int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func); - -int prioq_put(Prioq *q, void *data, unsigned *idx); -int prioq_remove(Prioq *q, void *data, unsigned *idx); -int prioq_reshuffle(Prioq *q, void *data, unsigned *idx); - -void *prioq_peek(Prioq *q) _pure_; -void *prioq_pop(Prioq *q); - -unsigned prioq_size(Prioq *q) _pure_; -bool prioq_isempty(Prioq *q) _pure_; diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c deleted file mode 100644 index 8297a222b7..0000000000 --- a/src/basic/proc-cmdline.c +++ /dev/null @@ -1,191 +0,0 @@ -/*** - 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 <stddef.h> -#include <string.h> - -#include "alloc-util.h" -#include "extract-word.h" -#include "fileio.h" -#include "macro.h" -#include "parse-util.h" -#include "proc-cmdline.h" -#include "process-util.h" -#include "special.h" -#include "string-util.h" -#include "util.h" -#include "virt.h" - -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, void *data), - void *data, - bool strip_prefix) { - _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, *unprefixed; - - 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 */ - unprefixed = startswith(word, "rd."); - if (unprefixed && !in_initrd()) - continue; - - value = strchr(word, '='); - if (value) - *(value++) = 0; - - r = parse_item(strip_prefix && unprefixed ? unprefixed : word, value, data); - 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 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); -} - -static const char * const rlmap[] = { - "emergency", SPECIAL_EMERGENCY_TARGET, - "-b", SPECIAL_EMERGENCY_TARGET, - "rescue", SPECIAL_RESCUE_TARGET, - "single", SPECIAL_RESCUE_TARGET, - "-s", SPECIAL_RESCUE_TARGET, - "s", SPECIAL_RESCUE_TARGET, - "S", SPECIAL_RESCUE_TARGET, - "1", SPECIAL_RESCUE_TARGET, - "2", SPECIAL_MULTI_USER_TARGET, - "3", SPECIAL_MULTI_USER_TARGET, - "4", SPECIAL_MULTI_USER_TARGET, - "5", SPECIAL_GRAPHICAL_TARGET, - NULL -}; - -static const char * const rlmap_initrd[] = { - "emergency", SPECIAL_EMERGENCY_TARGET, - "rescue", SPECIAL_RESCUE_TARGET, - NULL -}; - -const char* runlevel_to_target(const char *word) { - size_t i; - const char * const *rlmap_ptr = in_initrd() ? rlmap_initrd - : rlmap; - - if (!word) - return NULL; - - if (in_initrd() && (word = startswith(word, "rd.")) == NULL) - return NULL; - - for (i = 0; rlmap_ptr[i] != NULL; i += 2) - if (streq(word, rlmap_ptr[i])) - return rlmap_ptr[i+1]; - - return NULL; -} diff --git a/src/basic/proc-cmdline.h b/src/basic/proc-cmdline.h deleted file mode 100644 index 6d6ee95c11..0000000000 --- a/src/basic/proc-cmdline.h +++ /dev/null @@ -1,29 +0,0 @@ -#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/>. -***/ - -int proc_cmdline(char **ret); -int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, void *data), - void *data, - bool strip_prefix); -int get_proc_cmdline_key(const char *parameter, char **value); - -int shall_restore_state(void); -const char* runlevel_to_target(const char *rl); diff --git a/src/basic/process-util.c b/src/basic/process-util.c deleted file mode 100644 index 54b644ad56..0000000000 --- a/src/basic/process-util.c +++ /dev/null @@ -1,860 +0,0 @@ -/*** - 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 <errno.h> -#include <limits.h> -#include <linux/oom.h> -#include <sched.h> -#include <signal.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/personality.h> -#include <sys/prctl.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <syslog.h> -#include <unistd.h> -#ifdef HAVE_VALGRIND_VALGRIND_H -#include <valgrind/valgrind.h> -#endif - -#include "alloc-util.h" -#include "architecture.h" -#include "escape.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "ioprio.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "process-util.h" -#include "raw-clone.h" -#include "signal-util.h" -#include "stat-util.h" -#include "string-table.h" -#include "string-util.h" -#include "user-util.h" -#include "util.h" - -int get_process_state(pid_t pid) { - const char *p; - char state; - int r; - _cleanup_free_ char *line = NULL; - - assert(pid >= 0); - - p = procfs_file_alloca(pid, "stat"); - - r = read_one_line_file(p, &line); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - p = strrchr(line, ')'); - if (!p) - return -EIO; - - p++; - - if (sscanf(p, " %c", &state) != 1) - return -EIO; - - return (unsigned char) state; -} - -int get_process_comm(pid_t pid, char **name) { - const char *p; - int r; - - assert(name); - assert(pid >= 0); - - p = procfs_file_alloca(pid, "comm"); - - r = read_one_line_file(p, name); - if (r == -ENOENT) - return -ESRCH; - - return r; -} - -int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) { - _cleanup_fclose_ FILE *f = NULL; - bool space = false; - char *r = NULL, *k; - const char *p; - int c; - - assert(line); - assert(pid >= 0); - - /* Retrieves a process' command line. Replaces unprintable characters while doing so by whitespace (coalescing - * multiple sequential ones into one). If max_length is != 0 will return a string of the specified size at most - * (the trailing NUL byte does count towards the length here!), abbreviated with a "..." ellipsis. If - * comm_fallback is true and the process has no command line set (the case for kernel threads), or has a - * command line that resolves to the empty string will return the "comm" name of the process instead. - * - * Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and - * comm_fallback is false). */ - - p = procfs_file_alloca(pid, "cmdline"); - - f = fopen(p, "re"); - if (!f) { - if (errno == ENOENT) - return -ESRCH; - return -errno; - } - - if (max_length == 1) { - - /* If there's only room for one byte, return the empty string */ - r = new0(char, 1); - if (!r) - return -ENOMEM; - - *line = r; - return 0; - - } else if (max_length == 0) { - size_t len = 0, allocated = 0; - - while ((c = getc(f)) != EOF) { - - if (!GREEDY_REALLOC(r, allocated, len+3)) { - free(r); - return -ENOMEM; - } - - if (isprint(c)) { - if (space) { - r[len++] = ' '; - space = false; - } - - r[len++] = c; - } else if (len > 0) - space = true; - } - - if (len > 0) - r[len] = 0; - else - r = mfree(r); - - } else { - bool dotdotdot = false; - size_t left; - - r = new(char, max_length); - if (!r) - return -ENOMEM; - - k = r; - left = max_length; - while ((c = getc(f)) != EOF) { - - if (isprint(c)) { - - if (space) { - if (left <= 2) { - dotdotdot = true; - break; - } - - *(k++) = ' '; - left--; - space = false; - } - - if (left <= 1) { - dotdotdot = true; - break; - } - - *(k++) = (char) c; - left--; - } else if (k > r) - space = true; - } - - if (dotdotdot) { - if (max_length <= 4) { - k = r; - left = max_length; - } else { - k = r + max_length - 4; - left = 4; - - /* Eat up final spaces */ - while (k > r && isspace(k[-1])) { - k--; - left++; - } - } - - strncpy(k, "...", left-1); - k[left-1] = 0; - } else - *k = 0; - } - - /* Kernel threads have no argv[] */ - if (isempty(r)) { - _cleanup_free_ char *t = NULL; - int h; - - free(r); - - if (!comm_fallback) - return -ENOENT; - - h = get_process_comm(pid, &t); - if (h < 0) - return h; - - if (max_length == 0) - r = strjoin("[", t, "]", NULL); - else { - size_t l; - - l = strlen(t); - - if (l + 3 <= max_length) - r = strjoin("[", t, "]", NULL); - else if (max_length <= 6) { - - r = new(char, max_length); - if (!r) - return -ENOMEM; - - memcpy(r, "[...]", max_length-1); - r[max_length-1] = 0; - } else { - char *e; - - t[max_length - 6] = 0; - - /* Chop off final spaces */ - e = strchr(t, 0); - while (e > t && isspace(e[-1])) - e--; - *e = 0; - - r = strjoin("[", t, "...]", NULL); - } - } - if (!r) - return -ENOMEM; - } - - *line = r; - return 0; -} - -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 */ - - (void) 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])); - } - } -} - -int is_kernel_thread(pid_t pid) { - const char *p; - size_t count; - char c; - bool eof; - FILE *f; - - if (pid == 0 || pid == 1) /* pid 1, and we ourselves certainly aren't a kernel thread */ - return 0; - - assert(pid > 1); - - p = procfs_file_alloca(pid, "cmdline"); - f = fopen(p, "re"); - if (!f) { - if (errno == ENOENT) - return -ESRCH; - return -errno; - } - - count = fread(&c, 1, 1, f); - eof = feof(f); - fclose(f); - - /* Kernel threads have an empty cmdline */ - - if (count <= 0) - return eof ? 1 : -errno; - - return 0; -} - -int get_process_capeff(pid_t pid, char **capeff) { - const char *p; - int r; - - assert(capeff); - assert(pid >= 0); - - p = procfs_file_alloca(pid, "status"); - - r = get_proc_field(p, "CapEff", WHITESPACE, capeff); - if (r == -ENOENT) - return -ESRCH; - - return r; -} - -static int get_process_link_contents(const char *proc_file, char **name) { - int r; - - assert(proc_file); - assert(name); - - r = readlink_malloc(proc_file, name); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - return 0; -} - -int get_process_exe(pid_t pid, char **name) { - const char *p; - char *d; - int r; - - assert(pid >= 0); - - p = procfs_file_alloca(pid, "exe"); - r = get_process_link_contents(p, name); - if (r < 0) - return r; - - d = endswith(*name, " (deleted)"); - if (d) - *d = '\0'; - - return 0; -} - -static int get_process_id(pid_t pid, const char *field, uid_t *uid) { - _cleanup_fclose_ FILE *f = NULL; - char line[LINE_MAX]; - const char *p; - - assert(field); - assert(uid); - - p = procfs_file_alloca(pid, "status"); - f = fopen(p, "re"); - if (!f) { - if (errno == ENOENT) - return -ESRCH; - return -errno; - } - - FOREACH_LINE(line, f, return -errno) { - char *l; - - l = strstrip(line); - - if (startswith(l, field)) { - l += strlen(field); - l += strspn(l, WHITESPACE); - - l[strcspn(l, WHITESPACE)] = 0; - - return parse_uid(l, uid); - } - } - - return -EIO; -} - -int get_process_uid(pid_t pid, uid_t *uid) { - return get_process_id(pid, "Uid:", uid); -} - -int get_process_gid(pid_t pid, gid_t *gid) { - assert_cc(sizeof(uid_t) == sizeof(gid_t)); - return get_process_id(pid, "Gid:", gid); -} - -int get_process_cwd(pid_t pid, char **cwd) { - const char *p; - - assert(pid >= 0); - - p = procfs_file_alloca(pid, "cwd"); - - return get_process_link_contents(p, cwd); -} - -int get_process_root(pid_t pid, char **root) { - const char *p; - - assert(pid >= 0); - - p = procfs_file_alloca(pid, "root"); - - return get_process_link_contents(p, root); -} - -int get_process_environ(pid_t pid, char **env) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *outcome = NULL; - int c; - const char *p; - size_t allocated = 0, sz = 0; - - assert(pid >= 0); - assert(env); - - p = procfs_file_alloca(pid, "environ"); - - f = fopen(p, "re"); - if (!f) { - if (errno == ENOENT) - return -ESRCH; - return -errno; - } - - while ((c = fgetc(f)) != EOF) { - if (!GREEDY_REALLOC(outcome, allocated, sz + 5)) - return -ENOMEM; - - if (c == '\0') - outcome[sz++] = '\n'; - else - sz += cescape_char(c, outcome + sz); - } - - if (!outcome) { - outcome = strdup(""); - if (!outcome) - return -ENOMEM; - } else - outcome[sz] = '\0'; - - *env = outcome; - outcome = NULL; - - return 0; -} - -int get_process_ppid(pid_t pid, pid_t *_ppid) { - int r; - _cleanup_free_ char *line = NULL; - long unsigned ppid; - const char *p; - - assert(pid >= 0); - assert(_ppid); - - if (pid == 0) { - *_ppid = getppid(); - return 0; - } - - p = procfs_file_alloca(pid, "stat"); - r = read_one_line_file(p, &line); - if (r == -ENOENT) - return -ESRCH; - if (r < 0) - return r; - - /* Let's skip the pid and comm fields. The latter is enclosed - * in () but does not escape any () in its value, so let's - * skip over it manually */ - - p = strrchr(line, ')'); - if (!p) - return -EIO; - - p++; - - if (sscanf(p, " " - "%*c " /* state */ - "%lu ", /* ppid */ - &ppid) != 1) - return -EIO; - - if ((long unsigned) (pid_t) ppid != ppid) - return -ERANGE; - - *_ppid = (pid_t) ppid; - - return 0; -} - -int wait_for_terminate(pid_t pid, siginfo_t *status) { - siginfo_t dummy; - - assert(pid >= 1); - - if (!status) - status = &dummy; - - for (;;) { - zero(*status); - - if (waitid(P_PID, pid, status, WEXITED) < 0) { - - if (errno == EINTR) - continue; - - return negative_errno(); - } - - return 0; - } -} - -/* - * Return values: - * < 0 : wait_for_terminate() failed to get the state of the - * process, the process was terminated by a signal, or - * failed for an unknown reason. - * >=0 : The process terminated normally, and its exit code is - * returned. - * - * That is, success is indicated by a return value of zero, and an - * error is indicated by a non-zero value. - * - * A warning is emitted if the process terminates abnormally, - * and also if it returns non-zero unless check_exit_code is true. - */ -int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) { - int r; - siginfo_t status; - - assert(name); - assert(pid > 1); - - r = wait_for_terminate(pid, &status); - if (r < 0) - return log_warning_errno(r, "Failed to wait for %s: %m", name); - - if (status.si_code == CLD_EXITED) { - if (status.si_status != 0) - log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG, - "%s failed with error code %i.", name, status.si_status); - else - log_debug("%s succeeded.", name); - - return status.si_status; - } else if (status.si_code == CLD_KILLED || - status.si_code == CLD_DUMPED) { - - log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status)); - return -EPROTO; - } - - log_warning("%s failed due to unknown reason.", name); - return -EPROTO; -} - -void sigkill_wait(pid_t pid) { - assert(pid > 1); - - if (kill(pid, SIGKILL) > 0) - (void) wait_for_terminate(pid, NULL); -} - -void sigkill_waitp(pid_t *pid) { - if (!pid) - return; - if (*pid <= 1) - return; - - sigkill_wait(*pid); -} - -int kill_and_sigcont(pid_t pid, int sig) { - int r; - - r = kill(pid, sig) < 0 ? -errno : 0; - - /* If this worked, also send SIGCONT, unless we already just sent a SIGCONT, or SIGKILL was sent which isn't - * affected by a process being suspended anyway. */ - if (r >= 0 && !IN_SET(SIGCONT, SIGKILL)) - (void) kill(pid, SIGCONT); - - return r; -} - -int getenv_for_pid(pid_t pid, const char *field, char **_value) { - _cleanup_fclose_ FILE *f = NULL; - char *value = NULL; - int r; - bool done = false; - size_t l; - const char *path; - - assert(pid >= 0); - assert(field); - assert(_value); - - path = procfs_file_alloca(pid, "environ"); - - f = fopen(path, "re"); - if (!f) { - if (errno == ENOENT) - return -ESRCH; - return -errno; - } - - l = strlen(field); - r = 0; - - do { - char line[LINE_MAX]; - unsigned i; - - for (i = 0; i < sizeof(line)-1; i++) { - int c; - - c = getc(f); - if (_unlikely_(c == EOF)) { - done = true; - break; - } else if (c == 0) - break; - - line[i] = c; - } - line[i] = 0; - - if (memcmp(line, field, l) == 0 && line[l] == '=') { - value = strdup(line + l + 1); - if (!value) - return -ENOMEM; - - r = 1; - break; - } - - } while (!done); - - *_value = value; - return r; -} - -bool pid_is_unwaited(pid_t pid) { - /* Checks whether a PID is still valid at all, including a zombie */ - - if (pid < 0) - return false; - - if (pid <= 1) /* If we or PID 1 would be dead and have been waited for, this code would not be running */ - return true; - - if (kill(pid, 0) >= 0) - return true; - - return errno != ESRCH; -} - -bool pid_is_alive(pid_t pid) { - int r; - - /* Checks whether a PID is still valid and not a zombie */ - - if (pid < 0) - return false; - - if (pid <= 1) /* If we or PID 1 would be a zombie, this code would not be running */ - return true; - - r = get_process_state(pid); - if (r == -ESRCH || r == 'Z') - return false; - - return true; -} - -int pid_from_same_root_fs(pid_t pid) { - const char *root; - - if (pid < 0) - return 0; - - root = procfs_file_alloca(pid, "root"); - - return files_same(root, "/proc/1/root"); -} - -bool is_main_thread(void) { - static thread_local int cached = 0; - - if (_unlikely_(cached == 0)) - cached = getpid() == gettid() ? 1 : -1; - - return cached > 0; -} - -noreturn void freeze(void) { - - log_close(); - - /* Make sure nobody waits for us on a socket anymore */ - close_all_fds(NULL, 0); - - sync(); - - for (;;) - pause(); -} - -bool oom_score_adjust_is_valid(int oa) { - return oa >= OOM_SCORE_ADJ_MIN && oa <= OOM_SCORE_ADJ_MAX; -} - -unsigned long personality_from_string(const char *p) { - int architecture; - - if (!p) - return PERSONALITY_INVALID; - - /* Parse a personality specifier. We use 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. */ - - architecture = architecture_from_string(p); - if (architecture < 0) - return PERSONALITY_INVALID; - - if (architecture == native_architecture()) - return PER_LINUX; -#ifdef SECONDARY_ARCHITECTURE - if (architecture == SECONDARY_ARCHITECTURE) - return PER_LINUX32; -#endif - - return PERSONALITY_INVALID; -} - -const char* personality_to_string(unsigned long p) { - int architecture = _ARCHITECTURE_INVALID; - - if (p == PER_LINUX) - architecture = native_architecture(); -#ifdef SECONDARY_ARCHITECTURE - else if (p == PER_LINUX32) - architecture = SECONDARY_ARCHITECTURE; -#endif - - if (architecture < 0) - return NULL; - - return architecture_to_string(architecture); -} - -void valgrind_summary_hack(void) { -#ifdef HAVE_VALGRIND_VALGRIND_H - if (getpid() == 1 && RUNNING_ON_VALGRIND) { - pid_t pid; - pid = raw_clone(SIGCHLD); - if (pid < 0) - log_emergency_errno(errno, "Failed to fork off valgrind helper: %m"); - else if (pid == 0) - exit(EXIT_SUCCESS); - else { - log_info("Spawned valgrind helper as PID "PID_FMT".", pid); - (void) wait_for_terminate(pid, NULL); - } - } -#endif -} - -int pid_compare_func(const void *a, const void *b) { - const pid_t *p = a, *q = b; - - /* Suitable for usage in qsort() */ - - if (*p < *q) - return -1; - if (*p > *q) - return 1; - return 0; -} - -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 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); diff --git a/src/basic/process-util.h b/src/basic/process-util.h deleted file mode 100644 index 2568e3834f..0000000000 --- a/src/basic/process-util.h +++ /dev/null @@ -1,110 +0,0 @@ -#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 <signal.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdio.h> -#include <string.h> -#include <sys/types.h> -#include <sys/resource.h> - -#include "formats-util.h" -#include "macro.h" - -#define procfs_file_alloca(pid, field) \ - ({ \ - pid_t _pid_ = (pid); \ - const char *_r_; \ - if (_pid_ == 0) { \ - _r_ = ("/proc/self/" field); \ - } else { \ - _r_ = alloca(strlen("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \ - sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \ - } \ - _r_; \ - }) - -int get_process_state(pid_t pid); -int get_process_comm(pid_t pid, char **name); -int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line); -int get_process_exe(pid_t pid, char **name); -int get_process_uid(pid_t pid, uid_t *uid); -int get_process_gid(pid_t pid, gid_t *gid); -int get_process_capeff(pid_t pid, char **capeff); -int get_process_cwd(pid_t pid, char **cwd); -int get_process_root(pid_t pid, char **root); -int get_process_environ(pid_t pid, char **environ); -int get_process_ppid(pid_t pid, pid_t *ppid); - -int wait_for_terminate(pid_t pid, siginfo_t *status); -int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code); - -void sigkill_wait(pid_t pid); -void sigkill_waitp(pid_t *pid); - -int kill_and_sigcont(pid_t pid, int sig); - -void rename_process(const char name[8]); -int is_kernel_thread(pid_t pid); - -int getenv_for_pid(pid_t pid, const char *field, char **_value); - -bool pid_is_alive(pid_t pid); -bool pid_is_unwaited(pid_t pid); -int pid_from_same_root_fs(pid_t pid); - -bool is_main_thread(void); - -noreturn void freeze(void); - -bool oom_score_adjust_is_valid(int oa); - -#ifndef PERSONALITY_INVALID -/* personality(7) documents that 0xffffffffUL is used for querying the - * current personality, hence let's use that here as error - * indicator. */ -#define PERSONALITY_INVALID 0xffffffffLU -#endif - -unsigned long personality_from_string(const char *p); -const char *personality_to_string(unsigned long); - -int ioprio_class_to_string_alloc(int i, char **s); -int ioprio_class_from_string(const char *s); - -const char *sigchld_code_to_string(int i) _const_; -int sigchld_code_from_string(const char *s) _pure_; - -int sched_policy_to_string_alloc(int i, char **s); -int sched_policy_from_string(const char *s); - -#define PTR_TO_PID(p) ((pid_t) ((uintptr_t) p)) -#define PID_TO_PTR(p) ((void*) ((uintptr_t) p)) - -void valgrind_summary_hack(void); - -int pid_compare_func(const void *a, const void *b); - -static inline bool nice_is_valid(int n) { - return n >= PRIO_MIN && n < PRIO_MAX; -} diff --git a/src/basic/random-util.c b/src/basic/random-util.c deleted file mode 100644 index ad7b3eedf2..0000000000 --- a/src/basic/random-util.c +++ /dev/null @@ -1,133 +0,0 @@ -/*** - 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 <elf.h> -#include <errno.h> -#include <fcntl.h> -#include <stdbool.h> -#include <stdlib.h> -#include <sys/time.h> -#include <linux/random.h> -#include <stdint.h> - -#ifdef HAVE_SYS_AUXV_H -#include <sys/auxv.h> -#endif - -#include "fd-util.h" -#include "io-util.h" -#include "missing.h" -#include "random-util.h" -#include "time-util.h" - -int dev_urandom(void *p, size_t n) { - static int have_syscall = -1; - - _cleanup_close_ int fd = -1; - int r; - - /* Gathers some randomness from the kernel. This call will - * never block, and will always return some data from the - * kernel, regardless if the random pool is fully initialized - * or not. It thus makes no guarantee for the quality of the - * returned entropy, but is good enough for our usual usecases - * of seeding the hash functions for hashtable */ - - /* Use the getrandom() syscall unless we know we don't have - * it, or when the requested size is too large for it. */ - if (have_syscall != 0 || (size_t) (int) n != n) { - r = getrandom(p, n, GRND_NONBLOCK); - if (r == (int) n) { - have_syscall = true; - return 0; - } - - if (r < 0) { - if (errno == ENOSYS) - /* we lack the syscall, continue with - * reading from /dev/urandom */ - have_syscall = false; - else if (errno == EAGAIN) - /* not enough entropy for now. Let's - * remember to use the syscall the - * next time, again, but also read - * from /dev/urandom for now, which - * doesn't care about the current - * amount of entropy. */ - have_syscall = true; - else - return -errno; - } else - /* too short read? */ - return -ENODATA; - } - - fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - return errno == ENOENT ? -ENOSYS : -errno; - - return loop_read_exact(fd, p, n, true); -} - -void initialize_srand(void) { - static bool srand_called = false; - unsigned x; -#ifdef HAVE_SYS_AUXV_H - void *auxv; -#endif - - if (srand_called) - return; - -#ifdef HAVE_SYS_AUXV_H - /* The kernel provides us with 16 bytes of entropy in auxv, so let's try to make use of that to seed the - * pseudo-random generator. It's better than nothing... */ - - auxv = (void*) getauxval(AT_RANDOM); - if (auxv) { - assert_cc(sizeof(x) < 16); - memcpy(&x, auxv, sizeof(x)); - } else -#endif - x = 0; - - - x ^= (unsigned) now(CLOCK_REALTIME); - x ^= (unsigned) gettid(); - - srand(x); - srand_called = true; -} - -void random_bytes(void *p, size_t n) { - uint8_t *q; - int r; - - r = dev_urandom(p, n); - if (r >= 0) - return; - - /* If some idiot made /dev/urandom unavailable to us, he'll - * get a PRNG instead. */ - - initialize_srand(); - - for (q = p; q < (uint8_t*) p + n; q ++) - *q = rand(); -} diff --git a/src/basic/random-util.h b/src/basic/random-util.h deleted file mode 100644 index 3cee4c5014..0000000000 --- a/src/basic/random-util.h +++ /dev/null @@ -1,39 +0,0 @@ -#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 <stdint.h> - -int dev_urandom(void *p, size_t n); -void random_bytes(void *p, size_t n); -void initialize_srand(void); - -static inline uint64_t random_u64(void) { - uint64_t u; - random_bytes(&u, sizeof(u)); - return u; -} - -static inline uint32_t random_u32(void) { - uint32_t u; - random_bytes(&u, sizeof(u)); - return u; -} diff --git a/src/basic/ratelimit.c b/src/basic/ratelimit.c deleted file mode 100644 index 3ca5625e4d..0000000000 --- a/src/basic/ratelimit.c +++ /dev/null @@ -1,56 +0,0 @@ -/*** - 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 <sys/time.h> - -#include "macro.h" -#include "ratelimit.h" - -/* Modelled after Linux' lib/ratelimit.c by Dave Young - * <hidave.darkstar@gmail.com>, which is licensed GPLv2. */ - -bool ratelimit_test(RateLimit *r) { - usec_t ts; - - assert(r); - - if (r->interval <= 0 || r->burst <= 0) - return true; - - ts = now(CLOCK_MONOTONIC); - - if (r->begin <= 0 || - r->begin + r->interval < ts) { - r->begin = ts; - - /* Reset counter */ - r->num = 0; - goto good; - } - - if (r->num < r->burst) - goto good; - - return false; - -good: - r->num++; - return true; -} diff --git a/src/basic/ratelimit.h b/src/basic/ratelimit.h deleted file mode 100644 index 9c8dddf5ad..0000000000 --- a/src/basic/ratelimit.h +++ /dev/null @@ -1,58 +0,0 @@ -#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 "time-util.h" -#include "util.h" - -typedef struct RateLimit { - usec_t interval; - usec_t begin; - unsigned burst; - unsigned num; -} RateLimit; - -#define RATELIMIT_DEFINE(_name, _interval, _burst) \ - RateLimit _name = { \ - .interval = (_interval), \ - .burst = (_burst), \ - .num = 0, \ - .begin = 0 \ - } - -#define RATELIMIT_INIT(v, _interval, _burst) \ - do { \ - RateLimit *_r = &(v); \ - _r->interval = (_interval); \ - _r->burst = (_burst); \ - _r->num = 0; \ - _r->begin = 0; \ - } while (false) - -#define RATELIMIT_RESET(v) \ - do { \ - RateLimit *_r = &(v); \ - _r->num = 0; \ - _r->begin = 0; \ - } while (false) - -bool ratelimit_test(RateLimit *r); diff --git a/src/basic/raw-clone.h b/src/basic/raw-clone.h deleted file mode 100644 index d473828999..0000000000 --- a/src/basic/raw-clone.h +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2016 Michael Karcher - 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 <sched.h> -#include <sys/syscall.h> - -#include "log.h" -#include "macro.h" - -/** - * raw_clone() - uses clone to create a new process with clone flags - * @flags: Flags to pass to the clone system call - * - * Uses the clone system call to create a new process with the cloning - * flags and termination signal passed in the flags parameter. Opposed - * to glibc's clone funtion, using this function does not set up a - * separate stack for the child, but relies on copy-on-write semantics - * on the one stack at a common virtual address, just as fork does. - * - * To obtain copy-on-write semantics, flags must not contain CLONE_VM, - * and thus CLONE_THREAD and CLONE_SIGHAND (which require CLONE_VM) are - * not usabale. - * Additionally, as this function does not pass the ptid, newtls and ctid - * parameters to the kernel, flags must not contain CLONE_PARENT_SETTID, - * CLONE_CHILD_SETTID, CLONE_CHILD_CLEARTID or CLONE_SETTLS. - * - * Returns: 0 in the child process and the child process id in the parent. - */ -static inline int raw_clone(unsigned long flags) { - assert((flags & (CLONE_VM|CLONE_PARENT_SETTID|CLONE_CHILD_SETTID| - CLONE_CHILD_CLEARTID|CLONE_SETTLS)) == 0); -#if defined(__s390__) || defined(__CRIS__) - /* On s390 and cris the order of the first and second arguments - * of the raw clone() system call is reversed. */ - return (int) syscall(__NR_clone, NULL, flags); -#elif defined(__sparc__) && defined(__arch64__) - { - /** - * sparc64 always returns the other process id in %o0, and - * a boolean flag whether this is the child or the parent in - * %o1. Inline assembly is needed to get the flag returned - * in %o1. - */ - int in_child; - int child_pid; - asm volatile("mov %2, %%g1\n\t" - "mov %3, %%o0\n\t" - "mov 0 , %%o1\n\t" - "t 0x6d\n\t" - "mov %%o1, %0\n\t" - "mov %%o0, %1" : - "=r"(in_child), "=r"(child_pid) : - "i"(__NR_clone), "r"(flags) : - "%o1", "%o0", "%g1" ); - if (in_child) - return 0; - else - return child_pid; - } -#else - return (int) syscall(__NR_clone, flags, NULL); -#endif -} diff --git a/src/basic/refcnt.h b/src/basic/refcnt.h deleted file mode 100644 index 1d77a6445a..0000000000 --- a/src/basic/refcnt.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 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/>. -***/ - -/* A type-safe atomic refcounter. - * - * DO NOT USE THIS UNLESS YOU ACTUALLY CARE ABOUT THREAD SAFETY! */ - -typedef struct { - volatile unsigned _value; -} RefCount; - -#define REFCNT_GET(r) ((r)._value) -#define REFCNT_INC(r) (__sync_add_and_fetch(&(r)._value, 1)) -#define REFCNT_DEC(r) (__sync_sub_and_fetch(&(r)._value, 1)) - -#define REFCNT_INIT ((RefCount) { ._value = 1 }) diff --git a/src/basic/replace-var.c b/src/basic/replace-var.c deleted file mode 100644 index 0d21423a9c..0000000000 --- a/src/basic/replace-var.c +++ /dev/null @@ -1,111 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 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 <errno.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "macro.h" -#include "replace-var.h" -#include "string-util.h" - -/* - * Generic infrastructure for replacing @FOO@ style variables in - * strings. Will call a callback for each replacement. - */ - -static int get_variable(const char *b, char **r) { - size_t k; - char *t; - - assert(b); - assert(r); - - if (*b != '@') - return 0; - - k = strspn(b + 1, UPPERCASE_LETTERS "_"); - if (k <= 0 || b[k+1] != '@') - return 0; - - t = strndup(b + 1, k); - if (!t) - return -ENOMEM; - - *r = t; - return 1; -} - -char *replace_var(const char *text, char *(*lookup)(const char *variable, void*userdata), void *userdata) { - char *r, *t; - const char *f; - size_t l; - - assert(text); - assert(lookup); - - l = strlen(text); - r = new(char, l+1); - if (!r) - return NULL; - - f = text; - t = r; - while (*f) { - _cleanup_free_ char *v = NULL, *n = NULL; - char *a; - int k; - size_t skip, d, nl; - - k = get_variable(f, &v); - if (k < 0) - goto oom; - if (k == 0) { - *(t++) = *(f++); - continue; - } - - n = lookup(v, userdata); - if (!n) - goto oom; - - skip = strlen(v) + 2; - - d = t - r; - nl = l - skip + strlen(n); - a = realloc(r, nl + 1); - if (!a) - goto oom; - - l = nl; - r = a; - t = r + d; - - t = stpcpy(t, n); - f += skip; - } - - *t = 0; - return r; - -oom: - return mfree(r); -} diff --git a/src/basic/replace-var.h b/src/basic/replace-var.h deleted file mode 100644 index 78412910b2..0000000000 --- a/src/basic/replace-var.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 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/>. -***/ - -char *replace_var(const char *text, char *(*lookup)(const char *variable, void*userdata), void *userdata); diff --git a/src/basic/rlimit-util.c b/src/basic/rlimit-util.c deleted file mode 100644 index ee063720ed..0000000000 --- a/src/basic/rlimit-util.c +++ /dev/null @@ -1,321 +0,0 @@ -/*** - 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 <errno.h> -#include <sys/resource.h> - -#include "alloc-util.h" -#include "extract-word.h" -#include "formats-util.h" -#include "macro.h" -#include "missing.h" -#include "rlimit-util.h" -#include "string-table.h" -#include "time-util.h" - -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; -} - -static int rlimit_parse_u64(const char *val, rlim_t *ret) { - uint64_t u; - int r; - - assert(val); - assert(ret); - - if (streq(val, "infinity")) { - *ret = RLIM_INFINITY; - return 0; - } - - /* setrlimit(2) suggests rlim_t is always 64bit on Linux. */ - assert_cc(sizeof(rlim_t) == sizeof(uint64_t)); - - r = safe_atou64(val, &u); - if (r < 0) - return r; - if (u >= (uint64_t) RLIM_INFINITY) - return -ERANGE; - - *ret = (rlim_t) u; - return 0; -} - -static int rlimit_parse_size(const char *val, rlim_t *ret) { - uint64_t u; - int r; - - assert(val); - assert(ret); - - if (streq(val, "infinity")) { - *ret = RLIM_INFINITY; - return 0; - } - - r = parse_size(val, 1024, &u); - if (r < 0) - return r; - if (u >= (uint64_t) RLIM_INFINITY) - return -ERANGE; - - *ret = (rlim_t) u; - return 0; -} - -static int rlimit_parse_sec(const char *val, rlim_t *ret) { - uint64_t u; - usec_t t; - int r; - - assert(val); - assert(ret); - - if (streq(val, "infinity")) { - *ret = RLIM_INFINITY; - return 0; - } - - r = parse_sec(val, &t); - if (r < 0) - return r; - if (t == USEC_INFINITY) { - *ret = RLIM_INFINITY; - return 0; - } - - u = (uint64_t) DIV_ROUND_UP(t, USEC_PER_SEC); - if (u >= (uint64_t) RLIM_INFINITY) - return -ERANGE; - - *ret = (rlim_t) u; - return 0; -} - -static int rlimit_parse_usec(const char *val, rlim_t *ret) { - usec_t t; - int r; - - assert(val); - assert(ret); - - if (streq(val, "infinity")) { - *ret = RLIM_INFINITY; - return 0; - } - - r = parse_time(val, &t, 1); - if (r < 0) - return r; - if (t == USEC_INFINITY) { - *ret = RLIM_INFINITY; - return 0; - } - - *ret = (rlim_t) t; - return 0; -} - -static int rlimit_parse_nice(const char *val, rlim_t *ret) { - uint64_t rl; - int r; - - /* So, Linux is weird. The range for RLIMIT_NICE is 40..1, mapping to the nice levels -20..19. However, the - * RLIMIT_NICE limit defaults to 0 by the kernel, i.e. a value that maps to nice level 20, which of course is - * bogus and does not exist. In order to permit parsing the RLIMIT_NICE of 0 here we hence implement a slight - * asymmetry: when parsing as positive nice level we permit 0..19. When parsing as negative nice level, we - * permit -20..0. But when parsing as raw resource limit value then we also allow the special value 0. - * - * Yeah, Linux is quality engineering sometimes... */ - - if (val[0] == '+') { - - /* Prefixed with "+": Parse as positive user-friendly nice value */ - r = safe_atou64(val + 1, &rl); - if (r < 0) - return r; - - if (rl >= PRIO_MAX) - return -ERANGE; - - rl = 20 - rl; - - } else if (val[0] == '-') { - - /* Prefixed with "-": Parse as negative user-friendly nice value */ - r = safe_atou64(val + 1, &rl); - if (r < 0) - return r; - - if (rl > (uint64_t) (-PRIO_MIN)) - return -ERANGE; - - rl = 20 + rl; - } else { - - /* Not prefixed: parse as raw resource limit value */ - r = safe_atou64(val, &rl); - if (r < 0) - return r; - - if (rl > (uint64_t) (20 - PRIO_MIN)) - return -ERANGE; - } - - *ret = (rlim_t) rl; - return 0; -} - -static int (*const rlimit_parse_table[_RLIMIT_MAX])(const char *val, rlim_t *ret) = { - [RLIMIT_CPU] = rlimit_parse_sec, - [RLIMIT_FSIZE] = rlimit_parse_size, - [RLIMIT_DATA] = rlimit_parse_size, - [RLIMIT_STACK] = rlimit_parse_size, - [RLIMIT_CORE] = rlimit_parse_size, - [RLIMIT_RSS] = rlimit_parse_size, - [RLIMIT_NOFILE] = rlimit_parse_u64, - [RLIMIT_AS] = rlimit_parse_size, - [RLIMIT_NPROC] = rlimit_parse_u64, - [RLIMIT_MEMLOCK] = rlimit_parse_size, - [RLIMIT_LOCKS] = rlimit_parse_u64, - [RLIMIT_SIGPENDING] = rlimit_parse_u64, - [RLIMIT_MSGQUEUE] = rlimit_parse_size, - [RLIMIT_NICE] = rlimit_parse_nice, - [RLIMIT_RTPRIO] = rlimit_parse_u64, - [RLIMIT_RTTIME] = rlimit_parse_usec, -}; - -int rlimit_parse_one(int resource, const char *val, rlim_t *ret) { - assert(val); - assert(ret); - - if (resource < 0) - return -EINVAL; - if (resource >= _RLIMIT_MAX) - return -EINVAL; - - return rlimit_parse_table[resource](val, ret); -} - -int rlimit_parse(int resource, const char *val, struct rlimit *ret) { - _cleanup_free_ char *hard = NULL, *soft = NULL; - rlim_t hl, sl; - int r; - - assert(val); - assert(ret); - - r = extract_first_word(&val, &soft, ":", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return r; - if (r == 0) - return -EINVAL; - - r = rlimit_parse_one(resource, soft, &sl); - if (r < 0) - return r; - - r = extract_first_word(&val, &hard, ":", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return r; - if (!isempty(val)) - return -EINVAL; - if (r == 0) - hl = sl; - else { - r = rlimit_parse_one(resource, hard, &hl); - if (r < 0) - return r; - if (sl > hl) - return -EILSEQ; - } - - *ret = (struct rlimit) { - .rlim_cur = sl, - .rlim_max = hl, - }; - - return 0; -} - -int rlimit_format(const struct rlimit *rl, char **ret) { - char *s = NULL; - - assert(rl); - assert(ret); - - if (rl->rlim_cur >= RLIM_INFINITY && rl->rlim_max >= RLIM_INFINITY) - s = strdup("infinity"); - else if (rl->rlim_cur >= RLIM_INFINITY) - (void) asprintf(&s, "infinity:" RLIM_FMT, rl->rlim_max); - else if (rl->rlim_max >= RLIM_INFINITY) - (void) asprintf(&s, RLIM_FMT ":infinity", rl->rlim_cur); - else if (rl->rlim_cur == rl->rlim_max) - (void) asprintf(&s, RLIM_FMT, rl->rlim_cur); - else - (void) asprintf(&s, RLIM_FMT ":" RLIM_FMT, rl->rlim_cur, rl->rlim_max); - - if (!s) - return -ENOMEM; - - *ret = s; - return 0; -} - -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); diff --git a/src/basic/rlimit-util.h b/src/basic/rlimit-util.h deleted file mode 100644 index d4594eccd6..0000000000 --- a/src/basic/rlimit-util.h +++ /dev/null @@ -1,36 +0,0 @@ -#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 <sys/resource.h> - -#include "macro.h" - -const char *rlimit_to_string(int i) _const_; -int rlimit_from_string(const char *s) _pure_; - -int setrlimit_closest(int resource, const struct rlimit *rlim); - -int rlimit_parse_one(int resource, const char *val, rlim_t *ret); -int rlimit_parse(int resource, const char *val, struct rlimit *ret); - -int rlimit_format(const struct rlimit *rl, char **ret); - -#define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim }) diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c deleted file mode 100644 index baa70c2c8d..0000000000 --- a/src/basic/rm-rf.c +++ /dev/null @@ -1,242 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 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 <errno.h> -#include <fcntl.h> -#include <stdbool.h> -#include <stddef.h> -#include <sys/stat.h> -#include <sys/statfs.h> -#include <unistd.h> - -#include "btrfs-util.h" -#include "cgroup-util.h" -#include "fd-util.h" -#include "log.h" -#include "macro.h" -#include "mount-util.h" -#include "path-util.h" -#include "rm-rf.h" -#include "stat-util.h" -#include "string-util.h" - -static bool is_physical_fs(const struct statfs *sfs) { - return !is_temporary_fs(sfs) && !is_cgroup_fs(sfs); -} - -int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { - _cleanup_closedir_ DIR *d = NULL; - int ret = 0, r; - struct statfs sfs; - - assert(fd >= 0); - - /* This returns the first error we run into, but nevertheless - * tries to go on. This closes the passed fd. */ - - if (!(flags & REMOVE_PHYSICAL)) { - - r = fstatfs(fd, &sfs); - if (r < 0) { - safe_close(fd); - return -errno; - } - - if (is_physical_fs(&sfs)) { - /* We refuse to clean physical file systems - * with this call, unless explicitly - * requested. This is extra paranoia just to - * be sure we never ever remove non-state - * data */ - - log_error("Attempted to remove disk file system, and we can't allow that."); - safe_close(fd); - return -EPERM; - } - } - - d = fdopendir(fd); - if (!d) { - safe_close(fd); - return errno == ENOENT ? 0 : -errno; - } - - for (;;) { - struct dirent *de; - bool is_dir; - struct stat st; - - errno = 0; - de = readdir(d); - if (!de) { - if (errno > 0 && ret == 0) - ret = -errno; - return ret; - } - - if (streq(de->d_name, ".") || streq(de->d_name, "..")) - continue; - - if (de->d_type == DT_UNKNOWN || - (de->d_type == DT_DIR && (root_dev || (flags & REMOVE_SUBVOLUME)))) { - if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { - if (ret == 0 && errno != ENOENT) - ret = -errno; - continue; - } - - is_dir = S_ISDIR(st.st_mode); - } else - is_dir = de->d_type == DT_DIR; - - if (is_dir) { - int subdir_fd; - - /* if root_dev is set, remove subdirectories only if device is same */ - if (root_dev && st.st_dev != root_dev->st_dev) - continue; - - subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); - if (subdir_fd < 0) { - if (ret == 0 && errno != ENOENT) - ret = -errno; - continue; - } - - /* Stop at mount points */ - r = fd_is_mount_point(fd, de->d_name, 0); - if (r < 0) { - if (ret == 0 && r != -ENOENT) - ret = r; - - safe_close(subdir_fd); - continue; - } - if (r) { - safe_close(subdir_fd); - continue; - } - - if ((flags & REMOVE_SUBVOLUME) && st.st_ino == 256) { - - /* This could be a subvolume, try to remove it */ - - r = btrfs_subvol_remove_fd(fd, de->d_name, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); - if (r < 0) { - if (r != -ENOTTY && r != -EINVAL) { - if (ret == 0) - ret = r; - - safe_close(subdir_fd); - continue; - } - - /* ENOTTY, then it wasn't a - * btrfs subvolume, continue - * below. */ - } else { - /* It was a subvolume, continue. */ - safe_close(subdir_fd); - continue; - } - } - - /* We pass REMOVE_PHYSICAL here, to avoid - * doing the fstatfs() to check the file - * system type again for each directory */ - r = rm_rf_children(subdir_fd, flags | REMOVE_PHYSICAL, root_dev); - if (r < 0 && ret == 0) - ret = r; - - if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) { - if (ret == 0 && errno != ENOENT) - ret = -errno; - } - - } else if (!(flags & REMOVE_ONLY_DIRECTORIES)) { - - if (unlinkat(fd, de->d_name, 0) < 0) { - if (ret == 0 && errno != ENOENT) - ret = -errno; - } - } - } -} - -int rm_rf(const char *path, RemoveFlags flags) { - int fd, r; - struct statfs s; - - assert(path); - - /* We refuse to clean the root file system with this - * call. This is extra paranoia to never cause a really - * seriously broken system. */ - if (path_equal(path, "/")) { - log_error("Attempted to remove entire root file system, and we can't allow that."); - return -EPERM; - } - - if ((flags & (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) == (REMOVE_SUBVOLUME|REMOVE_ROOT|REMOVE_PHYSICAL)) { - /* Try to remove as subvolume first */ - r = btrfs_subvol_remove(path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); - if (r >= 0) - return r; - - if (r != -ENOTTY && r != -EINVAL && r != -ENOTDIR) - return r; - - /* Not btrfs or not a subvolume */ - } - - fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); - if (fd < 0) { - - if (errno != ENOTDIR && errno != ELOOP) - return -errno; - - if (!(flags & REMOVE_PHYSICAL)) { - if (statfs(path, &s) < 0) - return -errno; - - if (is_physical_fs(&s)) { - log_error("Attempted to remove disk file system, and we can't allow that."); - return -EPERM; - } - } - - if ((flags & REMOVE_ROOT) && !(flags & REMOVE_ONLY_DIRECTORIES)) - if (unlink(path) < 0 && errno != ENOENT) - return -errno; - - return 0; - } - - r = rm_rf_children(fd, flags, NULL); - - if (flags & REMOVE_ROOT) { - if (rmdir(path) < 0) { - if (r == 0 && errno != ENOENT) - r = -errno; - } - } - - return r; -} diff --git a/src/basic/rm-rf.h b/src/basic/rm-rf.h deleted file mode 100644 index f693a5bb7c..0000000000 --- a/src/basic/rm-rf.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2015 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 <sys/stat.h> - -typedef enum RemoveFlags { - REMOVE_ONLY_DIRECTORIES = 1, - REMOVE_ROOT = 2, - REMOVE_PHYSICAL = 4, /* if not set, only removes files on tmpfs, never physical file systems */ - REMOVE_SUBVOLUME = 8, -} RemoveFlags; - -int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev); -int rm_rf(const char *path, RemoveFlags flags); - -/* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */ -static inline void rm_rf_physical_and_free(char *p) { - if (!p) - return; - (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL); - free(p); -} -DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rm_rf_physical_and_free); diff --git a/src/basic/securebits.h b/src/basic/securebits.h deleted file mode 100644 index 98fbe0d433..0000000000 --- a/src/basic/securebits.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef _LINUX_SECUREBITS_H -#define _LINUX_SECUREBITS_H 1 - -/* This is minimal version of Linux' linux/securebits.h header file, - * which is licensed GPL2 */ - -#define SECUREBITS_DEFAULT 0x00000000 - -/* When set UID 0 has no special privileges. When unset, we support - inheritance of root-permissions and suid-root executable under - compatibility mode. We raise the effective and inheritable bitmasks - *of the executable file* if the effective uid of the new process is - 0. If the real uid is 0, we raise the effective (legacy) bit of the - executable file. */ -#define SECURE_NOROOT 0 -#define SECURE_NOROOT_LOCKED 1 /* make bit-0 immutable */ - -/* When set, setuid to/from uid 0 does not trigger capability-"fixup". - When unset, to provide compatibility with old programs relying on - set*uid to gain/lose privilege, transitions to/from uid 0 cause - capabilities to be gained/lost. */ -#define SECURE_NO_SETUID_FIXUP 2 -#define SECURE_NO_SETUID_FIXUP_LOCKED 3 /* make bit-2 immutable */ - -/* When set, a process can retain its capabilities even after - transitioning to a non-root user (the set-uid fixup suppressed by - bit 2). Bit-4 is cleared when a process calls exec(); setting both - bit 4 and 5 will create a barrier through exec that no exec()'d - child can use this feature again. */ -#define SECURE_KEEP_CAPS 4 -#define SECURE_KEEP_CAPS_LOCKED 5 /* make bit-4 immutable */ - -/* Each securesetting is implemented using two bits. One bit specifies - whether the setting is on or off. The other bit specify whether the - setting is locked or not. A setting which is locked cannot be - changed from user-level. */ -#define issecure_mask(X) (1 << (X)) -#define issecure(X) (issecure_mask(X) & current_cred_xxx(securebits)) - -#define SECURE_ALL_BITS (issecure_mask(SECURE_NOROOT) | \ - issecure_mask(SECURE_NO_SETUID_FIXUP) | \ - issecure_mask(SECURE_KEEP_CAPS)) -#define SECURE_ALL_LOCKS (SECURE_ALL_BITS << 1) - -#endif /* !_LINUX_SECUREBITS_H */ diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c deleted file mode 100644 index bc07654668..0000000000 --- a/src/basic/selinux-util.c +++ /dev/null @@ -1,485 +0,0 @@ -/*** - 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 <errno.h> -#include <malloc.h> -#include <stddef.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/un.h> -#include <syslog.h> - -#ifdef HAVE_SELINUX -#include <selinux/context.h> -#include <selinux/label.h> -#include <selinux/selinux.h> -#endif - -#include "alloc-util.h" -#include "log.h" -#include "macro.h" -#include "path-util.h" -#include "selinux-util.h" -#include "time-util.h" -#include "util.h" - -#ifdef HAVE_SELINUX -DEFINE_TRIVIAL_CLEANUP_FUNC(char*, freecon); -DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free); - -#define _cleanup_freecon_ _cleanup_(freeconp) -#define _cleanup_context_free_ _cleanup_(context_freep) - -static int cached_use = -1; -static struct selabel_handle *label_hnd = NULL; - -#define log_enforcing(...) log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG, __VA_ARGS__) -#endif - -bool mac_selinux_have(void) { -#ifdef HAVE_SELINUX - if (cached_use < 0) - cached_use = is_selinux_enabled() > 0; - - return cached_use; -#else - return false; -#endif -} - -bool mac_selinux_use(void) { - if (!mac_selinux_have()) - return false; - - /* Never try to configure SELinux features if we aren't - * root */ - - return getuid() == 0; -} - -void mac_selinux_retest(void) { -#ifdef HAVE_SELINUX - cached_use = -1; -#endif -} - -int mac_selinux_init(void) { - int r = 0; - -#ifdef HAVE_SELINUX - usec_t before_timestamp, after_timestamp; - struct mallinfo before_mallinfo, after_mallinfo; - - if (label_hnd) - return 0; - - if (!mac_selinux_use()) - return 0; - - before_mallinfo = mallinfo(); - before_timestamp = now(CLOCK_MONOTONIC); - - label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0); - if (!label_hnd) { - log_enforcing("Failed to initialize SELinux context: %m"); - r = security_getenforce() == 1 ? -errno : 0; - } else { - char timespan[FORMAT_TIMESPAN_MAX]; - int l; - - after_timestamp = now(CLOCK_MONOTONIC); - after_mallinfo = mallinfo(); - - l = after_mallinfo.uordblks > before_mallinfo.uordblks ? after_mallinfo.uordblks - before_mallinfo.uordblks : 0; - - log_debug("Successfully loaded SELinux database in %s, size on heap is %iK.", - format_timespan(timespan, sizeof(timespan), after_timestamp - before_timestamp, 0), - (l+1023)/1024); - } -#endif - - return r; -} - -void mac_selinux_finish(void) { - -#ifdef HAVE_SELINUX - if (!label_hnd) - return; - - selabel_close(label_hnd); - label_hnd = NULL; -#endif -} - -int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { - -#ifdef HAVE_SELINUX - struct stat st; - int r; - - assert(path); - - /* if mac_selinux_init() wasn't called before we are a NOOP */ - if (!label_hnd) - return 0; - - r = lstat(path, &st); - if (r >= 0) { - _cleanup_freecon_ char* fcon = NULL; - - r = selabel_lookup_raw(label_hnd, &fcon, path, st.st_mode); - - /* If there's no label to set, then exit without warning */ - if (r < 0 && errno == ENOENT) - return 0; - - if (r >= 0) { - r = lsetfilecon_raw(path, fcon); - - /* If the FS doesn't support labels, then exit without warning */ - if (r < 0 && errno == EOPNOTSUPP) - return 0; - } - } - - if (r < 0) { - /* Ignore ENOENT in some cases */ - if (ignore_enoent && errno == ENOENT) - return 0; - - if (ignore_erofs && errno == EROFS) - return 0; - - log_enforcing("Unable to fix SELinux security context of %s: %m", path); - if (security_getenforce() == 1) - return -errno; - } -#endif - - return 0; -} - -int mac_selinux_apply(const char *path, const char *label) { - -#ifdef HAVE_SELINUX - if (!mac_selinux_use()) - return 0; - - assert(path); - assert(label); - - if (setfilecon(path, label) < 0) { - log_enforcing("Failed to set SELinux security context %s on path %s: %m", label, path); - if (security_getenforce() > 0) - return -errno; - } -#endif - return 0; -} - -int mac_selinux_get_create_label_from_exe(const char *exe, char **label) { - int r = -EOPNOTSUPP; - -#ifdef HAVE_SELINUX - _cleanup_freecon_ char *mycon = NULL, *fcon = NULL; - security_class_t sclass; - - assert(exe); - assert(label); - - if (!mac_selinux_have()) - return -EOPNOTSUPP; - - r = getcon_raw(&mycon); - if (r < 0) - return -errno; - - r = getfilecon_raw(exe, &fcon); - if (r < 0) - return -errno; - - sclass = string_to_security_class("process"); - r = security_compute_create_raw(mycon, fcon, sclass, label); - if (r < 0) - return -errno; -#endif - - return r; -} - -int mac_selinux_get_our_label(char **label) { - int r = -EOPNOTSUPP; - - assert(label); - -#ifdef HAVE_SELINUX - if (!mac_selinux_have()) - return -EOPNOTSUPP; - - r = getcon_raw(label); - if (r < 0) - return -errno; -#endif - - return r; -} - -int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) { - int r = -EOPNOTSUPP; - -#ifdef HAVE_SELINUX - _cleanup_freecon_ char *mycon = NULL, *peercon = NULL, *fcon = NULL; - _cleanup_context_free_ context_t pcon = NULL, bcon = NULL; - security_class_t sclass; - const char *range = NULL; - - assert(socket_fd >= 0); - assert(exe); - assert(label); - - if (!mac_selinux_have()) - return -EOPNOTSUPP; - - r = getcon_raw(&mycon); - if (r < 0) - return -errno; - - r = getpeercon_raw(socket_fd, &peercon); - if (r < 0) - return -errno; - - if (!exec_label) { - /* If there is no context set for next exec let's use context - of target executable */ - r = getfilecon_raw(exe, &fcon); - if (r < 0) - return -errno; - } - - bcon = context_new(mycon); - if (!bcon) - return -ENOMEM; - - pcon = context_new(peercon); - if (!pcon) - return -ENOMEM; - - range = context_range_get(pcon); - if (!range) - return -errno; - - r = context_range_set(bcon, range); - if (r) - return -errno; - - freecon(mycon); - mycon = strdup(context_str(bcon)); - if (!mycon) - return -ENOMEM; - - sclass = string_to_security_class("process"); - r = security_compute_create_raw(mycon, fcon, sclass, label); - if (r < 0) - return -errno; -#endif - - return r; -} - -char* mac_selinux_free(char *label) { - -#ifdef HAVE_SELINUX - if (!label) - return NULL; - - if (!mac_selinux_have()) - return NULL; - - - freecon(label); -#endif - - return NULL; -} - -int mac_selinux_create_file_prepare(const char *path, mode_t mode) { - -#ifdef HAVE_SELINUX - _cleanup_freecon_ char *filecon = NULL; - int r; - - assert(path); - - if (!label_hnd) - return 0; - - if (path_is_absolute(path)) - r = selabel_lookup_raw(label_hnd, &filecon, path, mode); - else { - _cleanup_free_ char *newpath = NULL; - - r = path_make_absolute_cwd(path, &newpath); - if (r < 0) - return r; - - r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode); - } - - if (r < 0) { - /* No context specified by the policy? Proceed without setting it. */ - if (errno == ENOENT) - return 0; - - log_enforcing("Failed to determine SELinux security context for %s: %m", path); - } else { - if (setfscreatecon_raw(filecon) >= 0) - return 0; /* Success! */ - - log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path); - } - - if (security_getenforce() > 0) - return -errno; - -#endif - return 0; -} - -void mac_selinux_create_file_clear(void) { - -#ifdef HAVE_SELINUX - PROTECT_ERRNO; - - if (!mac_selinux_use()) - return; - - setfscreatecon_raw(NULL); -#endif -} - -int mac_selinux_create_socket_prepare(const char *label) { - -#ifdef HAVE_SELINUX - if (!mac_selinux_use()) - return 0; - - assert(label); - - if (setsockcreatecon(label) < 0) { - log_enforcing("Failed to set SELinux security context %s for sockets: %m", label); - - if (security_getenforce() == 1) - return -errno; - } -#endif - - return 0; -} - -void mac_selinux_create_socket_clear(void) { - -#ifdef HAVE_SELINUX - PROTECT_ERRNO; - - if (!mac_selinux_use()) - return; - - setsockcreatecon_raw(NULL); -#endif -} - -int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { - - /* Binds a socket and label its file system object according to the SELinux policy */ - -#ifdef HAVE_SELINUX - _cleanup_freecon_ char *fcon = NULL; - const struct sockaddr_un *un; - bool context_changed = false; - char *path; - int r; - - assert(fd >= 0); - assert(addr); - assert(addrlen >= sizeof(sa_family_t)); - - if (!label_hnd) - goto skipped; - - /* Filter out non-local sockets */ - if (addr->sa_family != AF_UNIX) - goto skipped; - - /* Filter out anonymous sockets */ - if (addrlen < offsetof(struct sockaddr_un, sun_path) + 1) - goto skipped; - - /* Filter out abstract namespace sockets */ - un = (const struct sockaddr_un*) addr; - if (un->sun_path[0] == 0) - goto skipped; - - path = strndupa(un->sun_path, addrlen - offsetof(struct sockaddr_un, sun_path)); - - if (path_is_absolute(path)) - r = selabel_lookup_raw(label_hnd, &fcon, path, S_IFSOCK); - else { - _cleanup_free_ char *newpath = NULL; - - r = path_make_absolute_cwd(path, &newpath); - if (r < 0) - return r; - - r = selabel_lookup_raw(label_hnd, &fcon, newpath, S_IFSOCK); - } - - if (r < 0) { - /* No context specified by the policy? Proceed without setting it */ - if (errno == ENOENT) - goto skipped; - - log_enforcing("Failed to determine SELinux security context for %s: %m", path); - if (security_getenforce() > 0) - return -errno; - - } else { - if (setfscreatecon_raw(fcon) < 0) { - log_enforcing("Failed to set SELinux security context %s for %s: %m", fcon, path); - if (security_getenforce() > 0) - return -errno; - } else - context_changed = true; - } - - r = bind(fd, addr, addrlen) < 0 ? -errno : 0; - - if (context_changed) - setfscreatecon_raw(NULL); - - return r; - -skipped: -#endif - if (bind(fd, addr, addrlen) < 0) - return -errno; - - return 0; -} diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h deleted file mode 100644 index ce6bc8e44c..0000000000 --- a/src/basic/selinux-util.h +++ /dev/null @@ -1,51 +0,0 @@ -#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/socket.h> -#include <sys/types.h> - -#include "macro.h" - -bool mac_selinux_use(void); -bool mac_selinux_have(void); -void mac_selinux_retest(void); - -int mac_selinux_init(void); -void mac_selinux_finish(void); - -int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs); -int mac_selinux_apply(const char *path, const char *label); - -int mac_selinux_get_create_label_from_exe(const char *exe, char **label); -int mac_selinux_get_our_label(char **label); -int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label); -char* mac_selinux_free(char *label); - -int mac_selinux_create_file_prepare(const char *path, mode_t mode); -void mac_selinux_create_file_clear(void); - -int mac_selinux_create_socket_prepare(const char *label); -void mac_selinux_create_socket_clear(void); - -int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen); - -DEFINE_TRIVIAL_CLEANUP_FUNC(char*, mac_selinux_free); diff --git a/src/basic/set.h b/src/basic/set.h deleted file mode 100644 index a5f8beb0c4..0000000000 --- a/src/basic/set.h +++ /dev/null @@ -1,138 +0,0 @@ -#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 "extract-word.h" -#include "hashmap.h" -#include "macro.h" - -Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS) - -static inline Set *set_free(Set *s) { - internal_hashmap_free(HASHMAP_BASE(s)); - return NULL; -} - -static inline Set *set_free_free(Set *s) { - internal_hashmap_free_free(HASHMAP_BASE(s)); - return NULL; -} - -/* no set_free_free_free */ - -static inline Set *set_copy(Set *s) { - return (Set*) internal_hashmap_copy(HASHMAP_BASE(s)); -} - -int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) - -int set_put(Set *s, const void *key); -/* no set_update */ -/* no set_replace */ -static inline void *set_get(Set *s, void *key) { - return internal_hashmap_get(HASHMAP_BASE(s), key); -} -/* no set_get2 */ - -static inline bool set_contains(Set *s, const void *key) { - return internal_hashmap_contains(HASHMAP_BASE(s), key); -} - -static inline void *set_remove(Set *s, const void *key) { - return internal_hashmap_remove(HASHMAP_BASE(s), key); -} - -/* no set_remove2 */ -/* no set_remove_value */ -int set_remove_and_put(Set *s, const void *old_key, const void *new_key); -/* no set_remove_and_replace */ -int set_merge(Set *s, Set *other); - -static inline int set_reserve(Set *h, unsigned entries_add) { - return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); -} - -static inline int set_move(Set *s, Set *other) { - return internal_hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other)); -} - -static inline int set_move_one(Set *s, Set *other, const void *key) { - return internal_hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key); -} - -static inline unsigned set_size(Set *s) { - return internal_hashmap_size(HASHMAP_BASE(s)); -} - -static inline bool set_isempty(Set *s) { - return set_size(s) == 0; -} - -static inline unsigned set_buckets(Set *s) { - return internal_hashmap_buckets(HASHMAP_BASE(s)); -} - -bool set_iterate(Set *s, Iterator *i, void **value); - -static inline void set_clear(Set *s) { - internal_hashmap_clear(HASHMAP_BASE(s)); -} - -static inline void set_clear_free(Set *s) { - internal_hashmap_clear_free(HASHMAP_BASE(s)); -} - -/* no set_clear_free_free */ - -static inline void *set_steal_first(Set *s) { - return internal_hashmap_steal_first(HASHMAP_BASE(s)); -} - -/* no set_steal_first_key */ -/* no set_first_key */ - -static inline void *set_first(Set *s) { - return internal_hashmap_first(HASHMAP_BASE(s)); -} - -/* no set_next */ - -static inline char **set_get_strv(Set *s) { - return internal_hashmap_get_strv(HASHMAP_BASE(s)); -} - -int set_consume(Set *s, void *value); -int set_put_strdup(Set *s, const char *p); -int set_put_strdupv(Set *s, char **l); -int set_put_strsplit(Set *s, const char *v, const char *separators, ExtractFlags flags); - -#define SET_FOREACH(e, s, i) \ - for ((i) = ITERATOR_FIRST; set_iterate((s), &(i), (void**)&(e)); ) - -#define SET_FOREACH_MOVE(e, d, s) \ - for (; ({ e = set_first(s); assert_se(!e || set_move_one(d, s, e) >= 0); e; }); ) - -DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free); -DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free); - -#define _cleanup_set_free_ _cleanup_(set_freep) -#define _cleanup_set_free_free_ _cleanup_(set_free_freep) diff --git a/src/basic/sigbus.c b/src/basic/sigbus.c deleted file mode 100644 index 0ce4f75684..0000000000 --- a/src/basic/sigbus.c +++ /dev/null @@ -1,152 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 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 <errno.h> -#include <signal.h> -#include <stddef.h> -#include <sys/mman.h> - -#include "macro.h" -#include "sigbus.h" -#include "util.h" - -#define SIGBUS_QUEUE_MAX 64 - -static struct sigaction old_sigaction; -static unsigned n_installed = 0; - -/* We maintain a fixed size list of page addresses that triggered a - SIGBUS. We access with list with atomic operations, so that we - don't have to deal with locks between signal handler and main - programs in possibly multiple threads. */ - -static void* volatile sigbus_queue[SIGBUS_QUEUE_MAX]; -static volatile sig_atomic_t n_sigbus_queue = 0; - -static void sigbus_push(void *addr) { - unsigned u; - - assert(addr); - - /* Find a free place, increase the number of entries and leave, if we can */ - for (u = 0; u < SIGBUS_QUEUE_MAX; u++) - if (__sync_bool_compare_and_swap(&sigbus_queue[u], NULL, addr)) { - __sync_fetch_and_add(&n_sigbus_queue, 1); - return; - } - - /* If we can't, make sure the queue size is out of bounds, to - * mark it as overflow */ - for (;;) { - unsigned c; - - __sync_synchronize(); - c = n_sigbus_queue; - - if (c > SIGBUS_QUEUE_MAX) /* already overflow */ - return; - - if (__sync_bool_compare_and_swap(&n_sigbus_queue, c, c + SIGBUS_QUEUE_MAX)) - return; - } -} - -int sigbus_pop(void **ret) { - assert(ret); - - for (;;) { - unsigned u, c; - - __sync_synchronize(); - c = n_sigbus_queue; - - if (_likely_(c == 0)) - return 0; - - if (_unlikely_(c >= SIGBUS_QUEUE_MAX)) - return -EOVERFLOW; - - for (u = 0; u < SIGBUS_QUEUE_MAX; u++) { - void *addr; - - addr = sigbus_queue[u]; - if (!addr) - continue; - - if (__sync_bool_compare_and_swap(&sigbus_queue[u], addr, NULL)) { - __sync_fetch_and_sub(&n_sigbus_queue, 1); - *ret = addr; - return 1; - } - } - } -} - -static void sigbus_handler(int sn, siginfo_t *si, void *data) { - unsigned long ul; - void *aligned; - - assert(sn == SIGBUS); - assert(si); - - if (si->si_code != BUS_ADRERR || !si->si_addr) { - assert_se(sigaction(SIGBUS, &old_sigaction, NULL) == 0); - raise(SIGBUS); - return; - } - - ul = (unsigned long) si->si_addr; - ul = ul / page_size(); - ul = ul * page_size(); - aligned = (void*) ul; - - /* Let's remember which address failed */ - sigbus_push(aligned); - - /* Replace mapping with an anonymous page, so that the - * execution can continue, however with a zeroed out page */ - assert_se(mmap(aligned, page_size(), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0) == aligned); -} - -void sigbus_install(void) { - struct sigaction sa = { - .sa_sigaction = sigbus_handler, - .sa_flags = SA_SIGINFO, - }; - - n_installed++; - - if (n_installed == 1) - assert_se(sigaction(SIGBUS, &sa, &old_sigaction) == 0); - - return; -} - -void sigbus_reset(void) { - - if (n_installed <= 0) - return; - - n_installed--; - - if (n_installed == 0) - assert_se(sigaction(SIGBUS, &old_sigaction, NULL) == 0); - - return; -} diff --git a/src/basic/sigbus.h b/src/basic/sigbus.h deleted file mode 100644 index 980243d9ce..0000000000 --- a/src/basic/sigbus.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 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/>. -***/ - -void sigbus_install(void); -void sigbus_reset(void); - -int sigbus_pop(void **ret); diff --git a/src/basic/signal-util.c b/src/basic/signal-util.c deleted file mode 100644 index 280b5c3251..0000000000 --- a/src/basic/signal-util.c +++ /dev/null @@ -1,278 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 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 <errno.h> -#include <stdarg.h> -#include <stdio.h> - -#include "macro.h" -#include "parse-util.h" -#include "signal-util.h" -#include "stdio-util.h" -#include "string-table.h" -#include "string-util.h" - -int reset_all_signal_handlers(void) { - static const struct sigaction sa = { - .sa_handler = SIG_DFL, - .sa_flags = SA_RESTART, - }; - int sig, r = 0; - - for (sig = 1; sig < _NSIG; sig++) { - - /* These two cannot be caught... */ - if (sig == SIGKILL || sig == SIGSTOP) - continue; - - /* On Linux the first two RT signals are reserved by - * glibc, and sigaction() will return EINVAL for them. */ - if ((sigaction(sig, &sa, NULL) < 0)) - if (errno != EINVAL && r >= 0) - r = -errno; - } - - return r; -} - -int reset_signal_mask(void) { - sigset_t ss; - - if (sigemptyset(&ss) < 0) - return -errno; - - if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0) - return -errno; - - return 0; -} - -static int sigaction_many_ap(const struct sigaction *sa, int sig, va_list ap) { - int r = 0; - - /* negative signal ends the list. 0 signal is skipped. */ - - if (sig < 0) - return 0; - - if (sig > 0) { - if (sigaction(sig, sa, NULL) < 0) - r = -errno; - } - - while ((sig = va_arg(ap, int)) >= 0) { - - if (sig == 0) - continue; - - if (sigaction(sig, sa, NULL) < 0) { - if (r >= 0) - r = -errno; - } - } - - return r; -} - -int sigaction_many(const struct sigaction *sa, ...) { - va_list ap; - int r; - - va_start(ap, sa); - r = sigaction_many_ap(sa, 0, ap); - va_end(ap); - - return r; -} - -int ignore_signals(int sig, ...) { - - static const struct sigaction sa = { - .sa_handler = SIG_IGN, - .sa_flags = SA_RESTART, - }; - - va_list ap; - int r; - - va_start(ap, sig); - r = sigaction_many_ap(&sa, sig, ap); - va_end(ap); - - return r; -} - -int default_signals(int sig, ...) { - - static const struct sigaction sa = { - .sa_handler = SIG_DFL, - .sa_flags = SA_RESTART, - }; - - va_list ap; - int r; - - va_start(ap, sig); - r = sigaction_many_ap(&sa, sig, ap); - va_end(ap); - - return r; -} - -static int sigset_add_many_ap(sigset_t *ss, va_list ap) { - int sig, r = 0; - - assert(ss); - - while ((sig = va_arg(ap, int)) >= 0) { - - if (sig == 0) - continue; - - if (sigaddset(ss, sig) < 0) { - if (r >= 0) - r = -errno; - } - } - - return r; -} - -int sigset_add_many(sigset_t *ss, ...) { - va_list ap; - int r; - - va_start(ap, ss); - r = sigset_add_many_ap(ss, ap); - va_end(ap); - - return r; -} - -int sigprocmask_many(int how, sigset_t *old, ...) { - va_list ap; - sigset_t ss; - int r; - - if (sigemptyset(&ss) < 0) - return -errno; - - va_start(ap, old); - r = sigset_add_many_ap(&ss, ap); - va_end(ap); - - if (r < 0) - return r; - - if (sigprocmask(how, &ss, old) < 0) - return -errno; - - return 0; -} - -static const char *const __signal_table[] = { - [SIGHUP] = "HUP", - [SIGINT] = "INT", - [SIGQUIT] = "QUIT", - [SIGILL] = "ILL", - [SIGTRAP] = "TRAP", - [SIGABRT] = "ABRT", - [SIGBUS] = "BUS", - [SIGFPE] = "FPE", - [SIGKILL] = "KILL", - [SIGUSR1] = "USR1", - [SIGSEGV] = "SEGV", - [SIGUSR2] = "USR2", - [SIGPIPE] = "PIPE", - [SIGALRM] = "ALRM", - [SIGTERM] = "TERM", -#ifdef SIGSTKFLT - [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */ -#endif - [SIGCHLD] = "CHLD", - [SIGCONT] = "CONT", - [SIGSTOP] = "STOP", - [SIGTSTP] = "TSTP", - [SIGTTIN] = "TTIN", - [SIGTTOU] = "TTOU", - [SIGURG] = "URG", - [SIGXCPU] = "XCPU", - [SIGXFSZ] = "XFSZ", - [SIGVTALRM] = "VTALRM", - [SIGPROF] = "PROF", - [SIGWINCH] = "WINCH", - [SIGIO] = "IO", - [SIGPWR] = "PWR", - [SIGSYS] = "SYS" -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int); - -const char *signal_to_string(int signo) { - static thread_local char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1]; - const char *name; - - name = __signal_to_string(signo); - if (name) - return name; - - if (signo >= SIGRTMIN && signo <= SIGRTMAX) - xsprintf(buf, "RTMIN+%d", signo - SIGRTMIN); - else - xsprintf(buf, "%d", signo); - - return buf; -} - -int signal_from_string(const char *s) { - int signo; - int offset = 0; - unsigned u; - - signo = __signal_from_string(s); - if (signo > 0) - return signo; - - if (startswith(s, "RTMIN+")) { - s += 6; - offset = SIGRTMIN; - } - if (safe_atou(s, &u) >= 0) { - signo = (int) u + offset; - if (SIGNAL_VALID(signo)) - return signo; - } - return -EINVAL; -} - -int signal_from_string_try_harder(const char *s) { - int signo; - assert(s); - - signo = signal_from_string(s); - if (signo <= 0) - if (startswith(s, "SIG")) - return signal_from_string(s+3); - - return signo; -} - -void nop_signal_handler(int sig) { - /* nothing here */ -} diff --git a/src/basic/signal-util.h b/src/basic/signal-util.h deleted file mode 100644 index dfd6eb564d..0000000000 --- a/src/basic/signal-util.h +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010-2015 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 <signal.h> - -#include "macro.h" - -int reset_all_signal_handlers(void); -int reset_signal_mask(void); - -int ignore_signals(int sig, ...); -int default_signals(int sig, ...); -int sigaction_many(const struct sigaction *sa, ...); - -int sigset_add_many(sigset_t *ss, ...); -int sigprocmask_many(int how, sigset_t *old, ...); - -const char *signal_to_string(int i) _const_; -int signal_from_string(const char *s) _pure_; - -int signal_from_string_try_harder(const char *s); - -void nop_signal_handler(int sig); - -static inline void block_signals_reset(sigset_t *ss) { - assert_se(sigprocmask(SIG_SETMASK, ss, NULL) >= 0); -} - -#define BLOCK_SIGNALS(...) \ - _cleanup_(block_signals_reset) _unused_ sigset_t _saved_sigset = ({ \ - sigset_t t; \ - assert_se(sigprocmask_many(SIG_BLOCK, &t, __VA_ARGS__, -1) >= 0); \ - t; \ - }) - -static inline bool SIGNAL_VALID(int signo) { - return signo > 0 && signo < _NSIG; -} diff --git a/src/basic/siphash24.c b/src/basic/siphash24.c deleted file mode 100644 index 8c1cdc3db6..0000000000 --- a/src/basic/siphash24.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - SipHash reference C implementation - - Written in 2012 by - Jean-Philippe Aumasson <jeanphilippe.aumasson@gmail.com> - Daniel J. Bernstein <djb@cr.yp.to> - - To the extent possible under law, the author(s) have dedicated all copyright - and related and neighboring rights to this software to the public domain - worldwide. This software is distributed without any warranty. - - You should have received a copy of the CC0 Public Domain Dedication along with - this software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>. - - (Minimal changes made by Lennart Poettering, to make clean for inclusion in systemd) - (Refactored by Tom Gundersen to split up in several functions and follow systemd - coding style) -*/ - -#include <stdio.h> - -#include "macro.h" -#include "siphash24.h" -#include "unaligned.h" - -static inline uint64_t rotate_left(uint64_t x, uint8_t b) { - assert(b < 64); - - return (x << b) | (x >> (64 - b)); -} - -static inline void sipround(struct siphash *state) { - assert(state); - - state->v0 += state->v1; - state->v1 = rotate_left(state->v1, 13); - state->v1 ^= state->v0; - state->v0 = rotate_left(state->v0, 32); - state->v2 += state->v3; - state->v3 = rotate_left(state->v3, 16); - state->v3 ^= state->v2; - state->v0 += state->v3; - state->v3 = rotate_left(state->v3, 21); - state->v3 ^= state->v0; - state->v2 += state->v1; - state->v1 = rotate_left(state->v1, 17); - state->v1 ^= state->v2; - state->v2 = rotate_left(state->v2, 32); -} - -void siphash24_init(struct siphash *state, const uint8_t k[16]) { - uint64_t k0, k1; - - assert(state); - assert(k); - - 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) { - - const uint8_t *in = _in; - const uint8_t *end = in + inlen; - size_t left = state->inlen & 7; - uint64_t m; - - assert(in); - assert(state); - - /* Update total length */ - state->inlen += inlen; - - /* If padding exists, fill it out */ - if (left > 0) { - 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 */ - return; - -#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) 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); - state->v0 ^= state->padding; - - state->padding = 0; - } - - end -= (state->inlen % sizeof(uint64_t)); - - 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); - 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) compress %08x %08x\n", state->inlen, (uint32_t) (m >> 32), (uint32_t) m); -#endif - state->v3 ^= m; - sipround(state); - sipround(state); - state->v0 ^= m; - } - - 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]); - case 0: - break; - } -} - -uint64_t siphash24_finalize(struct siphash *state) { - uint64_t b; - - 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) padding %08x %08x\n", state->inlen, (uint32_t) (state->padding >> 32), (uint32_t) state->padding); -#endif - - state->v3 ^= b; - sipround(state); - sipround(state); - state->v0 ^= b; - -#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); -#endif - state->v2 ^= 0xff; - - sipround(state); - sipround(state); - sipround(state); - sipround(state); - - return state->v0 ^ state->v1 ^ state->v2 ^ state->v3; -} - -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); - - return siphash24_finalize(&state); -} diff --git a/src/basic/siphash24.h b/src/basic/siphash24.h deleted file mode 100644 index 54e2420cc6..0000000000 --- a/src/basic/siphash24.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include <inttypes.h> -#include <stddef.h> -#include <stdint.h> -#include <sys/types.h> - -struct siphash { - 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); -#define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state)) - -uint64_t siphash24_finalize(struct siphash *state); - -uint64_t siphash24(const void *in, size_t inlen, const uint8_t k[16]); diff --git a/src/basic/smack-util.c b/src/basic/smack-util.c deleted file mode 100644 index 3a3df987df..0000000000 --- a/src/basic/smack-util.c +++ /dev/null @@ -1,241 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Intel Corporation - - Author: Auke Kok <auke-jan.h.kok@intel.com> - - 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 <errno.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/xattr.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "fileio.h" -#include "log.h" -#include "macro.h" -#include "path-util.h" -#include "process-util.h" -#include "smack-util.h" -#include "string-table.h" -#include "xattr-util.h" - -#ifdef HAVE_SMACK -bool mac_smack_use(void) { - static int cached_use = -1; - - if (cached_use < 0) - cached_use = access("/sys/fs/smackfs/", F_OK) >= 0; - - return cached_use; -} - -static const char* const smack_attr_table[_SMACK_ATTR_MAX] = { - [SMACK_ATTR_ACCESS] = "security.SMACK64", - [SMACK_ATTR_EXEC] = "security.SMACK64EXEC", - [SMACK_ATTR_MMAP] = "security.SMACK64MMAP", - [SMACK_ATTR_TRANSMUTE] = "security.SMACK64TRANSMUTE", - [SMACK_ATTR_IPIN] = "security.SMACK64IPIN", - [SMACK_ATTR_IPOUT] = "security.SMACK64IPOUT", -}; - -DEFINE_STRING_TABLE_LOOKUP(smack_attr, SmackAttr); - -int mac_smack_read(const char *path, SmackAttr attr, char **label) { - assert(path); - assert(attr >= 0 && attr < _SMACK_ATTR_MAX); - assert(label); - - if (!mac_smack_use()) - return 0; - - return getxattr_malloc(path, smack_attr_to_string(attr), label, true); -} - -int mac_smack_read_fd(int fd, SmackAttr attr, char **label) { - assert(fd >= 0); - assert(attr >= 0 && attr < _SMACK_ATTR_MAX); - assert(label); - - if (!mac_smack_use()) - return 0; - - return fgetxattr_malloc(fd, smack_attr_to_string(attr), label); -} - -int mac_smack_apply(const char *path, SmackAttr attr, const char *label) { - int r; - - assert(path); - assert(attr >= 0 && attr < _SMACK_ATTR_MAX); - - if (!mac_smack_use()) - return 0; - - if (label) - r = lsetxattr(path, smack_attr_to_string(attr), label, strlen(label), 0); - else - r = lremovexattr(path, smack_attr_to_string(attr)); - if (r < 0) - return -errno; - - return 0; -} - -int mac_smack_apply_fd(int fd, SmackAttr attr, const char *label) { - int r; - - assert(fd >= 0); - assert(attr >= 0 && attr < _SMACK_ATTR_MAX); - - if (!mac_smack_use()) - return 0; - - if (label) - r = fsetxattr(fd, smack_attr_to_string(attr), label, strlen(label), 0); - else - r = fremovexattr(fd, smack_attr_to_string(attr)); - if (r < 0) - return -errno; - - return 0; -} - -int mac_smack_apply_pid(pid_t pid, const char *label) { - const char *p; - int r = 0; - - assert(label); - - if (!mac_smack_use()) - return 0; - - p = procfs_file_alloca(pid, "attr/current"); - r = write_string_file(p, label, 0); - if (r < 0) - return r; - - return r; -} - -int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { - struct stat st; - int r = 0; - - assert(path); - - if (!mac_smack_use()) - return 0; - - /* - * Path must be in /dev and must exist - */ - if (!path_startswith(path, "/dev")) - return 0; - - r = lstat(path, &st); - if (r >= 0) { - const char *label; - - /* - * Label directories and character devices "*". - * Label symlinks "_". - * Don't change anything else. - */ - - if (S_ISDIR(st.st_mode)) - label = SMACK_STAR_LABEL; - else if (S_ISLNK(st.st_mode)) - label = SMACK_FLOOR_LABEL; - else if (S_ISCHR(st.st_mode)) - label = SMACK_STAR_LABEL; - else - return 0; - - r = lsetxattr(path, "security.SMACK64", label, strlen(label), 0); - - /* If the FS doesn't support labels, then exit without warning */ - if (r < 0 && errno == EOPNOTSUPP) - return 0; - } - - if (r < 0) { - /* Ignore ENOENT in some cases */ - if (ignore_enoent && errno == ENOENT) - return 0; - - if (ignore_erofs && errno == EROFS) - return 0; - - r = log_debug_errno(errno, "Unable to fix SMACK label of %s: %m", path); - } - - return r; -} - -int mac_smack_copy(const char *dest, const char *src) { - int r = 0; - _cleanup_free_ char *label = NULL; - - assert(dest); - assert(src); - - r = mac_smack_read(src, SMACK_ATTR_ACCESS, &label); - if (r < 0) - return r; - - r = mac_smack_apply(dest, SMACK_ATTR_ACCESS, label); - if (r < 0) - return r; - - return r; -} - -#else -bool mac_smack_use(void) { - return false; -} - -int mac_smack_read(const char *path, SmackAttr attr, char **label) { - return -EOPNOTSUPP; -} - -int mac_smack_read_fd(int fd, SmackAttr attr, char **label) { - return -EOPNOTSUPP; -} - -int mac_smack_apply(const char *path, SmackAttr attr, const char *label) { - return 0; -} - -int mac_smack_apply_fd(int fd, SmackAttr attr, const char *label) { - return 0; -} - -int mac_smack_apply_pid(pid_t pid, const char *label) { - return 0; -} - -int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { - return 0; -} - -int mac_smack_copy(const char *dest, const char *src) { - return 0; -} -#endif diff --git a/src/basic/smack-util.h b/src/basic/smack-util.h deleted file mode 100644 index f90ba0a027..0000000000 --- a/src/basic/smack-util.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Intel Corporation - - Author: Auke Kok <auke-jan.h.kok@intel.com> - - 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 "macro.h" - -#define SMACK_FLOOR_LABEL "_" -#define SMACK_STAR_LABEL "*" - -typedef enum SmackAttr { - SMACK_ATTR_ACCESS = 0, - SMACK_ATTR_EXEC = 1, - SMACK_ATTR_MMAP = 2, - SMACK_ATTR_TRANSMUTE = 3, - SMACK_ATTR_IPIN = 4, - SMACK_ATTR_IPOUT = 5, - _SMACK_ATTR_MAX, - _SMACK_ATTR_INVALID = -1, -} SmackAttr; - -bool mac_smack_use(void); - -int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs); - -const char* smack_attr_to_string(SmackAttr i) _const_; -SmackAttr smack_attr_from_string(const char *s) _pure_; -int mac_smack_read(const char *path, SmackAttr attr, char **label); -int mac_smack_read_fd(int fd, SmackAttr attr, char **label); -int mac_smack_apply(const char *path, SmackAttr attr, const char *label); -int mac_smack_apply_fd(int fd, SmackAttr attr, const char *label); -int mac_smack_apply_pid(pid_t pid, const char *label); -int mac_smack_copy(const char *dest, const char *src); diff --git a/src/basic/socket-label.c b/src/basic/socket-label.c deleted file mode 100644 index 6d1dc83874..0000000000 --- a/src/basic/socket-label.c +++ /dev/null @@ -1,170 +0,0 @@ -/*** - 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 <errno.h> -#include <netinet/in.h> -#include <stdbool.h> -#include <stddef.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "fd-util.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "mkdir.h" -#include "selinux-util.h" -#include "socket-util.h" -#include "umask-util.h" - -int socket_address_listen( - const SocketAddress *a, - int flags, - int backlog, - SocketAddressBindIPv6Only only, - const char *bind_to_device, - bool reuse_port, - bool free_bind, - bool transparent, - mode_t directory_mode, - mode_t socket_mode, - const char *label) { - - _cleanup_close_ int fd = -1; - int r, one; - - assert(a); - - r = socket_address_verify(a); - if (r < 0) - return r; - - if (socket_address_family(a) == AF_INET6 && !socket_ipv6_is_supported()) - return -EAFNOSUPPORT; - - if (label) { - r = mac_selinux_create_socket_prepare(label); - if (r < 0) - return r; - } - - fd = socket(socket_address_family(a), a->type | flags, a->protocol); - r = fd < 0 ? -errno : 0; - - if (label) - mac_selinux_create_socket_clear(); - - if (r < 0) - return r; - - if (socket_address_family(a) == AF_INET6 && only != SOCKET_ADDRESS_DEFAULT) { - int flag = only == SOCKET_ADDRESS_IPV6_ONLY; - - if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, sizeof(flag)) < 0) - return -errno; - } - - if (socket_address_family(a) == AF_INET || socket_address_family(a) == AF_INET6) { - if (bind_to_device) - if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0) - return -errno; - - if (reuse_port) { - one = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) < 0) - log_warning_errno(errno, "SO_REUSEPORT failed: %m"); - } - - if (free_bind) { - one = 1; - if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0) - log_warning_errno(errno, "IP_FREEBIND failed: %m"); - } - - if (transparent) { - one = 1; - if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0) - log_warning_errno(errno, "IP_TRANSPARENT failed: %m"); - } - } - - one = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) - return -errno; - - if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) { - /* Create parents */ - (void) mkdir_parents_label(a->sockaddr.un.sun_path, directory_mode); - - /* Enforce the right access mode for the socket */ - RUN_WITH_UMASK(~socket_mode) { - r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size); - if (r == -EADDRINUSE) { - /* Unlink and try again */ - unlink(a->sockaddr.un.sun_path); - if (bind(fd, &a->sockaddr.sa, a->size) < 0) - return -errno; - } else if (r < 0) - return r; - } - } else { - if (bind(fd, &a->sockaddr.sa, a->size) < 0) - return -errno; - } - - if (socket_address_can_accept(a)) - if (listen(fd, backlog) < 0) - return -errno; - - r = fd; - fd = -1; - - return r; -} - -int make_socket_fd(int log_level, const char* address, int type, int flags) { - SocketAddress a; - int fd, r; - - r = socket_address_parse(&a, address); - if (r < 0) - return log_error_errno(r, "Failed to parse socket address \"%s\": %m", address); - - a.type = type; - - fd = socket_address_listen(&a, type | flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT, - NULL, false, false, false, 0755, 0644, NULL); - if (fd < 0 || log_get_max_level() >= log_level) { - _cleanup_free_ char *p = NULL; - - r = socket_address_print(&a, &p); - if (r < 0) - return log_error_errno(r, "socket_address_print(): %m"); - - if (fd < 0) - log_error_errno(fd, "Failed to listen on %s: %m", p); - else - log_full(log_level, "Listening on %s", p); - } - - return fd; -} diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c deleted file mode 100644 index 1662c04705..0000000000 --- a/src/basic/socket-util.c +++ /dev/null @@ -1,1079 +0,0 @@ -/*** - 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 <arpa/inet.h> -#include <errno.h> -#include <limits.h> -#include <net/if.h> -#include <netdb.h> -#include <netinet/ip.h> -#include <poll.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "formats-util.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "path-util.h" -#include "socket-util.h" -#include "string-table.h" -#include "string-util.h" -#include "strv.h" -#include "user-util.h" -#include "utf8.h" -#include "util.h" - -int socket_address_parse(SocketAddress *a, const char *s) { - char *e, *n; - unsigned u; - int r; - - assert(a); - assert(s); - - zero(*a); - a->type = SOCK_STREAM; - - if (*s == '[') { - /* IPv6 in [x:.....:z]:p notation */ - - e = strchr(s+1, ']'); - if (!e) - return -EINVAL; - - n = strndupa(s+1, e-s-1); - - errno = 0; - if (inet_pton(AF_INET6, n, &a->sockaddr.in6.sin6_addr) <= 0) - return errno > 0 ? -errno : -EINVAL; - - e++; - if (*e != ':') - return -EINVAL; - - e++; - r = safe_atou(e, &u); - if (r < 0) - return r; - - if (u <= 0 || u > 0xFFFF) - return -EINVAL; - - a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); - a->size = sizeof(struct sockaddr_in6); - - } else if (*s == '/') { - /* AF_UNIX socket */ - - size_t l; - - l = strlen(s); - if (l >= sizeof(a->sockaddr.un.sun_path)) - return -EINVAL; - - a->sockaddr.un.sun_family = AF_UNIX; - memcpy(a->sockaddr.un.sun_path, s, l); - a->size = offsetof(struct sockaddr_un, sun_path) + l + 1; - - } else if (*s == '@') { - /* Abstract AF_UNIX socket */ - size_t l; - - l = strlen(s+1); - if (l >= sizeof(a->sockaddr.un.sun_path) - 1) - return -EINVAL; - - a->sockaddr.un.sun_family = AF_UNIX; - memcpy(a->sockaddr.un.sun_path+1, s+1, l); - a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l; - - } else { - e = strchr(s, ':'); - if (e) { - r = safe_atou(e+1, &u); - if (r < 0) - return r; - - if (u <= 0 || u > 0xFFFF) - return -EINVAL; - - n = strndupa(s, e-s); - - /* IPv4 in w.x.y.z:p notation? */ - r = inet_pton(AF_INET, n, &a->sockaddr.in.sin_addr); - if (r < 0) - return -errno; - - if (r > 0) { - /* Gotcha, it's a traditional IPv4 address */ - a->sockaddr.in.sin_family = AF_INET; - a->sockaddr.in.sin_port = htobe16((uint16_t)u); - a->size = sizeof(struct sockaddr_in); - } else { - unsigned idx; - - if (strlen(n) > IF_NAMESIZE-1) - return -EINVAL; - - /* Uh, our last resort, an interface name */ - idx = if_nametoindex(n); - if (idx == 0) - return -EINVAL; - - a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); - a->sockaddr.in6.sin6_scope_id = idx; - a->sockaddr.in6.sin6_addr = in6addr_any; - a->size = sizeof(struct sockaddr_in6); - } - } else { - - /* Just a port */ - r = safe_atou(s, &u); - if (r < 0) - return r; - - if (u <= 0 || u > 0xFFFF) - return -EINVAL; - - if (socket_ipv6_is_supported()) { - a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); - a->sockaddr.in6.sin6_addr = in6addr_any; - a->size = sizeof(struct sockaddr_in6); - } else { - a->sockaddr.in.sin_family = AF_INET; - a->sockaddr.in.sin_port = htobe16((uint16_t)u); - a->sockaddr.in.sin_addr.s_addr = INADDR_ANY; - a->size = sizeof(struct sockaddr_in); - } - } - } - - return 0; -} - -int socket_address_parse_and_warn(SocketAddress *a, const char *s) { - SocketAddress b; - int r; - - /* Similar to socket_address_parse() but warns for IPv6 sockets when we don't support them. */ - - r = socket_address_parse(&b, s); - if (r < 0) - return r; - - if (!socket_ipv6_is_supported() && b.sockaddr.sa.sa_family == AF_INET6) { - log_warning("Binding to IPv6 address not available since kernel does not support IPv6."); - return -EAFNOSUPPORT; - } - - *a = b; - return 0; -} - -int socket_address_parse_netlink(SocketAddress *a, const char *s) { - int family; - unsigned group = 0; - _cleanup_free_ char *sfamily = NULL; - assert(a); - assert(s); - - zero(*a); - a->type = SOCK_RAW; - - errno = 0; - if (sscanf(s, "%ms %u", &sfamily, &group) < 1) - return errno > 0 ? -errno : -EINVAL; - - family = netlink_family_from_string(sfamily); - if (family < 0) - return -EINVAL; - - a->sockaddr.nl.nl_family = AF_NETLINK; - a->sockaddr.nl.nl_groups = group; - - a->type = SOCK_RAW; - a->size = sizeof(struct sockaddr_nl); - a->protocol = family; - - return 0; -} - -int socket_address_verify(const SocketAddress *a) { - assert(a); - - switch (socket_address_family(a)) { - - case AF_INET: - if (a->size != sizeof(struct sockaddr_in)) - return -EINVAL; - - if (a->sockaddr.in.sin_port == 0) - return -EINVAL; - - if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM) - return -EINVAL; - - return 0; - - case AF_INET6: - if (a->size != sizeof(struct sockaddr_in6)) - return -EINVAL; - - if (a->sockaddr.in6.sin6_port == 0) - return -EINVAL; - - if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM) - return -EINVAL; - - return 0; - - case AF_UNIX: - if (a->size < offsetof(struct sockaddr_un, sun_path)) - return -EINVAL; - - if (a->size > offsetof(struct sockaddr_un, sun_path)) { - - if (a->sockaddr.un.sun_path[0] != 0) { - char *e; - - /* path */ - e = memchr(a->sockaddr.un.sun_path, 0, sizeof(a->sockaddr.un.sun_path)); - if (!e) - return -EINVAL; - - if (a->size != offsetof(struct sockaddr_un, sun_path) + (e - a->sockaddr.un.sun_path) + 1) - return -EINVAL; - } - } - - if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM && a->type != SOCK_SEQPACKET) - return -EINVAL; - - return 0; - - case AF_NETLINK: - - if (a->size != sizeof(struct sockaddr_nl)) - return -EINVAL; - - if (a->type != SOCK_RAW && a->type != SOCK_DGRAM) - return -EINVAL; - - return 0; - - default: - return -EAFNOSUPPORT; - } -} - -int socket_address_print(const SocketAddress *a, char **ret) { - int r; - - assert(a); - assert(ret); - - r = socket_address_verify(a); - if (r < 0) - return r; - - if (socket_address_family(a) == AF_NETLINK) { - _cleanup_free_ char *sfamily = NULL; - - r = netlink_family_to_string_alloc(a->protocol, &sfamily); - if (r < 0) - return r; - - r = asprintf(ret, "%s %u", sfamily, a->sockaddr.nl.nl_groups); - if (r < 0) - return -ENOMEM; - - return 0; - } - - return sockaddr_pretty(&a->sockaddr.sa, a->size, false, true, ret); -} - -bool socket_address_can_accept(const SocketAddress *a) { - assert(a); - - return - a->type == SOCK_STREAM || - a->type == SOCK_SEQPACKET; -} - -bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) { - assert(a); - assert(b); - - /* Invalid addresses are unequal to all */ - if (socket_address_verify(a) < 0 || - socket_address_verify(b) < 0) - return false; - - if (a->type != b->type) - return false; - - if (socket_address_family(a) != socket_address_family(b)) - return false; - - switch (socket_address_family(a)) { - - case AF_INET: - if (a->sockaddr.in.sin_addr.s_addr != b->sockaddr.in.sin_addr.s_addr) - return false; - - if (a->sockaddr.in.sin_port != b->sockaddr.in.sin_port) - return false; - - break; - - case AF_INET6: - if (memcmp(&a->sockaddr.in6.sin6_addr, &b->sockaddr.in6.sin6_addr, sizeof(a->sockaddr.in6.sin6_addr)) != 0) - return false; - - if (a->sockaddr.in6.sin6_port != b->sockaddr.in6.sin6_port) - return false; - - break; - - case AF_UNIX: - if (a->size <= offsetof(struct sockaddr_un, sun_path) || - b->size <= offsetof(struct sockaddr_un, sun_path)) - return false; - - if ((a->sockaddr.un.sun_path[0] == 0) != (b->sockaddr.un.sun_path[0] == 0)) - return false; - - if (a->sockaddr.un.sun_path[0]) { - if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path)) - return false; - } else { - if (a->size != b->size) - return false; - - if (memcmp(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, a->size) != 0) - return false; - } - - break; - - case AF_NETLINK: - if (a->protocol != b->protocol) - return false; - - if (a->sockaddr.nl.nl_groups != b->sockaddr.nl.nl_groups) - return false; - - break; - - default: - /* Cannot compare, so we assume the addresses are different */ - return false; - } - - return true; -} - -bool socket_address_is(const SocketAddress *a, const char *s, int type) { - struct SocketAddress b; - - assert(a); - assert(s); - - if (socket_address_parse(&b, s) < 0) - return false; - - b.type = type; - - return socket_address_equal(a, &b); -} - -bool socket_address_is_netlink(const SocketAddress *a, const char *s) { - struct SocketAddress b; - - assert(a); - assert(s); - - if (socket_address_parse_netlink(&b, s) < 0) - return false; - - return socket_address_equal(a, &b); -} - -const char* socket_address_get_path(const SocketAddress *a) { - assert(a); - - if (socket_address_family(a) != AF_UNIX) - return NULL; - - if (a->sockaddr.un.sun_path[0] == 0) - return NULL; - - return a->sockaddr.un.sun_path; -} - -bool socket_ipv6_is_supported(void) { - if (access("/proc/net/if_inet6", F_OK) != 0) - return false; - - return true; -} - -bool socket_address_matches_fd(const SocketAddress *a, int fd) { - SocketAddress b; - socklen_t solen; - - assert(a); - assert(fd >= 0); - - b.size = sizeof(b.sockaddr); - if (getsockname(fd, &b.sockaddr.sa, &b.size) < 0) - return false; - - if (b.sockaddr.sa.sa_family != a->sockaddr.sa.sa_family) - return false; - - solen = sizeof(b.type); - if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &b.type, &solen) < 0) - return false; - - if (b.type != a->type) - return false; - - if (a->protocol != 0) { - solen = sizeof(b.protocol); - if (getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &b.protocol, &solen) < 0) - return false; - - if (b.protocol != a->protocol) - return false; - } - - return socket_address_equal(a, &b); -} - -int sockaddr_port(const struct sockaddr *_sa) { - union sockaddr_union *sa = (union sockaddr_union*) _sa; - - assert(sa); - - if (!IN_SET(sa->sa.sa_family, AF_INET, AF_INET6)) - return -EAFNOSUPPORT; - - return be16toh(sa->sa.sa_family == AF_INET6 ? sa->in6.sin6_port : sa->in.sin_port); -} - -int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) { - union sockaddr_union *sa = (union sockaddr_union*) _sa; - char *p; - int r; - - assert(sa); - assert(salen >= sizeof(sa->sa.sa_family)); - - switch (sa->sa.sa_family) { - - case AF_INET: { - uint32_t a; - - a = be32toh(sa->in.sin_addr.s_addr); - - if (include_port) - r = asprintf(&p, - "%u.%u.%u.%u:%u", - a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF, - be16toh(sa->in.sin_port)); - else - r = asprintf(&p, - "%u.%u.%u.%u", - a >> 24, (a >> 16) & 0xFF, (a >> 8) & 0xFF, a & 0xFF); - if (r < 0) - return -ENOMEM; - break; - } - - case AF_INET6: { - static const unsigned char ipv4_prefix[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF - }; - - if (translate_ipv6 && - memcmp(&sa->in6.sin6_addr, ipv4_prefix, sizeof(ipv4_prefix)) == 0) { - const uint8_t *a = sa->in6.sin6_addr.s6_addr+12; - if (include_port) - r = asprintf(&p, - "%u.%u.%u.%u:%u", - a[0], a[1], a[2], a[3], - be16toh(sa->in6.sin6_port)); - else - r = asprintf(&p, - "%u.%u.%u.%u", - a[0], a[1], a[2], a[3]); - if (r < 0) - return -ENOMEM; - } else { - char a[INET6_ADDRSTRLEN]; - - inet_ntop(AF_INET6, &sa->in6.sin6_addr, a, sizeof(a)); - - if (include_port) { - r = asprintf(&p, - "[%s]:%u", - a, - be16toh(sa->in6.sin6_port)); - if (r < 0) - return -ENOMEM; - } else { - p = strdup(a); - if (!p) - return -ENOMEM; - } - } - - break; - } - - case AF_UNIX: - if (salen <= offsetof(struct sockaddr_un, sun_path)) { - p = strdup("<unnamed>"); - if (!p) - return -ENOMEM; - - } else if (sa->un.sun_path[0] == 0) { - /* abstract */ - - /* FIXME: We assume we can print the - * socket path here and that it hasn't - * more than one NUL byte. That is - * actually an invalid assumption */ - - p = new(char, sizeof(sa->un.sun_path)+1); - if (!p) - return -ENOMEM; - - p[0] = '@'; - memcpy(p+1, sa->un.sun_path+1, sizeof(sa->un.sun_path)-1); - p[sizeof(sa->un.sun_path)] = 0; - - } else { - p = strndup(sa->un.sun_path, sizeof(sa->un.sun_path)); - if (!p) - return -ENOMEM; - } - - break; - - default: - return -EOPNOTSUPP; - } - - - *ret = p; - return 0; -} - -int getpeername_pretty(int fd, bool include_port, char **ret) { - union sockaddr_union sa; - socklen_t salen = sizeof(sa); - int r; - - assert(fd >= 0); - assert(ret); - - if (getpeername(fd, &sa.sa, &salen) < 0) - return -errno; - - if (sa.sa.sa_family == AF_UNIX) { - struct ucred ucred = {}; - - /* UNIX connection sockets are anonymous, so let's use - * PID/UID as pretty credentials instead */ - - r = getpeercred(fd, &ucred); - if (r < 0) - return r; - - if (asprintf(ret, "PID "PID_FMT"/UID "UID_FMT, ucred.pid, ucred.uid) < 0) - return -ENOMEM; - - return 0; - } - - /* For remote sockets we translate IPv6 addresses back to IPv4 - * if applicable, since that's nicer. */ - - return sockaddr_pretty(&sa.sa, salen, true, include_port, ret); -} - -int getsockname_pretty(int fd, char **ret) { - union sockaddr_union sa; - socklen_t salen = sizeof(sa); - - assert(fd >= 0); - assert(ret); - - if (getsockname(fd, &sa.sa, &salen) < 0) - return -errno; - - /* For local sockets we do not translate IPv6 addresses back - * to IPv6 if applicable, since this is usually used for - * listening sockets where the difference between IPv4 and - * IPv6 matters. */ - - return sockaddr_pretty(&sa.sa, salen, false, true, ret); -} - -int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) { - int r; - char host[NI_MAXHOST], *ret; - - assert(_ret); - - r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0, - NI_IDN|NI_IDN_USE_STD3_ASCII_RULES); - if (r != 0) { - int saved_errno = errno; - - r = sockaddr_pretty(&sa->sa, salen, true, true, &ret); - if (r < 0) - return r; - - log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret); - } else { - ret = strdup(host); - if (!ret) - return -ENOMEM; - } - - *_ret = ret; - return 0; -} - -int getnameinfo_pretty(int fd, char **ret) { - union sockaddr_union sa; - socklen_t salen = sizeof(sa); - - assert(fd >= 0); - assert(ret); - - if (getsockname(fd, &sa.sa, &salen) < 0) - return -errno; - - return socknameinfo_pretty(&sa, salen, ret); -} - -int socket_address_unlink(SocketAddress *a) { - assert(a); - - if (socket_address_family(a) != AF_UNIX) - return 0; - - if (a->sockaddr.un.sun_path[0] == 0) - return 0; - - if (unlink(a->sockaddr.un.sun_path) < 0) - return -errno; - - return 1; -} - -static const char* const netlink_family_table[] = { - [NETLINK_ROUTE] = "route", - [NETLINK_FIREWALL] = "firewall", - [NETLINK_INET_DIAG] = "inet-diag", - [NETLINK_NFLOG] = "nflog", - [NETLINK_XFRM] = "xfrm", - [NETLINK_SELINUX] = "selinux", - [NETLINK_ISCSI] = "iscsi", - [NETLINK_AUDIT] = "audit", - [NETLINK_FIB_LOOKUP] = "fib-lookup", - [NETLINK_CONNECTOR] = "connector", - [NETLINK_NETFILTER] = "netfilter", - [NETLINK_IP6_FW] = "ip6-fw", - [NETLINK_DNRTMSG] = "dnrtmsg", - [NETLINK_KOBJECT_UEVENT] = "kobject-uevent", - [NETLINK_GENERIC] = "generic", - [NETLINK_SCSITRANSPORT] = "scsitransport", - [NETLINK_ECRYPTFS] = "ecryptfs" -}; - -DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(netlink_family, int, INT_MAX); - -static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX] = { - [SOCKET_ADDRESS_DEFAULT] = "default", - [SOCKET_ADDRESS_BOTH] = "both", - [SOCKET_ADDRESS_IPV6_ONLY] = "ipv6-only" -}; - -DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only); - -bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) { - assert(a); - assert(b); - - if (a->sa.sa_family != b->sa.sa_family) - return false; - - if (a->sa.sa_family == AF_INET) - return a->in.sin_addr.s_addr == b->in.sin_addr.s_addr; - - if (a->sa.sa_family == AF_INET6) - return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr)) == 0; - - return false; -} - -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; -} - -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 ifname_valid(const char *p) { - bool numeric = true; - - /* Checks whether a network interface name is valid. This is inspired by dev_valid_name() in the kernel sources - * but slightly stricter, as we only allow non-control, non-space ASCII characters in the interface name. We - * also don't permit names that only container numbers, to avoid confusion with numeric interface indexes. */ - - if (isempty(p)) - return false; - - if (strlen(p) >= IFNAMSIZ) - return false; - - if (STR_IN_SET(p, ".", "..")) - return false; - - while (*p) { - if ((unsigned char) *p >= 127U) - return false; - - if ((unsigned char) *p <= 32U) - return false; - - if (*p == ':' || *p == '/') - return false; - - numeric = numeric && (*p >= '0' && *p <= '9'); - p++; - } - - if (numeric) - return false; - - return true; -} - -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; -} - -int send_one_fd_sa( - int transport_fd, - int fd, - const struct sockaddr *sa, socklen_t len, - int flags) { - - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; - struct msghdr mh = { - .msg_name = (struct sockaddr*) sa, - .msg_namelen = len, - .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); -} - -ssize_t next_datagram_size_fd(int fd) { - ssize_t l; - int k; - - /* This is a bit like FIONREAD/SIOCINQ, however a bit more powerful. The difference being: recv(MSG_PEEK) will - * actually cause the next datagram in the queue to be validated regarding checksums, which FIONREAD doesn't - * do. This difference is actually of major importance as we need to be sure that the size returned here - * actually matches what we will read with recvmsg() next, as otherwise we might end up allocating a buffer of - * the wrong size. */ - - l = recv(fd, NULL, 0, MSG_PEEK|MSG_TRUNC); - if (l < 0) { - if (errno == EOPNOTSUPP || errno == EFAULT) - goto fallback; - - return -errno; - } - if (l == 0) - goto fallback; - - return l; - -fallback: - k = 0; - - /* Some sockets (AF_PACKET) do not support null-sized recv() with MSG_TRUNC set, let's fall back to FIONREAD - * for them. Checksums don't matter for raw sockets anyway, hence this should be fine. */ - - if (ioctl(fd, FIONREAD, &k) < 0) - return -errno; - - return (ssize_t) k; -} - -int flush_accept(int fd) { - - struct pollfd pollfd = { - .fd = fd, - .events = POLLIN, - }; - int r; - - - /* Similar to flush_fd() but flushes all incoming connection by accepting them and immediately closing them. */ - - for (;;) { - int cfd; - - r = poll(&pollfd, 1, 0); - if (r < 0) { - if (errno == EINTR) - continue; - - return -errno; - - } else if (r == 0) - return 0; - - cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); - if (cfd < 0) { - if (errno == EINTR) - continue; - - if (errno == EAGAIN) - return 0; - - return -errno; - } - - close(cfd); - } -} - -struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length) { - struct cmsghdr *cmsg; - - assert(mh); - - CMSG_FOREACH(cmsg, mh) - if (cmsg->cmsg_level == level && - cmsg->cmsg_type == type && - (length == (socklen_t) -1 || length == cmsg->cmsg_len)) - return cmsg; - - return NULL; -} - -int socket_ioctl_fd(void) { - int fd; - - /* Create a socket to invoke the various network interface ioctl()s on. Traditionally only AF_INET was good for - * that. Since kernel 4.6 AF_NETLINK works for this too. We first try to use AF_INET hence, but if that's not - * available (for example, because it is made unavailable via SECCOMP or such), we'll fall back to the more - * generic AF_NETLINK. */ - - fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0); - if (fd < 0) - fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_GENERIC); - if (fd < 0) - return -errno; - - return fd; -} diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h deleted file mode 100644 index 2ef572badb..0000000000 --- a/src/basic/socket-util.h +++ /dev/null @@ -1,158 +0,0 @@ -#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 <netinet/ether.h> -#include <netinet/in.h> -#include <stdbool.h> -#include <stddef.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> -#include <linux/netlink.h> -#include <linux/if_packet.h> - -#include "macro.h" -#include "util.h" - -union sockaddr_union { - struct sockaddr sa; - struct sockaddr_in in; - struct sockaddr_in6 in6; - struct sockaddr_un un; - struct sockaddr_nl nl; - struct sockaddr_storage storage; - struct sockaddr_ll ll; -}; - -typedef struct SocketAddress { - union sockaddr_union sockaddr; - - /* We store the size here explicitly due to the weird - * sockaddr_un semantics for abstract sockets */ - socklen_t size; - - /* Socket type, i.e. SOCK_STREAM, SOCK_DGRAM, ... */ - int type; - - /* Socket protocol, IPPROTO_xxx, usually 0, except for netlink */ - int protocol; -} SocketAddress; - -typedef enum SocketAddressBindIPv6Only { - SOCKET_ADDRESS_DEFAULT, - SOCKET_ADDRESS_BOTH, - SOCKET_ADDRESS_IPV6_ONLY, - _SOCKET_ADDRESS_BIND_IPV6_ONLY_MAX, - _SOCKET_ADDRESS_BIND_IPV6_ONLY_INVALID = -1 -} SocketAddressBindIPv6Only; - -#define socket_address_family(a) ((a)->sockaddr.sa.sa_family) - -int socket_address_parse(SocketAddress *a, const char *s); -int socket_address_parse_and_warn(SocketAddress *a, const char *s); -int socket_address_parse_netlink(SocketAddress *a, const char *s); -int socket_address_print(const SocketAddress *a, char **p); -int socket_address_verify(const SocketAddress *a) _pure_; -int socket_address_unlink(SocketAddress *a); - -bool socket_address_can_accept(const SocketAddress *a) _pure_; - -int socket_address_listen( - const SocketAddress *a, - int flags, - int backlog, - SocketAddressBindIPv6Only only, - const char *bind_to_device, - bool reuse_port, - bool free_bind, - bool transparent, - mode_t directory_mode, - mode_t socket_mode, - const char *label); -int make_socket_fd(int log_level, const char* address, int type, int flags); - -bool socket_address_is(const SocketAddress *a, const char *s, int type); -bool socket_address_is_netlink(const SocketAddress *a, const char *s); - -bool socket_address_matches_fd(const SocketAddress *a, int fd); - -bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) _pure_; - -const char* socket_address_get_path(const SocketAddress *a); - -bool socket_ipv6_is_supported(void); - -int sockaddr_port(const struct sockaddr *_sa) _pure_; - -int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret); -int getpeername_pretty(int fd, bool include_port, char **ret); -int getsockname_pretty(int fd, char **ret); - -int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret); -int getnameinfo_pretty(int fd, char **ret); - -const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_; -SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_; - -int netlink_family_to_string_alloc(int b, char **s); -int netlink_family_from_string(const char *s) _pure_; - -bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b); - -int fd_inc_sndbuf(int fd, size_t n); -int fd_inc_rcvbuf(int fd, size_t n); - -int ip_tos_to_string_alloc(int i, char **s); -int ip_tos_from_string(const char *s); - -bool ifname_valid(const char *p); - -int getpeercred(int fd, struct ucred *ucred); -int getpeersec(int fd, char **ret); - -int send_one_fd_sa(int transport_fd, - int fd, - const struct sockaddr *sa, socklen_t len, - int flags); -#define send_one_fd(transport_fd, fd, flags) send_one_fd_sa(transport_fd, fd, NULL, 0, flags) -int receive_one_fd(int transport_fd, int flags); - -ssize_t next_datagram_size_fd(int fd); - -int flush_accept(int fd); - -#define CMSG_FOREACH(cmsg, mh) \ - for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg))) - -struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length); - -/* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */ -#define SOCKADDR_UN_LEN(sa) \ - ({ \ - const struct sockaddr_un *_sa = &(sa); \ - assert(_sa->sun_family == AF_UNIX); \ - offsetof(struct sockaddr_un, sun_path) + \ - (_sa->sun_path[0] == 0 ? \ - 1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \ - strnlen(_sa->sun_path, sizeof(_sa->sun_path))); \ - }) - -int socket_ioctl_fd(void); diff --git a/src/basic/sparse-endian.h b/src/basic/sparse-endian.h deleted file mode 100644 index c913fda8c5..0000000000 --- a/src/basic/sparse-endian.h +++ /dev/null @@ -1,88 +0,0 @@ -/* Copyright (c) 2012 Josh Triplett <josh@joshtriplett.org> - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#ifndef SPARSE_ENDIAN_H -#define SPARSE_ENDIAN_H - -#include <byteswap.h> -#include <endian.h> -#include <stdint.h> - -#ifdef __CHECKER__ -#define __bitwise __attribute__((bitwise)) -#define __force __attribute__((force)) -#else -#define __bitwise -#define __force -#endif - -typedef uint16_t __bitwise le16_t; -typedef uint16_t __bitwise be16_t; -typedef uint32_t __bitwise le32_t; -typedef uint32_t __bitwise be32_t; -typedef uint64_t __bitwise le64_t; -typedef uint64_t __bitwise be64_t; - -#undef htobe16 -#undef htole16 -#undef be16toh -#undef le16toh -#undef htobe32 -#undef htole32 -#undef be32toh -#undef le32toh -#undef htobe64 -#undef htole64 -#undef be64toh -#undef le64toh - -#if __BYTE_ORDER == __LITTLE_ENDIAN -#define bswap_16_on_le(x) __bswap_16(x) -#define bswap_32_on_le(x) __bswap_32(x) -#define bswap_64_on_le(x) __bswap_64(x) -#define bswap_16_on_be(x) (x) -#define bswap_32_on_be(x) (x) -#define bswap_64_on_be(x) (x) -#elif __BYTE_ORDER == __BIG_ENDIAN -#define bswap_16_on_le(x) (x) -#define bswap_32_on_le(x) (x) -#define bswap_64_on_le(x) (x) -#define bswap_16_on_be(x) __bswap_16(x) -#define bswap_32_on_be(x) __bswap_32(x) -#define bswap_64_on_be(x) __bswap_64(x) -#endif - -static inline le16_t htole16(uint16_t value) { return (le16_t __force) bswap_16_on_be(value); } -static inline le32_t htole32(uint32_t value) { return (le32_t __force) bswap_32_on_be(value); } -static inline le64_t htole64(uint64_t value) { return (le64_t __force) bswap_64_on_be(value); } - -static inline be16_t htobe16(uint16_t value) { return (be16_t __force) bswap_16_on_le(value); } -static inline be32_t htobe32(uint32_t value) { return (be32_t __force) bswap_32_on_le(value); } -static inline be64_t htobe64(uint64_t value) { return (be64_t __force) bswap_64_on_le(value); } - -static inline uint16_t le16toh(le16_t value) { return bswap_16_on_be((uint16_t __force)value); } -static inline uint32_t le32toh(le32_t value) { return bswap_32_on_be((uint32_t __force)value); } -static inline uint64_t le64toh(le64_t value) { return bswap_64_on_be((uint64_t __force)value); } - -static inline uint16_t be16toh(be16_t value) { return bswap_16_on_le((uint16_t __force)value); } -static inline uint32_t be32toh(be32_t value) { return bswap_32_on_le((uint32_t __force)value); } -static inline uint64_t be64toh(be64_t value) { return bswap_64_on_le((uint64_t __force)value); } - -#endif /* SPARSE_ENDIAN_H */ diff --git a/src/basic/special.h b/src/basic/special.h deleted file mode 100644 index 5276bcf598..0000000000 --- a/src/basic/special.h +++ /dev/null @@ -1,122 +0,0 @@ -#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/>. -***/ - -#define SPECIAL_DEFAULT_TARGET "default.target" - -/* Shutdown targets */ -#define SPECIAL_UMOUNT_TARGET "umount.target" -/* This is not really intended to be started by directly. This is - * mostly so that other targets (reboot/halt/poweroff) can depend on - * it to bring all services down that want to be brought down on - * system shutdown. */ -#define SPECIAL_SHUTDOWN_TARGET "shutdown.target" -#define SPECIAL_HALT_TARGET "halt.target" -#define SPECIAL_POWEROFF_TARGET "poweroff.target" -#define SPECIAL_REBOOT_TARGET "reboot.target" -#define SPECIAL_KEXEC_TARGET "kexec.target" -#define SPECIAL_EXIT_TARGET "exit.target" -#define SPECIAL_SUSPEND_TARGET "suspend.target" -#define SPECIAL_HIBERNATE_TARGET "hibernate.target" -#define SPECIAL_HYBRID_SLEEP_TARGET "hybrid-sleep.target" - -/* Special boot targets */ -#define SPECIAL_RESCUE_TARGET "rescue.target" -#define SPECIAL_EMERGENCY_TARGET "emergency.target" -#define SPECIAL_MULTI_USER_TARGET "multi-user.target" -#define SPECIAL_GRAPHICAL_TARGET "graphical.target" - -/* Early boot targets */ -#define SPECIAL_SYSINIT_TARGET "sysinit.target" -#define SPECIAL_SOCKETS_TARGET "sockets.target" -#define SPECIAL_BUSNAMES_TARGET "busnames.target" -#define SPECIAL_TIMERS_TARGET "timers.target" -#define SPECIAL_PATHS_TARGET "paths.target" -#define SPECIAL_LOCAL_FS_TARGET "local-fs.target" -#define SPECIAL_LOCAL_FS_PRE_TARGET "local-fs-pre.target" -#define SPECIAL_INITRD_FS_TARGET "initrd-fs.target" -#define SPECIAL_INITRD_ROOT_DEVICE_TARGET "initrd-root-device.target" -#define SPECIAL_INITRD_ROOT_FS_TARGET "initrd-root-fs.target" -#define SPECIAL_REMOTE_FS_TARGET "remote-fs.target" /* LSB's $remote_fs */ -#define SPECIAL_REMOTE_FS_PRE_TARGET "remote-fs-pre.target" -#define SPECIAL_SWAP_TARGET "swap.target" -#define SPECIAL_NETWORK_ONLINE_TARGET "network-online.target" -#define SPECIAL_TIME_SYNC_TARGET "time-sync.target" /* LSB's $time */ -#define SPECIAL_BASIC_TARGET "basic.target" - -/* LSB compatibility */ -#define SPECIAL_NETWORK_TARGET "network.target" /* LSB's $network */ -#define SPECIAL_NSS_LOOKUP_TARGET "nss-lookup.target" /* LSB's $named */ -#define SPECIAL_RPCBIND_TARGET "rpcbind.target" /* LSB's $portmap */ - -/* - * Rules regarding adding further high level targets like the above: - * - * - Be conservative, only add more of these when we really need - * them. We need strong usecases for further additions. - * - * - When there can be multiple implementations running side-by-side, - * it needs to be a .target unit which can pull in all - * implementations. - * - * - If something can be implemented with socket activation, and - * without, it needs to be a .target unit, so that it can pull in - * the appropriate unit. - * - * - Otherwise, it should be a .service unit. - * - * - In some cases it is OK to have both a .service and a .target - * unit, i.e. if there can be multiple parallel implementations, but - * only one is the "system" one. Example: syslog. - * - * Or to put this in other words: .service symlinks can be used to - * arbitrate between multiple implementations if there can be only one - * of a kind. .target units can be used to support multiple - * implementations that can run side-by-side. - */ - -/* Magic early boot services */ -#define SPECIAL_FSCK_SERVICE "systemd-fsck@.service" -#define SPECIAL_QUOTACHECK_SERVICE "systemd-quotacheck.service" -#define SPECIAL_QUOTAON_SERVICE "quotaon.service" -#define SPECIAL_REMOUNT_FS_SERVICE "systemd-remount-fs.service" - -/* Services systemd relies on */ -#define SPECIAL_DBUS_SERVICE "dbus.service" -#define SPECIAL_DBUS_SOCKET "dbus.socket" -#define SPECIAL_JOURNALD_SOCKET "systemd-journald.socket" -#define SPECIAL_JOURNALD_SERVICE "systemd-journald.service" - -/* Magic init signals */ -#define SPECIAL_KBREQUEST_TARGET "kbrequest.target" -#define SPECIAL_SIGPWR_TARGET "sigpwr.target" -#define SPECIAL_CTRL_ALT_DEL_TARGET "ctrl-alt-del.target" - -/* Where we add all our system units, users and machines by default */ -#define SPECIAL_SYSTEM_SLICE "system.slice" -#define SPECIAL_USER_SLICE "user.slice" -#define SPECIAL_MACHINE_SLICE "machine.slice" -#define SPECIAL_ROOT_SLICE "-.slice" - -/* The scope unit systemd itself lives in. */ -#define SPECIAL_INIT_SCOPE "init.scope" - -/* The root directory. */ -#define SPECIAL_ROOT_MOUNT "-.mount" diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c deleted file mode 100644 index 309e84b93d..0000000000 --- a/src/basic/stat-util.c +++ /dev/null @@ -1,218 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010-2012 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 <errno.h> -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <linux/magic.h> -#include <sys/statvfs.h> -#include <unistd.h> - -#include "dirent-util.h" -#include "fd-util.h" -#include "macro.h" -#include "missing.h" -#include "stat-util.h" -#include "string-util.h" - -int is_symlink(const char *path) { - struct stat info; - - assert(path); - - if (lstat(path, &info) < 0) - return -errno; - - return !!S_ISLNK(info.st_mode); -} - -int is_dir(const char* path, bool follow) { - struct stat st; - int r; - - assert(path); - - if (follow) - r = stat(path, &st); - else - r = lstat(path, &st); - if (r < 0) - return -errno; - - return !!S_ISDIR(st.st_mode); -} - -int is_device_node(const char *path) { - struct stat info; - - assert(path); - - if (lstat(path, &info) < 0) - return -errno; - - return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode)); -} - -int dir_is_empty(const char *path) { - _cleanup_closedir_ DIR *d; - struct dirent *de; - - d = opendir(path); - if (!d) - return -errno; - - FOREACH_DIRENT(de, d, return -errno) - return 0; - - return 1; -} - -bool null_or_empty(struct stat *st) { - assert(st); - - if (S_ISREG(st->st_mode) && st->st_size <= 0) - return true; - - /* We don't want to hardcode the major/minor of /dev/null, - * hence we do a simpler "is this a device node?" check. */ - - if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) - return true; - - return false; -} - -int null_or_empty_path(const char *fn) { - struct stat st; - - assert(fn); - - if (stat(fn, &st) < 0) - return -errno; - - return null_or_empty(&st); -} - -int null_or_empty_fd(int fd) { - struct stat st; - - assert(fd >= 0); - - if (fstat(fd, &st) < 0) - return -errno; - - return null_or_empty(&st); -} - -int path_is_read_only_fs(const char *path) { - struct statvfs st; - - assert(path); - - if (statvfs(path, &st) < 0) - return -errno; - - if (st.f_flag & ST_RDONLY) - return true; - - /* On NFS, statvfs() might not reflect whether we can actually - * write to the remote share. Let's try again with - * access(W_OK) which is more reliable, at least sometimes. */ - if (access(path, W_OK) < 0 && errno == EROFS) - return true; - - return false; -} - -int path_is_os_tree(const char *path) { - char *p; - int r; - - assert(path); - - /* 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 files_same(const char *filea, const char *fileb) { - struct stat a, b; - - assert(filea); - assert(fileb); - - 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; -} - -bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) { - assert(s); - assert_cc(sizeof(statfs_f_type_t) >= sizeof(s->f_type)); - - return F_TYPE_EQUAL(s->f_type, magic_value); -} - -int fd_check_fstype(int fd, statfs_f_type_t magic_value) { - struct statfs s; - - if (fstatfs(fd, &s) < 0) - return -errno; - - return is_fs_type(&s, magic_value); -} - -int path_check_fstype(const char *path, statfs_f_type_t magic_value) { - _cleanup_close_ int fd = -1; - - fd = open(path, O_RDONLY); - if (fd < 0) - return -errno; - - return fd_check_fstype(fd, magic_value); -} - -bool is_temporary_fs(const struct statfs *s) { - return is_fs_type(s, TMPFS_MAGIC) || - is_fs_type(s, RAMFS_MAGIC); -} - -int fd_is_temporary_fs(int fd) { - struct statfs s; - - if (fstatfs(fd, &s) < 0) - return -errno; - - return is_temporary_fs(&s); -} diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h deleted file mode 100644 index 56d28f791e..0000000000 --- a/src/basic/stat-util.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2010-2012 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 <stddef.h> -#include <sys/stat.h> -#include <sys/statfs.h> -#include <sys/types.h> -#include <sys/vfs.h> - -#include "macro.h" - -int is_symlink(const char *path); -int is_dir(const char *path, bool follow); -int is_device_node(const char *path); - -int dir_is_empty(const char *path); - -static inline int dir_is_populated(const char *path) { - int r; - r = dir_is_empty(path); - if (r < 0) - return r; - return !r; -} - -bool null_or_empty(struct stat *st) _pure_; -int null_or_empty_path(const char *fn); -int null_or_empty_fd(int fd); - -int path_is_read_only_fs(const char *path); -int path_is_os_tree(const char *path); - -int files_same(const char *filea, const char *fileb); - -/* The .f_type field of struct statfs is really weird defined on - * different archs. Let's give its type a name. */ -typedef typeof(((struct statfs*)NULL)->f_type) statfs_f_type_t; - -bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) _pure_; -int fd_check_fstype(int fd, statfs_f_type_t magic_value); -int path_check_fstype(const char *path, statfs_f_type_t magic_value); - -bool is_temporary_fs(const struct statfs *s) _pure_; -int fd_is_temporary_fs(int fd); - -/* 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) diff --git a/src/basic/stdio-util.h b/src/basic/stdio-util.h deleted file mode 100644 index bd1144b4c9..0000000000 --- a/src/basic/stdio-util.h +++ /dev/null @@ -1,76 +0,0 @@ -#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 <printf.h> -#include <stdarg.h> -#include <stdio.h> -#include <sys/types.h> - -#include "macro.h" - -#define xsprintf(buf, fmt, ...) \ - assert_message_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf), "xsprintf: " #buf "[] must be big enough") - - -#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) diff --git a/src/basic/strbuf.c b/src/basic/strbuf.c deleted file mode 100644 index 00aaf9e621..0000000000 --- a/src/basic/strbuf.c +++ /dev/null @@ -1,204 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2012 Kay Sievers <kay@vrfy.org> - - 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 <errno.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "strbuf.h" - -/* - * Strbuf stores given strings in a single continuous allocated memory - * area. Identical strings are de-duplicated and return the same offset - * as the first string stored. If the tail of a string already exists - * in the buffer, the tail is returned. - * - * A trie (http://en.wikipedia.org/wiki/Trie) is used to maintain the - * information about the stored strings. - * - * Example of udev rules: - * $ ./udevadm test . - * ... - * read rules file: /usr/lib/udev/rules.d/99-systemd.rules - * rules contain 196608 bytes tokens (16384 * 12 bytes), 39742 bytes strings - * 23939 strings (207859 bytes), 20404 de-duplicated (171653 bytes), 3536 trie nodes used - * ... - */ - -struct strbuf *strbuf_new(void) { - struct strbuf *str; - - str = new0(struct strbuf, 1); - if (!str) - return NULL; - - str->buf = new0(char, 1); - if (!str->buf) - goto err; - str->len = 1; - - str->root = new0(struct strbuf_node, 1); - if (!str->root) - goto err; - str->nodes_count = 1; - return str; -err: - free(str->buf); - free(str->root); - return mfree(str); -} - -static void strbuf_node_cleanup(struct strbuf_node *node) { - size_t i; - - for (i = 0; i < node->children_count; i++) - strbuf_node_cleanup(node->children[i].child); - free(node->children); - free(node); -} - -/* clean up trie data, leave only the string buffer */ -void strbuf_complete(struct strbuf *str) { - if (!str) - return; - if (str->root) - strbuf_node_cleanup(str->root); - str->root = NULL; -} - -/* clean up everything */ -void strbuf_cleanup(struct strbuf *str) { - if (!str) - return; - if (str->root) - strbuf_node_cleanup(str->root); - free(str->buf); - free(str); -} - -static int strbuf_children_cmp(const struct strbuf_child_entry *n1, - const struct strbuf_child_entry *n2) { - return n1->c - n2->c; -} - -static void bubbleinsert(struct strbuf_node *node, - uint8_t c, - struct strbuf_node *node_child) { - - struct strbuf_child_entry new = { - .c = c, - .child = node_child, - }; - int left = 0, right = node->children_count; - - while (right > left) { - int middle = (right + left) / 2 ; - if (strbuf_children_cmp(&node->children[middle], &new) <= 0) - left = middle + 1; - else - right = middle; - } - - memmove(node->children + left + 1, node->children + left, - sizeof(struct strbuf_child_entry) * (node->children_count - left)); - node->children[left] = new; - - node->children_count++; -} - -/* add string, return the index/offset into the buffer */ -ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len) { - uint8_t c; - struct strbuf_node *node; - size_t depth; - char *buf_new; - struct strbuf_child_entry *child; - struct strbuf_node *node_child; - ssize_t off; - - if (!str->root) - return -EINVAL; - - /* search string; start from last character to find possibly matching tails */ - if (len == 0) - return 0; - str->in_count++; - str->in_len += len; - - node = str->root; - c = s[len-1]; - for (depth = 0; depth <= len; depth++) { - struct strbuf_child_entry search; - - /* match against current node */ - off = node->value_off + node->value_len - len; - if (depth == len || (node->value_len >= len && memcmp(str->buf + off, s, len) == 0)) { - str->dedup_len += len; - str->dedup_count++; - return off; - } - - c = s[len - 1 - depth]; - - /* bsearch is not allowed on a NULL sequence */ - if (node->children_count == 0) - break; - - /* lookup child node */ - search.c = c; - child = bsearch(&search, node->children, node->children_count, - sizeof(struct strbuf_child_entry), - (__compar_fn_t) strbuf_children_cmp); - if (!child) - break; - node = child->child; - } - - /* add new string */ - buf_new = realloc(str->buf, str->len + len+1); - if (!buf_new) - return -ENOMEM; - str->buf = buf_new; - off = str->len; - memcpy(str->buf + off, s, len); - str->len += len; - str->buf[str->len++] = '\0'; - - /* new node */ - node_child = new0(struct strbuf_node, 1); - if (!node_child) - return -ENOMEM; - node_child->value_off = off; - node_child->value_len = len; - - /* extend array, add new entry, sort for bisection */ - child = realloc(node->children, (node->children_count + 1) * sizeof(struct strbuf_child_entry)); - if (!child) { - free(node_child); - return -ENOMEM; - } - - str->nodes_count++; - - node->children = child; - bubbleinsert(node, c, node_child); - - return off; -} diff --git a/src/basic/strbuf.h b/src/basic/strbuf.h deleted file mode 100644 index a1632da0e8..0000000000 --- a/src/basic/strbuf.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 Kay Sievers <kay@vrfy.org> - - 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 <stdint.h> -#include <sys/types.h> - -struct strbuf { - char *buf; - size_t len; - struct strbuf_node *root; - - size_t nodes_count; - size_t in_count; - size_t in_len; - size_t dedup_len; - size_t dedup_count; -}; - -struct strbuf_node { - size_t value_off; - size_t value_len; - - struct strbuf_child_entry *children; - uint8_t children_count; -}; - -struct strbuf_child_entry { - uint8_t c; - struct strbuf_node *child; -}; - -struct strbuf *strbuf_new(void); -ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len); -void strbuf_complete(struct strbuf *str); -void strbuf_cleanup(struct strbuf *str); diff --git a/src/basic/string-table.c b/src/basic/string-table.c deleted file mode 100644 index a1499ab126..0000000000 --- a/src/basic/string-table.c +++ /dev/null @@ -1,34 +0,0 @@ -/*** - 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" -#include "string-util.h" - -ssize_t string_table_lookup(const char * const *table, size_t len, const char *key) { - size_t i; - - if (!key) - return -1; - - for (i = 0; i < len; ++i) - if (streq_ptr(table[i], key)) - return (ssize_t) i; - - return -1; -} diff --git a/src/basic/string-table.h b/src/basic/string-table.h deleted file mode 100644 index 369610efc8..0000000000 --- a/src/basic/string-table.h +++ /dev/null @@ -1,119 +0,0 @@ - -#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 <errno.h> -#include <stddef.h> -#include <stdio.h> -#include <string.h> -#include <sys/types.h> - -#include "macro.h" -#include "parse-util.h" -#include "string-util.h" - -ssize_t string_table_lookup(const char * const *table, size_t len, const char *key); - -/* For basic lookup tables with strictly enumerated entries */ -#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ - scope const char *name##_to_string(type i) { \ - if (i < 0 || i >= (type) ELEMENTSOF(name##_table)) \ - return NULL; \ - return name##_table[i]; \ - } - -#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \ - scope type name##_from_string(const char *s) { \ - return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ - } - -#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \ - scope type name##_from_string(const char *s) { \ - int b; \ - if (!s) \ - return -1; \ - b = parse_boolean(s); \ - if (b == 0) \ - return (type) 0; \ - else if (b > 0) \ - return yes; \ - return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ - } - -#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,scope) \ - scope int name##_to_string_alloc(type i, char **str) { \ - char *s; \ - if (i < 0 || i > max) \ - return -ERANGE; \ - if (i < (type) ELEMENTSOF(name##_table)) { \ - s = strdup(name##_table[i]); \ - if (!s) \ - return -ENOMEM; \ - } else { \ - if (asprintf(&s, "%i", i) < 0) \ - return -ENOMEM; \ - } \ - *str = s; \ - return 0; \ - } - -#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,scope) \ - type name##_from_string(const char *s) { \ - type i; \ - unsigned u = 0; \ - if (!s) \ - return (type) -1; \ - for (i = 0; i < (type) ELEMENTSOF(name##_table); i++) \ - if (streq_ptr(name##_table[i], s)) \ - return i; \ - if (safe_atou(s, &u) >= 0 && u <= max) \ - return (type) u; \ - return (type) -1; \ - } \ - - -#define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \ - struct __useless_struct_to_allow_trailing_semicolon__ - -#define _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \ - 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) - -#define DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,) - -/* For string conversions where numbers are also acceptable */ -#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \ - _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,) \ - struct __useless_struct_to_allow_trailing_semicolon__ - -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max) \ - _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,static) -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,static) diff --git a/src/basic/string-util.c b/src/basic/string-util.c deleted file mode 100644 index 6b06e643c9..0000000000 --- a/src/basic/string-util.c +++ /dev/null @@ -1,868 +0,0 @@ -/*** - 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 <errno.h> -#include <stdarg.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "gunicode.h" -#include "macro.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_tolower(char x) { - - if (x >= 'A' && x <= 'Z') - return x - 'A' + 'a'; - - return x; -} - -char ascii_toupper(char x) { - - if (x >= 'a' && x <= 'z') - return x - 'a' + 'A'; - - return x; -} - -char *ascii_strlower(char *t) { - char *p; - - assert(t); - - for (p = t; *p; p++) - *p = ascii_tolower(*p); - - return t; -} - -char *ascii_strupper(char *t) { - char *p; - - assert(t); - - for (p = t; *p; p++) - *p = ascii_toupper(*p); - - return t; -} - -char *ascii_strlower_n(char *t, size_t n) { - size_t i; - - if (n <= 0) - return t; - - for (i = 0; i < n; i++) - t[i] = ascii_tolower(t[i]); - - return t; -} - -int ascii_strcasecmp_n(const char *a, const char *b, size_t n) { - - for (; n > 0; a++, b++, n--) { - int x, y; - - x = (int) (uint8_t) ascii_tolower(*a); - y = (int) (uint8_t) ascii_tolower(*b); - - if (x != y) - return x - y; - } - - return 0; -} - -int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m) { - int r; - - r = ascii_strcasecmp_n(a, b, MIN(n, m)); - if (r != 0) - return r; - - if (n < m) - return -1; - else if (n > m) - return 1; - else - return 0; -} - -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+3); - if (!r) - return NULL; - - x = (new_length * percent) / 100; - - if (x > new_length - 3) - x = new_length - 3; - - memcpy(r, s, x); - r[x] = 0xe2; /* tri-dot ellipsis: … */ - r[x+1] = 0x80; - r[x+2] = 0xa6; - memcpy(r + x + 3, - s + old_length - (new_length - x - 1), - new_length - x - 1); - - 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; - int r; - - 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)) { - char32_t c; - - r = utf8_encoded_to_unichar(i, &c); - if (r < 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; ) { - char32_t c; - - j = utf8_prev_char(j); - r = utf8_encoded_to_unichar(j, &c); - if (r < 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: - return mfree(r); -} - -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); - return mfree(obuf); - } - - 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; -} - -/* - * Pointer to memset is volatile so that compiler must de-reference - * the pointer and can't assume that it points to any function in - * particular (such as memset, which it then might further "optimize") - * This approach is inspired by openssl's crypto/mem_clr.c. - */ -typedef void *(*memset_t)(void *,int,size_t); - -static volatile memset_t memset_func = memset; - -void* memory_erase(void *p, size_t l) { - return memset_func(p, 'x', l); -} - -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/basic/string-util.h b/src/basic/string-util.h deleted file mode 100644 index d029d538bd..0000000000 --- a/src/basic/string-util.h +++ /dev/null @@ -1,201 +0,0 @@ -#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 <stdbool.h> -#include <stddef.h> -#include <string.h> - -#include "macro.h" - -/* What is interpreted as whitespace? */ -#define WHITESPACE " \t\n\r" -#define NEWLINE "\n\r" -#define QUOTES "\"\'" -#define COMMENTS "#;" -#define GLOB_CHARS "*?[" -#define DIGITS "0123456789" -#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz" -#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" -#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS -#define ALPHANUMERICAL LETTERS DIGITS -#define HEXDIGITS DIGITS "abcdefABCDEF" - -#define streq(a,b) (strcmp((a),(b)) == 0) -#define strneq(a, b, n) (strncmp((a), (b), (n)) == 0) -#define strcaseeq(a,b) (strcasecmp((a),(b)) == 0) -#define strncaseeq(a, b, n) (strncasecmp((a), (b), (n)) == 0) - -int strcmp_ptr(const char *a, const char *b) _pure_; - -static inline bool streq_ptr(const char *a, const char *b) { - return strcmp_ptr(a, b) == 0; -} - -static inline const char* strempty(const char *s) { - return s ? 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 const char *empty_to_null(const char *p) { - return isempty(p) ? NULL : p; -} - -static inline const char *strdash_if_empty(const char *str) { - return isempty(str) ? "-" : str; -} - -static inline char *startswith(const char *s, const char *prefix) { - size_t l; - - l = strlen(prefix); - if (strncmp(s, prefix, l) == 0) - return (char*) s + l; - - return NULL; -} - -static inline char *startswith_no_case(const char *s, const char *prefix) { - size_t l; - - l = strlen(prefix); - if (strncasecmp(s, prefix, l) == 0) - return (char*) s + l; - - return NULL; -} - -char *endswith(const char *s, const char *postfix) _pure_; -char *endswith_no_case(const char *s, const char *postfix) _pure_; - -char *first_word(const char *s, const char *word) _pure_; - -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_tolower(char x); -char *ascii_strlower(char *s); -char *ascii_strlower_n(char *s, size_t n); - -char ascii_toupper(char x); -char *ascii_strupper(char *s); - -int ascii_strcasecmp_n(const char *a, const char *b, size_t n); -int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m); - -bool chars_intersect(const char *a, const char *b) _pure_; - -static inline bool _pure_ in_charset(const char *s, const char* charset) { - assert(s); - assert(charset); - return s[strspn(s, charset)] == '\0'; -} - -bool string_has_cc(const char *p, const char *ok) _pure_; - -char *ellipsize_mem(const char *s, size_t old_length_bytes, size_t new_length_columns, unsigned percent); -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/basic/strv.c b/src/basic/strv.c deleted file mode 100644 index 0eec868eed..0000000000 --- a/src/basic/strv.c +++ /dev/null @@ -1,940 +0,0 @@ -/*** - 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 <errno.h> -#include <fnmatch.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "escape.h" -#include "extract-word.h" -#include "fileio.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" - -char *strv_find(char **l, const char *name) { - char **i; - - assert(name); - - STRV_FOREACH(i, l) - if (streq(*i, name)) - return *i; - - return NULL; -} - -char *strv_find_prefix(char **l, const char *name) { - char **i; - - assert(name); - - STRV_FOREACH(i, l) - if (startswith(*i, name)) - return *i; - - return NULL; -} - -char *strv_find_startswith(char **l, const char *name) { - char **i, *e; - - assert(name); - - /* Like strv_find_prefix, but actually returns only the - * suffix, not the whole item */ - - STRV_FOREACH(i, l) { - e = startswith(*i, name); - if (e) - return e; - } - - return NULL; -} - -void strv_clear(char **l) { - char **k; - - if (!l) - return; - - for (k = l; *k; k++) - free(*k); - - *l = NULL; -} - -char **strv_free(char **l) { - strv_clear(l); - return mfree(l); -} - -char **strv_free_erase(char **l) { - char **i; - - STRV_FOREACH(i, l) - string_erase(*i); - - return strv_free(l); -} - -char **strv_copy(char * const *l) { - char **r, **k; - - k = r = new(char*, strv_length(l) + 1); - if (!r) - return NULL; - - if (l) - for (; *l; k++, l++) { - *k = strdup(*l); - if (!*k) { - strv_free(r); - return NULL; - } - } - - *k = NULL; - return r; -} - -unsigned strv_length(char * const *l) { - unsigned n = 0; - - if (!l) - return 0; - - for (; *l; l++) - n++; - - return n; -} - -char **strv_new_ap(const char *x, va_list ap) { - const char *s; - char **a; - unsigned n = 0, i = 0; - va_list aq; - - /* As a special trick we ignore all listed strings that equal - * STRV_IGNORE. This is supposed to be used with the - * STRV_IFNOTNULL() macro to include possibly NULL strings in - * the string list. */ - - if (x) { - n = x == STRV_IGNORE ? 0 : 1; - - va_copy(aq, ap); - while ((s = va_arg(aq, const char*))) { - if (s == STRV_IGNORE) - continue; - - n++; - } - - va_end(aq); - } - - a = new(char*, n+1); - if (!a) - return NULL; - - if (x) { - if (x != STRV_IGNORE) { - a[i] = strdup(x); - if (!a[i]) - goto fail; - i++; - } - - while ((s = va_arg(ap, const char*))) { - - if (s == STRV_IGNORE) - continue; - - a[i] = strdup(s); - if (!a[i]) - goto fail; - - i++; - } - } - - a[i] = NULL; - - return a; - -fail: - strv_free(a); - return NULL; -} - -char **strv_new(const char *x, ...) { - char **r; - va_list ap; - - va_start(ap, x); - r = strv_new_ap(x, ap); - va_end(ap); - - return r; -} - -int strv_extend_strv(char ***a, char **b, bool filter_duplicates) { - char **s, **t; - size_t p, q, i = 0, j; - - assert(a); - - if (strv_isempty(b)) - return 0; - - p = strv_length(*a); - q = strv_length(b); - - t = realloc(*a, sizeof(char*) * (p + q + 1)); - if (!t) - return -ENOMEM; - - t[p] = NULL; - *a = t; - - STRV_FOREACH(s, b) { - - if (filter_duplicates && strv_contains(t, *s)) - continue; - - t[p+i] = strdup(*s); - if (!t[p+i]) - goto rollback; - - i++; - t[p+i] = NULL; - } - - assert(i <= q); - - return (int) i; - -rollback: - for (j = 0; j < i; j++) - free(t[p + j]); - - t[p] = NULL; - return -ENOMEM; -} - -int strv_extend_strv_concat(char ***a, char **b, const char *suffix) { - int r; - char **s; - - STRV_FOREACH(s, b) { - char *v; - - v = strappend(*s, suffix); - if (!v) - return -ENOMEM; - - r = strv_push(a, v); - if (r < 0) { - free(v); - return r; - } - } - - return 0; -} - -char **strv_split(const char *s, const char *separator) { - const char *word, *state; - size_t l; - unsigned n, i; - char **r; - - assert(s); - - n = 0; - FOREACH_WORD_SEPARATOR(word, l, s, separator, state) - n++; - - r = new(char*, n+1); - if (!r) - return NULL; - - i = 0; - FOREACH_WORD_SEPARATOR(word, l, s, separator, state) { - r[i] = strndup(word, l); - if (!r[i]) { - strv_free(r); - return NULL; - } - - i++; - } - - r[i] = NULL; - return r; -} - -char **strv_split_newlines(const char *s) { - char **l; - unsigned n; - - assert(s); - - /* Special version of strv_split() that splits on newlines and - * suppresses an empty string at the end */ - - l = strv_split(s, NEWLINE); - if (!l) - return NULL; - - n = strv_length(l); - if (n <= 0) - return l; - - if (isempty(l[n - 1])) - l[n - 1] = mfree(l[n - 1]); - - return l; -} - -int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) { - _cleanup_strv_free_ char **l = NULL; - size_t n = 0, allocated = 0; - int r; - - assert(t); - assert(s); - - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&s, &word, separators, flags); - if (r < 0) - return r; - if (r == 0) - break; - - if (!GREEDY_REALLOC(l, allocated, n + 2)) - return -ENOMEM; - - l[n++] = word; - word = NULL; - - l[n] = NULL; - } - - if (!l) { - l = new0(char*, 1); - if (!l) - return -ENOMEM; - } - - *t = l; - l = NULL; - - return (int) n; -} - -char *strv_join(char **l, const char *separator) { - char *r, *e; - char **s; - size_t n, k; - - if (!separator) - separator = " "; - - k = strlen(separator); - - n = 0; - STRV_FOREACH(s, l) { - if (s != l) - n += k; - n += strlen(*s); - } - - r = new(char, n+1); - if (!r) - return NULL; - - e = r; - STRV_FOREACH(s, l) { - if (s != l) - e = stpcpy(e, separator); - - e = stpcpy(e, *s); - } - - *e = 0; - - return r; -} - -char *strv_join_quoted(char **l) { - char *buf = NULL; - char **s; - size_t allocated = 0, len = 0; - - STRV_FOREACH(s, l) { - /* assuming here that escaped string cannot be more - * than twice as long, and reserving space for the - * separator and quotes. - */ - _cleanup_free_ char *esc = NULL; - size_t needed; - - if (!GREEDY_REALLOC(buf, allocated, - len + strlen(*s) * 2 + 3)) - goto oom; - - esc = cescape(*s); - if (!esc) - goto oom; - - needed = snprintf(buf + len, allocated - len, "%s\"%s\"", - len > 0 ? " " : "", esc); - assert(needed < allocated - len); - len += needed; - } - - if (!buf) - buf = malloc0(1); - - return buf; - - oom: - return mfree(buf); -} - -int strv_push(char ***l, char *value) { - char **c; - unsigned n, m; - - if (!value) - return 0; - - n = strv_length(*l); - - /* Increase and check for overflow */ - m = n + 2; - if (m < n) - return -ENOMEM; - - c = realloc_multiply(*l, sizeof(char*), m); - if (!c) - return -ENOMEM; - - c[n] = value; - c[n+1] = NULL; - - *l = c; - return 0; -} - -int strv_push_pair(char ***l, char *a, char *b) { - char **c; - unsigned n, m; - - if (!a && !b) - return 0; - - n = strv_length(*l); - - /* increase and check for overflow */ - m = n + !!a + !!b + 1; - if (m < n) - return -ENOMEM; - - c = realloc_multiply(*l, sizeof(char*), m); - if (!c) - return -ENOMEM; - - if (a) - c[n++] = a; - if (b) - c[n++] = b; - c[n] = NULL; - - *l = c; - return 0; -} - -int strv_push_prepend(char ***l, char *value) { - char **c; - unsigned n, m, i; - - if (!value) - return 0; - - n = strv_length(*l); - - /* increase and check for overflow */ - m = n + 2; - if (m < n) - return -ENOMEM; - - c = new(char*, m); - if (!c) - return -ENOMEM; - - for (i = 0; i < n; i++) - c[i+1] = (*l)[i]; - - c[0] = value; - c[n+1] = NULL; - - free(*l); - *l = c; - - return 0; -} - -int strv_consume(char ***l, char *value) { - int r; - - r = strv_push(l, value); - if (r < 0) - free(value); - - return r; -} - -int strv_consume_pair(char ***l, char *a, char *b) { - int r; - - r = strv_push_pair(l, a, b); - if (r < 0) { - free(a); - free(b); - } - - return r; -} - -int strv_consume_prepend(char ***l, char *value) { - int r; - - r = strv_push_prepend(l, value); - if (r < 0) - free(value); - - return r; -} - -int strv_extend(char ***l, const char *value) { - char *v; - - if (!value) - return 0; - - v = strdup(value); - if (!v) - return -ENOMEM; - - return strv_consume(l, v); -} - -int strv_extend_front(char ***l, const char *value) { - size_t n, m; - char *v, **c; - - assert(l); - - /* Like strv_extend(), but prepends rather than appends the new entry */ - - if (!value) - return 0; - - n = strv_length(*l); - - /* Increase and overflow check. */ - m = n + 2; - if (m < n) - return -ENOMEM; - - v = strdup(value); - if (!v) - return -ENOMEM; - - c = realloc_multiply(*l, sizeof(char*), m); - if (!c) { - free(v); - return -ENOMEM; - } - - memmove(c+1, c, n * sizeof(char*)); - c[0] = v; - c[n+1] = NULL; - - *l = c; - return 0; -} - -char **strv_uniq(char **l) { - char **i; - - /* Drops duplicate entries. The first identical string will be - * kept, the others dropped */ - - STRV_FOREACH(i, l) - strv_remove(i+1, *i); - - return l; -} - -bool strv_is_uniq(char **l) { - char **i; - - STRV_FOREACH(i, l) - if (strv_find(i+1, *i)) - return false; - - return true; -} - -char **strv_remove(char **l, const char *s) { - char **f, **t; - - if (!l) - return NULL; - - assert(s); - - /* Drops every occurrence of s in the string list, edits - * in-place. */ - - for (f = t = l; *f; f++) - if (streq(*f, s)) - free(*f); - else - *(t++) = *f; - - *t = NULL; - return l; -} - -char **strv_parse_nulstr(const char *s, size_t l) { - /* l is the length of the input data, which will be split at NULs into - * elements of the resulting strv. Hence, the number of items in the resulting strv - * will be equal to one plus the number of NUL bytes in the l bytes starting at s, - * unless s[l-1] is NUL, in which case the final empty string is not stored in - * the resulting strv, and length is equal to the number of NUL bytes. - * - * Note that contrary to a normal nulstr which cannot contain empty strings, because - * the input data is terminated by any two consequent NUL bytes, this parser accepts - * empty strings in s. - */ - - const char *p; - unsigned c = 0, i = 0; - char **v; - - assert(s || l <= 0); - - if (l <= 0) - return new0(char*, 1); - - for (p = s; p < s + l; p++) - if (*p == 0) - c++; - - if (s[l-1] != 0) - c++; - - v = new0(char*, c+1); - if (!v) - return NULL; - - p = s; - while (p < s + l) { - const char *e; - - e = memchr(p, 0, s + l - p); - - v[i] = strndup(p, e ? e - p : s + l - p); - if (!v[i]) { - strv_free(v); - return NULL; - } - - i++; - - if (!e) - break; - - p = e + 1; - } - - assert(i == c); - - return v; -} - -char **strv_split_nulstr(const char *s) { - const char *i; - char **r = NULL; - - NULSTR_FOREACH(i, s) - if (strv_extend(&r, i) < 0) { - strv_free(r); - return NULL; - } - - if (!r) - return strv_new(NULL, NULL); - - return r; -} - -int strv_make_nulstr(char **l, char **p, size_t *q) { - /* A valid nulstr with two NULs at the end will be created, but - * q will be the length without the two trailing NULs. Thus the output - * string is a valid nulstr and can be iterated over using NULSTR_FOREACH, - * and can also be parsed by strv_parse_nulstr as long as the length - * is provided separately. - */ - - size_t n_allocated = 0, n = 0; - _cleanup_free_ char *m = NULL; - char **i; - - assert(p); - assert(q); - - STRV_FOREACH(i, l) { - size_t z; - - z = strlen(*i); - - if (!GREEDY_REALLOC(m, n_allocated, n + z + 2)) - return -ENOMEM; - - memcpy(m + n, *i, z + 1); - n += z + 1; - } - - if (!m) { - m = new0(char, 1); - if (!m) - return -ENOMEM; - n = 1; - } else - /* make sure there is a second extra NUL at the end of resulting nulstr */ - m[n] = '\0'; - - assert(n > 0); - *p = m; - *q = n - 1; - - m = NULL; - - return 0; -} - -bool strv_overlap(char **a, char **b) { - char **i; - - STRV_FOREACH(i, a) - if (strv_contains(b, *i)) - return true; - - return false; -} - -static int str_compare(const void *_a, const void *_b) { - const char **a = (const char**) _a, **b = (const char**) _b; - - return strcmp(*a, *b); -} - -char **strv_sort(char **l) { - - if (strv_isempty(l)) - return l; - - qsort(l, strv_length(l), sizeof(char*), str_compare); - return l; -} - -bool strv_equal(char **a, char **b) { - - if (strv_isempty(a)) - return strv_isempty(b); - - if (strv_isempty(b)) - return false; - - for ( ; *a || *b; ++a, ++b) - if (!streq_ptr(*a, *b)) - return false; - - return true; -} - -void strv_print(char **l) { - char **s; - - STRV_FOREACH(s, l) - puts(*s); -} - -int strv_extendf(char ***l, const char *format, ...) { - va_list ap; - char *x; - int r; - - va_start(ap, format); - r = vasprintf(&x, format, ap); - va_end(ap); - - if (r < 0) - return -ENOMEM; - - return strv_consume(l, x); -} - -char **strv_reverse(char **l) { - unsigned n, i; - - n = strv_length(l); - if (n <= 1) - return l; - - for (i = 0; i < n / 2; i++) - SWAP_TWO(l[i], l[n-1-i]); - - return l; -} - -char **strv_shell_escape(char **l, const char *bad) { - char **s; - - /* Escapes every character in every string in l that is in bad, - * edits in-place, does not roll-back on error. */ - - STRV_FOREACH(s, l) { - char *v; - - v = shell_escape(*s, bad); - if (!v) - return NULL; - - free(*s); - *s = v; - } - - return l; -} - -bool strv_fnmatch(char* const* patterns, const char *s, int flags) { - char* const* p; - - STRV_FOREACH(p, patterns) - if (fnmatch(*p, s, flags) == 0) - return true; - - return false; -} - -char ***strv_free_free(char ***l) { - char ***i; - - if (!l) - return NULL; - - for (i = l; *i; i++) - strv_free(*i); - - return mfree(l); -} - -char **strv_skip(char **l, size_t n) { - - while (n > 0) { - if (strv_isempty(l)) - return l; - - l++, n--; - } - - return l; -} - -int strv_extend_n(char ***l, const char *value, size_t n) { - size_t i, j, k; - char **nl; - - assert(l); - - if (!value) - return 0; - if (n == 0) - return 0; - - /* Adds the value n times to l */ - - k = strv_length(*l); - - nl = realloc(*l, sizeof(char*) * (k + n + 1)); - if (!nl) - return -ENOMEM; - - *l = nl; - - for (i = k; i < k + n; i++) { - nl[i] = strdup(value); - if (!nl[i]) - goto rollback; - } - - nl[i] = NULL; - return 0; - -rollback: - for (j = k; j < i; j++) - free(nl[j]); - - nl[k] = NULL; - return -ENOMEM; -} - -int fputstrv(FILE *f, char **l, const char *separator, bool *space) { - bool b = false; - char **s; - int r; - - /* Like fputs(), but for strv, and with a less stupid argument order */ - - if (!space) - space = &b; - - STRV_FOREACH(s, l) { - r = fputs_with_space(f, *s, separator, space); - if (r < 0) - return r; - } - - return 0; -} diff --git a/src/basic/strv.h b/src/basic/strv.h deleted file mode 100644 index 385ad17779..0000000000 --- a/src/basic/strv.h +++ /dev/null @@ -1,182 +0,0 @@ -#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 <fnmatch.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stddef.h> - -#include "alloc-util.h" -#include "extract-word.h" -#include "macro.h" -#include "util.h" - -char *strv_find(char **l, const char *name) _pure_; -char *strv_find_prefix(char **l, const char *name) _pure_; -char *strv_find_startswith(char **l, const char *name) _pure_; - -char **strv_free(char **l); -DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free); -#define _cleanup_strv_free_ _cleanup_(strv_freep) - -char **strv_free_erase(char **l); -DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase); -#define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep) - -void strv_clear(char **l); - -char **strv_copy(char * const *l); -unsigned strv_length(char * const *l) _pure_; - -int strv_extend_strv(char ***a, char **b, bool filter_duplicates); -int strv_extend_strv_concat(char ***a, char **b, const char *suffix); -int strv_extend(char ***l, const char *value); -int strv_extendf(char ***l, const char *format, ...) _printf_(2,0); -int strv_extend_front(char ***l, const char *value); -int strv_push(char ***l, char *value); -int strv_push_pair(char ***l, char *a, char *b); -int strv_push_prepend(char ***l, char *value); -int strv_consume(char ***l, char *value); -int strv_consume_pair(char ***l, char *a, char *b); -int strv_consume_prepend(char ***l, char *value); - -char **strv_remove(char **l, const char *s); -char **strv_uniq(char **l); -bool strv_is_uniq(char **l); - -bool strv_equal(char **a, char **b); - -#define strv_contains(l, s) (!!strv_find((l), (s))) - -char **strv_new(const char *x, ...) _sentinel_; -char **strv_new_ap(const char *x, va_list ap); - -#define STRV_IGNORE ((const char *) -1) - -static inline const char* STRV_IFNOTNULL(const char *x) { - return x ? x : STRV_IGNORE; -} - -static inline bool strv_isempty(char * const *l) { - return !l || !*l; -} - -char **strv_split(const char *s, const char *separator); -char **strv_split_newlines(const char *s); - -int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags); - -char *strv_join(char **l, const char *separator); -char *strv_join_quoted(char **l); - -char **strv_parse_nulstr(const char *s, size_t l); -char **strv_split_nulstr(const char *s); -int strv_make_nulstr(char **l, char **p, size_t *n); - -bool strv_overlap(char **a, char **b) _pure_; - -#define STRV_FOREACH(s, l) \ - for ((s) = (l); (s) && *(s); (s)++) - -#define STRV_FOREACH_BACKWARDS(s, l) \ - for (s = ({ \ - char **_l = l; \ - _l ? _l + strv_length(_l) - 1U : NULL; \ - }); \ - (l) && ((s) >= (l)); \ - (s)--) - -#define STRV_FOREACH_PAIR(x, y, l) \ - for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1)) - -char **strv_sort(char **l); -void strv_print(char **l); - -#define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL })) - -#define STRV_MAKE_EMPTY ((char*[1]) { NULL }) - -#define strv_from_stdarg_alloca(first) \ - ({ \ - char **_l; \ - \ - if (!first) \ - _l = (char**) &first; \ - else { \ - unsigned _n; \ - va_list _ap; \ - \ - _n = 1; \ - va_start(_ap, first); \ - while (va_arg(_ap, char*)) \ - _n++; \ - va_end(_ap); \ - \ - _l = newa(char*, _n+1); \ - _l[_n = 0] = (char*) first; \ - va_start(_ap, first); \ - for (;;) { \ - _l[++_n] = va_arg(_ap, char*); \ - if (!_l[_n]) \ - break; \ - } \ - va_end(_ap); \ - } \ - _l; \ - }) - -#define STR_IN_SET(x, ...) strv_contains(STRV_MAKE(__VA_ARGS__), x) -#define STRPTR_IN_SET(x, ...) \ - ({ \ - const char* _x = (x); \ - _x && strv_contains(STRV_MAKE(__VA_ARGS__), _x); \ - }) - -#define FOREACH_STRING(x, ...) \ - for (char **_l = ({ \ - char **_ll = STRV_MAKE(__VA_ARGS__); \ - x = _ll ? _ll[0] : NULL; \ - _ll; \ - }); \ - _l && *_l; \ - x = ({ \ - _l ++; \ - _l[0]; \ - })) - -char **strv_reverse(char **l); -char **strv_shell_escape(char **l, const char *bad); - -bool strv_fnmatch(char* const* patterns, const char *s, int flags); - -static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, int flags) { - assert(s); - return strv_isempty(patterns) || - strv_fnmatch(patterns, s, flags); -} - -char ***strv_free_free(char ***l); - -char **strv_skip(char **l, size_t n); - -int strv_extend_n(char ***l, const char *value, size_t n); - -int fputstrv(FILE *f, char **l, const char *separator, bool *space); diff --git a/src/basic/strxcpyx.c b/src/basic/strxcpyx.c deleted file mode 100644 index aaf11d21f6..0000000000 --- a/src/basic/strxcpyx.c +++ /dev/null @@ -1,100 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Kay Sievers - - 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/>. -***/ - -/* - * Concatenates/copies strings. In any case, terminates in all cases - * with '\0' * and moves the @dest pointer forward to the added '\0'. - * Returns the * remaining size, and 0 if the string was truncated. - */ - -#include <stdarg.h> -#include <stdio.h> -#include <string.h> - -#include "strxcpyx.h" - -size_t strpcpy(char **dest, size_t size, const char *src) { - size_t len; - - len = strlen(src); - if (len >= size) { - if (size > 1) - *dest = mempcpy(*dest, src, size-1); - size = 0; - } else { - if (len > 0) { - *dest = mempcpy(*dest, src, len); - size -= len; - } - } - *dest[0] = '\0'; - return size; -} - -size_t strpcpyf(char **dest, size_t size, const char *src, ...) { - va_list va; - int i; - - va_start(va, src); - i = vsnprintf(*dest, size, src, va); - if (i < (int)size) { - *dest += i; - size -= i; - } else { - *dest += size; - size = 0; - } - va_end(va); - *dest[0] = '\0'; - return size; -} - -size_t strpcpyl(char **dest, size_t size, const char *src, ...) { - va_list va; - - va_start(va, src); - do { - size = strpcpy(dest, size, src); - src = va_arg(va, char *); - } while (src != NULL); - va_end(va); - return size; -} - -size_t strscpy(char *dest, size_t size, const char *src) { - char *s; - - s = dest; - return strpcpy(&s, size, src); -} - -size_t strscpyl(char *dest, size_t size, const char *src, ...) { - va_list va; - char *s; - - va_start(va, src); - s = dest; - do { - size = strpcpy(&s, size, src); - src = va_arg(va, char *); - } while (src != NULL); - va_end(va); - - return size; -} diff --git a/src/basic/strxcpyx.h b/src/basic/strxcpyx.h deleted file mode 100644 index 80ff58726b..0000000000 --- a/src/basic/strxcpyx.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Kay Sievers - - 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 "macro.h" - -size_t strpcpy(char **dest, size_t size, const char *src); -size_t strpcpyf(char **dest, size_t size, const char *src, ...) _printf_(3, 4); -size_t strpcpyl(char **dest, size_t size, const char *src, ...) _sentinel_; -size_t strscpy(char *dest, size_t size, const char *src); -size_t strscpyl(char *dest, size_t size, const char *src, ...) _sentinel_; diff --git a/src/basic/syslog-util.c b/src/basic/syslog-util.c deleted file mode 100644 index db3405154e..0000000000 --- a/src/basic/syslog-util.c +++ /dev/null @@ -1,114 +0,0 @@ -/*** - 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.h> -#include <syslog.h> - -#include "hexdecoct.h" -#include "macro.h" -#include "string-table.h" -#include "syslog-util.h" - -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; -} - -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)); - -bool log_facility_unshifted_is_valid(int facility) { - return facility >= 0 && facility <= 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); - -bool log_level_is_valid(int level) { - return level >= 0 && level <= LOG_DEBUG; -} diff --git a/src/basic/syslog-util.h b/src/basic/syslog-util.h deleted file mode 100644 index 5cb606a1bf..0000000000 --- a/src/basic/syslog-util.h +++ /dev/null @@ -1,32 +0,0 @@ -#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> - -int log_facility_unshifted_to_string_alloc(int i, char **s); -int log_facility_unshifted_from_string(const char *s); -bool log_facility_unshifted_is_valid(int faciliy); - -int log_level_to_string_alloc(int i, char **s); -int log_level_from_string(const char *s); -bool log_level_is_valid(int level); - -int syslog_parse_priority(const char **p, int *priority, bool with_facility); diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c deleted file mode 100644 index eafdea9eb3..0000000000 --- a/src/basic/terminal-util.c +++ /dev/null @@ -1,1224 +0,0 @@ -/*** - 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 <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <stdarg.h> -#include <stddef.h> -#include <stdlib.h> -#include <string.h> -#include <sys/inotify.h> -#include <sys/socket.h> -#include <sys/sysmacros.h> -#include <sys/time.h> -#include <linux/kd.h> -#include <linux/tiocl.h> -#include <linux/vt.h> -#include <poll.h> -#include <signal.h> -#include <sys/ioctl.h> -#include <sys/types.h> -#include <termios.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "env-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "io-util.h" -#include "log.h" -#include "macro.h" -#include "parse-util.h" -#include "process-util.h" -#include "socket-util.h" -#include "stat-util.h" -#include "string-util.h" -#include "strv.h" -#include "terminal-util.h" -#include "time-util.h" -#include "util.h" - -static volatile unsigned cached_columns = 0; -static volatile unsigned cached_lines = 0; - -int chvt(int vt) { - _cleanup_close_ int fd; - - fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); - if (fd < 0) - return -errno; - - if (vt <= 0) { - int tiocl[2] = { - TIOCL_GETKMSGREDIRECT, - 0 - }; - - if (ioctl(fd, TIOCLINUX, tiocl) < 0) - return -errno; - - vt = tiocl[0] <= 0 ? 1 : tiocl[0]; - } - - if (ioctl(fd, VT_ACTIVATE, vt) < 0) - return -errno; - - return 0; -} - -int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) { - struct termios old_termios, new_termios; - char c, line[LINE_MAX]; - - assert(f); - assert(ret); - - if (tcgetattr(fileno(f), &old_termios) >= 0) { - new_termios = old_termios; - - new_termios.c_lflag &= ~ICANON; - new_termios.c_cc[VMIN] = 1; - new_termios.c_cc[VTIME] = 0; - - if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) { - size_t k; - - if (t != USEC_INFINITY) { - if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) { - tcsetattr(fileno(f), TCSADRAIN, &old_termios); - return -ETIMEDOUT; - } - } - - k = fread(&c, 1, 1, f); - - tcsetattr(fileno(f), TCSADRAIN, &old_termios); - - if (k <= 0) - return -EIO; - - if (need_nl) - *need_nl = c != '\n'; - - *ret = c; - return 0; - } - } - - if (t != USEC_INFINITY) { - if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) - return -ETIMEDOUT; - } - - errno = 0; - if (!fgets(line, sizeof(line), f)) - return errno > 0 ? -errno : -EIO; - - truncate_nl(line); - - if (strlen(line) != 1) - return -EBADMSG; - - if (need_nl) - *need_nl = false; - - *ret = line[0]; - return 0; -} - -int ask_char(char *ret, const char *replies, const char *text, ...) { - int r; - - assert(ret); - assert(replies); - assert(text); - - for (;;) { - va_list ap; - char c; - bool need_nl = true; - - if (colors_enabled()) - fputs(ANSI_HIGHLIGHT, stdout); - - va_start(ap, text); - vprintf(text, ap); - va_end(ap); - - if (colors_enabled()) - fputs(ANSI_NORMAL, stdout); - - fflush(stdout); - - r = read_one_char(stdin, &c, USEC_INFINITY, &need_nl); - if (r < 0) { - - if (r == -EBADMSG) { - puts("Bad input, please try again."); - continue; - } - - putchar('\n'); - return r; - } - - if (need_nl) - putchar('\n'); - - if (strchr(replies, c)) { - *ret = c; - return 0; - } - - puts("Read unexpected character, please try again."); - } -} - -int ask_string(char **ret, const char *text, ...) { - assert(ret); - assert(text); - - for (;;) { - char line[LINE_MAX]; - va_list ap; - - if (colors_enabled()) - fputs(ANSI_HIGHLIGHT, stdout); - - va_start(ap, text); - vprintf(text, ap); - va_end(ap); - - if (colors_enabled()) - fputs(ANSI_NORMAL, stdout); - - fflush(stdout); - - errno = 0; - if (!fgets(line, sizeof(line), stdin)) - return errno > 0 ? -errno : -EIO; - - if (!endswith(line, "\n")) - putchar('\n'); - else { - char *s; - - if (isempty(line)) - continue; - - truncate_nl(line); - s = strdup(line); - if (!s) - return -ENOMEM; - - *ret = s; - return 0; - } - } -} - -int reset_terminal_fd(int fd, bool switch_to_text) { - struct termios termios; - int r = 0; - - /* Set terminal to some sane defaults */ - - assert(fd >= 0); - - /* We leave locked terminal attributes untouched, so that - * Plymouth may set whatever it wants to set, and we don't - * interfere with that. */ - - /* Disable exclusive mode, just in case */ - (void) ioctl(fd, TIOCNXCL); - - /* Switch to text mode */ - if (switch_to_text) - (void) ioctl(fd, KDSETMODE, KD_TEXT); - - /* Enable console unicode mode */ - (void) ioctl(fd, KDSKBMODE, K_UNICODE); - - if (tcgetattr(fd, &termios) < 0) { - r = -errno; - goto finish; - } - - /* We only reset the stuff that matters to the software. How - * hardware is set up we don't touch assuming that somebody - * else will do that for us */ - - termios.c_iflag &= ~(IGNBRK | BRKINT | ISTRIP | INLCR | IGNCR | IUCLC); - termios.c_iflag |= ICRNL | IMAXBEL | IUTF8; - termios.c_oflag |= ONLCR; - termios.c_cflag |= CREAD; - termios.c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOPRT | ECHOKE; - - termios.c_cc[VINTR] = 03; /* ^C */ - termios.c_cc[VQUIT] = 034; /* ^\ */ - termios.c_cc[VERASE] = 0177; - termios.c_cc[VKILL] = 025; /* ^X */ - termios.c_cc[VEOF] = 04; /* ^D */ - termios.c_cc[VSTART] = 021; /* ^Q */ - termios.c_cc[VSTOP] = 023; /* ^S */ - termios.c_cc[VSUSP] = 032; /* ^Z */ - termios.c_cc[VLNEXT] = 026; /* ^V */ - termios.c_cc[VWERASE] = 027; /* ^W */ - termios.c_cc[VREPRINT] = 022; /* ^R */ - termios.c_cc[VEOL] = 0; - termios.c_cc[VEOL2] = 0; - - termios.c_cc[VTIME] = 0; - termios.c_cc[VMIN] = 1; - - if (tcsetattr(fd, TCSANOW, &termios) < 0) - r = -errno; - -finish: - /* Just in case, flush all crap out */ - (void) tcflush(fd, TCIOFLUSH); - - return r; -} - -int reset_terminal(const char *name) { - _cleanup_close_ int fd = -1; - - /* We open the terminal with O_NONBLOCK here, to ensure we - * don't block on carrier if this is a terminal with carrier - * configured. */ - - fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); - if (fd < 0) - return fd; - - return reset_terminal_fd(fd, true); -} - -int open_terminal(const char *name, int mode) { - int fd, r; - unsigned c = 0; - - /* - * If a TTY is in the process of being closed opening it might - * cause EIO. This is horribly awful, but unlikely to be - * changed in the kernel. Hence we work around this problem by - * retrying a couple of times. - * - * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245 - */ - - if (mode & O_CREAT) - return -EINVAL; - - for (;;) { - fd = open(name, mode, 0); - if (fd >= 0) - break; - - if (errno != EIO) - return -errno; - - /* Max 1s in total */ - if (c >= 20) - return -errno; - - usleep(50 * USEC_PER_MSEC); - c++; - } - - r = isatty(fd); - if (r == 0) { - safe_close(fd); - return -ENOTTY; - } - - return fd; -} - -int acquire_terminal( - const char *name, - bool fail, - bool force, - bool ignore_tiocstty_eperm, - usec_t timeout) { - - int fd = -1, notify = -1, r = 0, wd = -1; - usec_t ts = 0; - - assert(name); - - /* We use inotify to be notified when the tty is closed. We - * create the watch before checking if we can actually acquire - * it, so that we don't lose any event. - * - * Note: strictly speaking this actually watches for the - * device being closed, it does *not* really watch whether a - * tty loses its controlling process. However, unless some - * rogue process uses TIOCNOTTY on /dev/tty *after* closing - * its tty otherwise this will not become a problem. As long - * as the administrator makes sure not configure any service - * on the same tty as an untrusted user this should not be a - * problem. (Which he probably should not do anyway.) */ - - if (timeout != USEC_INFINITY) - ts = now(CLOCK_MONOTONIC); - - if (!fail && !force) { - notify = inotify_init1(IN_CLOEXEC | (timeout != USEC_INFINITY ? IN_NONBLOCK : 0)); - if (notify < 0) { - r = -errno; - goto fail; - } - - wd = inotify_add_watch(notify, name, IN_CLOSE); - if (wd < 0) { - r = -errno; - goto fail; - } - } - - for (;;) { - struct sigaction sa_old, sa_new = { - .sa_handler = SIG_IGN, - .sa_flags = SA_RESTART, - }; - - if (notify >= 0) { - r = flush_fd(notify); - if (r < 0) - goto fail; - } - - /* We pass here O_NOCTTY only so that we can check the return - * value TIOCSCTTY and have a reliable way to figure out if we - * successfully became the controlling process of the tty */ - fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return fd; - - /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed - * if we already own the tty. */ - assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0); - - /* First, try to get the tty */ - if (ioctl(fd, TIOCSCTTY, force) < 0) - r = -errno; - - assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0); - - /* Sometimes, it makes sense to ignore TIOCSCTTY - * returning EPERM, i.e. when very likely we already - * are have this controlling terminal. */ - if (r < 0 && r == -EPERM && ignore_tiocstty_eperm) - r = 0; - - if (r < 0 && (force || fail || r != -EPERM)) - goto fail; - - if (r >= 0) - break; - - assert(!fail); - assert(!force); - assert(notify >= 0); - - for (;;) { - union inotify_event_buffer buffer; - struct inotify_event *e; - ssize_t l; - - if (timeout != USEC_INFINITY) { - usec_t n; - - n = now(CLOCK_MONOTONIC); - if (ts + timeout < n) { - r = -ETIMEDOUT; - goto fail; - } - - r = fd_wait_for_event(fd, POLLIN, ts + timeout - n); - if (r < 0) - goto fail; - - if (r == 0) { - r = -ETIMEDOUT; - goto fail; - } - } - - l = read(notify, &buffer, sizeof(buffer)); - if (l < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - - r = -errno; - goto fail; - } - - FOREACH_INOTIFY_EVENT(e, buffer, l) { - if (e->wd != wd || !(e->mask & IN_CLOSE)) { - r = -EIO; - goto fail; - } - } - - break; - } - - /* We close the tty fd here since if the old session - * ended our handle will be dead. It's important that - * we do this after sleeping, so that we don't enter - * an endless loop. */ - fd = safe_close(fd); - } - - safe_close(notify); - - return fd; - -fail: - safe_close(fd); - safe_close(notify); - - return r; -} - -int release_terminal(void) { - static const struct sigaction sa_new = { - .sa_handler = SIG_IGN, - .sa_flags = SA_RESTART, - }; - - _cleanup_close_ int fd = -1; - struct sigaction sa_old; - int r = 0; - - fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); - if (fd < 0) - return -errno; - - /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed - * by our own TIOCNOTTY */ - assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0); - - if (ioctl(fd, TIOCNOTTY) < 0) - r = -errno; - - assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0); - - return r; -} - -int terminal_vhangup_fd(int fd) { - assert(fd >= 0); - - if (ioctl(fd, TIOCVHANGUP) < 0) - return -errno; - - return 0; -} - -int terminal_vhangup(const char *name) { - _cleanup_close_ int fd; - - fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); - if (fd < 0) - return fd; - - return terminal_vhangup_fd(fd); -} - -int vt_disallocate(const char *name) { - _cleanup_close_ int fd = -1; - unsigned u; - int r; - - /* Deallocate the VT if possible. If not possible - * (i.e. because it is the active one), at least clear it - * entirely (including the scrollback buffer) */ - - if (!startswith(name, "/dev/")) - return -EINVAL; - - if (!tty_is_vc(name)) { - /* So this is not a VT. I guess we cannot deallocate - * it then. But let's at least clear the screen */ - - fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return fd; - - loop_write(fd, - "\033[r" /* clear scrolling region */ - "\033[H" /* move home */ - "\033[2J", /* clear screen */ - 10, false); - return 0; - } - - if (!startswith(name, "/dev/tty")) - return -EINVAL; - - r = safe_atou(name+8, &u); - if (r < 0) - return r; - - if (u <= 0) - return -EINVAL; - - /* Try to deallocate */ - fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); - if (fd < 0) - return fd; - - r = ioctl(fd, VT_DISALLOCATE, u); - fd = safe_close(fd); - - if (r >= 0) - return 0; - - if (errno != EBUSY) - return -errno; - - /* Couldn't deallocate, so let's clear it fully with - * scrollback */ - fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) - return fd; - - loop_write(fd, - "\033[r" /* clear scrolling region */ - "\033[H" /* move home */ - "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */ - 10, false); - return 0; -} - -int make_console_stdio(void) { - int fd, r; - - /* Make /dev/console the controlling terminal and stdin/stdout/stderr */ - - fd = acquire_terminal("/dev/console", false, true, true, USEC_INFINITY); - if (fd < 0) - return log_error_errno(fd, "Failed to acquire terminal: %m"); - - r = reset_terminal_fd(fd, true); - if (r < 0) - log_warning_errno(r, "Failed to reset terminal, ignoring: %m"); - - r = make_stdio(fd); - if (r < 0) - return log_error_errno(r, "Failed to duplicate terminal fd: %m"); - - return 0; -} - -bool tty_is_vc(const char *tty) { - assert(tty); - - return vtnr_from_tty(tty) >= 0; -} - -bool tty_is_console(const char *tty) { - assert(tty); - - if (startswith(tty, "/dev/")) - tty += 5; - - return streq(tty, "console"); -} - -int vtnr_from_tty(const char *tty) { - int i, r; - - assert(tty); - - if (startswith(tty, "/dev/")) - tty += 5; - - if (!startswith(tty, "tty") ) - return -EINVAL; - - if (tty[3] < '0' || tty[3] > '9') - return -EINVAL; - - r = safe_atoi(tty+3, &i); - if (r < 0) - return r; - - if (i < 0 || i > 63) - return -EINVAL; - - return i; -} - -char *resolve_dev_console(char **active) { - char *tty; - - /* Resolve where /dev/console is pointing to, if /sys is actually ours - * (i.e. not read-only-mounted which is a sign for container setups) */ - - if (path_is_read_only_fs("/sys") > 0) - return NULL; - - if (read_one_line_file("/sys/class/tty/console/active", active) < 0) - return NULL; - - /* If multiple log outputs are configured the last one is what - * /dev/console points to */ - tty = strrchr(*active, ' '); - if (tty) - tty++; - else - tty = *active; - - if (streq(tty, "tty0")) { - char *tmp; - - /* Get the active VC (e.g. tty1) */ - if (read_one_line_file("/sys/class/tty/tty0/active", &tmp) >= 0) { - free(*active); - tty = *active = tmp; - } - } - - return tty; -} - -int get_kernel_consoles(char ***consoles) { - _cleanup_strv_free_ char **con = NULL; - _cleanup_free_ char *line = NULL; - const char *active; - int r; - - assert(consoles); - - r = read_one_line_file("/sys/class/tty/console/active", &line); - if (r < 0) - return r; - - active = line; - for (;;) { - _cleanup_free_ char *tty = NULL; - char *path; - - r = extract_first_word(&active, &tty, NULL, 0); - if (r < 0) - return r; - if (r == 0) - break; - - if (streq(tty, "tty0")) { - tty = mfree(tty); - r = read_one_line_file("/sys/class/tty/tty0/active", &tty); - if (r < 0) - return r; - } - - path = strappend("/dev/", tty); - if (!path) - return -ENOMEM; - - if (access(path, F_OK) < 0) { - log_debug_errno(errno, "Console device %s is not accessible, skipping: %m", path); - free(path); - continue; - } - - r = strv_consume(&con, path); - if (r < 0) - return r; - } - - if (strv_isempty(con)) { - log_debug("No devices found for system console"); - - r = strv_extend(&con, "/dev/console"); - if (r < 0) - return r; - } - - *consoles = con; - con = NULL; - return 0; -} - -bool tty_is_vc_resolve(const char *tty) { - _cleanup_free_ char *active = NULL; - - assert(tty); - - if (startswith(tty, "/dev/")) - tty += 5; - - if (streq(tty, "console")) { - tty = resolve_dev_console(&active); - if (!tty) - return false; - } - - return tty_is_vc(tty); -} - -const char *default_term_for_tty(const char *tty) { - return tty && tty_is_vc_resolve(tty) ? "linux" : "vt220"; -} - -int fd_columns(int fd) { - struct winsize ws = {}; - - if (ioctl(fd, TIOCGWINSZ, &ws) < 0) - return -errno; - - if (ws.ws_col <= 0) - return -EIO; - - return ws.ws_col; -} - -unsigned columns(void) { - const char *e; - int c; - - if (_likely_(cached_columns > 0)) - return cached_columns; - - c = 0; - e = getenv("COLUMNS"); - if (e) - (void) safe_atoi(e, &c); - - if (c <= 0) - c = fd_columns(STDOUT_FILENO); - - if (c <= 0) - c = 80; - - cached_columns = c; - return cached_columns; -} - -int fd_lines(int fd) { - struct winsize ws = {}; - - if (ioctl(fd, TIOCGWINSZ, &ws) < 0) - return -errno; - - if (ws.ws_row <= 0) - return -EIO; - - return ws.ws_row; -} - -unsigned lines(void) { - const char *e; - int l; - - if (_likely_(cached_lines > 0)) - return cached_lines; - - l = 0; - e = getenv("LINES"); - if (e) - (void) safe_atoi(e, &l); - - if (l <= 0) - l = fd_lines(STDOUT_FILENO); - - if (l <= 0) - l = 24; - - cached_lines = l; - return cached_lines; -} - -/* intended to be used as a SIGWINCH sighandler */ -void columns_lines_cache_reset(int signum) { - cached_columns = 0; - cached_lines = 0; -} - -bool on_tty(void) { - static int cached_on_tty = -1; - - if (_unlikely_(cached_on_tty < 0)) - cached_on_tty = isatty(STDOUT_FILENO) > 0; - - return cached_on_tty; -} - -int make_stdio(int fd) { - int r, s, t; - - assert(fd >= 0); - - r = dup2(fd, STDIN_FILENO); - s = dup2(fd, STDOUT_FILENO); - t = dup2(fd, STDERR_FILENO); - - if (fd >= 3) - safe_close(fd); - - if (r < 0 || s < 0 || t < 0) - return -errno; - - /* Explicitly unset O_CLOEXEC, since if fd was < 3, then - * dup2() was a NOP and the bit hence possibly set. */ - stdio_unset_cloexec(); - - return 0; -} - -int make_null_stdio(void) { - int null_fd; - - null_fd = open("/dev/null", O_RDWR|O_NOCTTY); - if (null_fd < 0) - return -errno; - - return make_stdio(null_fd); -} - -int getttyname_malloc(int fd, char **ret) { - size_t l = 100; - int r; - - assert(fd >= 0); - assert(ret); - - for (;;) { - char path[l]; - - r = ttyname_r(fd, path, sizeof(path)); - if (r == 0) { - const char *p; - char *c; - - p = startswith(path, "/dev/"); - c = strdup(p ?: path); - if (!c) - return -ENOMEM; - - *ret = c; - return 0; - } - - if (r != ERANGE) - return -r; - - l *= 2; - } - - return 0; -} - -int getttyname_harder(int fd, char **r) { - int k; - char *s = NULL; - - k = getttyname_malloc(fd, &s); - if (k < 0) - return k; - - if (streq(s, "tty")) { - free(s); - return get_ctty(0, NULL, r); - } - - *r = s; - return 0; -} - -int get_ctty_devnr(pid_t pid, dev_t *d) { - int r; - _cleanup_free_ char *line = NULL; - const char *p; - unsigned long ttynr; - - assert(pid >= 0); - - p = procfs_file_alloca(pid, "stat"); - r = read_one_line_file(p, &line); - if (r < 0) - return r; - - p = strrchr(line, ')'); - if (!p) - return -EIO; - - p++; - - if (sscanf(p, " " - "%*c " /* state */ - "%*d " /* ppid */ - "%*d " /* pgrp */ - "%*d " /* session */ - "%lu ", /* ttynr */ - &ttynr) != 1) - return -EIO; - - if (major(ttynr) == 0 && minor(ttynr) == 0) - return -ENXIO; - - if (d) - *d = (dev_t) ttynr; - - return 0; -} - -int get_ctty(pid_t pid, dev_t *_devnr, char **r) { - char fn[sizeof("/dev/char/")-1 + 2*DECIMAL_STR_MAX(unsigned) + 1 + 1], *b = NULL; - _cleanup_free_ char *s = NULL; - const char *p; - dev_t devnr; - int k; - - assert(r); - - k = get_ctty_devnr(pid, &devnr); - if (k < 0) - return k; - - sprintf(fn, "/dev/char/%u:%u", major(devnr), minor(devnr)); - - k = readlink_malloc(fn, &s); - if (k < 0) { - - if (k != -ENOENT) - return k; - - /* This is an ugly hack */ - if (major(devnr) == 136) { - if (asprintf(&b, "pts/%u", minor(devnr)) < 0) - return -ENOMEM; - } else { - /* Probably something like the ptys which have no - * symlink in /dev/char. Let's return something - * vaguely useful. */ - - b = strdup(fn + 5); - if (!b) - return -ENOMEM; - } - } else { - if (startswith(s, "/dev/")) - p = s + 5; - else if (startswith(s, "../")) - p = s + 3; - else - p = s; - - b = strdup(p); - if (!b) - return -ENOMEM; - } - - *r = b; - if (_devnr) - *_devnr = devnr; - - return 0; -} - -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 ptsname_namespace(int pty, char **ret) { - int no = -1, r; - - /* Like ptsname(), but doesn't assume that the path is - * accessible in the local namespace. */ - - r = ioctl(pty, TIOCGPTN, &no); - if (r < 0) - return -errno; - - if (no < 0) - return -EIO; - - if (asprintf(ret, "/dev/pts/%i", no) < 0) - return -ENOMEM; - - return 0; -} - -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|O_NOCTTY|O_CLOEXEC); - 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); -} - -int open_terminal_in_namespace(pid_t pid, const char *name, int mode) { - _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; - - 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 = open_terminal(name, mode|O_NOCTTY|O_CLOEXEC); - if (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); -} - -static bool getenv_terminal_is_dumb(void) { - const char *e; - - e = getenv("TERM"); - if (!e) - return true; - - return streq(e, "dumb"); -} - -bool terminal_is_dumb(void) { - if (!on_tty()) - return true; - - return getenv_terminal_is_dumb(); -} - -bool colors_enabled(void) { - static int enabled = -1; - - if (_unlikely_(enabled < 0)) { - int val; - - val = getenv_bool("SYSTEMD_COLORS"); - if (val >= 0) - enabled = val; - else if (getpid() == 1) - /* PID1 outputs to the console without holding it open all the time */ - enabled = !getenv_terminal_is_dumb(); - else - enabled = !terminal_is_dumb(); - } - - return enabled; -} diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h deleted file mode 100644 index b862bfaf05..0000000000 --- a/src/basic/terminal-util.h +++ /dev/null @@ -1,119 +0,0 @@ -#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 <stdarg.h> -#include <stdbool.h> -#include <stdio.h> -#include <sys/types.h> - -#include "macro.h" -#include "time-util.h" - -#define ANSI_RED "\x1B[0;31m" -#define ANSI_GREEN "\x1B[0;32m" -#define ANSI_UNDERLINE "\x1B[0;4m" -#define ANSI_HIGHLIGHT "\x1B[0;1;39m" -#define ANSI_HIGHLIGHT_RED "\x1B[0;1;31m" -#define ANSI_HIGHLIGHT_GREEN "\x1B[0;1;32m" -#define ANSI_HIGHLIGHT_YELLOW "\x1B[0;1;33m" -#define ANSI_HIGHLIGHT_BLUE "\x1B[0;1;34m" -#define ANSI_HIGHLIGHT_UNDERLINE "\x1B[0;1;4m" -#define ANSI_HIGHLIGHT_RED_UNDERLINE "\x1B[0;1;4;31m" -#define ANSI_HIGHLIGHT_GREEN_UNDERLINE "\x1B[0;1;4;32m" -#define ANSI_HIGHLIGHT_YELLOW_UNDERLINE "\x1B[0;1;4;33m" -#define ANSI_HIGHLIGHT_BLUE_UNDERLINE "\x1B[0;1;4;34m" -#define ANSI_NORMAL "\x1B[0m" - -#define ANSI_ERASE_TO_END_OF_LINE "\x1B[K" - -/* Set cursor to top left corner and clear screen */ -#define ANSI_HOME_CLEAR "\x1B[H\x1B[2J" - -int reset_terminal_fd(int fd, bool switch_to_text); -int reset_terminal(const char *name); - -int open_terminal(const char *name, int mode); -int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm, usec_t timeout); -int release_terminal(void); - -int terminal_vhangup_fd(int fd); -int terminal_vhangup(const char *name); - -int chvt(int vt); - -int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl); -int ask_char(char *ret, const char *replies, const char *text, ...) _printf_(3, 4); -int ask_string(char **ret, const char *text, ...) _printf_(2, 3); - -int vt_disallocate(const char *name); - -char *resolve_dev_console(char **active); -int get_kernel_consoles(char ***consoles); -bool tty_is_vc(const char *tty); -bool tty_is_vc_resolve(const char *tty); -bool tty_is_console(const char *tty) _pure_; -int vtnr_from_tty(const char *tty); -const char *default_term_for_tty(const char *tty); - -int make_stdio(int fd); -int make_null_stdio(void); -int make_console_stdio(void); - -int fd_columns(int fd); -unsigned columns(void); -int fd_lines(int fd); -unsigned lines(void); -void columns_lines_cache_reset(int _unused_ signum); - -bool on_tty(void); -bool terminal_is_dumb(void); -bool colors_enabled(void); - -#define DEFINE_ANSI_FUNC(name, NAME) \ - static inline const char *ansi_##name(void) { \ - return colors_enabled() ? ANSI_##NAME : ""; \ - } \ - struct __useless_struct_to_allow_trailing_semicolon__ - -DEFINE_ANSI_FUNC(underline, UNDERLINE); -DEFINE_ANSI_FUNC(highlight, HIGHLIGHT); -DEFINE_ANSI_FUNC(highlight_underline, HIGHLIGHT_UNDERLINE); -DEFINE_ANSI_FUNC(highlight_red, HIGHLIGHT_RED); -DEFINE_ANSI_FUNC(highlight_green, HIGHLIGHT_GREEN); -DEFINE_ANSI_FUNC(highlight_yellow, HIGHLIGHT_YELLOW); -DEFINE_ANSI_FUNC(highlight_blue, HIGHLIGHT_BLUE); -DEFINE_ANSI_FUNC(highlight_red_underline, HIGHLIGHT_RED_UNDERLINE); -DEFINE_ANSI_FUNC(highlight_green_underline, HIGHLIGHT_GREEN_UNDERLINE); -DEFINE_ANSI_FUNC(highlight_yellow_underline, HIGHLIGHT_YELLOW_UNDERLINE); -DEFINE_ANSI_FUNC(highlight_blue_underline, HIGHLIGHT_BLUE_UNDERLINE); -DEFINE_ANSI_FUNC(normal, NORMAL); - -int get_ctty_devnr(pid_t pid, dev_t *d); -int get_ctty(pid_t, dev_t *_devnr, char **r); - -int getttyname_malloc(int fd, char **r); -int getttyname_harder(int fd, char **r); - -int ptsname_malloc(int fd, char **ret); -int ptsname_namespace(int pty, char **ret); - -int openpt_in_namespace(pid_t pid, int flags); -int open_terminal_in_namespace(pid_t pid, const char *name, int mode); diff --git a/src/basic/time-util.c b/src/basic/time-util.c deleted file mode 100644 index fedff1362c..0000000000 --- a/src/basic/time-util.c +++ /dev/null @@ -1,1327 +0,0 @@ -/*** - 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 <errno.h> -#include <limits.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/timerfd.h> -#include <sys/timex.h> -#include <sys/types.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "log.h" -#include "macro.h" -#include "parse-util.h" -#include "path-util.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" - -static clockid_t map_clock_id(clockid_t c) { - - /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will - * fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is - * when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on - * those archs. */ - - switch (c) { - - case CLOCK_BOOTTIME_ALARM: - return CLOCK_BOOTTIME; - - case CLOCK_REALTIME_ALARM: - return CLOCK_REALTIME; - - default: - return c; - } -} - -usec_t now(clockid_t clock_id) { - struct timespec ts; - - assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0); - - return timespec_load(&ts); -} - -nsec_t now_nsec(clockid_t clock_id) { - struct timespec ts; - - assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0); - - return timespec_load_nsec(&ts); -} - -dual_timestamp* dual_timestamp_get(dual_timestamp *ts) { - assert(ts); - - ts->realtime = now(CLOCK_REALTIME); - ts->monotonic = now(CLOCK_MONOTONIC); - - return ts; -} - -triple_timestamp* triple_timestamp_get(triple_timestamp *ts) { - assert(ts); - - ts->realtime = now(CLOCK_REALTIME); - ts->monotonic = now(CLOCK_MONOTONIC); - ts->boottime = clock_boottime_supported() ? now(CLOCK_BOOTTIME) : USEC_INFINITY; - - return ts; -} - -dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) { - int64_t delta; - assert(ts); - - if (u == USEC_INFINITY || u <= 0) { - ts->realtime = ts->monotonic = u; - return ts; - } - - ts->realtime = u; - - delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u; - ts->monotonic = usec_sub(now(CLOCK_MONOTONIC), delta); - - return ts; -} - -triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) { - int64_t delta; - - assert(ts); - - if (u == USEC_INFINITY || u <= 0) { - ts->realtime = ts->monotonic = ts->boottime = u; - return ts; - } - - ts->realtime = u; - delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u; - ts->monotonic = usec_sub(now(CLOCK_MONOTONIC), delta); - ts->boottime = clock_boottime_supported() ? usec_sub(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY; - - return ts; -} - -dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { - int64_t delta; - assert(ts); - - if (u == USEC_INFINITY) { - ts->realtime = ts->monotonic = USEC_INFINITY; - return ts; - } - - ts->monotonic = u; - delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u; - ts->realtime = usec_sub(now(CLOCK_REALTIME), delta); - - return ts; -} - -dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) { - int64_t delta; - - if (u == USEC_INFINITY) { - ts->realtime = ts->monotonic = USEC_INFINITY; - return ts; - } - - dual_timestamp_get(ts); - delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u; - ts->realtime = usec_sub(ts->realtime, delta); - ts->monotonic = usec_sub(ts->monotonic, delta); - - return ts; -} - -usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) { - - switch (clock) { - - case CLOCK_REALTIME: - case CLOCK_REALTIME_ALARM: - return ts->realtime; - - case CLOCK_MONOTONIC: - return ts->monotonic; - - case CLOCK_BOOTTIME: - case CLOCK_BOOTTIME_ALARM: - return ts->boottime; - - default: - return USEC_INFINITY; - } -} - -usec_t timespec_load(const struct timespec *ts) { - assert(ts); - - if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1) - return USEC_INFINITY; - - if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC) - return USEC_INFINITY; - - return - (usec_t) ts->tv_sec * USEC_PER_SEC + - (usec_t) ts->tv_nsec / NSEC_PER_USEC; -} - -nsec_t timespec_load_nsec(const struct timespec *ts) { - assert(ts); - - if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1) - return NSEC_INFINITY; - - if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC) - return NSEC_INFINITY; - - return (nsec_t) ts->tv_sec * NSEC_PER_SEC + (nsec_t) ts->tv_nsec; -} - -struct timespec *timespec_store(struct timespec *ts, usec_t u) { - assert(ts); - - if (u == USEC_INFINITY) { - ts->tv_sec = (time_t) -1; - ts->tv_nsec = (long) -1; - return ts; - } - - ts->tv_sec = (time_t) (u / USEC_PER_SEC); - ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC); - - return ts; -} - -usec_t timeval_load(const struct timeval *tv) { - assert(tv); - - if (tv->tv_sec == (time_t) -1 && - tv->tv_usec == (suseconds_t) -1) - return USEC_INFINITY; - - if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC) - return USEC_INFINITY; - - return - (usec_t) tv->tv_sec * USEC_PER_SEC + - (usec_t) tv->tv_usec; -} - -struct timeval *timeval_store(struct timeval *tv, usec_t u) { - assert(tv); - - if (u == USEC_INFINITY) { - tv->tv_sec = (time_t) -1; - tv->tv_usec = (suseconds_t) -1; - } else { - tv->tv_sec = (time_t) (u / USEC_PER_SEC); - tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC); - } - - return tv; -} - -static char *format_timestamp_internal( - char *buf, - size_t l, - usec_t t, - bool utc, - bool us) { - - /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our - * generated timestamps may be parsed with parse_timestamp(), and always read the same. */ - static const char * const weekdays[] = { - [0] = "Sun", - [1] = "Mon", - [2] = "Tue", - [3] = "Wed", - [4] = "Thu", - [5] = "Fri", - [6] = "Sat", - }; - - struct tm tm; - time_t sec; - size_t n; - - assert(buf); - - if (l < - 3 + /* week day */ - 1 + 10 + /* space and date */ - 1 + 8 + /* space and time */ - (us ? 1 + 6 : 0) + /* "." and microsecond part */ - 1 + 1 + /* space and shortest possible zone */ - 1) - return NULL; /* Not enough space even for the shortest form. */ - if (t <= 0 || t == USEC_INFINITY) - return NULL; /* Timestamp is unset */ - - sec = (time_t) (t / USEC_PER_SEC); /* Round down */ - if ((usec_t) sec != (t / USEC_PER_SEC)) - return NULL; /* overflow? */ - - if (!localtime_or_gmtime_r(&sec, &tm, utc)) - return NULL; - - /* Start with the week day */ - assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays)); - memcpy(buf, weekdays[tm.tm_wday], 4); - - /* Add the main components */ - if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0) - return NULL; /* Doesn't fit */ - - /* Append the microseconds part, if that's requested */ - if (us) { - n = strlen(buf); - if (n + 8 > l) - return NULL; /* Microseconds part doesn't fit. */ - - sprintf(buf + n, ".%06llu", (unsigned long long) (t % USEC_PER_SEC)); - } - - /* Append the timezone */ - n = strlen(buf); - if (utc) { - /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the - * obsolete "GMT" instead. */ - if (n + 5 > l) - return NULL; /* "UTC" doesn't fit. */ - - strcpy(buf + n, " UTC"); - - } else if (!isempty(tm.tm_zone)) { - size_t tn; - - /* An explicit timezone is specified, let's use it, if it fits */ - tn = strlen(tm.tm_zone); - if (n + 1 + tn + 1 > l) { - /* The full time zone does not fit in. Yuck. */ - - if (n + 1 + _POSIX_TZNAME_MAX + 1 > l) - return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */ - - /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX - * minimum time zone length. In this case suppress the timezone entirely, in order not to dump - * an overly long, hard to read string on the user. This should be safe, because the user will - * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */ - } else { - buf[n++] = ' '; - strcpy(buf + n, tm.tm_zone); - } - } - - return buf; -} - -char *format_timestamp(char *buf, size_t l, usec_t t) { - return format_timestamp_internal(buf, l, t, false, false); -} - -char *format_timestamp_utc(char *buf, size_t l, usec_t t) { - return format_timestamp_internal(buf, l, t, true, false); -} - -char *format_timestamp_us(char *buf, size_t l, usec_t t) { - return format_timestamp_internal(buf, l, t, false, true); -} - -char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) { - return format_timestamp_internal(buf, l, t, true, true); -} - -char *format_timestamp_relative(char *buf, size_t l, usec_t t) { - const char *s; - usec_t n, d; - - if (t <= 0 || t == USEC_INFINITY) - return NULL; - - n = now(CLOCK_REALTIME); - if (n > t) { - d = n - t; - s = "ago"; - } else { - d = t - n; - s = "left"; - } - - if (d >= USEC_PER_YEAR) - snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s", - d / USEC_PER_YEAR, - (d % USEC_PER_YEAR) / USEC_PER_MONTH, s); - else if (d >= USEC_PER_MONTH) - snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s", - d / USEC_PER_MONTH, - (d % USEC_PER_MONTH) / USEC_PER_DAY, s); - else if (d >= USEC_PER_WEEK) - snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s", - d / USEC_PER_WEEK, - (d % USEC_PER_WEEK) / USEC_PER_DAY, s); - else if (d >= 2*USEC_PER_DAY) - snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s); - else if (d >= 25*USEC_PER_HOUR) - snprintf(buf, l, "1 day " USEC_FMT "h %s", - (d - USEC_PER_DAY) / USEC_PER_HOUR, s); - else if (d >= 6*USEC_PER_HOUR) - snprintf(buf, l, USEC_FMT "h %s", - d / USEC_PER_HOUR, s); - else if (d >= USEC_PER_HOUR) - snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s", - d / USEC_PER_HOUR, - (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s); - else if (d >= 5*USEC_PER_MINUTE) - snprintf(buf, l, USEC_FMT "min %s", - d / USEC_PER_MINUTE, s); - else if (d >= USEC_PER_MINUTE) - snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s", - d / USEC_PER_MINUTE, - (d % USEC_PER_MINUTE) / USEC_PER_SEC, s); - else if (d >= USEC_PER_SEC) - snprintf(buf, l, USEC_FMT "s %s", - d / USEC_PER_SEC, s); - else if (d >= USEC_PER_MSEC) - snprintf(buf, l, USEC_FMT "ms %s", - d / USEC_PER_MSEC, s); - else if (d > 0) - snprintf(buf, l, USEC_FMT"us %s", - d, s); - else - snprintf(buf, l, "now"); - - buf[l-1] = 0; - return buf; -} - -char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { - static const struct { - 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 }, - }; - - unsigned i; - char *p = buf; - bool something = false; - - assert(buf); - assert(l > 0); - - if (t == USEC_INFINITY) { - strncpy(p, "infinity", l-1); - p[l-1] = 0; - return p; - } - - if (t <= 0) { - strncpy(p, "0", l-1); - p[l-1] = 0; - return p; - } - - /* The result of this function can be parsed with parse_sec */ - - for (i = 0; i < ELEMENTSOF(table); i++) { - int k = 0; - size_t n; - bool done = false; - usec_t a, b; - - if (t <= 0) - break; - - if (t < accuracy && something) - break; - - if (t < table[i].usec) - continue; - - if (l <= 1) - break; - - a = t / table[i].usec; - b = t % table[i].usec; - - /* Let's see if we should shows this in dot notation */ - if (t < USEC_PER_MINUTE && b > 0) { - usec_t cc; - int j; - - j = 0; - for (cc = table[i].usec; cc > 1; cc /= 10) - j++; - - for (cc = accuracy; cc > 1; cc /= 10) { - b /= 10; - j--; - } - - if (j > 0) { - k = snprintf(p, l, - "%s"USEC_FMT".%0*llu%s", - p > buf ? " " : "", - a, - j, - (unsigned long long) b, - table[i].suffix); - - t = 0; - done = true; - } - } - - /* No? Then let's show it normally */ - if (!done) { - k = snprintf(p, l, - "%s"USEC_FMT"%s", - p > buf ? " " : "", - a, - table[i].suffix); - - t = b; - } - - n = MIN((size_t) k, l); - - l -= n; - p += n; - - something = true; - } - - *p = 0; - - return buf; -} - -void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) { - - assert(f); - assert(name); - assert(t); - - if (!dual_timestamp_is_set(t)) - return; - - fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n", - name, - t->realtime, - t->monotonic); -} - -int dual_timestamp_deserialize(const char *value, dual_timestamp *t) { - unsigned long long a, b; - - assert(value); - assert(t); - - if (sscanf(value, "%llu %llu", &a, &b) != 2) { - log_debug("Failed to parse dual timestamp value \"%s\": %m", value); - return -EINVAL; - } - - t->realtime = a; - t->monotonic = b; - - return 0; -} - -int timestamp_deserialize(const char *value, usec_t *timestamp) { - int r; - - assert(value); - - r = safe_atou64(value, timestamp); - if (r < 0) - return log_debug_errno(r, "Failed to parse timestamp value \"%s\": %m", value); - - return r; -} - -int parse_timestamp(const char *t, usec_t *usec) { - static const struct { - const char *name; - const int nr; - } day_nr[] = { - { "Sunday", 0 }, - { "Sun", 0 }, - { "Monday", 1 }, - { "Mon", 1 }, - { "Tuesday", 2 }, - { "Tue", 2 }, - { "Wednesday", 3 }, - { "Wed", 3 }, - { "Thursday", 4 }, - { "Thu", 4 }, - { "Friday", 5 }, - { "Fri", 5 }, - { "Saturday", 6 }, - { "Sat", 6 }, - }; - - const char *k, *utc, *tzn = NULL; - struct tm tm, copy; - time_t x; - usec_t x_usec, plus = 0, minus = 0, ret; - int r, weekday = -1, dst = -1; - unsigned i; - - /* - * Allowed syntaxes: - * - * 2012-09-22 16:34:22 - * 2012-09-22 16:34 (seconds will be set to 0) - * 2012-09-22 (time will be set to 00:00:00) - * 16:34:22 (date will be set to today) - * 16:34 (date will be set to today, seconds to 0) - * now - * yesterday (time is set to 00:00:00) - * today (time is set to 00:00:00) - * tomorrow (time is set to 00:00:00) - * +5min - * -5days - * @2147483647 (seconds since epoch) - * - */ - - assert(t); - assert(usec); - - if (t[0] == '@') - return parse_sec(t + 1, usec); - - ret = now(CLOCK_REALTIME); - - if (streq(t, "now")) - goto finish; - - else if (t[0] == '+') { - r = parse_sec(t+1, &plus); - if (r < 0) - return r; - - goto finish; - - } else if (t[0] == '-') { - r = parse_sec(t+1, &minus); - if (r < 0) - return r; - - goto finish; - - } else if ((k = endswith(t, " ago"))) { - t = strndupa(t, k - t); - - r = parse_sec(t, &minus); - if (r < 0) - return r; - - goto finish; - - } else if ((k = endswith(t, " left"))) { - t = strndupa(t, k - t); - - r = parse_sec(t, &plus); - if (r < 0) - return r; - - goto finish; - } - - /* See if the timestamp is suffixed with UTC */ - utc = endswith_no_case(t, " UTC"); - if (utc) - t = strndupa(t, utc - t); - else { - const char *e = NULL; - int j; - - tzset(); - - /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only - * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because - * there are no nice APIs available to cover this. By accepting the local time zone strings, we make - * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't - * support arbitrary timezone specifications. */ - - for (j = 0; j <= 1; j++) { - - if (isempty(tzname[j])) - continue; - - e = endswith_no_case(t, tzname[j]); - if (!e) - continue; - if (e == t) - continue; - if (e[-1] != ' ') - continue; - - break; - } - - if (IN_SET(j, 0, 1)) { - /* Found one of the two timezones specified. */ - t = strndupa(t, e - t - 1); - dst = j; - tzn = tzname[j]; - } - } - - x = (time_t) (ret / USEC_PER_SEC); - x_usec = 0; - - if (!localtime_or_gmtime_r(&x, &tm, utc)) - return -EINVAL; - - tm.tm_isdst = dst; - if (tzn) - tm.tm_zone = tzn; - - 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; - - if (!startswith_no_case(t, day_nr[i].name)) - continue; - - skip = strlen(day_nr[i].name); - if (t[skip] != ' ') - continue; - - weekday = day_nr[i].nr; - t += skip + 1; - break; - } - - copy = tm; - k = strptime(t, "%y-%m-%d %H:%M:%S", &tm); - 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) { - 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 from_tm; - } - - tm = copy; - k = strptime(t, "%Y-%m-%d %H:%M", &tm); - if (k && *k == 0) { - tm.tm_sec = 0; - 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 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 from_tm; - } - - tm = copy; - k = strptime(t, "%H:%M:%S", &tm); - 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 from_tm; - } - - return -EINVAL; - -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 + x_usec; - -finish: - ret += plus; - if (ret > minus) - ret -= minus; - else - ret = 0; - - *usec = ret; - - return 0; -} - -static char* extract_multiplier(char *p, usec_t *multiplier) { - 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 }, - { "minutes", USEC_PER_MINUTE }, - { "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 }, - }; - unsigned i; - - for (i = 0; i < ELEMENTSOF(table); i++) { - char *e; - - e = startswith(p, table[i].suffix); - if (e) { - *multiplier = table[i].usec; - return e; - } - } - - return p; -} - -int parse_time(const char *t, usec_t *usec, usec_t default_unit) { - const char *p, *s; - usec_t r = 0; - bool something = false; - - assert(t); - assert(usec); - assert(default_unit > 0); - - p = t; - - p += strspn(p, WHITESPACE); - s = startswith(p, "infinity"); - if (s) { - s += strspn(s, WHITESPACE); - if (*s != 0) - return -EINVAL; - - *usec = USEC_INFINITY; - return 0; - } - - for (;;) { - long long l, z = 0; - char *e; - unsigned n = 0; - usec_t multiplier = default_unit, k; - - p += strspn(p, WHITESPACE); - - if (*p == 0) { - if (!something) - return -EINVAL; - - break; - } - - errno = 0; - l = strtoll(p, &e, 10); - if (errno > 0) - return -errno; - if (l < 0) - return -ERANGE; - - if (*e == '.') { - char *b = e + 1; - - errno = 0; - z = strtoll(b, &e, 10); - if (errno > 0) - return -errno; - - if (z < 0) - return -ERANGE; - - if (e == b) - return -EINVAL; - - n = e - b; - - } else if (e == p) - return -EINVAL; - - e += strspn(e, WHITESPACE); - p = extract_multiplier(e, &multiplier); - - something = true; - - k = (usec_t) z * multiplier; - - for (; n > 0; n--) - k /= 10; - - r += (usec_t) l * multiplier + k; - } - - *usec = r; - - 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; - nsec_t nsec; - } table[] = { - { "seconds", NSEC_PER_SEC }, - { "second", NSEC_PER_SEC }, - { "sec", NSEC_PER_SEC }, - { "s", NSEC_PER_SEC }, - { "minutes", NSEC_PER_MINUTE }, - { "minute", NSEC_PER_MINUTE }, - { "min", NSEC_PER_MINUTE }, - { "months", NSEC_PER_MONTH }, - { "month", NSEC_PER_MONTH }, - { "msec", NSEC_PER_MSEC }, - { "ms", NSEC_PER_MSEC }, - { "m", NSEC_PER_MINUTE }, - { "hours", NSEC_PER_HOUR }, - { "hour", NSEC_PER_HOUR }, - { "hr", NSEC_PER_HOUR }, - { "h", NSEC_PER_HOUR }, - { "days", NSEC_PER_DAY }, - { "day", NSEC_PER_DAY }, - { "d", NSEC_PER_DAY }, - { "weeks", NSEC_PER_WEEK }, - { "week", NSEC_PER_WEEK }, - { "w", NSEC_PER_WEEK }, - { "years", NSEC_PER_YEAR }, - { "year", NSEC_PER_YEAR }, - { "y", NSEC_PER_YEAR }, - { "usec", NSEC_PER_USEC }, - { "us", NSEC_PER_USEC }, - { "nsec", 1ULL }, - { "ns", 1ULL }, - { "", 1ULL }, /* default is nsec */ - }; - - const char *p, *s; - nsec_t r = 0; - bool something = false; - - assert(t); - assert(nsec); - - p = t; - - p += strspn(p, WHITESPACE); - s = startswith(p, "infinity"); - if (s) { - s += strspn(s, WHITESPACE); - if (*s != 0) - return -EINVAL; - - *nsec = NSEC_INFINITY; - return 0; - } - - for (;;) { - long long l, z = 0; - char *e; - unsigned i, n = 0; - - p += strspn(p, WHITESPACE); - - if (*p == 0) { - if (!something) - return -EINVAL; - - break; - } - - errno = 0; - l = strtoll(p, &e, 10); - - if (errno > 0) - return -errno; - - if (l < 0) - return -ERANGE; - - if (*e == '.') { - char *b = e + 1; - - errno = 0; - z = strtoll(b, &e, 10); - if (errno > 0) - return -errno; - - if (z < 0) - return -ERANGE; - - if (e == b) - return -EINVAL; - - n = e - b; - - } else if (e == p) - return -EINVAL; - - e += strspn(e, WHITESPACE); - - for (i = 0; i < ELEMENTSOF(table); i++) - if (startswith(e, table[i].suffix)) { - nsec_t k = (nsec_t) z * table[i].nsec; - - for (; n > 0; n--) - k /= 10; - - r += (nsec_t) l * table[i].nsec + k; - p = e + strlen(table[i].suffix); - - something = true; - break; - } - - if (i >= ELEMENTSOF(table)) - return -EINVAL; - - } - - *nsec = r; - - return 0; -} - -bool ntp_synced(void) { - struct timex txc = {}; - - if (adjtimex(&txc) < 0) - return false; - - if (txc.status & STA_UNSYNC) - return false; - - return true; -} - -int get_timezones(char ***ret) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_strv_free_ char **zones = NULL; - size_t n_zones = 0, n_allocated = 0; - - assert(ret); - - zones = strv_new("UTC", NULL); - if (!zones) - return -ENOMEM; - - n_allocated = 2; - n_zones = 1; - - f = fopen("/usr/share/zoneinfo/zone.tab", "re"); - if (f) { - char l[LINE_MAX]; - - FOREACH_LINE(l, f, return -errno) { - char *p, *w; - size_t k; - - p = strstrip(l); - - if (isempty(p) || *p == '#') - continue; - - /* Skip over country code */ - p += strcspn(p, WHITESPACE); - p += strspn(p, WHITESPACE); - - /* Skip over coordinates */ - p += strcspn(p, WHITESPACE); - p += strspn(p, WHITESPACE); - - /* Found timezone name */ - k = strcspn(p, WHITESPACE); - if (k <= 0) - continue; - - w = strndup(p, k); - if (!w) - return -ENOMEM; - - if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) { - free(w); - return -ENOMEM; - } - - zones[n_zones++] = w; - zones[n_zones] = NULL; - } - - strv_sort(zones); - - } else if (errno != ENOENT) - return -errno; - - *ret = zones; - zones = NULL; - - return 0; -} - -bool timezone_is_valid(const char *name) { - bool slash = false; - const char *p, *t; - struct stat st; - - if (isempty(name)) - return false; - - if (name[0] == '/') - return false; - - for (p = name; *p; p++) { - if (!(*p >= '0' && *p <= '9') && - !(*p >= 'a' && *p <= 'z') && - !(*p >= 'A' && *p <= 'Z') && - !(*p == '-' || *p == '_' || *p == '+' || *p == '/')) - return false; - - if (*p == '/') { - - if (slash) - return false; - - slash = true; - } else - slash = false; - } - - if (slash) - return false; - - t = strjoina("/usr/share/zoneinfo/", name); - if (stat(t, &st) < 0) - return false; - - if (!S_ISREG(st.st_mode)) - return false; - - return true; -} - -bool clock_boottime_supported(void) { - static int supported = -1; - - /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */ - - if (supported < 0) { - int fd; - - fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC); - if (fd < 0) - supported = false; - else { - safe_close(fd); - supported = true; - } - } - - return supported; -} - -clockid_t clock_boottime_or_monotonic(void) { - if (clock_boottime_supported()) - return CLOCK_BOOTTIME; - else - return CLOCK_MONOTONIC; -} - -bool clock_supported(clockid_t clock) { - struct timespec ts; - - switch (clock) { - - case CLOCK_MONOTONIC: - case CLOCK_REALTIME: - return true; - - case CLOCK_BOOTTIME: - return clock_boottime_supported(); - - case CLOCK_BOOTTIME_ALARM: - if (!clock_boottime_supported()) - return false; - - /* fall through, after checking the cached value for CLOCK_BOOTTIME. */ - - default: - /* For everything else, check properly */ - return clock_gettime(clock, &ts) >= 0; - } -} - -int get_timezone(char **tz) { - _cleanup_free_ char *t = NULL; - const char *e; - char *z; - int r; - - r = readlink_malloc("/etc/localtime", &t); - if (r < 0) - return r; /* returns EINVAL if not a symlink */ - - e = path_startswith(t, "/usr/share/zoneinfo/"); - if (!e) - e = path_startswith(t, "../usr/share/zoneinfo/"); - if (!e) - return -EINVAL; - - if (!timezone_is_valid(e)) - return -EINVAL; - - z = strdup(e); - if (!z) - return -ENOMEM; - - *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/basic/time-util.h b/src/basic/time-util.h deleted file mode 100644 index 558b0b5b7f..0000000000 --- a/src/basic/time-util.h +++ /dev/null @@ -1,181 +0,0 @@ -#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 <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <time.h> - -typedef uint64_t usec_t; -typedef uint64_t nsec_t; - -#define NSEC_FMT "%" PRIu64 -#define USEC_FMT "%" PRIu64 - -#include "macro.h" - -typedef struct dual_timestamp { - usec_t realtime; - usec_t monotonic; -} dual_timestamp; - -typedef struct triple_timestamp { - usec_t realtime; - usec_t monotonic; - usec_t boottime; -} triple_timestamp; - -#define USEC_INFINITY ((usec_t) -1) -#define NSEC_INFINITY ((nsec_t) -1) - -#define MSEC_PER_SEC 1000ULL -#define USEC_PER_SEC ((usec_t) 1000000ULL) -#define USEC_PER_MSEC ((usec_t) 1000ULL) -#define NSEC_PER_SEC ((nsec_t) 1000000000ULL) -#define NSEC_PER_MSEC ((nsec_t) 1000000ULL) -#define NSEC_PER_USEC ((nsec_t) 1000ULL) - -#define USEC_PER_MINUTE ((usec_t) (60ULL*USEC_PER_SEC)) -#define NSEC_PER_MINUTE ((nsec_t) (60ULL*NSEC_PER_SEC)) -#define USEC_PER_HOUR ((usec_t) (60ULL*USEC_PER_MINUTE)) -#define NSEC_PER_HOUR ((nsec_t) (60ULL*NSEC_PER_MINUTE)) -#define USEC_PER_DAY ((usec_t) (24ULL*USEC_PER_HOUR)) -#define NSEC_PER_DAY ((nsec_t) (24ULL*NSEC_PER_HOUR)) -#define USEC_PER_WEEK ((usec_t) (7ULL*USEC_PER_DAY)) -#define NSEC_PER_WEEK ((nsec_t) (7ULL*NSEC_PER_DAY)) -#define USEC_PER_MONTH ((usec_t) (2629800ULL*USEC_PER_SEC)) -#define NSEC_PER_MONTH ((nsec_t) (2629800ULL*NSEC_PER_SEC)) -#define USEC_PER_YEAR ((usec_t) (31557600ULL*USEC_PER_SEC)) -#define NSEC_PER_YEAR ((nsec_t) (31557600ULL*NSEC_PER_SEC)) - -/* We assume a maximum timezone length of 6. TZNAME_MAX is not defined on Linux, but glibc internally initializes this - * to 6. Let's rely on that. */ -#define FORMAT_TIMESTAMP_MAX (3+1+10+1+8+1+6+1+6+1) -#define FORMAT_TIMESTAMP_WIDTH 28 /* when outputting, assume this width */ -#define FORMAT_TIMESTAMP_RELATIVE_MAX 256 -#define FORMAT_TIMESPAN_MAX 64 - -#define TIME_T_MAX (time_t)((UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1) - -#define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) {}) -#define TRIPLE_TIMESTAMP_NULL ((struct triple_timestamp) {}) - -usec_t now(clockid_t clock); -nsec_t now_nsec(clockid_t clock); - -dual_timestamp* dual_timestamp_get(dual_timestamp *ts); -dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u); -dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u); -dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u); - -triple_timestamp* triple_timestamp_get(triple_timestamp *ts); -triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u); - -#define DUAL_TIMESTAMP_HAS_CLOCK(clock) \ - IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC) - -#define TRIPLE_TIMESTAMP_HAS_CLOCK(clock) \ - IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) - -static inline bool dual_timestamp_is_set(dual_timestamp *ts) { - return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) || - (ts->monotonic > 0 && ts->monotonic != USEC_INFINITY)); -} - -static inline bool triple_timestamp_is_set(triple_timestamp *ts) { - return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) || - (ts->monotonic > 0 && ts->monotonic != USEC_INFINITY) || - (ts->boottime > 0 && ts->boottime != USEC_INFINITY)); -} - -usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock); - -usec_t timespec_load(const struct timespec *ts) _pure_; -nsec_t timespec_load_nsec(const struct timespec *ts) _pure_; -struct timespec *timespec_store(struct timespec *ts, usec_t u); - -usec_t timeval_load(const struct timeval *tv) _pure_; -struct timeval *timeval_store(struct timeval *tv, usec_t u); - -char *format_timestamp(char *buf, size_t l, usec_t t); -char *format_timestamp_utc(char *buf, size_t l, usec_t t); -char *format_timestamp_us(char *buf, size_t l, usec_t t); -char *format_timestamp_us_utc(char *buf, size_t l, usec_t t); -char *format_timestamp_relative(char *buf, size_t l, usec_t t); -char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy); - -void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t); -int dual_timestamp_deserialize(const char *value, dual_timestamp *t); -int timestamp_deserialize(const char *value, usec_t *timestamp); - -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); - -int get_timezones(char ***l); -bool timezone_is_valid(const char *name); - -bool clock_boottime_supported(void); -bool clock_supported(clockid_t clock); -clockid_t clock_boottime_or_monotonic(void); - -#define xstrftime(buf, fmt, tm) \ - assert_message_se(strftime(buf, ELEMENTSOF(buf), fmt, tm) > 0, \ - "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); - -static inline usec_t usec_add(usec_t a, usec_t b) { - usec_t c; - - /* Adds two time values, and makes sure USEC_INFINITY as input results as USEC_INFINITY in output, and doesn't - * overflow. */ - - c = a + b; - if (c < a || c < b) /* overflow check */ - return USEC_INFINITY; - - return c; -} - -static inline usec_t usec_sub(usec_t timestamp, int64_t delta) { - if (delta < 0) - return usec_add(timestamp, (usec_t) (-delta)); - - if (timestamp == USEC_INFINITY) /* Make sure infinity doesn't degrade */ - return USEC_INFINITY; - - if (timestamp < (usec_t) delta) - return 0; - - return timestamp - delta; -} diff --git a/src/basic/umask-util.h b/src/basic/umask-util.h deleted file mode 100644 index 359d87d27c..0000000000 --- a/src/basic/umask-util.h +++ /dev/null @@ -1,46 +0,0 @@ -#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/basic/unaligned.h b/src/basic/unaligned.h deleted file mode 100644 index 7c847a3ccb..0000000000 --- a/src/basic/unaligned.h +++ /dev/null @@ -1,129 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Tom Gundersen - - 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 <endian.h> -#include <stdint.h> - -/* BE */ - -static inline uint16_t unaligned_read_be16(const void *_u) { - const uint8_t *u = _u; - - return (((uint16_t) u[0]) << 8) | - ((uint16_t) u[1]); -} - -static inline uint32_t unaligned_read_be32(const void *_u) { - const uint8_t *u = _u; - - return (((uint32_t) unaligned_read_be16(u)) << 16) | - ((uint32_t) unaligned_read_be16(u + 2)); -} - -static inline uint64_t unaligned_read_be64(const void *_u) { - const uint8_t *u = _u; - - return (((uint64_t) unaligned_read_be32(u)) << 32) | - ((uint64_t) unaligned_read_be32(u + 4)); -} - -static inline void unaligned_write_be16(void *_u, uint16_t a) { - uint8_t *u = _u; - - u[0] = (uint8_t) (a >> 8); - u[1] = (uint8_t) a; -} - -static inline void unaligned_write_be32(void *_u, uint32_t a) { - uint8_t *u = _u; - - unaligned_write_be16(u, (uint16_t) (a >> 16)); - unaligned_write_be16(u + 2, (uint16_t) a); -} - -static inline void unaligned_write_be64(void *_u, uint64_t a) { - uint8_t *u = _u; - - 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)); -} - -#if __BYTE_ORDER == __BIG_ENDIAN -#define unaligned_read_ne16 unaligned_read_be16 -#define unaligned_read_ne32 unaligned_read_be32 -#define unaligned_read_ne64 unaligned_read_be64 - -#define unaligned_write_ne16 unaligned_write_be16 -#define unaligned_write_ne32 unaligned_write_be32 -#define unaligned_write_ne64 unaligned_write_be64 -#else -#define unaligned_read_ne16 unaligned_read_le16 -#define unaligned_read_ne32 unaligned_read_le32 -#define unaligned_read_ne64 unaligned_read_le64 - -#define unaligned_write_ne16 unaligned_write_le16 -#define unaligned_write_ne32 unaligned_write_le32 -#define unaligned_write_ne64 unaligned_write_le64 -#endif diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c deleted file mode 100644 index fe883b95c7..0000000000 --- a/src/basic/unit-name.c +++ /dev/null @@ -1,1049 +0,0 @@ -/*** - 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 <errno.h> -#include <stddef.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "bus-label.h" -#include "glob-util.h" -#include "hexdecoct.h" -#include "macro.h" -#include "path-util.h" -#include "string-table.h" -#include "string-util.h" -#include "strv.h" -#include "unit-name.h" - -/* Characters valid in a unit name. */ -#define VALID_CHARS \ - DIGITS \ - LETTERS \ - ":-_.\\" - -/* The same, but also permits the single @ character that may appear */ -#define VALID_CHARS_WITH_AT \ - "@" \ - VALID_CHARS - -/* All chars valid in a unit name glob */ -#define VALID_CHARS_GLOB \ - VALID_CHARS_WITH_AT \ - "[]!-*?" - -bool unit_name_is_valid(const char *n, UnitNameFlags flags) { - const char *e, *i, *at; - - assert((flags & ~(UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) == 0); - - if (_unlikely_(flags == 0)) - return false; - - if (isempty(n)) - return false; - - if (strlen(n) >= UNIT_NAME_MAX) - return false; - - e = strrchr(n, '.'); - if (!e || e == n) - return false; - - if (unit_type_from_string(e + 1) < 0) - return false; - - for (i = n, at = NULL; i < e; i++) { - - if (*i == '@' && !at) - at = i; - - if (!strchr("@" VALID_CHARS, *i)) - return false; - } - - if (at == n) - return false; - - if (flags & UNIT_NAME_PLAIN) - if (!at) - return true; - - if (flags & UNIT_NAME_INSTANCE) - if (at && e > at + 1) - return true; - - if (flags & UNIT_NAME_TEMPLATE) - if (at && e == at + 1) - return true; - - return false; -} - -bool unit_prefix_is_valid(const char *p) { - - /* We don't allow additional @ in the prefix string */ - - if (isempty(p)) - return false; - - return in_charset(p, VALID_CHARS); -} - -bool unit_instance_is_valid(const char *i) { - - /* The max length depends on the length of the string, so we - * don't really check this here. */ - - if (isempty(i)) - return false; - - /* We allow additional @ in the instance string, we do not - * allow them in the prefix! */ - - return in_charset(i, "@" VALID_CHARS); -} - -bool unit_suffix_is_valid(const char *s) { - if (isempty(s)) - return false; - - if (s[0] != '.') - return false; - - if (unit_type_from_string(s + 1) < 0) - return false; - - return true; -} - -int unit_name_to_prefix(const char *n, char **ret) { - const char *p; - char *s; - - assert(n); - assert(ret); - - if (!unit_name_is_valid(n, UNIT_NAME_ANY)) - return -EINVAL; - - p = strchr(n, '@'); - if (!p) - p = strrchr(n, '.'); - - assert_se(p); - - s = strndup(n, p - n); - if (!s) - return -ENOMEM; - - *ret = s; - return 0; -} - -int unit_name_to_instance(const char *n, char **instance) { - const char *p, *d; - char *i; - - assert(n); - assert(instance); - - if (!unit_name_is_valid(n, UNIT_NAME_ANY)) - return -EINVAL; - - /* Everything past the first @ and before the last . is the instance */ - p = strchr(n, '@'); - if (!p) { - *instance = NULL; - return 0; - } - - p++; - - d = strrchr(p, '.'); - if (!d) - return -EINVAL; - - i = strndup(p, d-p); - if (!i) - return -ENOMEM; - - *instance = i; - return 1; -} - -int unit_name_to_prefix_and_instance(const char *n, char **ret) { - const char *d; - char *s; - - assert(n); - assert(ret); - - if (!unit_name_is_valid(n, UNIT_NAME_ANY)) - return -EINVAL; - - d = strrchr(n, '.'); - if (!d) - return -EINVAL; - - s = strndup(n, d - n); - if (!s) - return -ENOMEM; - - *ret = s; - return 0; -} - -UnitType unit_name_to_type(const char *n) { - const char *e; - - assert(n); - - if (!unit_name_is_valid(n, UNIT_NAME_ANY)) - return _UNIT_TYPE_INVALID; - - assert_se(e = strrchr(n, '.')); - - return unit_type_from_string(e + 1); -} - -int unit_name_change_suffix(const char *n, const char *suffix, char **ret) { - char *e, *s; - size_t a, b; - - assert(n); - assert(suffix); - assert(ret); - - if (!unit_name_is_valid(n, UNIT_NAME_ANY)) - return -EINVAL; - - if (!unit_suffix_is_valid(suffix)) - return -EINVAL; - - assert_se(e = strrchr(n, '.')); - - a = e - n; - b = strlen(suffix); - - s = new(char, a + b + 1); - if (!s) - return -ENOMEM; - - strcpy(mempcpy(s, n, a), suffix); - *ret = s; - - return 0; -} - -int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret) { - char *s; - - assert(prefix); - assert(suffix); - assert(ret); - - if (!unit_prefix_is_valid(prefix)) - return -EINVAL; - - if (instance && !unit_instance_is_valid(instance)) - return -EINVAL; - - if (!unit_suffix_is_valid(suffix)) - return -EINVAL; - - if (!instance) - s = strappend(prefix, suffix); - else - s = strjoin(prefix, "@", instance, suffix, NULL); - if (!s) - return -ENOMEM; - - *ret = s; - return 0; -} - -static char *do_escape_char(char c, char *t) { - assert(t); - - *(t++) = '\\'; - *(t++) = 'x'; - *(t++) = hexchar(c >> 4); - *(t++) = hexchar(c); - - return t; -} - -static char *do_escape(const char *f, char *t) { - assert(f); - assert(t); - - /* do not create units with a leading '.', like for "/.dotdir" mount points */ - if (*f == '.') { - t = do_escape_char(*f, t); - f++; - } - - for (; *f; f++) { - if (*f == '/') - *(t++) = '-'; - else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f)) - t = do_escape_char(*f, t); - else - *(t++) = *f; - } - - return t; -} - -char *unit_name_escape(const char *f) { - char *r, *t; - - assert(f); - - r = new(char, strlen(f)*4+1); - if (!r) - return NULL; - - t = do_escape(f, r); - *t = 0; - - return r; -} - -int unit_name_unescape(const char *f, char **ret) { - _cleanup_free_ char *r = NULL; - char *t; - - assert(f); - - r = strdup(f); - if (!r) - return -ENOMEM; - - for (t = r; *f; f++) { - if (*f == '-') - *(t++) = '/'; - else if (*f == '\\') { - int a, b; - - if (f[1] != 'x') - return -EINVAL; - - a = unhexchar(f[2]); - if (a < 0) - return -EINVAL; - - b = unhexchar(f[3]); - if (b < 0) - return -EINVAL; - - *(t++) = (char) (((uint8_t) a << 4U) | (uint8_t) b); - f += 3; - } else - *(t++) = *f; - } - - *t = 0; - - *ret = r; - r = NULL; - - return 0; -} - -int unit_name_path_escape(const char *f, char **ret) { - char *p, *s; - - assert(f); - assert(ret); - - p = strdupa(f); - if (!p) - return -ENOMEM; - - path_kill_slashes(p); - - if (STR_IN_SET(p, "/", "")) - s = strdup("-"); - else { - char *e; - - if (!path_is_safe(p)) - return -EINVAL; - - /* Truncate trailing slashes */ - e = endswith(p, "/"); - if (e) - *e = 0; - - /* Truncate leading slashes */ - if (p[0] == '/') - p++; - - s = unit_name_escape(p); - } - if (!s) - return -ENOMEM; - - *ret = s; - return 0; -} - -int unit_name_path_unescape(const char *f, char **ret) { - char *s; - int r; - - assert(f); - - if (isempty(f)) - return -EINVAL; - - if (streq(f, "-")) { - s = strdup("/"); - if (!s) - return -ENOMEM; - } else { - char *w; - - r = unit_name_unescape(f, &w); - if (r < 0) - return r; - - /* Don't accept trailing or leading slashes */ - if (startswith(w, "/") || endswith(w, "/")) { - free(w); - return -EINVAL; - } - - /* Prefix a slash again */ - s = strappend("/", w); - free(w); - if (!s) - return -ENOMEM; - - if (!path_is_safe(s)) { - free(s); - return -EINVAL; - } - } - - if (ret) - *ret = s; - else - free(s); - - return 0; -} - -int unit_name_replace_instance(const char *f, const char *i, char **ret) { - const char *p, *e; - char *s; - size_t a, b; - - assert(f); - assert(i); - assert(ret); - - if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) - return -EINVAL; - if (!unit_instance_is_valid(i)) - return -EINVAL; - - assert_se(p = strchr(f, '@')); - assert_se(e = strrchr(f, '.')); - - a = p - f; - b = strlen(i); - - s = new(char, a + 1 + b + strlen(e) + 1); - if (!s) - return -ENOMEM; - - strcpy(mempcpy(mempcpy(s, f, a + 1), i, b), e); - - *ret = s; - return 0; -} - -int unit_name_template(const char *f, char **ret) { - const char *p, *e; - char *s; - size_t a; - - assert(f); - assert(ret); - - if (!unit_name_is_valid(f, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) - return -EINVAL; - - assert_se(p = strchr(f, '@')); - assert_se(e = strrchr(f, '.')); - - a = p - f; - - s = new(char, a + 1 + strlen(e) + 1); - if (!s) - return -ENOMEM; - - strcpy(mempcpy(s, f, a + 1), e); - - *ret = s; - return 0; -} - -int unit_name_from_path(const char *path, const char *suffix, char **ret) { - _cleanup_free_ char *p = NULL; - char *s = NULL; - int r; - - assert(path); - assert(suffix); - assert(ret); - - if (!unit_suffix_is_valid(suffix)) - return -EINVAL; - - r = unit_name_path_escape(path, &p); - if (r < 0) - return r; - - s = strappend(p, suffix); - if (!s) - return -ENOMEM; - - *ret = s; - return 0; -} - -int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret) { - _cleanup_free_ char *p = NULL; - char *s; - int r; - - assert(prefix); - assert(path); - assert(suffix); - assert(ret); - - if (!unit_prefix_is_valid(prefix)) - return -EINVAL; - - if (!unit_suffix_is_valid(suffix)) - return -EINVAL; - - r = unit_name_path_escape(path, &p); - if (r < 0) - return r; - - s = strjoin(prefix, "@", p, suffix, NULL); - if (!s) - return -ENOMEM; - - *ret = s; - return 0; -} - -int unit_name_to_path(const char *name, char **ret) { - _cleanup_free_ char *prefix = NULL; - int r; - - assert(name); - - r = unit_name_to_prefix(name, &prefix); - if (r < 0) - return r; - - return unit_name_path_unescape(prefix, ret); -} - -char *unit_dbus_path_from_name(const char *name) { - _cleanup_free_ char *e = NULL; - - assert(name); - - e = bus_label_escape(name); - if (!e) - return NULL; - - return strappend("/org/freedesktop/systemd1/unit/", e); -} - -int unit_name_from_dbus_path(const char *path, char **name) { - const char *e; - char *n; - - e = startswith(path, "/org/freedesktop/systemd1/unit/"); - if (!e) - return -EINVAL; - - n = bus_label_unescape(e); - if (!n) - return -ENOMEM; - - *name = n; - return 0; -} - -const char* unit_dbus_interface_from_type(UnitType t) { - - static const char *const table[_UNIT_TYPE_MAX] = { - [UNIT_SERVICE] = "org.freedesktop.systemd1.Service", - [UNIT_SOCKET] = "org.freedesktop.systemd1.Socket", - [UNIT_BUSNAME] = "org.freedesktop.systemd1.BusName", - [UNIT_TARGET] = "org.freedesktop.systemd1.Target", - [UNIT_DEVICE] = "org.freedesktop.systemd1.Device", - [UNIT_MOUNT] = "org.freedesktop.systemd1.Mount", - [UNIT_AUTOMOUNT] = "org.freedesktop.systemd1.Automount", - [UNIT_SWAP] = "org.freedesktop.systemd1.Swap", - [UNIT_TIMER] = "org.freedesktop.systemd1.Timer", - [UNIT_PATH] = "org.freedesktop.systemd1.Path", - [UNIT_SLICE] = "org.freedesktop.systemd1.Slice", - [UNIT_SCOPE] = "org.freedesktop.systemd1.Scope", - }; - - if (t < 0) - return NULL; - if (t >= _UNIT_TYPE_MAX) - return NULL; - - return table[t]; -} - -const char *unit_dbus_interface_from_name(const char *name) { - UnitType t; - - t = unit_name_to_type(name); - if (t < 0) - return NULL; - - return unit_dbus_interface_from_type(t); -} - -static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) { - const char *valid_chars; - - assert(f); - assert(IN_SET(allow_globs, UNIT_NAME_GLOB, UNIT_NAME_NOGLOB)); - assert(t); - - /* We'll only escape the obvious characters here, to play - * safe. */ - - valid_chars = allow_globs == UNIT_NAME_GLOB ? VALID_CHARS_GLOB : VALID_CHARS_WITH_AT; - - for (; *f; f++) { - if (*f == '/') - *(t++) = '-'; - else if (!strchr(valid_chars, *f)) - t = do_escape_char(*f, t); - else - *(t++) = *f; - } - - return t; -} - -/** - * Convert a string to a unit name. /dev/blah is converted to dev-blah.device, - * /blah/blah is converted to blah-blah.mount, anything else is left alone, - * except that @suffix is appended if a valid unit suffix is not present. - * - * If @allow_globs, globs characters are preserved. Otherwise, they are escaped. - */ -int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret) { - char *s, *t; - int r; - - assert(name); - assert(suffix); - assert(ret); - - if (isempty(name)) /* We cannot mangle empty unit names to become valid, sorry. */ - return -EINVAL; - - if (!unit_suffix_is_valid(suffix)) - return -EINVAL; - - /* Already a fully valid unit name? If so, no mangling is necessary... */ - if (unit_name_is_valid(name, UNIT_NAME_ANY)) - goto good; - - /* Already a fully valid globbing expression? If so, no mangling is necessary either... */ - if (allow_globs == UNIT_NAME_GLOB && - string_is_glob(name) && - in_charset(name, VALID_CHARS_GLOB)) - goto good; - - if (is_device_path(name)) { - r = unit_name_from_path(name, ".device", ret); - if (r >= 0) - return 1; - if (r != -EINVAL) - return r; - } - - if (path_is_absolute(name)) { - r = unit_name_from_path(name, ".mount", ret); - if (r >= 0) - return 1; - if (r != -EINVAL) - return r; - } - - s = new(char, strlen(name) * 4 + strlen(suffix) + 1); - if (!s) - return -ENOMEM; - - t = do_escape_mangle(name, allow_globs, s); - *t = 0; - - /* Append a suffix if it doesn't have any, but only if this is not a glob, so that we can allow "foo.*" as a - * valid glob. */ - if ((allow_globs != UNIT_NAME_GLOB || !string_is_glob(s)) && unit_name_to_type(s) < 0) - strcpy(t, suffix); - - *ret = s; - return 1; - -good: - s = strdup(name); - if (!s) - return -ENOMEM; - - *ret = s; - return 0; -} - -int slice_build_parent_slice(const char *slice, char **ret) { - char *s, *dash; - int r; - - assert(slice); - assert(ret); - - if (!slice_name_is_valid(slice)) - return -EINVAL; - - if (streq(slice, "-.slice")) { - *ret = NULL; - return 0; - } - - s = strdup(slice); - if (!s) - return -ENOMEM; - - dash = strrchr(s, '-'); - if (dash) - strcpy(dash, ".slice"); - else { - r = free_and_strdup(&s, "-.slice"); - if (r < 0) { - free(s); - return r; - } - } - - *ret = s; - return 1; -} - -int slice_build_subslice(const char *slice, const char*name, char **ret) { - char *subslice; - - assert(slice); - assert(name); - assert(ret); - - if (!slice_name_is_valid(slice)) - return -EINVAL; - - if (!unit_prefix_is_valid(name)) - return -EINVAL; - - if (streq(slice, "-.slice")) - subslice = strappend(name, ".slice"); - else { - char *e; - - assert_se(e = endswith(slice, ".slice")); - - subslice = new(char, (e - slice) + 1 + strlen(name) + 6 + 1); - if (!subslice) - return -ENOMEM; - - stpcpy(stpcpy(stpcpy(mempcpy(subslice, slice, e - slice), "-"), name), ".slice"); - } - - *ret = subslice; - return 0; -} - -bool slice_name_is_valid(const char *name) { - const char *p, *e; - bool dash = false; - - if (!unit_name_is_valid(name, UNIT_NAME_PLAIN)) - return false; - - if (streq(name, "-.slice")) - return true; - - e = endswith(name, ".slice"); - if (!e) - return false; - - for (p = name; p < e; p++) { - - if (*p == '-') { - - /* Don't allow initial dash */ - if (p == name) - return false; - - /* Don't allow multiple dashes */ - if (dash) - return false; - - dash = true; - } else - dash = false; - } - - /* Don't allow trailing hash */ - if (dash) - return false; - - return true; -} - -static const char* const unit_type_table[_UNIT_TYPE_MAX] = { - [UNIT_SERVICE] = "service", - [UNIT_SOCKET] = "socket", - [UNIT_BUSNAME] = "busname", - [UNIT_TARGET] = "target", - [UNIT_DEVICE] = "device", - [UNIT_MOUNT] = "mount", - [UNIT_AUTOMOUNT] = "automount", - [UNIT_SWAP] = "swap", - [UNIT_TIMER] = "timer", - [UNIT_PATH] = "path", - [UNIT_SLICE] = "slice", - [UNIT_SCOPE] = "scope", -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType); - -static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = { - [UNIT_STUB] = "stub", - [UNIT_LOADED] = "loaded", - [UNIT_NOT_FOUND] = "not-found", - [UNIT_ERROR] = "error", - [UNIT_MERGED] = "merged", - [UNIT_MASKED] = "masked" -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_load_state, UnitLoadState); - -static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = { - [UNIT_ACTIVE] = "active", - [UNIT_RELOADING] = "reloading", - [UNIT_INACTIVE] = "inactive", - [UNIT_FAILED] = "failed", - [UNIT_ACTIVATING] = "activating", - [UNIT_DEACTIVATING] = "deactivating" -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState); - -static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = { - [AUTOMOUNT_DEAD] = "dead", - [AUTOMOUNT_WAITING] = "waiting", - [AUTOMOUNT_RUNNING] = "running", - [AUTOMOUNT_FAILED] = "failed" -}; - -DEFINE_STRING_TABLE_LOOKUP(automount_state, AutomountState); - -static const char* const busname_state_table[_BUSNAME_STATE_MAX] = { - [BUSNAME_DEAD] = "dead", - [BUSNAME_MAKING] = "making", - [BUSNAME_REGISTERED] = "registered", - [BUSNAME_LISTENING] = "listening", - [BUSNAME_RUNNING] = "running", - [BUSNAME_SIGTERM] = "sigterm", - [BUSNAME_SIGKILL] = "sigkill", - [BUSNAME_FAILED] = "failed", -}; - -DEFINE_STRING_TABLE_LOOKUP(busname_state, BusNameState); - -static const char* const device_state_table[_DEVICE_STATE_MAX] = { - [DEVICE_DEAD] = "dead", - [DEVICE_TENTATIVE] = "tentative", - [DEVICE_PLUGGED] = "plugged", -}; - -DEFINE_STRING_TABLE_LOOKUP(device_state, DeviceState); - -static const char* const mount_state_table[_MOUNT_STATE_MAX] = { - [MOUNT_DEAD] = "dead", - [MOUNT_MOUNTING] = "mounting", - [MOUNT_MOUNTING_DONE] = "mounting-done", - [MOUNT_MOUNTED] = "mounted", - [MOUNT_REMOUNTING] = "remounting", - [MOUNT_UNMOUNTING] = "unmounting", - [MOUNT_MOUNTING_SIGTERM] = "mounting-sigterm", - [MOUNT_MOUNTING_SIGKILL] = "mounting-sigkill", - [MOUNT_REMOUNTING_SIGTERM] = "remounting-sigterm", - [MOUNT_REMOUNTING_SIGKILL] = "remounting-sigkill", - [MOUNT_UNMOUNTING_SIGTERM] = "unmounting-sigterm", - [MOUNT_UNMOUNTING_SIGKILL] = "unmounting-sigkill", - [MOUNT_FAILED] = "failed" -}; - -DEFINE_STRING_TABLE_LOOKUP(mount_state, MountState); - -static const char* const path_state_table[_PATH_STATE_MAX] = { - [PATH_DEAD] = "dead", - [PATH_WAITING] = "waiting", - [PATH_RUNNING] = "running", - [PATH_FAILED] = "failed" -}; - -DEFINE_STRING_TABLE_LOOKUP(path_state, PathState); - -static const char* const scope_state_table[_SCOPE_STATE_MAX] = { - [SCOPE_DEAD] = "dead", - [SCOPE_RUNNING] = "running", - [SCOPE_ABANDONED] = "abandoned", - [SCOPE_STOP_SIGTERM] = "stop-sigterm", - [SCOPE_STOP_SIGKILL] = "stop-sigkill", - [SCOPE_FAILED] = "failed", -}; - -DEFINE_STRING_TABLE_LOOKUP(scope_state, ScopeState); - -static const char* const service_state_table[_SERVICE_STATE_MAX] = { - [SERVICE_DEAD] = "dead", - [SERVICE_START_PRE] = "start-pre", - [SERVICE_START] = "start", - [SERVICE_START_POST] = "start-post", - [SERVICE_RUNNING] = "running", - [SERVICE_EXITED] = "exited", - [SERVICE_RELOAD] = "reload", - [SERVICE_STOP] = "stop", - [SERVICE_STOP_SIGABRT] = "stop-sigabrt", - [SERVICE_STOP_SIGTERM] = "stop-sigterm", - [SERVICE_STOP_SIGKILL] = "stop-sigkill", - [SERVICE_STOP_POST] = "stop-post", - [SERVICE_FINAL_SIGTERM] = "final-sigterm", - [SERVICE_FINAL_SIGKILL] = "final-sigkill", - [SERVICE_FAILED] = "failed", - [SERVICE_AUTO_RESTART] = "auto-restart", -}; - -DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState); - -static const char* const slice_state_table[_SLICE_STATE_MAX] = { - [SLICE_DEAD] = "dead", - [SLICE_ACTIVE] = "active" -}; - -DEFINE_STRING_TABLE_LOOKUP(slice_state, SliceState); - -static const char* const socket_state_table[_SOCKET_STATE_MAX] = { - [SOCKET_DEAD] = "dead", - [SOCKET_START_PRE] = "start-pre", - [SOCKET_START_CHOWN] = "start-chown", - [SOCKET_START_POST] = "start-post", - [SOCKET_LISTENING] = "listening", - [SOCKET_RUNNING] = "running", - [SOCKET_STOP_PRE] = "stop-pre", - [SOCKET_STOP_PRE_SIGTERM] = "stop-pre-sigterm", - [SOCKET_STOP_PRE_SIGKILL] = "stop-pre-sigkill", - [SOCKET_STOP_POST] = "stop-post", - [SOCKET_FINAL_SIGTERM] = "final-sigterm", - [SOCKET_FINAL_SIGKILL] = "final-sigkill", - [SOCKET_FAILED] = "failed" -}; - -DEFINE_STRING_TABLE_LOOKUP(socket_state, SocketState); - -static const char* const swap_state_table[_SWAP_STATE_MAX] = { - [SWAP_DEAD] = "dead", - [SWAP_ACTIVATING] = "activating", - [SWAP_ACTIVATING_DONE] = "activating-done", - [SWAP_ACTIVE] = "active", - [SWAP_DEACTIVATING] = "deactivating", - [SWAP_ACTIVATING_SIGTERM] = "activating-sigterm", - [SWAP_ACTIVATING_SIGKILL] = "activating-sigkill", - [SWAP_DEACTIVATING_SIGTERM] = "deactivating-sigterm", - [SWAP_DEACTIVATING_SIGKILL] = "deactivating-sigkill", - [SWAP_FAILED] = "failed" -}; - -DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState); - -static const char* const target_state_table[_TARGET_STATE_MAX] = { - [TARGET_DEAD] = "dead", - [TARGET_ACTIVE] = "active" -}; - -DEFINE_STRING_TABLE_LOOKUP(target_state, TargetState); - -static const char* const timer_state_table[_TIMER_STATE_MAX] = { - [TIMER_DEAD] = "dead", - [TIMER_WAITING] = "waiting", - [TIMER_RUNNING] = "running", - [TIMER_ELAPSED] = "elapsed", - [TIMER_FAILED] = "failed" -}; - -DEFINE_STRING_TABLE_LOOKUP(timer_state, TimerState); - -static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = { - [UNIT_REQUIRES] = "Requires", - [UNIT_REQUISITE] = "Requisite", - [UNIT_WANTS] = "Wants", - [UNIT_BINDS_TO] = "BindsTo", - [UNIT_PART_OF] = "PartOf", - [UNIT_REQUIRED_BY] = "RequiredBy", - [UNIT_REQUISITE_OF] = "RequisiteOf", - [UNIT_WANTED_BY] = "WantedBy", - [UNIT_BOUND_BY] = "BoundBy", - [UNIT_CONSISTS_OF] = "ConsistsOf", - [UNIT_CONFLICTS] = "Conflicts", - [UNIT_CONFLICTED_BY] = "ConflictedBy", - [UNIT_BEFORE] = "Before", - [UNIT_AFTER] = "After", - [UNIT_ON_FAILURE] = "OnFailure", - [UNIT_TRIGGERS] = "Triggers", - [UNIT_TRIGGERED_BY] = "TriggeredBy", - [UNIT_PROPAGATES_RELOAD_TO] = "PropagatesReloadTo", - [UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom", - [UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf", - [UNIT_REFERENCES] = "References", - [UNIT_REFERENCED_BY] = "ReferencedBy", -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency); diff --git a/src/basic/unit-name.h b/src/basic/unit-name.h deleted file mode 100644 index 44eadf0347..0000000000 --- a/src/basic/unit-name.h +++ /dev/null @@ -1,367 +0,0 @@ -#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 "macro.h" - -#define UNIT_NAME_MAX 256 - -typedef enum UnitType { - UNIT_SERVICE = 0, - UNIT_SOCKET, - UNIT_BUSNAME, - UNIT_TARGET, - UNIT_DEVICE, - UNIT_MOUNT, - UNIT_AUTOMOUNT, - UNIT_SWAP, - UNIT_TIMER, - UNIT_PATH, - UNIT_SLICE, - UNIT_SCOPE, - _UNIT_TYPE_MAX, - _UNIT_TYPE_INVALID = -1 -} UnitType; - -typedef enum UnitLoadState { - UNIT_STUB = 0, - UNIT_LOADED, - UNIT_NOT_FOUND, - UNIT_ERROR, - UNIT_MERGED, - UNIT_MASKED, - _UNIT_LOAD_STATE_MAX, - _UNIT_LOAD_STATE_INVALID = -1 -} UnitLoadState; - -typedef enum UnitActiveState { - UNIT_ACTIVE, - UNIT_RELOADING, - UNIT_INACTIVE, - UNIT_FAILED, - UNIT_ACTIVATING, - UNIT_DEACTIVATING, - _UNIT_ACTIVE_STATE_MAX, - _UNIT_ACTIVE_STATE_INVALID = -1 -} UnitActiveState; - -typedef enum AutomountState { - AUTOMOUNT_DEAD, - AUTOMOUNT_WAITING, - AUTOMOUNT_RUNNING, - AUTOMOUNT_FAILED, - _AUTOMOUNT_STATE_MAX, - _AUTOMOUNT_STATE_INVALID = -1 -} AutomountState; - -typedef enum BusNameState { - BUSNAME_DEAD, - BUSNAME_MAKING, - BUSNAME_REGISTERED, - BUSNAME_LISTENING, - BUSNAME_RUNNING, - BUSNAME_SIGTERM, - BUSNAME_SIGKILL, - BUSNAME_FAILED, - _BUSNAME_STATE_MAX, - _BUSNAME_STATE_INVALID = -1 -} BusNameState; - -/* We simply watch devices, we cannot plug/unplug them. That - * simplifies the state engine greatly */ -typedef enum DeviceState { - DEVICE_DEAD, - DEVICE_TENTATIVE, /* mounted or swapped, but not (yet) announced by udev */ - DEVICE_PLUGGED, /* announced by udev */ - _DEVICE_STATE_MAX, - _DEVICE_STATE_INVALID = -1 -} DeviceState; - -typedef enum MountState { - MOUNT_DEAD, - MOUNT_MOUNTING, /* /usr/bin/mount is running, but the mount is not done yet. */ - MOUNT_MOUNTING_DONE, /* /usr/bin/mount is running, and the mount is done. */ - MOUNT_MOUNTED, - MOUNT_REMOUNTING, - MOUNT_UNMOUNTING, - MOUNT_MOUNTING_SIGTERM, - MOUNT_MOUNTING_SIGKILL, - MOUNT_REMOUNTING_SIGTERM, - MOUNT_REMOUNTING_SIGKILL, - MOUNT_UNMOUNTING_SIGTERM, - MOUNT_UNMOUNTING_SIGKILL, - MOUNT_FAILED, - _MOUNT_STATE_MAX, - _MOUNT_STATE_INVALID = -1 -} MountState; - -typedef enum PathState { - PATH_DEAD, - PATH_WAITING, - PATH_RUNNING, - PATH_FAILED, - _PATH_STATE_MAX, - _PATH_STATE_INVALID = -1 -} PathState; - -typedef enum ScopeState { - SCOPE_DEAD, - SCOPE_RUNNING, - SCOPE_ABANDONED, - SCOPE_STOP_SIGTERM, - SCOPE_STOP_SIGKILL, - SCOPE_FAILED, - _SCOPE_STATE_MAX, - _SCOPE_STATE_INVALID = -1 -} ScopeState; - -typedef enum ServiceState { - SERVICE_DEAD, - SERVICE_START_PRE, - SERVICE_START, - SERVICE_START_POST, - SERVICE_RUNNING, - SERVICE_EXITED, /* Nothing is running anymore, but RemainAfterExit is true hence this is OK */ - SERVICE_RELOAD, - SERVICE_STOP, /* No STOP_PRE state, instead just register multiple STOP executables */ - SERVICE_STOP_SIGABRT, /* Watchdog timeout */ - SERVICE_STOP_SIGTERM, - SERVICE_STOP_SIGKILL, - SERVICE_STOP_POST, - SERVICE_FINAL_SIGTERM, /* In case the STOP_POST executable hangs, we shoot that down, too */ - SERVICE_FINAL_SIGKILL, - SERVICE_FAILED, - SERVICE_AUTO_RESTART, - _SERVICE_STATE_MAX, - _SERVICE_STATE_INVALID = -1 -} ServiceState; - -typedef enum SliceState { - SLICE_DEAD, - SLICE_ACTIVE, - _SLICE_STATE_MAX, - _SLICE_STATE_INVALID = -1 -} SliceState; - -typedef enum SocketState { - SOCKET_DEAD, - SOCKET_START_PRE, - SOCKET_START_CHOWN, - SOCKET_START_POST, - SOCKET_LISTENING, - SOCKET_RUNNING, - SOCKET_STOP_PRE, - SOCKET_STOP_PRE_SIGTERM, - SOCKET_STOP_PRE_SIGKILL, - SOCKET_STOP_POST, - SOCKET_FINAL_SIGTERM, - SOCKET_FINAL_SIGKILL, - SOCKET_FAILED, - _SOCKET_STATE_MAX, - _SOCKET_STATE_INVALID = -1 -} SocketState; - -typedef enum SwapState { - SWAP_DEAD, - SWAP_ACTIVATING, /* /sbin/swapon is running, but the swap not yet enabled. */ - SWAP_ACTIVATING_DONE, /* /sbin/swapon is running, and the swap is done. */ - SWAP_ACTIVE, - SWAP_DEACTIVATING, - SWAP_ACTIVATING_SIGTERM, - SWAP_ACTIVATING_SIGKILL, - SWAP_DEACTIVATING_SIGTERM, - SWAP_DEACTIVATING_SIGKILL, - SWAP_FAILED, - _SWAP_STATE_MAX, - _SWAP_STATE_INVALID = -1 -} SwapState; - -typedef enum TargetState { - TARGET_DEAD, - TARGET_ACTIVE, - _TARGET_STATE_MAX, - _TARGET_STATE_INVALID = -1 -} TargetState; - -typedef enum TimerState { - TIMER_DEAD, - TIMER_WAITING, - TIMER_RUNNING, - TIMER_ELAPSED, - TIMER_FAILED, - _TIMER_STATE_MAX, - _TIMER_STATE_INVALID = -1 -} TimerState; - -typedef enum UnitDependency { - /* Positive dependencies */ - UNIT_REQUIRES, - UNIT_REQUISITE, - UNIT_WANTS, - UNIT_BINDS_TO, - UNIT_PART_OF, - - /* Inverse of the above */ - UNIT_REQUIRED_BY, /* inverse of 'requires' is 'required_by' */ - UNIT_REQUISITE_OF, /* inverse of 'requisite' is 'requisite_of' */ - UNIT_WANTED_BY, /* inverse of 'wants' */ - UNIT_BOUND_BY, /* inverse of 'binds_to' */ - UNIT_CONSISTS_OF, /* inverse of 'part_of' */ - - /* Negative dependencies */ - UNIT_CONFLICTS, /* inverse of 'conflicts' is 'conflicted_by' */ - UNIT_CONFLICTED_BY, - - /* Order */ - UNIT_BEFORE, /* inverse of 'before' is 'after' and vice versa */ - UNIT_AFTER, - - /* On Failure */ - UNIT_ON_FAILURE, - - /* Triggers (i.e. a socket triggers a service) */ - UNIT_TRIGGERS, - UNIT_TRIGGERED_BY, - - /* Propagate reloads */ - UNIT_PROPAGATES_RELOAD_TO, - UNIT_RELOAD_PROPAGATED_FROM, - - /* Joins namespace of */ - UNIT_JOINS_NAMESPACE_OF, - - /* Reference information for GC logic */ - UNIT_REFERENCES, /* Inverse of 'references' is 'referenced_by' */ - UNIT_REFERENCED_BY, - - _UNIT_DEPENDENCY_MAX, - _UNIT_DEPENDENCY_INVALID = -1 -} UnitDependency; - -typedef enum UnitNameFlags { - UNIT_NAME_PLAIN = 1, /* Allow foo.service */ - UNIT_NAME_INSTANCE = 2, /* Allow foo@bar.service */ - UNIT_NAME_TEMPLATE = 4, /* Allow foo@.service */ - UNIT_NAME_ANY = UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE, -} UnitNameFlags; - -bool unit_name_is_valid(const char *n, UnitNameFlags flags) _pure_; -bool unit_prefix_is_valid(const char *p) _pure_; -bool unit_instance_is_valid(const char *i) _pure_; -bool unit_suffix_is_valid(const char *s) _pure_; - -static inline int unit_prefix_and_instance_is_valid(const char *p) { - /* For prefix+instance and instance the same rules apply */ - return unit_instance_is_valid(p); -} - -int unit_name_to_prefix(const char *n, char **prefix); -int unit_name_to_instance(const char *n, char **instance); -int unit_name_to_prefix_and_instance(const char *n, char **ret); - -UnitType unit_name_to_type(const char *n) _pure_; - -int unit_name_change_suffix(const char *n, const char *suffix, char **ret); - -int unit_name_build(const char *prefix, const char *instance, const char *suffix, char **ret); - -char *unit_name_escape(const char *f); -int unit_name_unescape(const char *f, char **ret); -int unit_name_path_escape(const char *f, char **ret); -int unit_name_path_unescape(const char *f, char **ret); - -int unit_name_replace_instance(const char *f, const char *i, char **ret); - -int unit_name_template(const char *f, char **ret); - -int unit_name_from_path(const char *path, const char *suffix, char **ret); -int unit_name_from_path_instance(const char *prefix, const char *path, const char *suffix, char **ret); -int unit_name_to_path(const char *name, char **ret); - -char *unit_dbus_path_from_name(const char *name); -int unit_name_from_dbus_path(const char *path, char **name); - -const char* unit_dbus_interface_from_type(UnitType t); -const char *unit_dbus_interface_from_name(const char *name); - -typedef enum UnitNameMangle { - UNIT_NAME_NOGLOB, - UNIT_NAME_GLOB, -} UnitNameMangle; - -int unit_name_mangle_with_suffix(const char *name, UnitNameMangle allow_globs, const char *suffix, char **ret); - -static inline int unit_name_mangle(const char *name, UnitNameMangle allow_globs, char **ret) { - return unit_name_mangle_with_suffix(name, allow_globs, ".service", ret); -} - -int slice_build_parent_slice(const char *slice, char **ret); -int slice_build_subslice(const char *slice, const char*name, char **subslice); -bool slice_name_is_valid(const char *name); - -const char *unit_type_to_string(UnitType i) _const_; -UnitType unit_type_from_string(const char *s) _pure_; - -const char *unit_load_state_to_string(UnitLoadState i) _const_; -UnitLoadState unit_load_state_from_string(const char *s) _pure_; - -const char *unit_active_state_to_string(UnitActiveState i) _const_; -UnitActiveState unit_active_state_from_string(const char *s) _pure_; - -const char* automount_state_to_string(AutomountState i) _const_; -AutomountState automount_state_from_string(const char *s) _pure_; - -const char* busname_state_to_string(BusNameState i) _const_; -BusNameState busname_state_from_string(const char *s) _pure_; - -const char* device_state_to_string(DeviceState i) _const_; -DeviceState device_state_from_string(const char *s) _pure_; - -const char* mount_state_to_string(MountState i) _const_; -MountState mount_state_from_string(const char *s) _pure_; - -const char* path_state_to_string(PathState i) _const_; -PathState path_state_from_string(const char *s) _pure_; - -const char* scope_state_to_string(ScopeState i) _const_; -ScopeState scope_state_from_string(const char *s) _pure_; - -const char* service_state_to_string(ServiceState i) _const_; -ServiceState service_state_from_string(const char *s) _pure_; - -const char* slice_state_to_string(SliceState i) _const_; -SliceState slice_state_from_string(const char *s) _pure_; - -const char* socket_state_to_string(SocketState i) _const_; -SocketState socket_state_from_string(const char *s) _pure_; - -const char* swap_state_to_string(SwapState i) _const_; -SwapState swap_state_from_string(const char *s) _pure_; - -const char* target_state_to_string(TargetState i) _const_; -TargetState target_state_from_string(const char *s) _pure_; - -const char *timer_state_to_string(TimerState i) _const_; -TimerState timer_state_from_string(const char *s) _pure_; - -const char *unit_dependency_to_string(UnitDependency i) _const_; -UnitDependency unit_dependency_from_string(const char *s) _pure_; diff --git a/src/basic/user-util.c b/src/basic/user-util.c deleted file mode 100644 index de6c93056e..0000000000 --- a/src/basic/user-util.c +++ /dev/null @@ -1,636 +0,0 @@ -/*** - 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 <errno.h> -#include <fcntl.h> -#include <grp.h> -#include <pwd.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/stat.h> -#include <unistd.h> -#include <utmp.h> - -#include "alloc-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "formats-util.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "path-util.h" -#include "string-util.h" -#include "strv.h" -#include "user-util.h" -#include "utf8.h" - -bool uid_is_valid(uid_t uid) { - - /* Some libc APIs use UID_INVALID as special placeholder */ - if (uid == (uid_t) UINT32_C(0xFFFFFFFF)) - return false; - - /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */ - if (uid == (uid_t) UINT32_C(0xFFFF)) - return false; - - return true; -} - -int parse_uid(const char *s, uid_t *ret) { - uint32_t uid = 0; - int r; - - assert(s); - - assert_cc(sizeof(uid_t) == sizeof(uint32_t)); - r = safe_atou32(s, &uid); - if (r < 0) - return r; - - if (!uid_is_valid(uid)) - return -ENXIO; /* we return ENXIO instead of EINVAL - * here, to make it easy to distuingish - * invalid numeric uids from invalid - * strings. */ - - if (ret) - *ret = uid; - - return 0; -} - -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 uid_to_name(uid); -} - -char *getusername_malloc(void) { - const char *e; - - e = getenv("USER"); - if (e) - return strdup(e); - - return uid_to_name(getuid()); -} - -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) { - if (!uid_is_valid(p->pw_uid)) - return -EBADMSG; - - *uid = p->pw_uid; - } - - if (gid) { - if (!gid_is_valid(p->pw_gid)) - return -EBADMSG; - - *gid = p->pw_gid; - } - - if (home) - *home = p->pw_dir; - - if (shell) - *shell = p->pw_shell; - - return 0; -} - -int get_user_creds_clean( - const char **username, - uid_t *uid, gid_t *gid, - const char **home, - const char **shell) { - - int r; - - /* Like get_user_creds(), but resets home/shell to NULL if they don't contain anything relevant. */ - - r = get_user_creds(username, uid, gid, home, shell); - if (r < 0) - return r; - - if (shell && - (isempty(*shell) || PATH_IN_SET(*shell, - "/bin/nologin", - "/sbin/nologin", - "/usr/bin/nologin", - "/usr/sbin/nologin"))) - *shell = NULL; - - if (home && - (isempty(*home) || path_equal(*home, "/"))) - *home = NULL; - - return 0; -} - -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) { - if (!gid_is_valid(g->gr_gid)) - return -EBADMSG; - - *gid = g->gr_gid; - } - - return 0; -} - -char* uid_to_name(uid_t uid) { - char *ret; - int r; - - /* Shortcut things to avoid NSS lookups */ - if (uid == 0) - return strdup("root"); - - if (uid_is_valid(uid)) { - long bufsize; - - bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); - if (bufsize <= 0) - bufsize = 4096; - - for (;;) { - struct passwd pwbuf, *pw = NULL; - _cleanup_free_ char *buf = NULL; - - buf = malloc(bufsize); - if (!buf) - return NULL; - - r = getpwuid_r(uid, &pwbuf, buf, (size_t) bufsize, &pw); - if (r == 0 && pw) - return strdup(pw->pw_name); - if (r != ERANGE) - break; - - bufsize *= 2; - } - } - - if (asprintf(&ret, UID_FMT, uid) < 0) - return NULL; - - return ret; -} - -char* gid_to_name(gid_t gid) { - char *ret; - int r; - - if (gid == 0) - return strdup("root"); - - if (gid_is_valid(gid)) { - long bufsize; - - bufsize = sysconf(_SC_GETGR_R_SIZE_MAX); - if (bufsize <= 0) - bufsize = 4096; - - for (;;) { - struct group grbuf, *gr = NULL; - _cleanup_free_ char *buf = NULL; - - buf = malloc(bufsize); - if (!buf) - return NULL; - - r = getgrgid_r(gid, &grbuf, buf, (size_t) bufsize, &gr); - if (r == 0 && gr) - return strdup(gr->gr_name); - if (r != ERANGE) - break; - - bufsize *= 2; - } - } - - if (asprintf(&ret, GID_FMT, gid) < 0) - return NULL; - - return ret; -} - -int in_gid(gid_t gid) { - gid_t *gids; - int ngroups_max, r, i; - - if (getgid() == gid) - return 1; - - if (getegid() == gid) - return 1; - - if (!gid_is_valid(gid)) - return -EINVAL; - - 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 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; -} - -int reset_uid_gid(void) { - int r; - - r = maybe_setgroups(0, NULL); - if (r < 0) - return r; - - if (setresgid(0, 0, 0) < 0) - return -errno; - - if (setresuid(0, 0, 0) < 0) - return -errno; - - return 0; -} - -int take_etc_passwd_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 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 = prefix_roota(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; -} - -bool valid_user_group_name(const char *u) { - const char *i; - long sz; - - /* Checks if the specified name is a valid user/group name. */ - - if (isempty(u)) - return false; - - if (!(u[0] >= 'a' && u[0] <= 'z') && - !(u[0] >= 'A' && u[0] <= 'Z') && - u[0] != '_') - return false; - - for (i = u+1; *i; i++) { - if (!(*i >= 'a' && *i <= 'z') && - !(*i >= 'A' && *i <= 'Z') && - !(*i >= '0' && *i <= '9') && - *i != '_' && - *i != '-') - return false; - } - - sz = sysconf(_SC_LOGIN_NAME_MAX); - assert_se(sz > 0); - - if ((size_t) (i-u) > (size_t) sz) - return false; - - if ((size_t) (i-u) > UT_NAMESIZE - 1) - return false; - - return true; -} - -bool valid_user_group_name_or_id(const char *u) { - - /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the right - * range, and not the invalid user ids. */ - - if (isempty(u)) - return false; - - if (valid_user_group_name(u)) - return true; - - return parse_uid(u, NULL) >= 0; -} - -bool valid_gecos(const char *d) { - - if (!d) - return false; - - if (!utf8_is_valid(d)) - return false; - - if (string_has_cc(d, NULL)) - return false; - - /* Colons are used as field separators, and hence not OK */ - if (strchr(d, ':')) - return false; - - return true; -} - -bool valid_home(const char *p) { - - if (isempty(p)) - return false; - - if (!utf8_is_valid(p)) - return false; - - if (string_has_cc(p, NULL)) - return false; - - if (!path_is_absolute(p)) - return false; - - if (!path_is_safe(p)) - return false; - - /* Colons are used as field separators, and hence not OK */ - if (strchr(p, ':')) - return false; - - return true; -} - -int maybe_setgroups(size_t size, const gid_t *list) { - int r; - - /* Check if setgroups is allowed before we try to drop all the auxiliary groups */ - if (size == 0) { /* Dropping all aux groups? */ - _cleanup_free_ char *setgroups_content = NULL; - bool can_setgroups; - - r = read_one_line_file("/proc/self/setgroups", &setgroups_content); - if (r == -ENOENT) - /* Old kernels don't have /proc/self/setgroups, so assume we can use setgroups */ - can_setgroups = true; - else if (r < 0) - return r; - else - can_setgroups = streq(setgroups_content, "allow"); - - if (!can_setgroups) { - log_debug("Skipping setgroups(), /proc/self/setgroups is set to 'deny'"); - return 0; - } - } - - if (setgroups(size, list) < 0) - return -errno; - - return 0; -} diff --git a/src/basic/user-util.h b/src/basic/user-util.h deleted file mode 100644 index dfea561bde..0000000000 --- a/src/basic/user-util.h +++ /dev/null @@ -1,90 +0,0 @@ -#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 <stdint.h> -#include <sys/types.h> -#include <unistd.h> - -bool uid_is_valid(uid_t uid); - -static inline bool gid_is_valid(gid_t gid) { - return uid_is_valid((uid_t) gid); -} - -int parse_uid(const char *s, uid_t* ret_uid); - -static inline int parse_gid(const char *s, gid_t *ret_gid) { - return parse_uid(s, (uid_t*) ret_gid); -} - -char* getlogname_malloc(void); -char* getusername_malloc(void); - -int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell); -int get_user_creds_clean(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); - -char* uid_to_name(uid_t uid); -char* gid_to_name(gid_t gid); - -int in_gid(gid_t gid); -int in_group(const char *name); - -int get_home_dir(char **ret); -int get_shell(char **_ret); - -int reset_uid_gid(void); - -int take_etc_passwd_lock(const char *root); - -#define UID_INVALID ((uid_t) -1) -#define GID_INVALID ((gid_t) -1) - -/* Let's pick a UIDs within the 16bit range, so that we are compatible with containers using 16bit - * user namespacing. At least on Fedora normal users are allocated until UID 60000, hence do not - * allocate from below this. Also stay away from the upper end of the range as that is often used - * for overflow/nobody users. */ -#define DYNAMIC_UID_MIN ((uid_t) UINT32_C(0x0000EF00)) -#define DYNAMIC_UID_MAX ((uid_t) UINT32_C(0x0000FFEF)) - -static inline bool uid_is_dynamic(uid_t uid) { - return DYNAMIC_UID_MIN <= uid && uid <= DYNAMIC_UID_MAX; -} - -/* 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)) - -static inline bool userns_supported(void) { - return access("/proc/self/uid_map", F_OK) >= 0; -} - -bool valid_user_group_name(const char *u); -bool valid_user_group_name_or_id(const char *u); -bool valid_gecos(const char *d); -bool valid_home(const char *p); - -int maybe_setgroups(size_t size, const gid_t *list); diff --git a/src/basic/utf8.c b/src/basic/utf8.c deleted file mode 100644 index 6eae2b983d..0000000000 --- a/src/basic/utf8.c +++ /dev/null @@ -1,409 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2008-2011 Kay Sievers - Copyright 2012 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/>. -***/ - -/* Parts of this file are based on the GLIB utf8 validation functions. The - * original license text follows. */ - -/* gutf8.c - Operations on UTF-8 strings. - * - * Copyright (C) 1999 Tom Tromey - * Copyright (C) 2000 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include <errno.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> - -#include "alloc-util.h" -#include "hexdecoct.h" -#include "macro.h" -#include "utf8.h" - -bool unichar_is_valid(char32_t ch) { - - if (ch >= 0x110000) /* End of unicode space */ - return false; - if ((ch & 0xFFFFF800) == 0xD800) /* Reserved area for UTF-16 */ - return false; - if ((ch >= 0xFDD0) && (ch <= 0xFDEF)) /* Reserved */ - return false; - if ((ch & 0xFFFE) == 0xFFFE) /* BOM (Byte Order Mark) */ - return false; - - return true; -} - -static bool unichar_is_control(char32_t ch) { - - /* - 0 to ' '-1 is the C0 range. - DEL=0x7F, and DEL+1 to 0x9F is C1 range. - '\t' is in C0 range, but more or less harmless and commonly used. - */ - - return (ch < ' ' && ch != '\t' && ch != '\n') || - (0x7F <= ch && ch <= 0x9F); -} - -/* count of characters used to encode one unicode char */ -static int utf8_encoded_expected_len(const char *str) { - unsigned char c; - - assert(str); - - c = (unsigned char) str[0]; - if (c < 0x80) - return 1; - if ((c & 0xe0) == 0xc0) - return 2; - if ((c & 0xf0) == 0xe0) - return 3; - if ((c & 0xf8) == 0xf0) - return 4; - if ((c & 0xfc) == 0xf8) - return 5; - if ((c & 0xfe) == 0xfc) - return 6; - - return 0; -} - -/* decode one unicode char */ -int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) { - char32_t unichar; - int len, i; - - assert(str); - - len = utf8_encoded_expected_len(str); - - switch (len) { - case 1: - *ret_unichar = (char32_t)str[0]; - return 0; - case 2: - unichar = str[0] & 0x1f; - break; - case 3: - unichar = (char32_t)str[0] & 0x0f; - break; - case 4: - unichar = (char32_t)str[0] & 0x07; - break; - case 5: - unichar = (char32_t)str[0] & 0x03; - break; - case 6: - unichar = (char32_t)str[0] & 0x01; - break; - default: - return -EINVAL; - } - - for (i = 1; i < len; i++) { - if (((char32_t)str[i] & 0xc0) != 0x80) - return -EINVAL; - unichar <<= 6; - unichar |= (char32_t)str[i] & 0x3f; - } - - *ret_unichar = unichar; - - return 0; -} - -bool utf8_is_printable_newline(const char* str, size_t length, bool newline) { - const char *p; - - assert(str); - - for (p = str; length;) { - int encoded_len, r; - char32_t val; - - encoded_len = utf8_encoded_valid_unichar(p); - if (encoded_len < 0 || - (size_t) encoded_len > length) - return false; - - r = utf8_encoded_to_unichar(p, &val); - if (r < 0 || - unichar_is_control(val) || - (!newline && val == '\n')) - return false; - - length -= encoded_len; - p += encoded_len; - } - - return true; -} - -const char *utf8_is_valid(const char *str) { - const uint8_t *p; - - assert(str); - - for (p = (const uint8_t*) str; *p; ) { - int len; - - len = utf8_encoded_valid_unichar((const char *)p); - if (len < 0) - return NULL; - - p += len; - } - - return str; -} - -char *utf8_escape_invalid(const char *str) { - char *p, *s; - - assert(str); - - p = s = malloc(strlen(str) * 4 + 1); - if (!p) - return NULL; - - while (*str) { - int len; - - len = utf8_encoded_valid_unichar(str); - if (len > 0) { - s = mempcpy(s, str, len); - str += len; - } else { - s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER); - str += 1; - } - } - - *s = '\0'; - - return p; -} - -char *utf8_escape_non_printable(const char *str) { - char *p, *s; - - assert(str); - - p = s = malloc(strlen(str) * 4 + 1); - if (!p) - return NULL; - - while (*str) { - int len; - - len = utf8_encoded_valid_unichar(str); - if (len > 0) { - if (utf8_is_printable(str, len)) { - s = mempcpy(s, str, len); - str += len; - } else { - while (len > 0) { - *(s++) = '\\'; - *(s++) = 'x'; - *(s++) = hexchar((int) *str >> 4); - *(s++) = hexchar((int) *str); - - str += 1; - len--; - } - } - } else { - s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER); - str += 1; - } - } - - *s = '\0'; - - return p; -} - -char *ascii_is_valid(const char *str) { - const char *p; - - assert(str); - - for (p = str; *p; p++) - if ((unsigned char) *p >= 128) - return NULL; - - return (char*) str; -} - -/** - * utf8_encode_unichar() - Encode single UCS-4 character as UTF-8 - * @out_utf8: output buffer of at least 4 bytes or NULL - * @g: UCS-4 character to encode - * - * This encodes a single UCS-4 character as UTF-8 and writes it into @out_utf8. - * The length of the character is returned. It is not zero-terminated! If the - * output buffer is NULL, only the length is returned. - * - * Returns: The length in bytes that the UTF-8 representation does or would - * occupy. - */ -size_t utf8_encode_unichar(char *out_utf8, char32_t g) { - - if (g < (1 << 7)) { - if (out_utf8) - out_utf8[0] = g & 0x7f; - return 1; - } else if (g < (1 << 11)) { - if (out_utf8) { - out_utf8[0] = 0xc0 | ((g >> 6) & 0x1f); - out_utf8[1] = 0x80 | (g & 0x3f); - } - return 2; - } else if (g < (1 << 16)) { - if (out_utf8) { - out_utf8[0] = 0xe0 | ((g >> 12) & 0x0f); - out_utf8[1] = 0x80 | ((g >> 6) & 0x3f); - out_utf8[2] = 0x80 | (g & 0x3f); - } - return 3; - } else if (g < (1 << 21)) { - if (out_utf8) { - out_utf8[0] = 0xf0 | ((g >> 18) & 0x07); - out_utf8[1] = 0x80 | ((g >> 12) & 0x3f); - out_utf8[2] = 0x80 | ((g >> 6) & 0x3f); - out_utf8[3] = 0x80 | (g & 0x3f); - } - return 4; - } - - return 0; -} - -char *utf16_to_utf8(const void *s, size_t length) { - const uint8_t *f; - char *r, *t; - - r = new(char, (length * 4 + 1) / 2 + 1); - if (!r) - return NULL; - - f = s; - t = r; - - while (f < (const uint8_t*) s + length) { - char16_t w1, w2; - - /* see RFC 2781 section 2.2 */ - - w1 = f[1] << 8 | f[0]; - f += 2; - - if (!utf16_is_surrogate(w1)) { - t += utf8_encode_unichar(t, w1); - - continue; - } - - if (utf16_is_trailing_surrogate(w1)) - continue; - else if (f >= (const uint8_t*) s + length) - break; - - w2 = f[1] << 8 | f[0]; - f += 2; - - if (!utf16_is_trailing_surrogate(w2)) { - f -= 2; - continue; - } - - t += utf8_encode_unichar(t, utf16_surrogate_pair_to_unichar(w1, w2)); - } - - *t = 0; - return r; -} - -/* expected size used to encode one unicode char */ -static int utf8_unichar_to_encoded_len(char32_t unichar) { - - if (unichar < 0x80) - return 1; - if (unichar < 0x800) - return 2; - if (unichar < 0x10000) - return 3; - if (unichar < 0x200000) - return 4; - if (unichar < 0x4000000) - return 5; - - return 6; -} - -/* validate one encoded unicode char and return its length */ -int utf8_encoded_valid_unichar(const char *str) { - int len, i, r; - char32_t unichar; - - assert(str); - - len = utf8_encoded_expected_len(str); - if (len == 0) - return -EINVAL; - - /* ascii is valid */ - if (len == 1) - return 1; - - /* check if expected encoded chars are available */ - for (i = 0; i < len; i++) - if ((str[i] & 0x80) != 0x80) - return -EINVAL; - - r = utf8_encoded_to_unichar(str, &unichar); - if (r < 0) - return r; - - /* check if encoded length matches encoded value */ - if (utf8_unichar_to_encoded_len(unichar) != len) - return -EINVAL; - - /* check if value has valid range */ - if (!unichar_is_valid(unichar)) - return -EINVAL; - - return len; -} diff --git a/src/basic/utf8.h b/src/basic/utf8.h deleted file mode 100644 index f9b9c9468b..0000000000 --- a/src/basic/utf8.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2012 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 <stddef.h> -#include <stdint.h> -#include <uchar.h> - -#include "macro.h" -#include "missing.h" - -#define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd" -#define UTF8_BYTE_ORDER_MARK "\xef\xbb\xbf" - -bool unichar_is_valid(char32_t c); - -const char *utf8_is_valid(const char *s) _pure_; -char *ascii_is_valid(const char *s) _pure_; - -bool utf8_is_printable_newline(const char* str, size_t length, bool newline) _pure_; -#define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true) - -char *utf8_escape_invalid(const char *s); -char *utf8_escape_non_printable(const char *str); - -size_t utf8_encode_unichar(char *out_utf8, char32_t g); -char *utf16_to_utf8(const void *s, size_t length); - -int utf8_encoded_valid_unichar(const char *str); -int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar); - -static inline bool utf16_is_surrogate(char16_t c) { - return (0xd800 <= c && c <= 0xdfff); -} - -static inline bool utf16_is_trailing_surrogate(char16_t c) { - return (0xdc00 <= c && c <= 0xdfff); -} - -static inline char32_t utf16_surrogate_pair_to_unichar(char16_t lead, char16_t trail) { - return ((lead - 0xd800) << 10) + (trail - 0xdc00) + 0x10000; -} diff --git a/src/basic/util.c b/src/basic/util.c deleted file mode 100644 index ec7939dc83..0000000000 --- a/src/basic/util.c +++ /dev/null @@ -1,876 +0,0 @@ -/*** - 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 <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <sched.h> -#include <signal.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/prctl.h> -#include <sys/statfs.h> -#include <sys/sysmacros.h> -#include <sys/types.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "build.h" -#include "cgroup-util.h" -#include "def.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "formats-util.h" -#include "hashmap.h" -#include "hostname-util.h" -#include "log.h" -#include "macro.h" -#include "missing.h" -#include "parse-util.h" -#include "path-util.h" -#include "process-util.h" -#include "set.h" -#include "signal-util.h" -#include "stat-util.h" -#include "string-util.h" -#include "strv.h" -#include "time-util.h" -#include "umask-util.h" -#include "user-util.h" -#include "util.h" - -/* Put this test here for a lack of better place */ -assert_cc(EAGAIN == EWOULDBLOCK); - -int saved_argc = 0; -char **saved_argv = NULL; -static int saved_in_initrd = -1; - -size_t page_size(void) { - static thread_local size_t pgsz = 0; - long r; - - if (_likely_(pgsz > 0)) - return pgsz; - - r = sysconf(_SC_PAGESIZE); - assert(r > 0); - - pgsz = (size_t) r; - return pgsz; -} - -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; - char **directory; - - /* We fork this all off from a child process so that we can - * somewhat cleanly make use of SIGALRM to set a time limit */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - - pids = hashmap_new(NULL); - if (!pids) - return log_oom(); - - seen = set_new(&string_hash_ops); - if (!seen) - return log_oom(); - - STRV_FOREACH(directory, directories) { - _cleanup_closedir_ DIR *d; - struct dirent *de; - - d = opendir(*directory); - if (!d) { - if (errno == ENOENT) - continue; - - return log_error_errno(errno, "Failed to open directory %s: %m", *directory); - } - - FOREACH_DIRENT(de, d, break) { - _cleanup_free_ char *path = NULL; - pid_t pid; - int r; - - if (!dirent_is_file(de)) - continue; - - if (set_contains(seen, de->d_name)) { - log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name); - continue; - } - - r = set_put_strdup(seen, de->d_name); - if (r < 0) - return log_oom(); - - path = strjoin(*directory, "/", de->d_name, NULL); - if (!path) - return log_oom(); - - if (null_or_empty_path(path)) { - log_debug("%s is empty (a mask).", path); - continue; - } - - pid = fork(); - if (pid < 0) { - log_error_errno(errno, "Failed to fork: %m"); - continue; - } else if (pid == 0) { - char *_argv[2]; - - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - - if (!argv) { - _argv[0] = path; - _argv[1] = NULL; - argv = _argv; - } else - argv[0] = path; - - execv(path, argv); - return log_error_errno(errno, "Failed to execute %s: %m", path); - } - - log_debug("Spawned %s as " PID_FMT ".", path, pid); - - r = hashmap_put(pids, PID_TO_PTR(pid), path); - if (r < 0) - return log_oom(); - path = NULL; - } - } - - /* Abort execution of this process after the timout. We simply - * rely on SIGALRM as default action terminating the process, - * and turn on alarm(). */ - - if (timeout != USEC_INFINITY) - alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC); - - while (!hashmap_isempty(pids)) { - _cleanup_free_ char *path = NULL; - pid_t pid; - - pid = PTR_TO_PID(hashmap_first_key(pids)); - assert(pid > 0); - - path = hashmap_remove(pids, PID_TO_PTR(pid)); - assert(path); - - wait_for_terminate_and_warn(path, pid, true); - } - - return 0; -} - -void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) { - pid_t executor_pid; - int r; - char *name; - char **dirs = (char**) directories; - - assert(!strv_isempty(dirs)); - - name = basename(dirs[0]); - assert(!isempty(name)); - - /* Executes all binaries in the directories in parallel and waits - * for them to finish. Optionally a timeout is applied. If a file - * with the same name exists in more than one directory, the - * earliest one wins. */ - - executor_pid = fork(); - if (executor_pid < 0) { - log_error_errno(errno, "Failed to fork: %m"); - return; - - } else if (executor_pid == 0) { - r = do_execute(dirs, timeout, argv); - _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); - } - - wait_for_terminate_and_warn(name, executor_pid, true); -} - -bool plymouth_running(void) { - return access("/run/plymouth/pid", F_OK) >= 0; -} - -bool display_is_local(const char *display) { - assert(display); - - return - display[0] == ':' && - display[1] >= '0' && - display[1] <= '9'; -} - -int socket_from_display(const char *display, char **path) { - size_t k; - char *f, *c; - - assert(display); - assert(path); - - if (!display_is_local(display)) - return -EINVAL; - - k = strspn(display+1, "0123456789"); - - f = new(char, strlen("/tmp/.X11-unix/X") + k + 1); - if (!f) - return -ENOMEM; - - c = stpcpy(f, "/tmp/.X11-unix/X"); - memcpy(c, display+1, k); - c[k] = 0; - - *path = f; - - return 0; -} - -int block_get_whole_disk(dev_t d, dev_t *ret) { - char *p, *s; - int r; - unsigned n, m; - - assert(ret); - - /* If it has a queue this is good enough for us */ - if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0) - return -ENOMEM; - - r = access(p, F_OK); - free(p); - - if (r >= 0) { - *ret = d; - return 0; - } - - /* If it is a partition find the originating device */ - if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0) - return -ENOMEM; - - r = access(p, F_OK); - free(p); - - if (r < 0) - return -ENOENT; - - /* Get parent dev_t */ - if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0) - return -ENOMEM; - - r = read_one_line_file(p, &s); - free(p); - - if (r < 0) - return r; - - r = sscanf(s, "%u:%u", &m, &n); - free(s); - - if (r != 2) - return -EINVAL; - - /* Only return this if it is really good enough for us. */ - if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0) - return -ENOMEM; - - r = access(p, F_OK); - free(p); - - if (r >= 0) { - *ret = makedev(m, n); - return 0; - } - - return -ENOENT; -} - -bool kexec_loaded(void) { - bool loaded = false; - char *s; - - if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) { - if (s[0] == '1') - loaded = true; - free(s); - } - return loaded; -} - -int prot_from_flags(int flags) { - - switch (flags & O_ACCMODE) { - - case O_RDONLY: - return PROT_READ; - - case O_WRONLY: - return PROT_WRITE; - - case O_RDWR: - return PROT_READ|PROT_WRITE; - - default: - return -EINVAL; - } -} - -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; - sigset_t ss, saved_ss; - unsigned n, i; - va_list ap; - char **l; - - assert(pid); - assert(path); - - /* Spawns a temporary TTY agent, making sure it goes away when - * we go away */ - - parent_pid = getpid(); - - /* First we temporarily block all signals, so that the new - * child has them blocked initially. This way, we can be sure - * that SIGTERMs are not lost we might send to the agent. */ - assert_se(sigfillset(&ss) >= 0); - assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0); - - agent_pid = fork(); - if (agent_pid < 0) { - assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0); - return -errno; - } - - if (agent_pid != 0) { - assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0); - *pid = agent_pid; - return 0; - } - - /* In the child: - * - * Make sure the agent goes away when the parent dies */ - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) - _exit(EXIT_FAILURE); - - /* Make sure we actually can kill the agent, if we need to, in - * case somebody invoked us from a shell script that trapped - * SIGTERM or so... */ - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - /* Check whether our parent died before we were able - * to set the death signal and unblock the signals */ - if (getppid() != parent_pid) - _exit(EXIT_SUCCESS); - - /* Don't leak fds to the agent */ - close_all_fds(except, n_except); - - stdout_is_tty = isatty(STDOUT_FILENO); - stderr_is_tty = isatty(STDERR_FILENO); - - if (!stdout_is_tty || !stderr_is_tty) { - int fd; - - /* Detach from stdout/stderr. and reopen - * /dev/tty for them. This is important to - * ensure that when systemctl is started via - * popen() or a similar call that expects to - * read EOF we actually do generate EOF and - * not delay this indefinitely by because we - * keep an unused copy of stdin around. */ - fd = open("/dev/tty", O_WRONLY); - if (fd < 0) { - log_error_errno(errno, "Failed to open /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) { - log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) { - log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - if (fd > STDERR_FILENO) - close(fd); - } - - /* Count arguments */ - va_start(ap, path); - for (n = 0; va_arg(ap, char*); n++) - ; - va_end(ap); - - /* Allocate strv */ - l = alloca(sizeof(char *) * (n + 1)); - - /* Fill in arguments */ - va_start(ap, path); - for (i = 0; i <= n; i++) - l[i] = va_arg(ap, char*); - va_end(ap); - - execv(path, l); - _exit(EXIT_FAILURE); -} - -bool in_initrd(void) { - struct statfs s; - - if (saved_in_initrd >= 0) - return saved_in_initrd; - - /* We make two checks here: - * - * 1. the flag file /etc/initrd-release must exist - * 2. the root file system must be a memory file system - * - * The second check is extra paranoia, since misdetecting an - * initrd can have bad consequences due the initrd - * emptying when transititioning to the main systemd. - */ - - saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 && - statfs("/", &s) >= 0 && - is_temporary_fs(&s); - - return saved_in_initrd; -} - -void in_initrd_force(bool value) { - saved_in_initrd = value; -} - -/* hey glibc, APIs with callbacks without a user pointer are so useless */ -void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, - int (*compar) (const void *, const void *, void *), void *arg) { - size_t l, u, idx; - const void *p; - int comparison; - - l = 0; - u = nmemb; - while (l < u) { - idx = (l + u) / 2; - p = (void *)(((const char *) base) + (idx * size)); - comparison = compar(key, p, arg); - if (comparison < 0) - u = idx; - else if (comparison > 0) - l = idx + 1; - else - return (void *)p; - } - return NULL; -} - -int on_ac_power(void) { - bool found_offline = false, found_online = false; - _cleanup_closedir_ DIR *d = NULL; - - d = opendir("/sys/class/power_supply"); - if (!d) - return errno == ENOENT ? true : -errno; - - for (;;) { - struct dirent *de; - _cleanup_close_ int fd = -1, device = -1; - char contents[6]; - ssize_t n; - - errno = 0; - de = readdir(d); - if (!de && errno > 0) - return -errno; - - if (!de) - break; - - if (hidden_or_backup_file(de->d_name)) - continue; - - device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (device < 0) { - if (errno == ENOENT || errno == ENOTDIR) - continue; - - return -errno; - } - - fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - if (errno == ENOENT) - continue; - - return -errno; - } - - n = read(fd, contents, sizeof(contents)); - if (n < 0) - return -errno; - - if (n != 6 || memcmp(contents, "Mains\n", 6)) - continue; - - safe_close(fd); - fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - if (errno == ENOENT) - continue; - - return -errno; - } - - n = read(fd, contents, sizeof(contents)); - if (n < 0) - return -errno; - - if (n != 2 || contents[1] != '\n') - return -EIO; - - if (contents[0] == '1') { - found_online = true; - break; - } else if (contents[0] == '0') - found_offline = true; - else - return -EIO; - } - - return found_online || !found_offline; -} - -int container_get_leader(const char *machine, pid_t *pid) { - _cleanup_free_ char *s = NULL, *class = NULL; - const char *p; - pid_t leader; - int r; - - assert(machine); - assert(pid); - - if (!machine_name_is_valid(machine)) - return -EINVAL; - - p = strjoina("/run/systemd/machines/", machine); - r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL); - if (r == -ENOENT) - return -EHOSTDOWN; - if (r < 0) - return r; - if (!s) - return -EIO; - - if (!streq_ptr(class, "container")) - return -EIO; - - r = parse_pid(s, &leader); - if (r < 0) - return r; - if (leader <= 1) - return -EIO; - - *pid = leader; - return 0; -} - -int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) { - _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1; - int rfd = -1; - - assert(pid >= 0); - - if (mntns_fd) { - const char *mntns; - - mntns = procfs_file_alloca(pid, "ns/mnt"); - mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (mntnsfd < 0) - return -errno; - } - - if (pidns_fd) { - const char *pidns; - - pidns = procfs_file_alloca(pid, "ns/pid"); - pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (pidnsfd < 0) - return -errno; - } - - if (netns_fd) { - const char *netns; - - netns = procfs_file_alloca(pid, "ns/net"); - netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (netnsfd < 0) - return -errno; - } - - if (userns_fd) { - const char *userns; - - userns = procfs_file_alloca(pid, "ns/user"); - usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (usernsfd < 0 && errno != ENOENT) - return -errno; - } - - if (root_fd) { - const char *root; - - root = procfs_file_alloca(pid, "root"); - rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (rfd < 0) - return -errno; - } - - if (pidns_fd) - *pidns_fd = pidnsfd; - - if (mntns_fd) - *mntns_fd = mntnsfd; - - if (netns_fd) - *netns_fd = netnsfd; - - if (userns_fd) - *userns_fd = usernsfd; - - if (root_fd) - *root_fd = rfd; - - pidnsfd = mntnsfd = netnsfd = usernsfd = -1; - - return 0; -} - -int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) { - if (userns_fd >= 0) { - /* Can't setns to your own userns, since then you could - * escalate from non-root to root in your own namespace, so - * check if namespaces equal before attempting to enter. */ - _cleanup_free_ char *userns_fd_path = NULL; - int r; - if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0) - return -ENOMEM; - - r = files_same(userns_fd_path, "/proc/self/ns/user"); - if (r < 0) - return r; - if (r) - userns_fd = -1; - } - - if (pidns_fd >= 0) - if (setns(pidns_fd, CLONE_NEWPID) < 0) - return -errno; - - if (mntns_fd >= 0) - if (setns(mntns_fd, CLONE_NEWNS) < 0) - return -errno; - - if (netns_fd >= 0) - if (setns(netns_fd, CLONE_NEWNET) < 0) - return -errno; - - if (userns_fd >= 0) - if (setns(userns_fd, CLONE_NEWUSER) < 0) - return -errno; - - if (root_fd >= 0) { - if (fchdir(root_fd) < 0) - return -errno; - - if (chroot(".") < 0) - return -errno; - } - - return reset_uid_gid(); -} - -uint64_t physical_memory(void) { - _cleanup_free_ char *root = NULL, *value = NULL; - uint64_t mem, lim; - size_t ps; - long sc; - - /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of - * memory. - * - * In order to support containers nicely that have a configured memory limit we'll take the minimum of the - * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */ - - sc = sysconf(_SC_PHYS_PAGES); - assert(sc > 0); - - ps = page_size(); - mem = (uint64_t) sc * (uint64_t) ps; - - if (cg_get_root_path(&root) < 0) - return mem; - - if (cg_get_attribute("memory", root, "memory.limit_in_bytes", &value)) - return mem; - - if (safe_atou64(value, &lim) < 0) - return mem; - - /* Make sure the limit is a multiple of our own page size */ - lim /= ps; - lim *= ps; - - return MIN(mem, lim); -} - -uint64_t physical_memory_scale(uint64_t v, uint64_t max) { - uint64_t p, m, ps, r; - - assert(max > 0); - - /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success - * the result is a multiple of the page size (rounds down). */ - - ps = page_size(); - assert(ps > 0); - - p = physical_memory() / ps; - assert(p > 0); - - m = p * v; - if (m / p != v) - return UINT64_MAX; - - m /= max; - - r = m * ps; - if (r / ps != m) - return UINT64_MAX; - - return r; -} - -uint64_t system_tasks_max(void) { - -#if SIZEOF_PID_T == 4 -#define TASKS_MAX ((uint64_t) (INT32_MAX-1)) -#elif SIZEOF_PID_T == 2 -#define TASKS_MAX ((uint64_t) (INT16_MAX-1)) -#else -#error "Unknown pid_t size" -#endif - - _cleanup_free_ char *value = NULL, *root = NULL; - uint64_t a = TASKS_MAX, b = TASKS_MAX; - - /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this - * limit: - * - * a) the maximum value for the pid_t type - * b) the cgroups pids_max attribute for the system - * c) the kernel's configure maximum PID value - * - * And then pick the smallest of the three */ - - if (read_one_line_file("/proc/sys/kernel/pid_max", &value) >= 0) - (void) safe_atou64(value, &a); - - if (cg_get_root_path(&root) >= 0) { - value = mfree(value); - - if (cg_get_attribute("pids", root, "pids.max", &value) >= 0) - (void) safe_atou64(value, &b); - } - - return MIN3(TASKS_MAX, - a <= 0 ? TASKS_MAX : a, - b <= 0 ? TASKS_MAX : b); -} - -uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) { - uint64_t t, m; - - assert(max > 0); - - /* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages - * relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */ - - t = system_tasks_max(); - assert(t > 0); - - m = t * v; - if (m / t != v) /* overflow? */ - return UINT64_MAX; - - return m / max; -} - -int update_reboot_parameter_and_warn(const char *param) { - int r; - - if (isempty(param)) { - if (unlink("/run/systemd/reboot-param") < 0) { - if (errno == ENOENT) - return 0; - - return log_warning_errno(errno, "Failed to unlink reboot parameter file: %m"); - } - - return 0; - } - - RUN_WITH_UMASK(0022) { - r = write_string_file("/run/systemd/reboot-param", param, WRITE_STRING_FILE_CREATE); - if (r < 0) - return log_warning_errno(r, "Failed to write reboot parameter file: %m"); - } - - return 0; -} - -int version(void) { - puts(PACKAGE_STRING "\n" - SYSTEMD_FEATURES); - return 0; -} diff --git a/src/basic/util.h b/src/basic/util.h deleted file mode 100644 index bb2fc318ef..0000000000 --- a/src/basic/util.h +++ /dev/null @@ -1,196 +0,0 @@ -#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 <errno.h> -#include <fcntl.h> -#include <inttypes.h> -#include <limits.h> -#include <locale.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/inotify.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/statfs.h> -#include <sys/sysmacros.h> -#include <sys/types.h> -#include <time.h> -#include <unistd.h> - -#include "formats-util.h" -#include "macro.h" -#include "missing.h" -#include "time-util.h" - -size_t page_size(void) _pure_; -#define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) - -static inline const char* yes_no(bool b) { - return b ? "yes" : "no"; -} - -static inline const char* true_false(bool b) { - return b ? "true" : "false"; -} - -static inline const char* one_zero(bool b) { - return b ? "1" : "0"; -} - -static inline const char* enable_disable(bool b) { - return b ? "enable" : "disable"; -} - -void execute_directories(const char* const* directories, usec_t timeout, char *argv[]); - -bool plymouth_running(void); - -bool display_is_local(const char *display) _pure_; -int socket_from_display(const char *display, char **path); - -int block_get_whole_disk(dev_t d, dev_t *ret); - -#define NULSTR_FOREACH(i, l) \ - for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) - -#define NULSTR_FOREACH_PAIR(i, j, l) \ - for ((i) = (l), (j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i)) - -extern int saved_argc; -extern char **saved_argv; - -bool kexec_loaded(void); - -int prot_from_flags(int flags) _const_; - -int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...); - -bool in_initrd(void); -void in_initrd_force(bool value); - -void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, - int (*compar) (const void *, const void *, void *), - void *arg); - -/** - * 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 memcpy requires src to be nonnull. We do nothing if n is 0. - */ -static inline void memcpy_safe(void *dst, const void *src, size_t n) { - if (n == 0) - return; - assert(src); - memcpy(dst, src, n); -} - -int on_ac_power(void); - -#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; -} - -static inline void _reset_errno_(int *saved_errno) { - errno = *saved_errno; -} - -#define PROTECT_ERRNO _cleanup_(_reset_errno_) __attribute__((unused)) int _saved_errno_ = errno - -static inline int negative_errno(void) { - /* This helper should be used to shut up gcc if you know 'errno' is - * negative. Instead of "return -errno;", use "return negative_errno();" - * It will suppress bogus gcc warnings in case it assumes 'errno' might - * be 0 and thus the caller's error-handling might not be triggered. */ - assert_return(errno > 0, -EINVAL); - return -errno; -} - -static inline unsigned u64log2(uint64_t n) { -#if __SIZEOF_LONG_LONG__ == 8 - return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0; -#else -#error "Wut?" -#endif -} - -static inline unsigned u32ctz(uint32_t n) { -#if __SIZEOF_INT__ == 4 - return __builtin_ctz(n); -#else -#error "Wut?" -#endif -} - -static inline unsigned log2i(int x) { - assert(x > 0); - - return __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1; -} - -static inline unsigned log2u(unsigned x) { - assert(x > 0); - - return sizeof(unsigned) * 8 - __builtin_clz(x) - 1; -} - -static inline unsigned log2u_round_up(unsigned x) { - assert(x > 0); - - if (x == 1) - return 0; - - return log2u(x - 1) + 1; -} - -int container_get_leader(const char *machine, pid_t *pid); - -int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd); -int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd); - -uint64_t physical_memory(void); -uint64_t physical_memory_scale(uint64_t v, uint64_t max); - -uint64_t system_tasks_max(void); -uint64_t system_tasks_max_scale(uint64_t v, uint64_t max); - -int update_reboot_parameter_and_warn(const char *param); - -int version(void); diff --git a/src/basic/verbs.c b/src/basic/verbs.c deleted file mode 100644 index d9cdb38d65..0000000000 --- a/src/basic/verbs.c +++ /dev/null @@ -1,101 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 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 <errno.h> -#include <getopt.h> -#include <stdbool.h> -#include <stddef.h> - -#include "log.h" -#include "macro.h" -#include "string-util.h" -#include "verbs.h" -#include "virt.h" - -int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) { - const Verb *verb; - const char *name; - unsigned i; - int left; - - assert(verbs); - assert(verbs[0].dispatch); - assert(argc >= 0); - assert(argv); - assert(argc >= optind); - - left = argc - optind; - name = argv[optind]; - - for (i = 0;; i++) { - bool found; - - /* At the end of the list? */ - if (!verbs[i].dispatch) { - if (name) - log_error("Unknown operation %s.", name); - else - log_error("Requires operation parameter."); - return -EINVAL; - } - - if (name) - found = streq(name, verbs[i].verb); - else - found = !!(verbs[i].flags & VERB_DEFAULT); - - if (found) { - verb = &verbs[i]; - break; - } - } - - assert(verb); - - if (!name) - left = 1; - - if (verb->min_args != VERB_ANY && - (unsigned) left < verb->min_args) { - log_error("Too few arguments."); - return -EINVAL; - } - - if (verb->max_args != VERB_ANY && - (unsigned) left > verb->max_args) { - log_error("Too many arguments."); - return -EINVAL; - } - - if ((verb->flags & VERB_NOCHROOT) && running_in_chroot() > 0) { - log_info("Running in chroot, ignoring request."); - return 0; - } - - if (name) - return verb->dispatch(left, argv + optind, userdata); - else { - char* fake[2] = { - (char*) verb->verb, - NULL - }; - - return verb->dispatch(1, fake, userdata); - } -} diff --git a/src/basic/verbs.h b/src/basic/verbs.h deleted file mode 100644 index 7b5e18510f..0000000000 --- a/src/basic/verbs.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 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/>. -***/ - -#define VERB_ANY ((unsigned) -1) -#define VERB_DEFAULT 1U -#define VERB_NOCHROOT 2U - -typedef struct { - const char *verb; - unsigned min_args, max_args; - unsigned flags; - int (* const dispatch)(int argc, char *argv[], void *userdata); -} Verb; - -int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata); diff --git a/src/basic/virt.c b/src/basic/virt.c deleted file mode 100644 index d8d57381ad..0000000000 --- a/src/basic/virt.c +++ /dev/null @@ -1,595 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 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 <errno.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "macro.h" -#include "process-util.h" -#include "stat-util.h" -#include "string-table.h" -#include "string-util.h" -#include "virt.h" -#include "env-util.h" - -static int detect_vm_cpuid(void) { - - /* CPUID is an x86 specific interface. */ -#if defined(__i386__) || defined(__x86_64__) - - static const struct { - const char *cpuid; - int id; - } cpuid_vendor_table[] = { - { "XenVMMXenVMM", VIRTUALIZATION_XEN }, - { "KVMKVMKVM", VIRTUALIZATION_KVM }, - /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ - { "VMwareVMware", VIRTUALIZATION_VMWARE }, - /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */ - { "Microsoft Hv", VIRTUALIZATION_MICROSOFT }, - /* https://wiki.freebsd.org/bhyve */ - { "bhyve bhyve ", VIRTUALIZATION_BHYVE }, - }; - - uint32_t eax, ecx; - bool hypervisor; - - /* http://lwn.net/Articles/301888/ */ - -#if defined (__i386__) -#define REG_a "eax" -#define REG_b "ebx" -#elif defined (__amd64__) -#define REG_a "rax" -#define REG_b "rbx" -#endif - - /* First detect whether there is a hypervisor */ - eax = 1; - __asm__ __volatile__ ( - /* ebx/rbx is being used for PIC! */ - " push %%"REG_b" \n\t" - " cpuid \n\t" - " pop %%"REG_b" \n\t" - - : "=a" (eax), "=c" (ecx) - : "0" (eax) - ); - - hypervisor = !!(ecx & 0x80000000U); - - if (hypervisor) { - union { - uint32_t sig32[3]; - char text[13]; - } sig = {}; - unsigned j; - - /* There is a hypervisor, see what it is */ - eax = 0x40000000U; - __asm__ __volatile__ ( - /* ebx/rbx is being used for PIC! */ - " push %%"REG_b" \n\t" - " cpuid \n\t" - " mov %%ebx, %1 \n\t" - " pop %%"REG_b" \n\t" - - : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2]) - : "0" (eax) - ); - - log_debug("Virtualization found, CPUID=%s", sig.text); - - for (j = 0; j < ELEMENTSOF(cpuid_vendor_table); j ++) - if (streq(sig.text, cpuid_vendor_table[j].cpuid)) - return cpuid_vendor_table[j].id; - - return VIRTUALIZATION_VM_OTHER; - } -#endif - log_debug("No virtualization found in CPUID"); - - return VIRTUALIZATION_NONE; -} - -static int detect_vm_device_tree(void) { -#if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__) || defined(__powerpc64__) - _cleanup_free_ char *hvtype = NULL; - int r; - - r = read_one_line_file("/proc/device-tree/hypervisor/compatible", &hvtype); - if (r == -ENOENT) { - _cleanup_closedir_ DIR *dir = NULL; - struct dirent *dent; - - dir = opendir("/proc/device-tree"); - if (!dir) { - if (errno == ENOENT) { - log_debug_errno(errno, "/proc/device-tree: %m"); - return VIRTUALIZATION_NONE; - } - return -errno; - } - - FOREACH_DIRENT(dent, dir, return -errno) - if (strstr(dent->d_name, "fw-cfg")) { - log_debug("Virtualization QEMU: \"fw-cfg\" present in /proc/device-tree/%s", dent->d_name); - return VIRTUALIZATION_QEMU; - } - - log_debug("No virtualization found in /proc/device-tree/*"); - return VIRTUALIZATION_NONE; - } else if (r < 0) - return r; - - log_debug("Virtualization %s found in /proc/device-tree/hypervisor/compatible", hvtype); - if (streq(hvtype, "linux,kvm")) - return VIRTUALIZATION_KVM; - else if (strstr(hvtype, "xen")) - return VIRTUALIZATION_XEN; - else - return VIRTUALIZATION_VM_OTHER; -#else - log_debug("This platform does not support /proc/device-tree"); - return VIRTUALIZATION_NONE; -#endif -} - -static int detect_vm_dmi(void) { -#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) - - static const char *const dmi_vendors[] = { - "/sys/class/dmi/id/product_name", /* Test this before sys_vendor to detect KVM over QEMU */ - "/sys/class/dmi/id/sys_vendor", - "/sys/class/dmi/id/board_vendor", - "/sys/class/dmi/id/bios_vendor" - }; - - static const struct { - const char *vendor; - int id; - } dmi_vendor_table[] = { - { "KVM", VIRTUALIZATION_KVM }, - { "QEMU", VIRTUALIZATION_QEMU }, - /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ - { "VMware", VIRTUALIZATION_VMWARE }, - { "VMW", VIRTUALIZATION_VMWARE }, - { "innotek GmbH", VIRTUALIZATION_ORACLE }, - { "Xen", VIRTUALIZATION_XEN }, - { "Bochs", VIRTUALIZATION_BOCHS }, - { "Parallels", VIRTUALIZATION_PARALLELS }, - /* https://wiki.freebsd.org/bhyve */ - { "BHYVE", VIRTUALIZATION_BHYVE }, - }; - unsigned i; - int r; - - for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) { - _cleanup_free_ char *s = NULL; - unsigned j; - - r = read_one_line_file(dmi_vendors[i], &s); - if (r < 0) { - if (r == -ENOENT) - continue; - - return r; - } - - - - for (j = 0; j < ELEMENTSOF(dmi_vendor_table); j++) - if (startswith(s, dmi_vendor_table[j].vendor)) { - log_debug("Virtualization %s found in DMI (%s)", s, dmi_vendors[i]); - return dmi_vendor_table[j].id; - } - } -#endif - - log_debug("No virtualization found in DMI"); - - return VIRTUALIZATION_NONE; -} - -static int detect_vm_xen(void) { - /* Check for Dom0 will be executed later in detect_vm_xen_dom0 - Thats why we dont check the content of /proc/xen/capabilities here. */ - if (access("/proc/xen/capabilities", F_OK) < 0) { - log_debug("Virtualization XEN not found, /proc/xen/capabilities does not exist"); - return VIRTUALIZATION_NONE; - } - - log_debug("Virtualization XEN found (/proc/xen/capabilities exists)"); - return VIRTUALIZATION_XEN; - -} - -static bool detect_vm_xen_dom0(void) { - _cleanup_free_ char *domcap = NULL; - char *cap, *i; - int r; - - r = read_one_line_file("/proc/xen/capabilities", &domcap); - if (r == -ENOENT) { - log_debug("Virtualization XEN not found, /proc/xen/capabilities does not exist"); - return false; - } - if (r < 0) - return r; - - i = domcap; - while ((cap = strsep(&i, ","))) - if (streq(cap, "control_d")) - break; - if (!cap) { - log_debug("Virtualization XEN DomU found (/proc/xen/capabilites)"); - return false; - } - - log_debug("Virtualization XEN Dom0 ignored (/proc/xen/capabilities)"); - return true; -} - -static int detect_vm_hypervisor(void) { - _cleanup_free_ char *hvtype = NULL; - int r; - - r = read_one_line_file("/sys/hypervisor/type", &hvtype); - if (r == -ENOENT) - return VIRTUALIZATION_NONE; - if (r < 0) - return r; - - log_debug("Virtualization %s found in /sys/hypervisor/type", hvtype); - - if (streq(hvtype, "xen")) - return VIRTUALIZATION_XEN; - else - return VIRTUALIZATION_VM_OTHER; -} - -static int detect_vm_uml(void) { - _cleanup_free_ char *cpuinfo_contents = NULL; - int r; - - /* Detect User-Mode Linux by reading /proc/cpuinfo */ - r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL); - if (r < 0) - return r; - - if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) { - log_debug("UML virtualization found in /proc/cpuinfo"); - return VIRTUALIZATION_UML; - } - - log_debug("No virtualization found in /proc/cpuinfo."); - return VIRTUALIZATION_NONE; -} - -static int detect_vm_zvm(void) { - -#if defined(__s390__) - _cleanup_free_ char *t = NULL; - int r; - - r = get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE, &t); - if (r == -ENOENT) - return VIRTUALIZATION_NONE; - if (r < 0) - return r; - - log_debug("Virtualization %s found in /proc/sysinfo", t); - if (streq(t, "z/VM")) - return VIRTUALIZATION_ZVM; - else - return VIRTUALIZATION_KVM; -#else - log_debug("This platform does not support /proc/sysinfo"); - return VIRTUALIZATION_NONE; -#endif -} - -/* Returns a short identifier for the various VM implementations */ -int detect_vm(void) { - static thread_local int cached_found = _VIRTUALIZATION_INVALID; - int r; - - if (cached_found >= 0) - return cached_found; - - /* We have to use the correct order here: - * Some virtualization technologies do use KVM hypervisor but are - * expected to be detected as something else. So detect DMI first. - * - * An example is Virtualbox since version 5.0, which uses KVM backend. - * Detection via DMI works corretly, the CPU ID would find KVM - * only. */ - r = detect_vm_dmi(); - if (r < 0) - return r; - if (r != VIRTUALIZATION_NONE) - goto finish; - - r = detect_vm_cpuid(); - if (r < 0) - return r; - if (r != VIRTUALIZATION_NONE) - goto finish; - - /* x86 xen will most likely be detected by cpuid. If not (most likely - * because we're not an x86 guest), then we should try the xen capabilities - * file next. If that's not found, then we check for the high-level - * hypervisor sysfs file: - * - * https://bugs.freedesktop.org/show_bug.cgi?id=77271 */ - - r = detect_vm_xen(); - if (r < 0) - return r; - if (r != VIRTUALIZATION_NONE) - goto finish; - - r = detect_vm_hypervisor(); - if (r < 0) - return r; - if (r != VIRTUALIZATION_NONE) - goto finish; - - r = detect_vm_device_tree(); - if (r < 0) - return r; - if (r != VIRTUALIZATION_NONE) - goto finish; - - r = detect_vm_uml(); - if (r < 0) - return r; - if (r != VIRTUALIZATION_NONE) - goto finish; - - r = detect_vm_zvm(); - if (r < 0) - return r; - -finish: - /* x86 xen Dom0 is detected as XEN in hypervisor and maybe others. - * In order to detect the Dom0 as not virtualization we need to - * double-check it */ - if (r == VIRTUALIZATION_XEN && detect_vm_xen_dom0()) - r = VIRTUALIZATION_NONE; - - cached_found = r; - log_debug("Found VM virtualization %s", virtualization_to_string(r)); - return r; -} - -int detect_container(void) { - - static const struct { - const char *value; - int id; - } value_table[] = { - { "lxc", VIRTUALIZATION_LXC }, - { "lxc-libvirt", VIRTUALIZATION_LXC_LIBVIRT }, - { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN }, - { "docker", VIRTUALIZATION_DOCKER }, - { "rkt", VIRTUALIZATION_RKT }, - }; - - static thread_local int cached_found = _VIRTUALIZATION_INVALID; - _cleanup_free_ char *m = NULL; - const char *e = NULL; - unsigned j; - int r; - - if (cached_found >= 0) - return cached_found; - - /* /proc/vz exists in container and outside of the container, - * /proc/bc only outside of the container. */ - if (access("/proc/vz", F_OK) >= 0 && - access("/proc/bc", F_OK) < 0) { - r = VIRTUALIZATION_OPENVZ; - goto finish; - } - - if (getpid() == 1) { - /* If we are PID 1 we can just check our own - * environment variable */ - - e = getenv("container"); - if (isempty(e)) { - r = VIRTUALIZATION_NONE; - goto finish; - } - } else { - - /* Otherwise, PID 1 dropped this information into a - * file in /run. This is better than accessing - * /proc/1/environ, since we don't need CAP_SYS_PTRACE - * for that. */ - - r = read_one_line_file("/run/systemd/container", &m); - if (r == -ENOENT) { - - /* Fallback for cases where PID 1 was not - * systemd (for example, cases where - * init=/bin/sh is used. */ - - r = getenv_for_pid(1, "container", &m); - if (r <= 0) { - - /* If that didn't work, give up, - * assume no container manager. - * - * Note: This means we still cannot - * detect containers if init=/bin/sh - * is passed but privileges dropped, - * as /proc/1/environ is only readable - * with privileges. */ - - r = VIRTUALIZATION_NONE; - goto finish; - } - } - if (r < 0) - return r; - - e = m; - } - - for (j = 0; j < ELEMENTSOF(value_table); j++) - if (streq(e, value_table[j].value)) { - r = value_table[j].id; - goto finish; - } - - r = VIRTUALIZATION_CONTAINER_OTHER; - -finish: - log_debug("Found container virtualization %s", virtualization_to_string(r)); - cached_found = r; - return r; -} - -int detect_virtualization(void) { - int r; - - r = detect_container(); - if (r == 0) - r = detect_vm(); - - return r; -} - -static int userns_has_mapping(const char *name) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *buf = NULL; - size_t n_allocated = 0; - ssize_t n; - uint32_t a, b, c; - int r; - - f = fopen(name, "re"); - if (!f) { - log_debug_errno(errno, "Failed to open %s: %m", name); - return errno == ENOENT ? false : -errno; - } - - n = getline(&buf, &n_allocated, f); - if (n < 0) { - if (feof(f)) { - log_debug("%s is empty, we're in an uninitialized user namespace", name); - return true; - } - - return log_debug_errno(errno, "Failed to read %s: %m", name); - } - - r = sscanf(buf, "%"PRIu32" %"PRIu32" %"PRIu32, &a, &b, &c); - if (r < 3) - return log_debug_errno(errno, "Failed to parse %s: %m", name); - - if (a == 0 && b == 0 && c == UINT32_MAX) { - /* The kernel calls mappings_overlap() and does not allow overlaps */ - log_debug("%s has a full 1:1 mapping", name); - return false; - } - - /* Anything else implies that we are in a user namespace */ - log_debug("Mapping found in %s, we're in a user namespace", name); - return true; -} - -int running_in_userns(void) { - _cleanup_free_ char *line = NULL; - int r; - - r = userns_has_mapping("/proc/self/uid_map"); - if (r != 0) - return r; - - r = userns_has_mapping("/proc/self/gid_map"); - if (r != 0) - return r; - - /* "setgroups" file was added in kernel v3.18-rc6-15-g9cc46516dd. It is also - * possible to compile a kernel without CONFIG_USER_NS, in which case "setgroups" - * also does not exist. We cannot distinguish those two cases, so assume that - * we're running on a stripped-down recent kernel, rather than on an old one, - * and if the file is not found, return false. - */ - r = read_one_line_file("/proc/self/setgroups", &line); - if (r < 0) { - log_debug_errno(r, "/proc/self/setgroups: %m"); - return r == -ENOENT ? false : r; - } - - truncate_nl(line); - r = streq(line, "deny"); - /* See user_namespaces(7) for a description of this "setgroups" contents. */ - log_debug("/proc/self/setgroups contains \"%s\", %s user namespace", line, r ? "in" : "not in"); - return r; -} - -int running_in_chroot(void) { - int ret; - - if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0) - return 0; - - ret = files_same("/proc/1/root", "/"); - if (ret < 0) - return ret; - - return ret == 0; -} - -static const char *const virtualization_table[_VIRTUALIZATION_MAX] = { - [VIRTUALIZATION_NONE] = "none", - [VIRTUALIZATION_KVM] = "kvm", - [VIRTUALIZATION_QEMU] = "qemu", - [VIRTUALIZATION_BOCHS] = "bochs", - [VIRTUALIZATION_XEN] = "xen", - [VIRTUALIZATION_UML] = "uml", - [VIRTUALIZATION_VMWARE] = "vmware", - [VIRTUALIZATION_ORACLE] = "oracle", - [VIRTUALIZATION_MICROSOFT] = "microsoft", - [VIRTUALIZATION_ZVM] = "zvm", - [VIRTUALIZATION_PARALLELS] = "parallels", - [VIRTUALIZATION_BHYVE] = "bhyve", - [VIRTUALIZATION_VM_OTHER] = "vm-other", - - [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn", - [VIRTUALIZATION_LXC_LIBVIRT] = "lxc-libvirt", - [VIRTUALIZATION_LXC] = "lxc", - [VIRTUALIZATION_OPENVZ] = "openvz", - [VIRTUALIZATION_DOCKER] = "docker", - [VIRTUALIZATION_RKT] = "rkt", - [VIRTUALIZATION_CONTAINER_OTHER] = "container-other", -}; - -DEFINE_STRING_TABLE_LOOKUP(virtualization, int); diff --git a/src/basic/virt.h b/src/basic/virt.h deleted file mode 100644 index 7d15169112..0000000000 --- a/src/basic/virt.h +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2011 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 "macro.h" - -enum { - VIRTUALIZATION_NONE = 0, - - VIRTUALIZATION_VM_FIRST, - VIRTUALIZATION_KVM = VIRTUALIZATION_VM_FIRST, - VIRTUALIZATION_QEMU, - VIRTUALIZATION_BOCHS, - VIRTUALIZATION_XEN, - VIRTUALIZATION_UML, - VIRTUALIZATION_VMWARE, - VIRTUALIZATION_ORACLE, - VIRTUALIZATION_MICROSOFT, - VIRTUALIZATION_ZVM, - VIRTUALIZATION_PARALLELS, - VIRTUALIZATION_BHYVE, - VIRTUALIZATION_VM_OTHER, - VIRTUALIZATION_VM_LAST = VIRTUALIZATION_VM_OTHER, - - VIRTUALIZATION_CONTAINER_FIRST, - VIRTUALIZATION_SYSTEMD_NSPAWN = VIRTUALIZATION_CONTAINER_FIRST, - VIRTUALIZATION_LXC_LIBVIRT, - VIRTUALIZATION_LXC, - VIRTUALIZATION_OPENVZ, - VIRTUALIZATION_DOCKER, - VIRTUALIZATION_RKT, - VIRTUALIZATION_CONTAINER_OTHER, - VIRTUALIZATION_CONTAINER_LAST = VIRTUALIZATION_CONTAINER_OTHER, - - _VIRTUALIZATION_MAX, - _VIRTUALIZATION_INVALID = -1 -}; - -static inline bool VIRTUALIZATION_IS_VM(int x) { - return x >= VIRTUALIZATION_VM_FIRST && x <= VIRTUALIZATION_VM_LAST; -} - -static inline bool VIRTUALIZATION_IS_CONTAINER(int x) { - return x >= VIRTUALIZATION_CONTAINER_FIRST && x <= VIRTUALIZATION_CONTAINER_LAST; -} - -int detect_vm(void); -int detect_container(void); -int detect_virtualization(void); - -int running_in_userns(void); -int running_in_chroot(void); - -const char *virtualization_to_string(int v) _const_; -int virtualization_from_string(const char *s) _pure_; diff --git a/src/basic/web-util.c b/src/basic/web-util.c deleted file mode 100644 index 595688ed93..0000000000 --- a/src/basic/web-util.c +++ /dev/null @@ -1,76 +0,0 @@ -/*** - 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-util.h" -#include "utf8.h" -#include "web-util.h" - -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); -} diff --git a/src/basic/web-util.h b/src/basic/web-util.h deleted file mode 100644 index e6bb6b53f5..0000000000 --- a/src/basic/web-util.h +++ /dev/null @@ -1,30 +0,0 @@ -#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 "macro.h" - -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); diff --git a/src/basic/xattr-util.c b/src/basic/xattr-util.c deleted file mode 100644 index 8256899eda..0000000000 --- a/src/basic/xattr-util.c +++ /dev/null @@ -1,200 +0,0 @@ -/*** - 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 <errno.h> -#include <fcntl.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <sys/time.h> -#include <sys/xattr.h> - -#include "alloc-util.h" -#include "fd-util.h" -#include "macro.h" -#include "sparse-endian.h" -#include "stdio-util.h" -#include "time-util.h" -#include "xattr-util.h" - -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; - } -} - -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_CLOEXEC|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; -} diff --git a/src/basic/xattr-util.h b/src/basic/xattr-util.h deleted file mode 100644 index 6fa097bf7e..0000000000 --- a/src/basic/xattr-util.h +++ /dev/null @@ -1,37 +0,0 @@ -#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 <stddef.h> -#include <sys/types.h> - -#include "time-util.h" - -int getxattr_malloc(const char *path, const char *name, char **value, bool allow_symlink); -int fgetxattr_malloc(int fd, const char *name, char **value); - -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); diff --git a/src/basic/xml.c b/src/basic/xml.c deleted file mode 100644 index 1dbeac7324..0000000000 --- a/src/basic/xml.c +++ /dev/null @@ -1,255 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 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 <errno.h> -#include <stddef.h> -#include <string.h> - -#include "macro.h" -#include "string-util.h" -#include "xml.h" - -enum { - STATE_NULL, - STATE_TEXT, - STATE_TAG, - STATE_ATTRIBUTE, -}; - -static void inc_lines(unsigned *line, const char *s, size_t n) { - const char *p = s; - - if (!line) - return; - - for (;;) { - const char *f; - - f = memchr(p, '\n', n); - if (!f) - return; - - n -= (f - p) + 1; - p = f + 1; - (*line)++; - } -} - -/* We don't actually do real XML here. We only read a simplistic - * subset, that is a bit less strict that XML and lacks all the more - * complex features, like entities, or namespaces. However, we do - * support some HTML5-like simplifications */ - -int xml_tokenize(const char **p, char **name, void **state, unsigned *line) { - const char *c, *e, *b; - char *ret; - int t; - - assert(p); - assert(*p); - assert(name); - assert(state); - - t = PTR_TO_INT(*state); - c = *p; - - if (t == STATE_NULL) { - if (line) - *line = 1; - t = STATE_TEXT; - } - - for (;;) { - if (*c == 0) - return XML_END; - - switch (t) { - - case STATE_TEXT: { - int x; - - e = strchrnul(c, '<'); - if (e > c) { - /* More text... */ - ret = strndup(c, e - c); - if (!ret) - return -ENOMEM; - - inc_lines(line, c, e - c); - - *name = ret; - *p = e; - *state = INT_TO_PTR(STATE_TEXT); - - return XML_TEXT; - } - - assert(*e == '<'); - b = c + 1; - - if (startswith(b, "!--")) { - /* A comment */ - e = strstr(b + 3, "-->"); - if (!e) - return -EINVAL; - - inc_lines(line, b, e + 3 - b); - - c = e + 3; - continue; - } - - if (*b == '?') { - /* Processing instruction */ - - e = strstr(b + 1, "?>"); - if (!e) - return -EINVAL; - - inc_lines(line, b, e + 2 - b); - - c = e + 2; - continue; - } - - if (*b == '!') { - /* DTD */ - - e = strchr(b + 1, '>'); - if (!e) - return -EINVAL; - - inc_lines(line, b, e + 1 - b); - - c = e + 1; - continue; - } - - if (*b == '/') { - /* A closing tag */ - x = XML_TAG_CLOSE; - b++; - } else - x = XML_TAG_OPEN; - - e = strpbrk(b, WHITESPACE "/>"); - if (!e) - return -EINVAL; - - ret = strndup(b, e - b); - if (!ret) - return -ENOMEM; - - *name = ret; - *p = e; - *state = INT_TO_PTR(STATE_TAG); - - return x; - } - - case STATE_TAG: - - b = c + strspn(c, WHITESPACE); - if (*b == 0) - return -EINVAL; - - inc_lines(line, c, b - c); - - e = b + strcspn(b, WHITESPACE "=/>"); - if (e > b) { - /* An attribute */ - - ret = strndup(b, e - b); - if (!ret) - return -ENOMEM; - - *name = ret; - *p = e; - *state = INT_TO_PTR(STATE_ATTRIBUTE); - - return XML_ATTRIBUTE_NAME; - } - - if (startswith(b, "/>")) { - /* An empty tag */ - - *name = NULL; /* For empty tags we return a NULL name, the caller must be prepared for that */ - *p = b + 2; - *state = INT_TO_PTR(STATE_TEXT); - - return XML_TAG_CLOSE_EMPTY; - } - - if (*b != '>') - return -EINVAL; - - c = b + 1; - t = STATE_TEXT; - continue; - - case STATE_ATTRIBUTE: - - if (*c == '=') { - c++; - - if (*c == '\'' || *c == '\"') { - /* Tag with a quoted value */ - - e = strchr(c+1, *c); - if (!e) - return -EINVAL; - - inc_lines(line, c, e - c); - - ret = strndup(c+1, e - c - 1); - if (!ret) - return -ENOMEM; - - *name = ret; - *p = e + 1; - *state = INT_TO_PTR(STATE_TAG); - - return XML_ATTRIBUTE_VALUE; - - } - - /* Tag with a value without quotes */ - - b = strpbrk(c, WHITESPACE ">"); - if (!b) - b = c; - - ret = strndup(c, b - c); - if (!ret) - return -ENOMEM; - - *name = ret; - *p = b; - *state = INT_TO_PTR(STATE_TAG); - return XML_ATTRIBUTE_VALUE; - } - - t = STATE_TAG; - continue; - } - - } - - assert_not_reached("Bad state"); -} diff --git a/src/basic/xml.h b/src/basic/xml.h deleted file mode 100644 index 41cb69f0dc..0000000000 --- a/src/basic/xml.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 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/>. -***/ - -enum { - XML_END, - XML_TEXT, - XML_TAG_OPEN, - XML_TAG_CLOSE, - XML_TAG_CLOSE_EMPTY, - XML_ATTRIBUTE_NAME, - XML_ATTRIBUTE_VALUE, -}; - -int xml_tokenize(const char **p, char **name, void **state, unsigned *line); |