diff options
Diffstat (limited to 'src/basic')
38 files changed, 1691 insertions, 844 deletions
diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c index 2dcc9c5575..a2296f4709 100644 --- a/src/basic/calendarspec.c +++ b/src/basic/calendarspec.c @@ -279,6 +279,9 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) { fputc(':', f); format_chain(f, 2, c->second); + if (c->utc) + fputs(" UTC", f); + r = fflush_and_check(f); if (r < 0) { free(buf); @@ -657,6 +660,10 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { if (!c) return -ENOMEM; + c->utc = endswith_no_case(p, "UTC"); + if (c->utc) + p = strndupa(p, strlen(p) - strlen(" UTC")); + if (strcaseeq(p, "minutely")) { r = const_chain(0, &c->second); if (r < 0) @@ -859,13 +866,13 @@ static int find_matching_component(const CalendarComponent *c, int *val) { return r; } -static bool tm_out_of_bounds(const struct tm *tm) { +static bool tm_out_of_bounds(const struct tm *tm, bool utc) { struct tm t; assert(tm); t = *tm; - if (mktime(&t) == (time_t) -1) + if (mktime_or_timegm(&t, utc) == (time_t) -1) return true; /* Did any normalization take place? If so, it was out of bounds before */ @@ -878,7 +885,7 @@ static bool tm_out_of_bounds(const struct tm *tm) { t.tm_sec != tm->tm_sec; } -static bool matches_weekday(int weekdays_bits, const struct tm *tm) { +static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) { struct tm t; int k; @@ -886,7 +893,7 @@ static bool matches_weekday(int weekdays_bits, const struct tm *tm) { return true; t = *tm; - if (mktime(&t) == (time_t) -1) + if (mktime_or_timegm(&t, utc) == (time_t) -1) return false; k = t.tm_wday == 0 ? 6 : t.tm_wday - 1; @@ -904,7 +911,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { for (;;) { /* Normalize the current date */ - mktime(&c); + mktime_or_timegm(&c, spec->utc); c.tm_isdst = -1; c.tm_year += 1900; @@ -916,7 +923,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { c.tm_mday = 1; c.tm_hour = c.tm_min = c.tm_sec = 0; } - if (r < 0 || tm_out_of_bounds(&c)) + if (r < 0 || tm_out_of_bounds(&c, spec->utc)) return r; c.tm_mon += 1; @@ -927,7 +934,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { c.tm_mday = 1; c.tm_hour = c.tm_min = c.tm_sec = 0; } - if (r < 0 || tm_out_of_bounds(&c)) { + if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { c.tm_year ++; c.tm_mon = 0; c.tm_mday = 1; @@ -938,14 +945,14 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { r = find_matching_component(spec->day, &c.tm_mday); if (r > 0) c.tm_hour = c.tm_min = c.tm_sec = 0; - if (r < 0 || tm_out_of_bounds(&c)) { + 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 = 0; continue; } - if (!matches_weekday(spec->weekdays_bits, &c)) { + if (!matches_weekday(spec->weekdays_bits, &c, spec->utc)) { c.tm_mday++; c.tm_hour = c.tm_min = c.tm_sec = 0; continue; @@ -954,7 +961,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { r = find_matching_component(spec->hour, &c.tm_hour); if (r > 0) c.tm_min = c.tm_sec = 0; - if (r < 0 || tm_out_of_bounds(&c)) { + if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { c.tm_mday ++; c.tm_hour = c.tm_min = c.tm_sec = 0; continue; @@ -963,14 +970,14 @@ static int find_next(const CalendarSpec *spec, struct tm *tm) { r = find_matching_component(spec->minute, &c.tm_min); if (r > 0) c.tm_sec = 0; - if (r < 0 || tm_out_of_bounds(&c)) { + if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { c.tm_hour ++; c.tm_min = c.tm_sec = 0; continue; } r = find_matching_component(spec->second, &c.tm_sec); - if (r < 0 || tm_out_of_bounds(&c)) { + if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { c.tm_min ++; c.tm_sec = 0; continue; @@ -991,13 +998,13 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) assert(next); t = (time_t) (usec / USEC_PER_SEC) + 1; - assert_se(localtime_r(&t, &tm)); + assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc)); r = find_next(spec, &tm); if (r < 0) return r; - t = mktime(&tm); + t = mktime_or_timegm(&tm, spec->utc); if (t == (time_t) -1) return -EINVAL; diff --git a/src/basic/calendarspec.h b/src/basic/calendarspec.h index 7baf318249..56dc02f391 100644 --- a/src/basic/calendarspec.h +++ b/src/basic/calendarspec.h @@ -36,6 +36,7 @@ typedef struct CalendarComponent { typedef struct CalendarSpec { int weekdays_bits; + bool utc; CalendarComponent *year; CalendarComponent *month; diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 94a25585b2..95fc2b9e5d 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -2018,9 +2018,10 @@ int cg_mask_supported(CGroupMask *ret) { mask |= CGROUP_CONTROLLER_TO_MASK(v); } - /* Currently, we only support the memory controller in - * the unified hierarchy, mask everything else off. */ - mask &= CGROUP_MASK_MEMORY; + /* Currently, we only support the memory and pids + * controller in the unified hierarchy, mask + * everything else off. */ + mask &= CGROUP_MASK_MEMORY | CGROUP_MASK_PIDS; } else { CGroupController c; @@ -2206,12 +2207,54 @@ bool cg_is_legacy_wanted(void) { return !cg_is_unified_wanted(); } +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; +} + static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = { [CGROUP_CONTROLLER_CPU] = "cpu", [CGROUP_CONTROLLER_CPUACCT] = "cpuacct", [CGROUP_CONTROLLER_BLKIO] = "blkio", [CGROUP_CONTROLLER_MEMORY] = "memory", [CGROUP_CONTROLLER_DEVICES] = "devices", + [CGROUP_CONTROLLER_PIDS] = "pids", + [CGROUP_CONTROLLER_NET_CLS] = "net_cls", }; DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController); diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h index 16d439fd9d..01359fa7cb 100644 --- a/src/basic/cgroup-util.h +++ b/src/basic/cgroup-util.h @@ -35,6 +35,8 @@ typedef enum CGroupController { CGROUP_CONTROLLER_BLKIO, CGROUP_CONTROLLER_MEMORY, CGROUP_CONTROLLER_DEVICES, + CGROUP_CONTROLLER_PIDS, + CGROUP_CONTROLLER_NET_CLS, _CGROUP_CONTROLLER_MAX, _CGROUP_CONTROLLER_INVALID = -1, } CGroupController; @@ -48,9 +50,35 @@ typedef enum CGroupMask { 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_NET_CLS = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_NET_CLS), _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1 } CGroupMask; +/* 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); +} + /* * General rules: * @@ -159,3 +187,6 @@ bool cg_is_legacy_wanted(void); const char* cgroup_controller_to_string(CGroupController c) _const_; CGroupController cgroup_controller_from_string(const char *s) _pure_; + +int cg_cpu_shares_parse(const char *s, uint64_t *ret); +int cg_blkio_weight_parse(const char *s, uint64_t *ret); diff --git a/src/basic/copy.c b/src/basic/copy.c index b8cbe644d4..b20c178727 100644 --- a/src/basic/copy.c +++ b/src/basic/copy.c @@ -37,10 +37,14 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { assert(fdt >= 0); /* Try btrfs reflinks first. */ - if (try_reflink && max_bytes == (uint64_t) -1) { + 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 r; + return 0; /* we copied the whole thing, hence hit EOF, return 0 */ } for (;;) { @@ -50,7 +54,7 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { if (max_bytes != (uint64_t) -1) { if (max_bytes <= 0) - return -EFBIG; + return 1; /* return > 0 if we hit the max_bytes limit */ if ((uint64_t) m > max_bytes) m = (size_t) max_bytes; @@ -75,7 +79,7 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { /* The try splice, unless we already tried */ if (try_splice) { - n = splice(fdf, NULL, fdt, NULL, m, 0); + n = splice(fdf, NULL, fdt, NULL, m, 0); if (n < 0) { if (errno != EINVAL && errno != ENOSYS) return -errno; @@ -91,7 +95,7 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { /* As a fallback just copy bits by hand */ { - char buf[m]; + uint8_t buf[m]; n = read(fdf, buf, m); if (n < 0) @@ -111,7 +115,7 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { } } - return 0; + 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) { diff --git a/src/basic/cpu-set-util.c b/src/basic/cpu-set-util.c new file mode 100644 index 0000000000..519583c167 --- /dev/null +++ b/src/basic/cpu-set-util.c @@ -0,0 +1,105 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + 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 "util.h" +#include "cpu-set-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; + int r; + + r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue); + return r; + } + if (r == 0) + break; + + if (!c) { + c = cpu_set_malloc(&ncpus); + if (!c) + return log_oom(); + } + + r = safe_atou(word, &cpu); + if (r < 0 || cpu >= ncpus) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU affinity '%s'", rvalue); + return -EINVAL; + } + + 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 new file mode 100644 index 0000000000..19b457a684 --- /dev/null +++ b/src/basic/cpu-set-util.h @@ -0,0 +1,34 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#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/env-util.c b/src/basic/env-util.c index 4804a67f91..ecb2192c4d 100644 --- a/src/basic/env-util.c +++ b/src/basic/env-util.c @@ -541,7 +541,7 @@ char **replace_env_argv(char **argv, char **env) { STRV_FOREACH(i, argv) { /* If $FOO appears as single word, replace it by the split up variable */ - if ((*i)[0] == '$' && (*i)[1] != '{') { + if ((*i)[0] == '$' && (*i)[1] != '{' && (*i)[1] != '$') { char *e; char **w, **m = NULL; unsigned q; diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 4a9105f421..13a85e1158 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -775,15 +775,19 @@ int executable_is_script(const char *path, char **interpreter) { /** * Retrieve one field from a file like /proc/self/status. pattern - * should start with '\n' and end with a ':'. Whitespace and zeros - * after the ':' will be skipped. field must be freed afterwards. + * 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_status_field(const char *filename, const char *pattern, char **field) { +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); @@ -792,11 +796,31 @@ int get_status_field(const char *filename, const char *pattern, char **field) { if (r < 0) return r; - t = strstr(status, pattern); - if (!t) - return -ENOENT; + 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++; - t += strlen(pattern); if (*t) { t += strspn(t, " \t"); @@ -812,7 +836,7 @@ int get_status_field(const char *filename, const char *pattern, char **field) { t --; } - len = strcspn(t, WHITESPACE); + len = strcspn(t, terminator); f = strndup(t, len); if (!f) diff --git a/src/basic/fileio.h b/src/basic/fileio.h index 2e8148ff24..4998d4d042 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -48,4 +48,4 @@ int write_env_file(const char *fname, char **l); int executable_is_script(const char *path, char **interpreter); -int get_status_field(const char *filename, const char *pattern, char **field); +int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field); diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c index 7d2a4160c6..20e7e51d9e 100644 --- a/src/basic/hashmap.c +++ b/src/basic/hashmap.c @@ -276,10 +276,8 @@ static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = { }, }; -unsigned long string_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) { - uint64_t u; - siphash24((uint8_t*) &u, p, strlen(p), hash_key); - return (unsigned long) u; +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) { @@ -291,10 +289,8 @@ const struct hash_ops string_hash_ops = { .compare = string_compare_func }; -unsigned long trivial_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) { - uint64_t u; - siphash24((uint8_t*) &u, &p, sizeof(p), hash_key); - return (unsigned long) u; +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) { @@ -306,10 +302,8 @@ const struct hash_ops trivial_hash_ops = { .compare = trivial_compare_func }; -unsigned long uint64_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) { - uint64_t u; - siphash24((uint8_t*) &u, p, sizeof(uint64_t), hash_key); - return (unsigned long) u; +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) { @@ -325,10 +319,8 @@ const struct hash_ops uint64_hash_ops = { }; #if SIZEOF_DEV_T != 8 -unsigned long devt_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) { - uint64_t u; - siphash24((uint8_t*) &u, p, sizeof(dev_t), hash_key); - return (unsigned long) u; +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) { @@ -379,7 +371,16 @@ static uint8_t *hash_key(HashmapBase *h) { } static unsigned base_bucket_hash(HashmapBase *h, const void *p) { - return (unsigned) (h->hash_ops->hash(p, hash_key(h)) % n_buckets(h)); + struct siphash state; + uint64_t hash; + + siphash24_init(&state, hash_key(h)); + + h->hash_ops->hash(p, &state); + + siphash24_finalize((uint8_t*)&hash, &state); + + return (unsigned) (hash % n_buckets(h)); } #define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p) diff --git a/src/basic/hashmap.h b/src/basic/hashmap.h index 2af23024de..ed6a092d82 100644 --- a/src/basic/hashmap.h +++ b/src/basic/hashmap.h @@ -25,6 +25,7 @@ #include <stdbool.h> #include "macro.h" +#include "siphash24.h" #include "util.h" /* @@ -67,7 +68,7 @@ typedef struct { #define _IDX_ITERATOR_FIRST (UINT_MAX - 1) #define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL }) -typedef unsigned long (*hash_func_t)(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]); +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 { @@ -75,28 +76,28 @@ struct hash_ops { compare_func_t compare; }; -unsigned long string_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_; +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. */ -unsigned long trivial_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_; +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 embedd 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. */ -unsigned long uint64_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_; +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 -unsigned long devt_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_; +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, diff --git a/src/basic/log.c b/src/basic/log.c index b96afc4de4..e6d7d15182 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -340,10 +340,10 @@ static int write_to_console( } if (highlight) - IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_RED_ON); + IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_RED); IOVEC_SET_STRING(iovec[n++], buffer); if (highlight) - IOVEC_SET_STRING(iovec[n++], ANSI_HIGHLIGHT_OFF); + IOVEC_SET_STRING(iovec[n++], ANSI_NORMAL); IOVEC_SET_STRING(iovec[n++], "\n"); if (writev(console_fd, iovec, n) < 0) { @@ -922,7 +922,7 @@ int log_set_max_level_from_string(const char *e) { t = log_level_from_string(e); if (t < 0) - return t; + return -EINVAL; log_set_max_level(t); return 0; diff --git a/src/basic/log.h b/src/basic/log.h index 569762d083..369d6b1127 100644 --- a/src/basic/log.h +++ b/src/basic/log.h @@ -227,3 +227,15 @@ int log_syntax_internal( ? 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)); \ + } \ + -EINVAL; \ + }) diff --git a/src/basic/macro.h b/src/basic/macro.h index cbc3ca97b8..f55d65e2f1 100644 --- a/src/basic/macro.h +++ b/src/basic/macro.h @@ -123,8 +123,11 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL)); } -#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0])) - +#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. @@ -213,18 +216,20 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { (__x / __y + !!(__x % __y)); \ }) -#define assert_se(expr) \ +#define assert_message_se(expr, message) \ do { \ if (_unlikely_(!(expr))) \ - log_assert_failed(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ - } while (false) \ + 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_se(expr) +#define assert(expr) assert_message_se(expr, #expr) #endif #define assert_not_reached(t) \ @@ -249,19 +254,19 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { REENABLE_WARNING #endif -#define assert_log(expr) ((_likely_(expr)) \ - ? (true) \ - : (log_assert_failed_return(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__), false)) +#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)) \ + if (!assert_log(expr, #expr)) \ return (r); \ } while (false) #define assert_return_errno(expr, r, err) \ do { \ - if (!assert_log(expr)) { \ + if (!assert_log(expr, #expr)) { \ errno = err; \ return (r); \ } \ @@ -465,18 +470,6 @@ do { \ #define GID_INVALID ((gid_t) -1) #define MODE_INVALID ((mode_t) -1) -static inline bool UID_IS_INVALID(uid_t uid) { - /* We consider both the old 16bit -1 user and the newer 32bit - * -1 user invalid, since they are or used to be incompatible - * with syscalls such as setresuid() or chown(). */ - - return uid == (uid_t) ((uint32_t) -1) || uid == (uid_t) ((uint16_t) -1); -} - -static inline bool GID_IS_INVALID(gid_t gid) { - return gid == (gid_t) ((uint32_t) -1) || gid == (gid_t) ((uint16_t) -1); -} - #define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func) \ static inline void func##p(type *p) { \ if (*p) \ diff --git a/src/basic/missing.h b/src/basic/missing.h index dc1f244d4c..59e835a466 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -139,6 +139,8 @@ static inline int pivot_root(const char *new_root, const char *put_old) { # 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 @@ -840,6 +842,19 @@ static inline int setns(int fd, int nstype) { #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1) #endif +#if !HAVE_DECL_IFLA_BR_PRIORITY +#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_MAX 7 + +#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 @@ -1028,7 +1043,12 @@ static inline int renameat2(int oldfd, const char *oldname, int newfd, const cha #if !HAVE_DECL_KCMP static inline int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) { +#if defined(__NR_kcmp) return syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2); +#else + errno = ENOSYS; + return -1; +#endif } #endif @@ -1043,3 +1063,48 @@ static inline int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, uns #ifndef INPUT_PROP_ACCELEROMETER #define INPUT_PROP_ACCELEROMETER 0x06 #endif + +#if !HAVE_DECL_KEY_SERIAL_T +typedef int32_t key_serial_t; +#endif + +#if !HAVE_DECL_KEYCTL +static inline long keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4,unsigned long arg5) { +#if defined(__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) { +#if defined (__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) { +#if defined (__NR_request_key) + return syscall(__NR_request_key, type, description, callout_info, destringid); +#else + errno = ENOSYS; + return -1; +#endif +} +#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 diff --git a/src/basic/prioq.c b/src/basic/prioq.c index b89888be0e..d55b348c22 100644 --- a/src/basic/prioq.c +++ b/src/basic/prioq.c @@ -19,6 +19,16 @@ 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 "util.h" #include "prioq.h" @@ -101,7 +111,7 @@ static unsigned shuffle_up(Prioq *q, unsigned idx) { k = (idx-1)/2; - if (q->compare_func(q->items[k].data, q->items[idx].data) < 0) + if (q->compare_func(q->items[k].data, q->items[idx].data) <= 0) break; swap(q, idx, k); diff --git a/src/basic/process-util.c b/src/basic/process-util.c index cff2d2a034..d8a94a4572 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -215,7 +215,7 @@ int get_process_capeff(pid_t pid, char **capeff) { p = procfs_file_alloca(pid, "status"); - r = get_status_field(p, "\nCapEff:", capeff); + r = get_proc_field(p, "CapEff", WHITESPACE, capeff); if (r == -ENOENT) return -ESRCH; diff --git a/src/basic/ring.c b/src/basic/ring.c deleted file mode 100644 index 6814918464..0000000000 --- a/src/basic/ring.c +++ /dev/null @@ -1,209 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - 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 <stdlib.h> -#include <string.h> -#include <sys/uio.h> -#include "macro.h" -#include "ring.h" - -#define RING_MASK(_r, _v) ((_v) & ((_r)->size - 1)) - -void ring_flush(Ring *r) { - assert(r); - - r->start = 0; - r->used = 0; -} - -void ring_clear(Ring *r) { - assert(r); - - free(r->buf); - zero(*r); -} - -/* - * Get data pointers for current ring-buffer data. @vec must be an array of 2 - * iovec objects. They are filled according to the data available in the - * ring-buffer. 0, 1 or 2 is returned according to the number of iovec objects - * that were filled (0 meaning buffer is empty). - * - * Hint: "struct iovec" is defined in <sys/uio.h> and looks like this: - * struct iovec { - * void *iov_base; - * size_t iov_len; - * }; - */ -size_t ring_peek(Ring *r, struct iovec *vec) { - assert(r); - - if (r->used == 0) { - return 0; - } else if (r->start + r->used <= r->size) { - if (vec) { - vec[0].iov_base = &r->buf[r->start]; - vec[0].iov_len = r->used; - } - return 1; - } else { - if (vec) { - vec[0].iov_base = &r->buf[r->start]; - vec[0].iov_len = r->size - r->start; - vec[1].iov_base = r->buf; - vec[1].iov_len = r->used - (r->size - r->start); - } - return 2; - } -} - -/* - * Copy data from the ring buffer into the linear external buffer @buf. Copy - * at most @size bytes. If the ring buffer size is smaller, copy less bytes and - * return the number of bytes copied. - */ -size_t ring_copy(Ring *r, void *buf, size_t size) { - size_t l; - - assert(r); - assert(buf); - - if (size > r->used) - size = r->used; - - if (size > 0) { - l = r->size - r->start; - if (size <= l) { - memcpy(buf, &r->buf[r->start], size); - } else { - memcpy(buf, &r->buf[r->start], l); - memcpy((uint8_t*)buf + l, r->buf, size - l); - } - } - - return size; -} - -/* - * Resize ring-buffer to size @nsize. @nsize must be a power-of-2, otherwise - * ring operations will behave incorrectly. - */ -static int ring_resize(Ring *r, size_t nsize) { - uint8_t *buf; - size_t l; - - assert(r); - assert(nsize > 0); - - buf = malloc(nsize); - if (!buf) - return -ENOMEM; - - if (r->used > 0) { - l = r->size - r->start; - if (r->used <= l) { - memcpy(buf, &r->buf[r->start], r->used); - } else { - memcpy(buf, &r->buf[r->start], l); - memcpy(&buf[l], r->buf, r->used - l); - } - } - - free(r->buf); - r->buf = buf; - r->size = nsize; - r->start = 0; - - return 0; -} - -/* - * Resize ring-buffer to provide enough room for @add bytes of new data. This - * resizes the buffer if it is too small. It returns -ENOMEM on OOM and 0 on - * success. - */ -static int ring_grow(Ring *r, size_t add) { - size_t need; - - assert(r); - - if (r->size - r->used >= add) - return 0; - - need = r->used + add; - if (need <= r->used) - return -ENOMEM; - else if (need < 4096) - need = 4096; - - need = ALIGN_POWER2(need); - if (need == 0) - return -ENOMEM; - - return ring_resize(r, need); -} - -/* - * Push @len bytes from @u8 into the ring buffer. The buffer is resized if it - * is too small. -ENOMEM is returned on OOM, 0 on success. - */ -int ring_push(Ring *r, const void *u8, size_t size) { - int err; - size_t pos, l; - - assert(r); - assert(u8); - - if (size == 0) - return 0; - - err = ring_grow(r, size); - if (err < 0) - return err; - - pos = RING_MASK(r, r->start + r->used); - l = r->size - pos; - if (l >= size) { - memcpy(&r->buf[pos], u8, size); - } else { - memcpy(&r->buf[pos], u8, l); - memcpy(r->buf, (const uint8_t*)u8 + l, size - l); - } - - r->used += size; - - return 0; -} - -/* - * Remove @len bytes from the start of the ring-buffer. Note that we protect - * against overflows so removing more bytes than available is safe. - */ -void ring_pull(Ring *r, size_t size) { - assert(r); - - if (size > r->used) - size = r->used; - - r->start = RING_MASK(r, r->start + size); - r->used -= size; -} diff --git a/src/basic/ring.h b/src/basic/ring.h deleted file mode 100644 index dbd6296384..0000000000 --- a/src/basic/ring.h +++ /dev/null @@ -1,55 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#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/>. -***/ - - -typedef struct Ring Ring; - -struct Ring { - uint8_t *buf; /* buffer or NULL */ - size_t size; /* actual size of @buf */ - size_t start; /* start position of ring */ - size_t used; /* number of actually used bytes */ -}; - -/* flush buffer so it is empty again */ -void ring_flush(Ring *r); - -/* flush buffer, free allocated data and reset to initial state */ -void ring_clear(Ring *r); - -/* get pointers to buffer data and their length */ -size_t ring_peek(Ring *r, struct iovec *vec); - -/* copy data into external linear buffer */ -size_t ring_copy(Ring *r, void *buf, size_t size); - -/* push data to the end of the buffer */ -int ring_push(Ring *r, const void *u8, size_t size); - -/* pull data from the front of the buffer */ -void ring_pull(Ring *r, size_t size); - -/* return size of occupied buffer in bytes */ -static inline size_t ring_get_size(Ring *r) { - return r->used; -} diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c index a39a0f775a..747e6f4dbb 100644 --- a/src/basic/selinux-util.c +++ b/src/basic/selinux-util.c @@ -295,14 +295,20 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char * return r; } -void mac_selinux_free(char *label) { +char* mac_selinux_free(char *label) { #ifdef HAVE_SELINUX + if (!label) + return NULL; + if (!mac_selinux_use()) - return; + return NULL; + freecon((security_context_t) label); #endif + + return NULL; } int mac_selinux_create_file_prepare(const char *path, mode_t mode) { diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h index 8467185291..2afcaec183 100644 --- a/src/basic/selinux-util.h +++ b/src/basic/selinux-util.h @@ -24,6 +24,8 @@ #include <sys/socket.h> #include <stdbool.h> +#include "macro.h" + bool mac_selinux_use(void); void mac_selinux_retest(void); @@ -36,7 +38,7 @@ 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); -void mac_selinux_free(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); @@ -45,3 +47,5 @@ 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/siphash24.c b/src/basic/siphash24.c index f68bd283a1..3b61961389 100644 --- a/src/basic/siphash24.c +++ b/src/basic/siphash24.c @@ -13,123 +13,170 @@ 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 <stdint.h> -#include <stdio.h> -#include <string.h> + +#include "sparse-endian.h" #include "siphash24.h" +#include "util.h" -typedef uint64_t u64; -typedef uint32_t u32; -typedef uint8_t u8; - -#define ROTL(x,b) (u64)( ((x) << (b)) | ( (x) >> (64 - (b))) ) - -#define U32TO8_LE(p, v) \ - (p)[0] = (u8)((v) ); (p)[1] = (u8)((v) >> 8); \ - (p)[2] = (u8)((v) >> 16); (p)[3] = (u8)((v) >> 24); - -#define U64TO8_LE(p, v) \ - U32TO8_LE((p), (u32)((v) )); \ - U32TO8_LE((p) + 4, (u32)((v) >> 32)); - -#define U8TO64_LE(p) \ - (((u64)((p)[0]) ) | \ - ((u64)((p)[1]) << 8) | \ - ((u64)((p)[2]) << 16) | \ - ((u64)((p)[3]) << 24) | \ - ((u64)((p)[4]) << 32) | \ - ((u64)((p)[5]) << 40) | \ - ((u64)((p)[6]) << 48) | \ - ((u64)((p)[7]) << 56)) - -#define SIPROUND \ - do { \ - v0 += v1; v1=ROTL(v1,13); v1 ^= v0; v0=ROTL(v0,32); \ - v2 += v3; v3=ROTL(v3,16); v3 ^= v2; \ - v0 += v3; v3=ROTL(v3,21); v3 ^= v0; \ - v2 += v1; v1=ROTL(v1,17); v1 ^= v2; v2=ROTL(v2,32); \ - } while(0) +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 = le64toh(*(le64_t*) k); + k1 = le64toh(*(le64_t*) (k + 8)); + + /* "somepseudorandomlygeneratedbytes" */ + state->v0 = 0x736f6d6570736575ULL ^ k0; + state->v1 = 0x646f72616e646f6dULL ^ k1; + state->v2 = 0x6c7967656e657261ULL ^ k0; + state->v3 = 0x7465646279746573ULL ^ k1; + state->padding = 0; + state->inlen = 0; +} + +void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) { + uint64_t m; + const uint8_t *in = _in; + const uint8_t *end = in + inlen; + unsigned left = state->inlen & 7; + + 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; -/* SipHash-2-4 */ -void siphash24(uint8_t out[8], const void *_in, size_t inlen, const uint8_t k[16]) -{ - /* "somepseudorandomlygeneratedbytes" */ - u64 v0 = 0x736f6d6570736575ULL; - u64 v1 = 0x646f72616e646f6dULL; - u64 v2 = 0x6c7967656e657261ULL; - u64 v3 = 0x7465646279746573ULL; - u64 b; - u64 k0 = U8TO64_LE( k ); - u64 k1 = U8TO64_LE( k + 8 ); - u64 m; - const u8 *in = _in; - const u8 *end = in + inlen - ( inlen % sizeof( u64 ) ); - const int left = inlen & 7; - b = ( ( u64 )inlen ) << 56; - v3 ^= k1; - v2 ^= k0; - v1 ^= k1; - v0 ^= k0; - - for ( ; in != end; in += 8 ) - { - m = U8TO64_LE( in ); #ifdef DEBUG - printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 ); - printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 ); - printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 ); - printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 ); - printf( "(%3d) compress %08x %08x\n", ( int )inlen, ( u32 )( m >> 32 ), ( u32 )m ); + 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 - v3 ^= m; - SIPROUND; - SIPROUND; - v0 ^= m; - } + state->v3 ^= state->padding; + sipround(state); + sipround(state); + state->v0 ^= state->padding; - switch( left ) - { - case 7: b |= ( ( u64 )in[ 6] ) << 48; + state->padding = 0; + } - case 6: b |= ( ( u64 )in[ 5] ) << 40; + end -= ( state->inlen % sizeof (uint64_t) ); - case 5: b |= ( ( u64 )in[ 4] ) << 32; + for ( ; in < end; in += 8 ) { + m = le64toh(*(le64_t*) 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 4: b |= ( ( u64 )in[ 3] ) << 24; + case 6: state->padding |= ((uint64_t) in[5]) << 40; - case 3: b |= ( ( u64 )in[ 2] ) << 16; + case 5: state->padding |= ((uint64_t) in[4]) << 32; - case 2: b |= ( ( u64 )in[ 1] ) << 8; + case 4: state->padding |= ((uint64_t) in[3]) << 24; - case 1: b |= ( ( u64 )in[ 0] ); break; + case 3: state->padding |= ((uint64_t) in[2]) << 16; + + case 2: state->padding |= ((uint64_t) in[1]) << 8; + + case 1: state->padding |= ((uint64_t) in[0]); break; + + case 0: break; + } +} - case 0: break; - } +void siphash24_finalize(uint8_t out[8], struct siphash *state) { + uint64_t b; + b = state->padding | (( ( uint64_t )state->inlen ) << 56); #ifdef DEBUG - printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 ); - printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 ); - printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 ); - printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 ); - printf( "(%3d) padding %08x %08x\n", ( int )inlen, ( u32 )( b >> 32 ), ( u32 )b ); + 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 - v3 ^= b; - SIPROUND; - SIPROUND; - v0 ^= b; + state->v3 ^= b; + sipround(state); + sipround(state); + state->v0 ^= b; + #ifdef DEBUG - printf( "(%3d) v0 %08x %08x\n", ( int )inlen, ( u32 )( v0 >> 32 ), ( u32 )v0 ); - printf( "(%3d) v1 %08x %08x\n", ( int )inlen, ( u32 )( v1 >> 32 ), ( u32 )v1 ); - printf( "(%3d) v2 %08x %08x\n", ( int )inlen, ( u32 )( v2 >> 32 ), ( u32 )v2 ); - printf( "(%3d) v3 %08x %08x\n", ( int )inlen, ( u32 )( v3 >> 32 ), ( u32 )v3 ); + printf("(%3zu) v0 %08x %08x\n", state->inlen, (uint32_t) (state->v0 >> 32), (uint32_t) state->v0); + printf("(%3zu) v1 %08x %08x\n", state->inlen, (uint32_t) (state->v1 >> 32), (uint32_t) state->v1); + printf("(%3zu) v2 %08x %08x\n", state->inlen, (uint32_t) (state->v2 >> 32), (uint32_t) state->v2); + printf("(%3zu) v3 %08x %08x\n", state->inlen, (uint32_t) (state->v3 >> 32), (uint32_t) state->v3); #endif - v2 ^= 0xff; - SIPROUND; - SIPROUND; - SIPROUND; - SIPROUND; - b = v0 ^ v1 ^ v2 ^ v3; - U64TO8_LE( out, b ); + state->v2 ^= 0xff; + + sipround(state); + sipround(state); + sipround(state); + sipround(state); + + *(le64_t*)out = htole64(state->v0 ^ state->v1 ^ state->v2 ^ state->v3); +} + +/* SipHash-2-4 */ +void siphash24(uint8_t out[8], const void *_in, size_t inlen, const uint8_t k[16]) { + struct siphash state; + + siphash24_init(&state, k); + siphash24_compress(_in, inlen, &state); + siphash24_finalize(out, &state); } diff --git a/src/basic/siphash24.h b/src/basic/siphash24.h index 62e1168a79..6c5cd98ee8 100644 --- a/src/basic/siphash24.h +++ b/src/basic/siphash24.h @@ -3,4 +3,17 @@ #include <inttypes.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); +void siphash24_finalize(uint8_t out[8], struct siphash *state); + void siphash24(uint8_t out[8], 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 index 9e221d6eab..5f570ff02a 100644 --- a/src/basic/smack-util.c +++ b/src/basic/smack-util.c @@ -29,9 +29,6 @@ #include "fileio.h" #include "smack-util.h" -#define SMACK_FLOOR_LABEL "_" -#define SMACK_STAR_LABEL "*" - #ifdef HAVE_SMACK bool mac_smack_use(void) { static int cached_use = -1; diff --git a/src/basic/smack-util.h b/src/basic/smack-util.h index b3aa55eb8a..e756dc8c28 100644 --- a/src/basic/smack-util.h +++ b/src/basic/smack-util.h @@ -27,6 +27,9 @@ #include "macro.h" +#define SMACK_FLOOR_LABEL "_" +#define SMACK_STAR_LABEL "*" + typedef enum SmackAttr { SMACK_ATTR_ACCESS = 0, SMACK_ATTR_EXEC = 1, diff --git a/src/basic/socket-label.c b/src/basic/socket-label.c index 144e6fd86e..937124cc02 100644 --- a/src/basic/socket-label.c +++ b/src/basic/socket-label.c @@ -146,11 +146,8 @@ int make_socket_fd(int log_level, const char* address, int flags) { int fd, r; r = socket_address_parse(&a, address); - if (r < 0) { - log_error("Failed to parse socket address \"%s\": %s", - address, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to parse socket address \"%s\": %m", address); fd = socket_address_listen(&a, flags, SOMAXCONN, SOCKET_ADDRESS_DEFAULT, NULL, false, false, false, 0755, 0644, NULL); diff --git a/src/basic/strv.c b/src/basic/strv.c index b9aef64b15..b66c176487 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -188,17 +188,48 @@ char **strv_new(const char *x, ...) { return r; } -int strv_extend_strv(char ***a, char **b) { - int r; - char **s; +int strv_extend_strv(char ***a, char **b, bool filter_duplicates) { + char **s, **t; + size_t p, q, i = 0, j; + + assert(a); + + if (strv_isempty(b)) + return 0; + + p = strv_length(*a); + q = strv_length(b); + + t = realloc(*a, sizeof(char*) * (p + q + 1)); + if (!t) + return -ENOMEM; + + t[p] = NULL; + *a = t; STRV_FOREACH(s, b) { - r = strv_extend(a, *s); - if (r < 0) - return r; + + if (filter_duplicates && strv_contains(t, *s)) + continue; + + t[p+i] = strdup(*s); + if (!t[p+i]) + goto rollback; + + i++; + t[p+i] = NULL; } - return 0; + assert(i <= q); + + return (int) i; + +rollback: + for (j = 0; j < i; j++) + free(t[p + j]); + + t[p] = NULL; + return -ENOMEM; } int strv_extend_strv_concat(char ***a, char **b, const char *suffix) { @@ -277,8 +308,8 @@ char **strv_split_newlines(const char *s) { } int strv_split_extract(char ***t, const char *s, const char *separators, ExtractFlags flags) { - size_t n = 0, allocated = 0; _cleanup_strv_free_ char **l = NULL; + size_t n = 0, allocated = 0; int r; assert(t); @@ -302,13 +333,16 @@ int strv_split_extract(char ***t, const char *s, const char *separators, Extract l[n] = NULL; } - if (!l) + if (!l) { l = new0(char*, 1); + if (!l) + return -ENOMEM; + } *t = l; l = NULL; - return 0; + return (int) n; } char *strv_join(char **l, const char *separator) { @@ -615,6 +649,41 @@ char **strv_split_nulstr(const char *s) { return r; } +int strv_make_nulstr(char **l, char **p, size_t *q) { + size_t n_allocated = 0, n = 0; + _cleanup_free_ char *m = NULL; + char **i; + + assert(p); + assert(q); + + STRV_FOREACH(i, l) { + size_t z; + + z = strlen(*i); + + if (!GREEDY_REALLOC(m, n_allocated, n + z + 1)) + return -ENOMEM; + + memcpy(m + n, *i, z + 1); + n += z + 1; + } + + if (!m) { + m = new0(char, 1); + if (!m) + return -ENOMEM; + n = 0; + } + + *p = m; + *q = n; + + m = NULL; + + return 0; +} + bool strv_overlap(char **a, char **b) { char **i; @@ -641,8 +710,12 @@ char **strv_sort(char **l) { } bool strv_equal(char **a, char **b) { - if (!a || !b) - return a == b; + + if (strv_isempty(a)) + return strv_isempty(b); + + if (strv_isempty(b)) + return false; for ( ; *a || *b; ++a, ++b) if (!streq_ptr(*a, *b)) @@ -720,3 +793,66 @@ bool strv_fnmatch(char* const* patterns, const char *s, int flags) { return false; } + +char ***strv_free_free(char ***l) { + char ***i; + + if (!l) + return NULL; + + for (i = l; *i; i++) + strv_free(*i); + + free(l); + return NULL; +} + +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 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; +} diff --git a/src/basic/strv.h b/src/basic/strv.h index f07da8cdf3..e49f443835 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -40,7 +40,7 @@ void strv_clear(char **l); char **strv_copy(char * const *l); unsigned strv_length(char * const *l) _pure_; -int strv_extend_strv(char ***a, char **b); +int strv_extend_strv(char ***a, char **b, bool filter_duplicates); int strv_extend_strv_concat(char ***a, char **b, const char *suffix); int strv_extend(char ***l, const char *value); int strv_extendf(char ***l, const char *format, ...) _printf_(2,0); @@ -80,6 +80,7 @@ char *strv_join_quoted(char **l); char **strv_parse_nulstr(const char *s, size_t l); char **strv_split_nulstr(const char *s); +int strv_make_nulstr(char **l, char **p, size_t *n); bool strv_overlap(char **a, char **b) _pure_; @@ -154,3 +155,9 @@ static inline bool strv_fnmatch_or_empty(char* const* patterns, const char *s, i 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); diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index c5ef5ab0d1..ca7554a9fa 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -48,7 +48,7 @@ int chvt(int vt) { if (fd < 0) return -errno; - if (vt < 0) { + if (vt <= 0) { int tiocl[2] = { TIOCL_GETKMSGREDIRECT, 0 @@ -139,14 +139,14 @@ int ask_char(char *ret, const char *replies, const char *text, ...) { bool need_nl = true; if (on_tty()) - fputs(ANSI_HIGHLIGHT_ON, stdout); + fputs(ANSI_HIGHLIGHT, stdout); va_start(ap, text); vprintf(text, ap); va_end(ap); if (on_tty()) - fputs(ANSI_HIGHLIGHT_OFF, stdout); + fputs(ANSI_NORMAL, stdout); fflush(stdout); @@ -183,14 +183,14 @@ int ask_string(char **ret, const char *text, ...) { va_list ap; if (on_tty()) - fputs(ANSI_HIGHLIGHT_ON, stdout); + fputs(ANSI_HIGHLIGHT, stdout); va_start(ap, text); vprintf(text, ap); va_end(ap); if (on_tty()) - fputs(ANSI_HIGHLIGHT_OFF, stdout); + fputs(ANSI_NORMAL, stdout); fflush(stdout); @@ -480,10 +480,6 @@ int acquire_terminal( safe_close(notify); - r = reset_terminal_fd(fd, true); - if (r < 0) - log_warning_errno(r, "Failed to reset terminal: %m"); - return fd; fail: @@ -539,8 +535,9 @@ int terminal_vhangup(const char *name) { } int vt_disallocate(const char *name) { - int fd, r; + _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 @@ -562,8 +559,6 @@ int vt_disallocate(const char *name) { "\033[H" /* move home */ "\033[2J", /* clear screen */ 10, false); - safe_close(fd); - return 0; } @@ -583,7 +578,7 @@ int vt_disallocate(const char *name) { return fd; r = ioctl(fd, VT_DISALLOCATE, u); - safe_close(fd); + fd = safe_close(fd); if (r >= 0) return 0; @@ -602,32 +597,9 @@ int vt_disallocate(const char *name) { "\033[H" /* move home */ "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */ 10, false); - safe_close(fd); - return 0; } -void warn_melody(void) { - _cleanup_close_ int fd = -1; - - fd = open("/dev/console", O_WRONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) - return; - - /* Yeah, this is synchronous. Kinda sucks. But well... */ - - (void) ioctl(fd, KIOCSOUND, (int)(1193180/440)); - usleep(125*USEC_PER_MSEC); - - (void) ioctl(fd, KIOCSOUND, (int)(1193180/220)); - usleep(125*USEC_PER_MSEC); - - (void) ioctl(fd, KIOCSOUND, (int)(1193180/220)); - usleep(125*USEC_PER_MSEC); - - (void) ioctl(fd, KIOCSOUND, 0); -} - int make_console_stdio(void) { int fd, r; @@ -637,6 +609,10 @@ int make_console_stdio(void) { 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"); @@ -1075,6 +1051,33 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) { 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; @@ -1093,3 +1096,104 @@ int ptsname_namespace(int pty, char **ret) { 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); +} diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h index b9a3809a6c..ee0b68b433 100644 --- a/src/basic/terminal-util.h +++ b/src/basic/terminal-util.h @@ -26,16 +26,22 @@ #include "macro.h" #include "time-util.h" -#define ANSI_HIGHLIGHT_ON "\x1B[1;39m" -#define ANSI_RED_ON "\x1B[31m" -#define ANSI_HIGHLIGHT_RED_ON "\x1B[1;31m" -#define ANSI_GREEN_ON "\x1B[32m" -#define ANSI_HIGHLIGHT_GREEN_ON "\x1B[1;32m" -#define ANSI_HIGHLIGHT_YELLOW_ON "\x1B[1;33m" -#define ANSI_HIGHLIGHT_BLUE_ON "\x1B[1;34m" -#define ANSI_HIGHLIGHT_OFF "\x1B[0m" +#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_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); @@ -61,8 +67,6 @@ bool tty_is_console(const char *tty) _pure_; int vtnr_from_tty(const char *tty); const char *default_term_for_tty(const char *tty); -void warn_melody(void); - int make_stdio(int fd); int make_null_stdio(void); int make_console_stdio(void); @@ -78,28 +82,36 @@ void columns_lines_cache_reset(int _unused_ signum); bool on_tty(void); +static inline const char *ansi_underline(void) { + return on_tty() ? ANSI_UNDERLINE : ""; +} + static inline const char *ansi_highlight(void) { - return on_tty() ? ANSI_HIGHLIGHT_ON : ""; + return on_tty() ? ANSI_HIGHLIGHT : ""; +} + +static inline const char *ansi_highlight_underline(void) { + return on_tty() ? ANSI_HIGHLIGHT_UNDERLINE : ""; } static inline const char *ansi_highlight_red(void) { - return on_tty() ? ANSI_HIGHLIGHT_RED_ON : ""; + return on_tty() ? ANSI_HIGHLIGHT_RED : ""; } static inline const char *ansi_highlight_green(void) { - return on_tty() ? ANSI_HIGHLIGHT_GREEN_ON : ""; + return on_tty() ? ANSI_HIGHLIGHT_GREEN : ""; } static inline const char *ansi_highlight_yellow(void) { - return on_tty() ? ANSI_HIGHLIGHT_YELLOW_ON : ""; + return on_tty() ? ANSI_HIGHLIGHT_YELLOW : ""; } static inline const char *ansi_highlight_blue(void) { - return on_tty() ? ANSI_HIGHLIGHT_BLUE_ON : ""; + return on_tty() ? ANSI_HIGHLIGHT_BLUE : ""; } -static inline const char *ansi_highlight_off(void) { - return on_tty() ? ANSI_HIGHLIGHT_OFF : ""; +static inline const char *ansi_normal(void) { + return on_tty() ? ANSI_NORMAL : ""; } int get_ctty_devnr(pid_t pid, dev_t *d); @@ -108,4 +120,8 @@ 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 index 531931f6e1..d4e0914b27 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -19,7 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <time.h> #include <string.h> #include <sys/timex.h> #include <sys/timerfd.h> @@ -205,11 +204,8 @@ static char *format_timestamp_internal(char *buf, size_t l, usec_t t, bool utc) return NULL; sec = (time_t) (t / USEC_PER_SEC); + localtime_or_gmtime_r(&sec, &tm, utc); - if (utc) - gmtime_r(&sec, &tm); - else - localtime_r(&sec, &tm); if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm) <= 0) return NULL; @@ -235,10 +231,7 @@ static char *format_timestamp_internal_us(char *buf, size_t l, usec_t t, bool ut return NULL; sec = (time_t) (t / USEC_PER_SEC); - if (utc) - gmtime_r(&sec, &tm); - else - localtime_r(&sec, &tm); + localtime_or_gmtime_r(&sec, &tm, utc); if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0) return NULL; @@ -484,9 +477,10 @@ int parse_timestamp(const char *t, usec_t *usec) { }; const char *k; + bool utc; struct tm tm, copy; time_t x; - usec_t plus = 0, minus = 0, ret; + usec_t x_usec, plus = 0, minus = 0, ret; int r, weekday = -1; unsigned i; @@ -511,28 +505,15 @@ int parse_timestamp(const char *t, usec_t *usec) { assert(t); assert(usec); - x = time(NULL); - assert_se(localtime_r(&x, &tm)); - tm.tm_isdst = -1; - - if (streq(t, "now")) - goto finish; - - else if (streq(t, "today")) { - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto finish; + if (t[0] == '@') + return parse_sec(t + 1, usec); - } else if (streq(t, "yesterday")) { - tm.tm_mday --; - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto finish; + ret = now(CLOCK_REALTIME); - } else if (streq(t, "tomorrow")) { - tm.tm_mday ++; - tm.tm_sec = tm.tm_min = tm.tm_hour = 0; + if (streq(t, "now")) goto finish; - } else if (t[0] == '+') { + else if (t[0] == '+') { r = parse_sec(t+1, &plus); if (r < 0) return r; @@ -546,35 +527,51 @@ int parse_timestamp(const char *t, usec_t *usec) { goto finish; - } else if (t[0] == '@') - return parse_sec(t + 1, usec); - - else if (endswith(t, " ago")) { - _cleanup_free_ char *z; - - z = strndup(t, strlen(t) - 4); - if (!z) - return -ENOMEM; + } else if (endswith(t, " ago")) { + t = strndupa(t, strlen(t) - strlen(" ago")); - r = parse_sec(z, &minus); + r = parse_sec(t, &minus); if (r < 0) return r; goto finish; - } else if (endswith(t, " left")) { - _cleanup_free_ char *z; - z = strndup(t, strlen(t) - 4); - if (!z) - return -ENOMEM; + } else if (endswith(t, " left")) { + t = strndupa(t, strlen(t) - strlen(" left")); - r = parse_sec(z, &plus); + r = parse_sec(t, &plus); if (r < 0) return r; goto finish; } + utc = endswith_no_case(t, " UTC"); + if (utc) + t = strndupa(t, strlen(t) - strlen(" UTC")); + + x = ret / USEC_PER_SEC; + x_usec = 0; + + assert_se(localtime_or_gmtime_r(&x, &tm, utc)); + tm.tm_isdst = -1; + + if (streq(t, "today")) { + tm.tm_sec = tm.tm_min = tm.tm_hour = 0; + goto from_tm; + + } else if (streq(t, "yesterday")) { + tm.tm_mday --; + tm.tm_sec = tm.tm_min = tm.tm_hour = 0; + goto from_tm; + + } else if (streq(t, "tomorrow")) { + tm.tm_mday ++; + tm.tm_sec = tm.tm_min = tm.tm_hour = 0; + goto from_tm; + } + + for (i = 0; i < ELEMENTSOF(day_nr); i++) { size_t skip; @@ -592,66 +589,106 @@ int parse_timestamp(const char *t, usec_t *usec) { copy = tm; k = strptime(t, "%y-%m-%d %H:%M:%S", &tm); - if (k && *k == 0) - goto finish; + if (k) { + if (*k == '.') + goto parse_usec; + else if (*k == 0) + goto from_tm; + } tm = copy; k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm); - if (k && *k == 0) - goto finish; + if (k) { + if (*k == '.') + goto parse_usec; + else if (*k == 0) + goto from_tm; + } tm = copy; k = strptime(t, "%y-%m-%d %H:%M", &tm); if (k && *k == 0) { tm.tm_sec = 0; - goto finish; + goto from_tm; } tm = copy; k = strptime(t, "%Y-%m-%d %H:%M", &tm); if (k && *k == 0) { tm.tm_sec = 0; - goto finish; + goto from_tm; } tm = copy; k = strptime(t, "%y-%m-%d", &tm); if (k && *k == 0) { tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto finish; + goto from_tm; } tm = copy; k = strptime(t, "%Y-%m-%d", &tm); if (k && *k == 0) { tm.tm_sec = tm.tm_min = tm.tm_hour = 0; - goto finish; + goto from_tm; } tm = copy; k = strptime(t, "%H:%M:%S", &tm); - if (k && *k == 0) - goto finish; + if (k) { + if (*k == '.') + goto parse_usec; + else if (*k == 0) + goto from_tm; + } tm = copy; k = strptime(t, "%H:%M", &tm); if (k && *k == 0) { tm.tm_sec = 0; - goto finish; + goto from_tm; } return -EINVAL; -finish: - x = mktime(&tm); +parse_usec: + { + char *end; + unsigned long long val; + size_t l; + + k++; + if (*k < '0' || *k > '9') + return -EINVAL; + + /* base 10 instead of base 0, .09 is not base 8 */ + errno = 0; + val = strtoull(k, &end, 10); + if (*end || errno) + return -EINVAL; + + l = end-k; + + /* val has l digits, make them 6 */ + for (; l < 6; l++) + val *= 10; + for (; l > 6; l--) + val /= 10; + + x_usec = val; + } + +from_tm: + x = mktime_or_timegm(&tm, utc); if (x == (time_t) -1) return -EINVAL; if (weekday >= 0 && tm.tm_wday != weekday) return -EINVAL; - ret = (usec_t) x * USEC_PER_SEC; + ret = (usec_t) x * USEC_PER_SEC + x_usec; +finish: ret += plus; if (ret > minus) ret -= minus; @@ -1072,3 +1109,11 @@ int get_timezone(char **tz) { *tz = z; return 0; } + +time_t mktime_or_timegm(struct tm *tm, bool utc) { + return utc ? timegm(tm) : mktime(tm); +} + +struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) { + return utc ? gmtime_r(t, tm) : localtime_r(t, tm); +} diff --git a/src/basic/time-util.h b/src/basic/time-util.h index de881e8fe1..417376ea96 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -21,8 +21,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdio.h> #include <inttypes.h> +#include <stdio.h> +#include <time.h> typedef uint64_t usec_t; typedef uint64_t nsec_t; @@ -112,6 +113,11 @@ bool timezone_is_valid(const char *name); clockid_t clock_boottime_or_monotonic(void); -#define xstrftime(buf, fmt, tm) assert_se(strftime(buf, ELEMENTSOF(buf), fmt, tm) > 0) +#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); diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c index 8742ee757f..a8b6b6dace 100644 --- a/src/basic/unit-name.c +++ b/src/basic/unit-name.c @@ -839,6 +839,170 @@ static const char* const unit_load_state_table[_UNIT_LOAD_STATE_MAX] = { 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 snapshot_state_table[_SNAPSHOT_STATE_MAX] = { + [SNAPSHOT_DEAD] = "dead", + [SNAPSHOT_ACTIVE] = "active" +}; + +DEFINE_STRING_TABLE_LOOKUP(snapshot_state, SnapshotState); + +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_REQUIRES_OVERRIDABLE] = "RequiresOverridable", diff --git a/src/basic/unit-name.h b/src/basic/unit-name.h index 28b3a555f3..65b55d9554 100644 --- a/src/basic/unit-name.h +++ b/src/basic/unit-name.h @@ -27,11 +27,7 @@ #define UNIT_NAME_MAX 256 -typedef enum UnitType UnitType; -typedef enum UnitLoadState UnitLoadState; -typedef enum UnitDependency UnitDependency; - -enum UnitType { +typedef enum UnitType { UNIT_SERVICE = 0, UNIT_SOCKET, UNIT_BUSNAME, @@ -47,9 +43,9 @@ enum UnitType { UNIT_SCOPE, _UNIT_TYPE_MAX, _UNIT_TYPE_INVALID = -1 -}; +} UnitType; -enum UnitLoadState { +typedef enum UnitLoadState { UNIT_STUB = 0, UNIT_LOADED, UNIT_NOT_FOUND, @@ -58,9 +54,176 @@ enum UnitLoadState { UNIT_MASKED, _UNIT_LOAD_STATE_MAX, _UNIT_LOAD_STATE_INVALID = -1 -}; - -enum UnitDependency { +} 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 SnapshotState { + SNAPSHOT_DEAD, + SNAPSHOT_ACTIVE, + _SNAPSHOT_STATE_MAX, + _SNAPSHOT_STATE_INVALID = -1 +} SnapshotState; + +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_REQUIRES_OVERRIDABLE, @@ -107,7 +270,7 @@ enum UnitDependency { _UNIT_DEPENDENCY_MAX, _UNIT_DEPENDENCY_INVALID = -1 -}; +} UnitDependency; typedef enum UnitNameFlags { UNIT_NAME_PLAIN = 1, /* Allow foo.service */ @@ -176,5 +339,47 @@ 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* snapshot_state_to_string(SnapshotState i) _const_; +SnapshotState snapshot_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/util.c b/src/basic/util.c index e3b2af8e02..8b896a2df3 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -19,49 +19,48 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <string.h> -#include <unistd.h> +#include <ctype.h> +#include <dirent.h> #include <errno.h> -#include <stdlib.h> -#include <signal.h> +#include <fcntl.h> +#include <glob.h> +#include <grp.h> +#include <langinfo.h> #include <libintl.h> -#include <stdio.h> -#include <syslog.h> -#include <sched.h> -#include <sys/resource.h> +#include <limits.h> +#include <linux/magic.h> #include <linux/sched.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <dirent.h> -#include <sys/ioctl.h> -#include <stdarg.h> +#include <locale.h> +#include <netinet/ip.h> #include <poll.h> -#include <ctype.h> -#include <sys/prctl.h> -#include <sys/utsname.h> #include <pwd.h> -#include <netinet/ip.h> -#include <sys/wait.h> -#include <sys/time.h> -#include <glob.h> -#include <grp.h> +#include <sched.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/file.h> +#include <sys/ioctl.h> #include <sys/mman.h> -#include <sys/vfs.h> #include <sys/mount.h> -#include <linux/magic.h> -#include <limits.h> -#include <langinfo.h> -#include <locale.h> #include <sys/personality.h> -#include <sys/xattr.h> +#include <sys/prctl.h> +#include <sys/resource.h> +#include <sys/stat.h> #include <sys/statvfs.h> -#include <sys/file.h> -#include <linux/fs.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/utsname.h> +#include <sys/vfs.h> +#include <sys/wait.h> +#include <sys/xattr.h> +#include <syslog.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(). */ + * 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 @@ -69,31 +68,35 @@ #include <sys/auxv.h> #endif -#include "config.h" -#include "macro.h" -#include "util.h" +/* We include linux/fs.h as last of the system headers, as it + * otherwise conflicts with sys/mount.h. Yay, Linux is great! */ +#include <linux/fs.h> + +#include "build.h" +#include "def.h" +#include "device-nodes.h" +#include "env-util.h" +#include "exit-status.h" +#include "fileio.h" +#include "formats-util.h" +#include "gunicode.h" +#include "hashmap.h" +#include "hostname-util.h" #include "ioprio.h" -#include "missing.h" #include "log.h" -#include "strv.h" +#include "macro.h" +#include "missing.h" #include "mkdir.h" #include "path-util.h" -#include "exit-status.h" -#include "hashmap.h" -#include "env-util.h" -#include "fileio.h" -#include "device-nodes.h" -#include "utf8.h" -#include "gunicode.h" -#include "virt.h" -#include "def.h" -#include "sparse-endian.h" -#include "formats-util.h" #include "process-util.h" #include "random-util.h" -#include "terminal-util.h" -#include "hostname-util.h" #include "signal-util.h" +#include "sparse-endian.h" +#include "strv.h" +#include "terminal-util.h" +#include "utf8.h" +#include "util.h" +#include "virt.h" /* Put this test here for a lack of better place */ assert_cc(EAGAIN == EWOULDBLOCK); @@ -354,6 +357,17 @@ FILE* safe_fclose(FILE *f) { return NULL; } +DIR* safe_closedir(DIR *d) { + + if (d) { + PROTECT_ERRNO; + + assert_se(closedir(d) >= 0 || errno != EBADF); + } + + return NULL; +} + int unlink_noerrno(const char *path) { PROTECT_ERRNO; int r; @@ -2133,7 +2147,13 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { assert(fd >= 0); assert(buf); - while (nbytes > 0) { + /* 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); @@ -2147,7 +2167,7 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { * and expect that any error/EOF is reported * via read() */ - fd_wait_for_event(fd, POLLIN, USEC_INFINITY); + (void) fd_wait_for_event(fd, POLLIN, USEC_INFINITY); continue; } @@ -2157,10 +2177,12 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { if (k == 0) return n; + assert((size_t) k <= nbytes); + p += k; nbytes -= k; n += k; - } + } while (nbytes > 0); return n; } @@ -2170,9 +2192,10 @@ int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) { n = loop_read(fd, buf, nbytes, do_poll); if (n < 0) - return n; + return (int) n; if ((size_t) n != nbytes) return -EIO; + return 0; } @@ -2182,7 +2205,8 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { assert(fd >= 0); assert(buf); - errno = 0; + if (nbytes > (size_t) SSIZE_MAX) + return -EINVAL; do { ssize_t k; @@ -2197,16 +2221,18 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { * and expect that any error/EOF is reported * via write() */ - fd_wait_for_event(fd, POLLOUT, USEC_INFINITY); + (void) fd_wait_for_event(fd, POLLOUT, USEC_INFINITY); continue; } return -errno; } - if (nbytes > 0 && k == 0) /* Can't really happen */ + if (_unlikely_(nbytes > 0 && k == 0)) /* Can't really happen */ return -EIO; + assert((size_t) k <= nbytes); + p += k; nbytes -= k; } while (nbytes > 0); @@ -2525,34 +2551,6 @@ int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) { return 0; } -cpu_set_t* cpu_set_malloc(unsigned *ncpus) { - cpu_set_t *r; - unsigned n = 1024; - - /* Allocates the cpuset in the right size */ - - for (;;) { - if (!(r = CPU_ALLOC(n))) - return NULL; - - if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), r) >= 0) { - CPU_ZERO_S(CPU_ALLOC_SIZE(n), r); - - if (ncpus) - *ncpus = n; - - return r; - } - - CPU_FREE(r); - - if (errno != EINVAL) - return NULL; - - n *= 2; - } -} - int files_same(const char *filea, const char *fileb) { struct stat a, b; @@ -3701,6 +3699,10 @@ static const char *const log_facility_unshifted_table[LOG_NFACILITIES] = { 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", @@ -3714,6 +3716,10 @@ static const char *const log_level_table[] = { DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(log_level, int, LOG_DEBUG); +bool log_level_is_valid(int level) { + return level >= 0 && level <= LOG_DEBUG; +} + static const char* const sched_policy_table[] = { [SCHED_OTHER] = "other", [SCHED_BATCH] = "batch", @@ -5236,6 +5242,19 @@ unsigned long personality_from_string(const char *p) { if (streq(p, "x86")) return PER_LINUX; + +#elif defined(__s390x__) + + if (streq(p, "s390")) + return PER_LINUX32; + + if (streq(p, "s390x")) + return PER_LINUX; + +#elif defined(__s390__) + + if (streq(p, "s390")) + return PER_LINUX; #endif return PERSONALITY_INVALID; @@ -5255,6 +5274,20 @@ const char* personality_to_string(unsigned long p) { if (p == PER_LINUX) return "x86"; + +#elif defined(__s390x__) + + if (p == PER_LINUX) + return "s390x"; + + if (p == PER_LINUX32) + return "s390"; + +#elif defined(__s390__) + + if (p == PER_LINUX) + return "s390"; + #endif return NULL; @@ -5319,15 +5352,13 @@ int update_reboot_param_file(const char *param) { int r = 0; if (param) { - r = write_string_file(REBOOT_PARAM_FILE, param, WRITE_STRING_FILE_CREATE); if (r < 0) - log_error("Failed to write reboot param to " - REBOOT_PARAM_FILE": %s", strerror(-r)); + return log_error_errno(r, "Failed to write reboot param to "REBOOT_PARAM_FILE": %m"); } else - unlink(REBOOT_PARAM_FILE); + (void) unlink(REBOOT_PARAM_FILE); - return r; + return 0; } int umount_recursive(const char *prefix, int flags) { @@ -5961,6 +5992,7 @@ int extract_first_word_and_warn( 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. */ @@ -5969,17 +6001,17 @@ int extract_first_word_and_warn( save = *p; r = extract_first_word(p, ret, separators, flags); - if (r < 0 && !(flags&EXTRACT_CUNESCAPE_RELAX)) { + if (r < 0 && !(flags & EXTRACT_CUNESCAPE_RELAX)) { + /* Retry it with EXTRACT_CUNESCAPE_RELAX. */ *p = save; r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX); if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Unbalanced quoting in command line, ignoring: \"%s\"", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Unbalanced quoting in command line, ignoring: \"%s\"", rvalue); else - log_syntax(unit, LOG_WARNING, filename, line, EINVAL, - "Invalid escape sequences in command line: \"%s\"", rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid escape sequences in command line: \"%s\"", rvalue); } + return r; } @@ -6063,133 +6095,20 @@ int free_and_strdup(char **p, const char *s) { return 1; } -int ptsname_malloc(int fd, char **ret) { - size_t l = 100; - - assert(fd >= 0); - assert(ret); - - for (;;) { - char *c; - - c = new(char, l); - if (!c) - return -ENOMEM; - - if (ptsname_r(fd, c, l) == 0) { - *ret = c; - return 0; - } - if (errno != ERANGE) { - free(c); - return -errno; - } - - free(c); - l *= 2; - } -} - -int openpt_in_namespace(pid_t pid, int flags) { - _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1; - _cleanup_close_pair_ int pair[2] = { -1, -1 }; - 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; - siginfo_t si; - pid_t child; - int r; - - assert(pid > 0); - - r = namespace_open(pid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd); - if (r < 0) - return r; - - if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) - return -errno; - - child = fork(); - if (child < 0) - return -errno; - - if (child == 0) { - int master; - - pair[0] = safe_close(pair[0]); - - r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd); - if (r < 0) - _exit(EXIT_FAILURE); - - master = posix_openpt(flags); - if (master < 0) - _exit(EXIT_FAILURE); - - if (unlockpt(master) < 0) - _exit(EXIT_FAILURE); - - 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), &master, sizeof(int)); - - mh.msg_controllen = cmsg->cmsg_len; - - if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 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; - - if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0) - return -errno; - - CMSG_FOREACH(cmsg, &mh) - if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { - int *fds; - unsigned n_fds; - - fds = (int*) CMSG_DATA(cmsg); - n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); - - if (n_fds != 1) { - close_many(fds, n_fds); - return -EIO; - } - - return fds[0]; - } - - return -EIO; -} - ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags) { + char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; _cleanup_close_ int fd = -1; ssize_t l; /* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */ - fd = openat(dirfd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOATIME|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0)); + fd = openat(dirfd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0)); if (fd < 0) return -errno; - l = fgetxattr(fd, attribute, value, size); + xsprintf(fn, "/proc/self/fd/%i", fd); + + l = getxattr(fn, attribute, value, size); if (l < 0) return -errno; @@ -6538,7 +6457,7 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k for (i = 0; i < len; ++i) if (streq_ptr(table[i], key)) - return (ssize_t)i; + return (ssize_t) i; return -1; } @@ -6775,3 +6694,109 @@ int fgetxattr_malloc(int fd, const char *name, char **value) { return -errno; } } + +int send_one_fd(int transport_fd, int fd, int flags) { + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(int))]; + } control = {}; + struct msghdr mh = { + .msg_control = &control, + .msg_controllen = sizeof(control), + }; + struct cmsghdr *cmsg; + + assert(transport_fd >= 0); + assert(fd >= 0); + + cmsg = CMSG_FIRSTHDR(&mh); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); + + mh.msg_controllen = CMSG_SPACE(sizeof(int)); + if (sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags) < 0) + return -errno; + + return 0; +} + +int receive_one_fd(int transport_fd, int flags) { + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(int))]; + } control = {}; + struct msghdr mh = { + .msg_control = &control, + .msg_controllen = sizeof(control), + }; + struct cmsghdr *cmsg, *found = NULL; + + assert(transport_fd >= 0); + + /* + * Receive a single FD via @transport_fd. We don't care for + * the transport-type. We retrieve a single FD at most, so for + * packet-based transports, the caller must ensure to send + * only a single FD per packet. This is best used in + * combination with send_one_fd(). + */ + + if (recvmsg(transport_fd, &mh, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC | flags) < 0) + return -errno; + + CMSG_FOREACH(cmsg, &mh) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS && + cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { + assert(!found); + found = cmsg; + break; + } + } + + if (!found) { + cmsg_close_all(&mh); + return -EIO; + } + + return *(int*) CMSG_DATA(found); +} + +void nop_signal_handler(int sig) { + /* nothing here */ +} + +int version(void) { + puts(PACKAGE_STRING "\n" + SYSTEMD_FEATURES); + return 0; +} + +bool fdname_is_valid(const char *s) { + const char *p; + + /* Validates a name for $LISTEN_FDNAMES. We basically allow + * everything ASCII that's not a control character. Also, as + * special exception the ":" character is not allowed, as we + * use that as field separator in $LISTEN_FDNAMES. + * + * Note that the empty string is explicitly allowed + * here. However, we limit the length of the names to 255 + * characters. */ + + if (!s) + return false; + + for (p = s; *p; p++) { + if (*p < ' ') + return false; + if (*p >= 127) + return false; + if (*p == ':') + return false; + } + + return p - s < 256; +} diff --git a/src/basic/util.h b/src/basic/util.h index c7dff9a86d..2544ad0830 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -22,30 +22,29 @@ ***/ #include <alloca.h> +#include <dirent.h> #include <fcntl.h> #include <inttypes.h> -#include <time.h> +#include <limits.h> +#include <locale.h> +#include <mntent.h> #include <stdarg.h> #include <stdbool.h> -#include <stdlib.h> +#include <stddef.h> #include <stdio.h> -#include <sched.h> -#include <limits.h> -#include <sys/types.h> +#include <stdlib.h> +#include <sys/inotify.h> #include <sys/socket.h> #include <sys/stat.h> -#include <dirent.h> -#include <stddef.h> -#include <unistd.h> -#include <locale.h> -#include <mntent.h> -#include <sys/inotify.h> #include <sys/statfs.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" -#include "formats-util.h" /* What is interpreted as whitespace? */ #define WHITESPACE " \t\n\r" @@ -151,6 +150,7 @@ void close_many(const int fds[], unsigned n_fd); int fclose_nointr(FILE *f); FILE* safe_fclose(FILE *f); +DIR* safe_closedir(DIR *f); int parse_size(const char *t, uint64_t base, uint64_t *size); @@ -160,7 +160,10 @@ int parse_uid(const char *s, uid_t* ret_uid); #define parse_gid(s, ret_gid) parse_uid(s, ret_gid) bool uid_is_valid(uid_t uid); -#define gid_is_valid(gid) uid_is_valid(gid) + +static inline bool gid_is_valid(gid_t gid) { + return uid_is_valid((uid_t) gid); +} int safe_atou(const char *s, unsigned *ret_u); int safe_atoi(const char *s, int *ret_i); @@ -289,9 +292,9 @@ bool chars_intersect(const char *a, const char *b) _pure_; ssize_t string_table_lookup(const char * const *table, size_t len, const char *key); -#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \ - scope inline 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(name,type,scope) \ + scope type name##_from_string(const char *s) { \ + return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ } #define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \ @@ -308,17 +311,15 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k #define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \ int name##_to_string_alloc(type i, char **str) { \ char *s; \ - int r; \ if (i < 0 || i > max) \ return -ERANGE; \ if (i < (type) ELEMENTSOF(name##_table)) { \ s = strdup(name##_table[i]); \ if (!s) \ - return log_oom(); \ + return -ENOMEM; \ } else { \ - r = asprintf(&s, "%i", i); \ - if (r < 0) \ - return log_oom(); \ + if (asprintf(&s, "%i", i) < 0) \ + return -ENOMEM; \ } \ *str = s; \ return 0; \ @@ -326,10 +327,10 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k type name##_from_string(const char *s) { \ type i; \ unsigned u = 0; \ - assert(s); \ - for (i = 0; i < (type)ELEMENTSOF(name##_table); i++) \ - if (name##_table[i] && \ - streq(name##_table[i], s)) \ + 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; \ @@ -369,12 +370,9 @@ int fd_is_temporary_fs(int fd); int pipe_eof(int fd); -DEFINE_TRIVIAL_CLEANUP_FUNC(cpu_set_t*, CPU_FREE); -#define _cleanup_cpu_free_ _cleanup_(CPU_FREEp) - -cpu_set_t* cpu_set_malloc(unsigned *ncpus); - -#define xsprintf(buf, fmt, ...) assert_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf)) +#define xsprintf(buf, fmt, ...) \ + assert_message_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf), \ + "xsprintf: " #buf "[] must be big enough") int files_same(const char *filea, const char *fileb); @@ -458,9 +456,11 @@ int sigchld_code_from_string(const char *s) _pure_; int log_facility_unshifted_to_string_alloc(int i, char **s); int log_facility_unshifted_from_string(const char *s); +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 sched_policy_to_string_alloc(int i, char **s); int sched_policy_from_string(const char *s); @@ -893,10 +893,6 @@ union inotify_event_buffer { #define laccess(path, mode) faccessat(AT_FDCWD, (path), (mode), AT_SYMLINK_NOFOLLOW) -int ptsname_malloc(int fd, char **ret); - -int openpt_in_namespace(pid_t pid, int flags); - ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, void *value, size_t size, int flags); int fd_setcrtime(int fd, usec_t usec); @@ -936,3 +932,12 @@ int reset_uid_gid(void); int getxattr_malloc(const char *path, const char *name, char **value, bool allow_symlink); int fgetxattr_malloc(int fd, const char *name, char **value); + +int send_one_fd(int transport_fd, int fd, int flags); +int receive_one_fd(int transport_fd, int flags); + +void nop_signal_handler(int sig); + +int version(void); + +bool fdname_is_valid(const char *s); diff --git a/src/basic/virt.c b/src/basic/virt.c index 1fc6c1baba..70543177b6 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -240,7 +240,7 @@ static int detect_vm_zvm(void) { _cleanup_free_ char *t = NULL; int r; - r = get_status_field("/proc/sysinfo", "VM00 Control Program:", &t); + r = get_proc_field("/proc/sysinfo", "VM00 Control Program", WHITESPACE, &t); if (r == -ENOENT) return VIRTUALIZATION_NONE; if (r < 0) |