diff options
Diffstat (limited to 'src')
81 files changed, 1537 insertions, 1927 deletions
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 cc5faa80a1..b8cbe644d4 100644 --- a/src/basic/copy.c +++ b/src/basic/copy.c @@ -29,7 +29,7 @@ #define COPY_BUFFER_SIZE (16*1024) -int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) { +int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { bool try_sendfile = true, try_splice = true; int r; @@ -37,7 +37,7 @@ int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) { assert(fdt >= 0); /* Try btrfs reflinks first. */ - if (try_reflink && max_bytes == (off_t) -1) { + if (try_reflink && max_bytes == (uint64_t) -1) { r = btrfs_reflink(fdf, fdt); if (r >= 0) return r; @@ -47,12 +47,12 @@ int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) { size_t m = COPY_BUFFER_SIZE; ssize_t n; - if (max_bytes != (off_t) -1) { + if (max_bytes != (uint64_t) -1) { if (max_bytes <= 0) return -EFBIG; - if ((off_t) m > max_bytes) + if ((uint64_t) m > max_bytes) m = (size_t) max_bytes; } @@ -105,8 +105,8 @@ int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) { } next: - if (max_bytes != (off_t) -1) { - assert(max_bytes >= n); + if (max_bytes != (uint64_t) -1) { + assert(max_bytes >= (uint64_t) n); max_bytes -= n; } } @@ -152,7 +152,7 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int if (fdt < 0) return -errno; - r = copy_bytes(fdf, fdt, (off_t) -1, true); + r = copy_bytes(fdf, fdt, (uint64_t) -1, true); if (r < 0) { unlinkat(dt, to, 0); return r; @@ -371,7 +371,7 @@ int copy_file_fd(const char *from, int fdt, bool try_reflink) { if (fdf < 0) return -errno; - r = copy_bytes(fdf, fdt, (off_t) -1, try_reflink); + r = copy_bytes(fdf, fdt, (uint64_t) -1, try_reflink); (void) copy_times(fdf, fdt); (void) copy_xattr(fdf, fdt); diff --git a/src/basic/copy.h b/src/basic/copy.h index 8de0cfba32..ba0890b442 100644 --- a/src/basic/copy.h +++ b/src/basic/copy.h @@ -21,6 +21,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <inttypes.h> #include <stdbool.h> #include <sys/types.h> @@ -30,6 +31,6 @@ int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace int copy_tree(const char *from, const char *to, bool merge); int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge); int copy_directory_fd(int dirfd, const char *to, bool merge); -int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink); +int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink); int copy_times(int fdf, int fdt); int copy_xattr(int fdf, int fdt); diff --git a/src/basic/macro.h b/src/basic/macro.h index cbc3ca97b8..248f7a86dd 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); \ } \ 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/time-util.h b/src/basic/time-util.h index de881e8fe1..1af01541fc 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -112,6 +112,8 @@ 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); diff --git a/src/basic/util.c b/src/basic/util.c index f7b2edf88c..e3b2af8e02 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -2214,7 +2214,7 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { return 0; } -int parse_size(const char *t, off_t base, off_t *size) { +int parse_size(const char *t, uint64_t base, uint64_t *size) { /* Soo, sometimes we want to parse IEC binary suffixes, and * sometimes SI decimal suffixes. This function can parse @@ -2242,8 +2242,8 @@ int parse_size(const char *t, off_t base, off_t *size) { { "G", 1024ULL*1024ULL*1024ULL }, { "M", 1024ULL*1024ULL }, { "K", 1024ULL }, - { "B", 1 }, - { "", 1 }, + { "B", 1ULL }, + { "", 1ULL }, }; static const struct table si[] = { @@ -2253,8 +2253,8 @@ int parse_size(const char *t, off_t base, off_t *size) { { "G", 1000ULL*1000ULL*1000ULL }, { "M", 1000ULL*1000ULL }, { "K", 1000ULL }, - { "B", 1 }, - { "", 1 }, + { "B", 1ULL }, + { "", 1ULL }, }; const struct table *table; @@ -2276,33 +2276,32 @@ int parse_size(const char *t, off_t base, off_t *size) { p = t; do { - long long l; - unsigned long long l2; + unsigned long long l, tmp; double frac = 0; char *e; unsigned i; - errno = 0; - l = strtoll(p, &e, 10); + p += strspn(p, WHITESPACE); + if (*p == '-') + return -ERANGE; + errno = 0; + l = strtoull(p, &e, 10); if (errno > 0) return -errno; - - if (l < 0) - return -ERANGE; - if (e == p) return -EINVAL; if (*e == '.') { e++; + + /* strtoull() itself would accept space/+/- */ if (*e >= '0' && *e <= '9') { + unsigned long long l2; char *e2; - /* strotoull itself would accept space/+/- */ l2 = strtoull(e, &e2, 10); - - if (errno == ERANGE) + if (errno > 0) return -errno; /* Ignore failure. E.g. 10.M is valid */ @@ -2315,27 +2314,27 @@ int parse_size(const char *t, off_t base, off_t *size) { e += strspn(e, WHITESPACE); for (i = start_pos; i < n_entries; i++) - if (startswith(e, table[i].suffix)) { - unsigned long long tmp; - if ((unsigned long long) l + (frac > 0) > ULLONG_MAX / table[i].factor) - return -ERANGE; - tmp = l * table[i].factor + (unsigned long long) (frac * table[i].factor); - if (tmp > ULLONG_MAX - r) - return -ERANGE; - - r += tmp; - if ((unsigned long long) (off_t) r != r) - return -ERANGE; - - p = e + strlen(table[i].suffix); - - start_pos = i + 1; + if (startswith(e, table[i].suffix)) break; - } if (i >= n_entries) return -EINVAL; + if (l + (frac > 0) > ULLONG_MAX / table[i].factor) + return -ERANGE; + + tmp = l * table[i].factor + (unsigned long long) (frac * table[i].factor); + if (tmp > ULLONG_MAX - r) + return -ERANGE; + + r += tmp; + if ((unsigned long long) (uint64_t) r != r) + return -ERANGE; + + p = e + strlen(table[i].suffix); + + start_pos = i + 1; + } while (*p); *size = r; @@ -3785,38 +3784,38 @@ int prot_from_flags(int flags) { } } -char *format_bytes(char *buf, size_t l, off_t t) { +char *format_bytes(char *buf, size_t l, uint64_t t) { unsigned i; static const struct { const char *suffix; - off_t factor; + uint64_t factor; } table[] = { - { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL }, - { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL }, - { "T", 1024ULL*1024ULL*1024ULL*1024ULL }, - { "G", 1024ULL*1024ULL*1024ULL }, - { "M", 1024ULL*1024ULL }, - { "K", 1024ULL }, + { "E", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, + { "P", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, + { "T", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, + { "G", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, + { "M", UINT64_C(1024)*UINT64_C(1024) }, + { "K", UINT64_C(1024) }, }; - if (t == (off_t) -1) + if (t == (uint64_t) -1) return NULL; for (i = 0; i < ELEMENTSOF(table); i++) { if (t >= table[i].factor) { snprintf(buf, l, - "%llu.%llu%s", - (unsigned long long) (t / table[i].factor), - (unsigned long long) (((t*10ULL) / table[i].factor) % 10ULL), + "%" PRIu64 ".%" PRIu64 "%s", + t / table[i].factor, + ((t*UINT64_C(10)) / table[i].factor) % UINT64_C(10), table[i].suffix); goto finish; } } - snprintf(buf, l, "%lluB", (unsigned long long) t); + snprintf(buf, l, "%" PRIu64 "B", t); finish: buf[l-1] = 0; diff --git a/src/basic/util.h b/src/basic/util.h index 5fa44b5cf3..8abaa740b2 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -152,7 +152,7 @@ void close_many(const int fds[], unsigned n_fd); int fclose_nointr(FILE *f); FILE* safe_fclose(FILE *f); -int parse_size(const char *t, off_t base, off_t *size); +int parse_size(const char *t, uint64_t base, uint64_t *size); int parse_boolean(const char *v) _pure_; int parse_pid(const char *s, pid_t* ret_pid); @@ -374,7 +374,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(cpu_set_t*, CPU_FREE); 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); @@ -478,7 +480,7 @@ bool kexec_loaded(void); int prot_from_flags(int flags) _const_; -char *format_bytes(char *buf, size_t l, off_t t); +char *format_bytes(char *buf, size_t l, uint64_t t); int fd_wait_for_event(int fd, int event, usec_t timeout); diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index 1c94bea31a..4786a155da 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -45,7 +45,7 @@ typedef struct Group { bool memory_valid:1; bool io_valid:1; - unsigned n_tasks; + uint64_t n_tasks; unsigned cpu_iteration; nsec_t cpu_usage; @@ -65,7 +65,12 @@ static unsigned arg_iterations = (unsigned) -1; static bool arg_batch = false; static bool arg_raw = false; static usec_t arg_delay = 1*USEC_PER_SEC; -static bool arg_kernel_threads = false; + +enum { + COUNT_PIDS, + COUNT_USERSPACE_PROCESSES, + COUNT_ALL_PROCESSES, +} arg_count = COUNT_PIDS; static bool arg_recursive = true; static enum { @@ -73,7 +78,7 @@ static enum { ORDER_TASKS, ORDER_CPU, ORDER_MEMORY, - ORDER_IO + ORDER_IO, } arg_order = ORDER_CPU; static enum { @@ -100,7 +105,7 @@ static void group_hashmap_free(Hashmap *h) { hashmap_free(h); } -static const char *maybe_format_bytes(char *buf, size_t l, bool is_valid, off_t t) { +static const char *maybe_format_bytes(char *buf, size_t l, bool is_valid, uint64_t t) { if (!is_valid) return "-"; if (arg_raw) { @@ -153,7 +158,7 @@ static int process( } } - if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { + if (streq(controller, SYSTEMD_CGROUP_CONTROLLER) && IN_SET(arg_count, COUNT_ALL_PROCESSES, COUNT_USERSPACE_PROCESSES)) { _cleanup_fclose_ FILE *f = NULL; pid_t pid; @@ -166,7 +171,7 @@ static int process( g->n_tasks = 0; while (cg_read_pid(f, &pid) > 0) { - if (!arg_kernel_threads && is_kernel_thread(pid) > 0) + if (arg_count == COUNT_USERSPACE_PROCESSES && is_kernel_thread(pid) > 0) continue; g->n_tasks++; @@ -175,6 +180,26 @@ static int process( if (g->n_tasks > 0) g->n_tasks_valid = true; + } else if (streq(controller, "pids") && arg_count == COUNT_PIDS) { + _cleanup_free_ char *p = NULL, *v = NULL; + + r = cg_get_path(controller, path, "pids.current", &p); + if (r < 0) + return r; + + r = read_one_line_file(p, &v); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + + r = safe_atou64(v, &g->n_tasks); + if (r < 0) + return r; + + if (g->n_tasks > 0) + g->n_tasks_valid = true; + } else if (streq(controller, "cpuacct") && cg_unified() <= 0) { _cleanup_free_ char *p = NULL, *v = NULL; uint64_t new_usage; @@ -371,6 +396,7 @@ static int refresh_one( return r; if (arg_recursive && + IN_SET(arg_count, COUNT_ALL_PROCESSES, COUNT_USERSPACE_PROCESSES) && child && child->n_tasks_valid && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { @@ -409,6 +435,9 @@ static int refresh(const char *root, Hashmap *a, Hashmap *b, unsigned iteration) r = refresh_one("blkio", root, a, b, iteration, 0, NULL); if (r < 0) return r; + r = refresh_one("pids", root, a, b, iteration, 0, NULL); + if (r < 0) + return r; return 0; } @@ -549,7 +578,7 @@ static void display(Hashmap *a) { printf("%s%-*s%s %s%7s%s %s%s%s %s%8s%s %s%8s%s %s%8s%s\n\n", arg_order == ORDER_PATH ? ON : "", path_columns, "Control Group", arg_order == ORDER_PATH ? OFF : "", - arg_order == ORDER_TASKS ? ON : "", "Tasks", + arg_order == ORDER_TASKS ? ON : "", arg_count == COUNT_PIDS ? "Tasks" : arg_count == COUNT_USERSPACE_PROCESSES ? "Procs" : "Proc+", arg_order == ORDER_TASKS ? OFF : "", arg_order == ORDER_CPU ? ON : "", buffer, arg_order == ORDER_CPU ? OFF : "", @@ -576,7 +605,7 @@ static void display(Hashmap *a) { printf("%-*s", path_columns, ellipsized ?: path); if (g->n_tasks_valid) - printf(" %7u", g->n_tasks); + printf(" %7" PRIu64, g->n_tasks); else fputs(" -", stdout); @@ -602,15 +631,16 @@ static void help(void) { " -h --help Show this help\n" " --version Show package version\n" " -p --order=path Order by path\n" - " -t --order=tasks Order by number of tasks\n" + " -t --order=tasks Order by number of tasks/processes\n" " -c --order=cpu Order by CPU load (default)\n" " -m --order=memory Order by memory load\n" " -i --order=io Order by IO load\n" " -r --raw Provide raw (not human-readable) numbers\n" " --cpu=percentage Show CPU usage as percentage (default)\n" " --cpu=time Show CPU usage as time\n" - " -k Include kernel threads in task count\n" - " --recursive=BOOL Sum up task count recursively\n" + " -P Count userspace processes instead of tasks (excl. kernel)\n" + " -k Count all processes instead of tasks (incl. kernel)\n" + " --recursive=BOOL Sum up process count recursively\n" " -d --delay=DELAY Delay between updates\n" " -n --iterations=N Run for N iterations before exiting\n" " -b --batch Run in batch mode, accepting no input\n" @@ -642,12 +672,13 @@ static int parse_argv(int argc, char *argv[]) { {} }; + bool recursive_unset = false; int c, r; assert(argc >= 1); assert(argv); - while ((c = getopt_long(argc, argv, "hptcmin:brd:k", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "hptcmin:brd:kP", options, NULL)) >= 0) switch (c) { @@ -748,7 +779,11 @@ static int parse_argv(int argc, char *argv[]) { break; case 'k': - arg_kernel_threads = true; + arg_count = COUNT_ALL_PROCESSES; + break; + + case 'P': + arg_count = COUNT_USERSPACE_PROCESSES; break; case ARG_RECURSIVE: @@ -759,6 +794,7 @@ static int parse_argv(int argc, char *argv[]) { } arg_recursive = r; + recursive_unset = r == 0; break; case '?': @@ -773,9 +809,23 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } + if (recursive_unset && arg_count == COUNT_PIDS) { + log_error("Non-recursive counting is only supported when counting processes, not tasks. Use -P or -k."); + return -EINVAL; + } + return 1; } +static const char* counting_what(void) { + if (arg_count == COUNT_PIDS) + return "tasks"; + else if (arg_count == COUNT_ALL_PROCESSES) + return "all processes (incl. kernel)"; + else + return "userspace processes (excl. kernel)"; +} + int main(int argc, char *argv[]) { int r; Hashmap *a = NULL, *b = NULL; @@ -783,10 +833,19 @@ int main(int argc, char *argv[]) { usec_t last_refresh = 0; bool quit = false, immediate_refresh = false; _cleanup_free_ char *root = NULL; + CGroupMask mask; log_parse_environment(); log_open(); + r = cg_mask_supported(&mask); + if (r < 0) { + log_error_errno(r, "Failed to determine supported controllers: %m"); + goto finish; + } + + arg_count = (mask & CGROUP_MASK_PIDS) ? COUNT_PIDS : COUNT_USERSPACE_PROCESSES; + r = parse_argv(argc, argv); if (r <= 0) goto finish; @@ -899,15 +958,26 @@ int main(int argc, char *argv[]) { break; case 'k': - arg_kernel_threads = !arg_kernel_threads; - fprintf(stdout, "\nCounting kernel threads: %s.", yes_no(arg_kernel_threads)); + arg_count = arg_count != COUNT_ALL_PROCESSES ? COUNT_ALL_PROCESSES : COUNT_PIDS; + fprintf(stdout, "\nCounting: %s.", counting_what()); + fflush(stdout); + sleep(1); + break; + + case 'P': + arg_count = arg_count != COUNT_USERSPACE_PROCESSES ? COUNT_USERSPACE_PROCESSES : COUNT_PIDS; + fprintf(stdout, "\nCounting: %s.", counting_what()); fflush(stdout); sleep(1); break; case 'r': - arg_recursive = !arg_recursive; - fprintf(stdout, "\nRecursive task counting: %s", yes_no(arg_recursive)); + if (arg_count == COUNT_PIDS) + fprintf(stdout, "\n\aCannot toggle recursive counting, not available in task counting mode."); + else { + arg_recursive = !arg_recursive; + fprintf(stdout, "\nRecursive process counting: %s", yes_no(arg_recursive)); + } fflush(stdout); sleep(1); break; @@ -939,9 +1009,10 @@ int main(int argc, char *argv[]) { case '?': case 'h': fprintf(stdout, - "\t<" ON "p" OFF "> By path; <" ON "t" OFF "> By tasks; <" ON "c" OFF "> By CPU; <" ON "m" OFF "> By memory; <" ON "i" OFF "> By I/O\n" + "\t<" ON "p" OFF "> By path; <" ON "t" OFF "> By tasks/procs; <" ON "c" OFF "> By CPU; <" ON "m" OFF "> By memory; <" ON "i" OFF "> By I/O\n" "\t<" ON "+" OFF "> Inc. delay; <" ON "-" OFF "> Dec. delay; <" ON "%%" OFF "> Toggle time; <" ON "SPACE" OFF "> Refresh\n" - "\t<" ON "k" OFF "> Count kernel threads; <" ON "r" OFF "> Count recursively; <" ON "q" OFF "> Quit"); + "\t<" ON "P" OFF "> Toggle count userspace processes; <" ON "k" OFF "> Toggle count all processes\n" + "\t<" ON "r" OFF "> Count processes recursively; <" ON "q" OFF "> Quit"); fflush(stdout); sleep(3); break; diff --git a/src/core/automount.c b/src/core/automount.c index 5bb61b95d3..c88e3311bc 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -129,10 +129,8 @@ static void automount_done(Unit *u) { a->where = mfree(a->where); - set_free(a->tokens); - a->tokens = NULL; - set_free(a->expire_tokens); - a->expire_tokens = NULL; + a->tokens = set_free(a->tokens); + a->expire_tokens = set_free(a->expire_tokens); a->expire_event_source = sd_event_source_unref(a->expire_event_source); } diff --git a/src/core/cgroup.c b/src/core/cgroup.c index baa7cc5488..0c790c33da 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -22,10 +22,11 @@ #include <fcntl.h> #include <fnmatch.h> -#include "process-util.h" +#include "cgroup-util.h" #include "path-util.h" +#include "process-util.h" #include "special.h" -#include "cgroup-util.h" + #include "cgroup.h" #define CGROUP_CPU_QUOTA_PERIOD_USEC ((usec_t) 100 * USEC_PER_MSEC) @@ -36,13 +37,18 @@ void cgroup_context_init(CGroupContext *c) { /* Initialize everything to the kernel defaults, assuming the * structure is preinitialized to 0 */ - c->cpu_shares = (unsigned long) -1; - c->startup_cpu_shares = (unsigned long) -1; + c->cpu_shares = CGROUP_CPU_SHARES_INVALID; + c->startup_cpu_shares = CGROUP_CPU_SHARES_INVALID; + c->cpu_quota_per_sec_usec = USEC_INFINITY; + c->memory_limit = (uint64_t) -1; - c->blockio_weight = (unsigned long) -1; - c->startup_blockio_weight = (unsigned long) -1; - c->cpu_quota_per_sec_usec = USEC_INFINITY; + c->blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID; + c->startup_blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID; + + c->tasks_max = (uint64_t) -1; + + c->netclass_type = CGROUP_NETCLASS_TYPE_NONE; } void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) { @@ -100,23 +106,27 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { "%sCPUAccounting=%s\n" "%sBlockIOAccounting=%s\n" "%sMemoryAccounting=%s\n" - "%sCPUShares=%lu\n" - "%sStartupCPUShares=%lu\n" + "%sTasksAccounting=%s\n" + "%sCPUShares=%" PRIu64 "\n" + "%sStartupCPUShares=%" PRIu64 "\n" "%sCPUQuotaPerSecSec=%s\n" - "%sBlockIOWeight=%lu\n" - "%sStartupBlockIOWeight=%lu\n" + "%sBlockIOWeight=%" PRIu64 "\n" + "%sStartupBlockIOWeight=%" PRIu64 "\n" "%sMemoryLimit=%" PRIu64 "\n" + "%sTasksMax=%" PRIu64 "\n" "%sDevicePolicy=%s\n" "%sDelegate=%s\n", prefix, yes_no(c->cpu_accounting), prefix, yes_no(c->blockio_accounting), prefix, yes_no(c->memory_accounting), + prefix, yes_no(c->tasks_accounting), prefix, c->cpu_shares, prefix, c->startup_cpu_shares, prefix, format_timespan(u, sizeof(u), c->cpu_quota_per_sec_usec, 1), prefix, c->blockio_weight, prefix, c->startup_blockio_weight, prefix, c->memory_limit, + prefix, c->tasks_max, prefix, cgroup_device_policy_to_string(c->device_policy), prefix, yes_no(c->delegate)); @@ -129,7 +139,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { LIST_FOREACH(device_weights, w, c->blockio_device_weights) fprintf(f, - "%sBlockIODeviceWeight=%s %lu", + "%sBlockIODeviceWeight=%s %" PRIu64, prefix, w->path, w->weight); @@ -283,7 +293,7 @@ fail: return -errno; } -void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, ManagerState state) { +void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, uint32_t netclass, ManagerState state) { bool is_root; int r; @@ -305,11 +315,11 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M * and missing cgroups, i.e. EROFS and ENOENT. */ if ((mask & CGROUP_MASK_CPU) && !is_root) { - char buf[MAX(DECIMAL_STR_MAX(unsigned long), DECIMAL_STR_MAX(usec_t)) + 1]; + char buf[MAX(DECIMAL_STR_MAX(uint64_t), DECIMAL_STR_MAX(usec_t)) + 1]; - sprintf(buf, "%lu\n", - IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_cpu_shares != (unsigned long) -1 ? c->startup_cpu_shares : - c->cpu_shares != (unsigned long) -1 ? c->cpu_shares : 1024); + sprintf(buf, "%" PRIu64 "\n", + IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_cpu_shares != CGROUP_CPU_SHARES_INVALID ? c->startup_cpu_shares : + c->cpu_shares != CGROUP_CPU_SHARES_INVALID ? c->cpu_shares : CGROUP_CPU_SHARES_DEFAULT); r = cg_set_attribute("cpu", path, "cpu.shares", buf); if (r < 0) log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, @@ -332,15 +342,15 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M } if (mask & CGROUP_MASK_BLKIO) { - char buf[MAX3(DECIMAL_STR_MAX(unsigned long)+1, - DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(unsigned long)*1, - DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1)]; + char buf[MAX(DECIMAL_STR_MAX(uint64_t)+1, + DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1)]; CGroupBlockIODeviceWeight *w; CGroupBlockIODeviceBandwidth *b; if (!is_root) { - sprintf(buf, "%lu\n", IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_blockio_weight != (unsigned long) -1 ? c->startup_blockio_weight : - c->blockio_weight != (unsigned long) -1 ? c->blockio_weight : 1000); + sprintf(buf, "%" PRIu64 "\n", + IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID ? c->startup_blockio_weight : + c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID ? c->blockio_weight : CGROUP_BLKIO_WEIGHT_DEFAULT); r = cg_set_attribute("blkio", path, "blkio.weight", buf); if (r < 0) log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, @@ -354,7 +364,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M if (r < 0) continue; - sprintf(buf, "%u:%u %lu", major(dev), minor(dev), w->weight); + sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), w->weight); r = cg_set_attribute("blkio", path, "blkio.weight_device", buf); if (r < 0) log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, @@ -466,6 +476,32 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M log_debug("Ignoring device %s while writing cgroup attribute.", a->path); } } + + if ((mask & CGROUP_MASK_PIDS) && !is_root) { + + if (c->tasks_max != (uint64_t) -1) { + char buf[DECIMAL_STR_MAX(uint64_t) + 2]; + + sprintf(buf, "%" PRIu64 "\n", c->tasks_max); + r = cg_set_attribute("pids", path, "pids.max", buf); + } else + r = cg_set_attribute("pids", path, "pids.max", "max"); + + if (r < 0) + log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set pids.max on %s: %m", path); + } + + if (mask & CGROUP_MASK_NET_CLS) { + char buf[DECIMAL_STR_MAX(uint32_t)]; + + sprintf(buf, "%" PRIu32, netclass); + + r = cg_set_attribute("net_cls", path, "net_cls.classid", buf); + if (r < 0) + log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set net_cls.classid on %s: %m", path); + } } CGroupMask cgroup_context_get_mask(CGroupContext *c) { @@ -474,14 +510,14 @@ CGroupMask cgroup_context_get_mask(CGroupContext *c) { /* Figure out which controllers we need */ if (c->cpu_accounting || - c->cpu_shares != (unsigned long) -1 || - c->startup_cpu_shares != (unsigned long) -1 || + c->cpu_shares != CGROUP_CPU_SHARES_INVALID || + c->startup_cpu_shares != CGROUP_CPU_SHARES_INVALID || c->cpu_quota_per_sec_usec != USEC_INFINITY) mask |= CGROUP_MASK_CPUACCT | CGROUP_MASK_CPU; if (c->blockio_accounting || - c->blockio_weight != (unsigned long) -1 || - c->startup_blockio_weight != (unsigned long) -1 || + c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID || + c->startup_blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID || c->blockio_device_weights || c->blockio_device_bandwidths) mask |= CGROUP_MASK_BLKIO; @@ -494,6 +530,13 @@ CGroupMask cgroup_context_get_mask(CGroupContext *c) { c->device_policy != CGROUP_AUTO) mask |= CGROUP_MASK_DEVICES; + if (c->tasks_accounting || + c->tasks_max != (uint64_t) -1) + mask |= CGROUP_MASK_PIDS; + + if (c->netclass_type != CGROUP_NETCLASS_TYPE_NONE) + mask |= CGROUP_MASK_NET_CLS; + return mask; } @@ -861,6 +904,103 @@ static bool unit_has_mask_realized(Unit *u, CGroupMask target_mask) { return u->cgroup_realized && u->cgroup_realized_mask == target_mask; } +static int unit_find_free_netclass_cgroup(Unit *u, uint32_t *ret) { + + uint32_t start, i; + Manager *m; + + assert(u); + + m = u->manager; + + i = start = m->cgroup_netclass_registry_last; + + do { + i++; + + if (!hashmap_get(m->cgroup_netclass_registry, UINT_TO_PTR(i))) { + m->cgroup_netclass_registry_last = i; + *ret = i; + return 0; + } + + if (i == UINT32_MAX) + i = CGROUP_NETCLASS_FIXED_MAX; + + } while (i != start); + + return -ENOBUFS; +} + +int unit_add_to_netclass_cgroup(Unit *u) { + + CGroupContext *cc; + Unit *first; + void *key; + int r; + + assert(u); + + cc = unit_get_cgroup_context(u); + if (!cc) + return 0; + + switch (cc->netclass_type) { + case CGROUP_NETCLASS_TYPE_NONE: + return 0; + + case CGROUP_NETCLASS_TYPE_FIXED: + u->cgroup_netclass_id = cc->netclass_id; + break; + + case CGROUP_NETCLASS_TYPE_AUTO: + /* Allocate a new ID in case it was requested and not done yet */ + if (u->cgroup_netclass_id == 0) { + r = unit_find_free_netclass_cgroup(u, &u->cgroup_netclass_id); + if (r < 0) + return r; + + log_debug("Dynamically assigned netclass cgroup id %" PRIu32 " to %s", u->cgroup_netclass_id, u->id); + } + + break; + } + + r = hashmap_ensure_allocated(&u->manager->cgroup_netclass_registry, &trivial_hash_ops); + if (r < 0) + return r; + + key = UINT32_TO_PTR(u->cgroup_netclass_id); + first = hashmap_get(u->manager->cgroup_netclass_registry, key); + + if (first) { + LIST_PREPEND(cgroup_netclass, first, u); + return hashmap_replace(u->manager->cgroup_netclass_registry, key, u); + } + + return hashmap_put(u->manager->cgroup_netclass_registry, key, u); +} + +int unit_remove_from_netclass_cgroup(Unit *u) { + + Unit *head; + void *key; + + assert(u); + + key = UINT32_TO_PTR(u->cgroup_netclass_id); + + LIST_FIND_HEAD(cgroup_netclass, u, head); + LIST_REMOVE(cgroup_netclass, head, u); + + if (head) + return hashmap_replace(u->manager->cgroup_netclass_registry, key, head); + + hashmap_remove(u->manager->cgroup_netclass_registry, key); + + return 0; +} + /* Check if necessary controllers and attributes for a unit are in place. * * If so, do nothing. @@ -896,7 +1036,7 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) { return r; /* Finally, apply the necessary attributes. */ - cgroup_context_apply(unit_get_cgroup_context(u), target_mask, u->cgroup_path, state); + cgroup_context_apply(unit_get_cgroup_context(u), target_mask, u->cgroup_path, u->cgroup_netclass_id, state); return 0; } @@ -1459,6 +1599,28 @@ int unit_get_memory_current(Unit *u, uint64_t *ret) { return safe_atou64(v, ret); } +int unit_get_tasks_current(Unit *u, uint64_t *ret) { + _cleanup_free_ char *v = NULL; + int r; + + assert(u); + assert(ret); + + if (!u->cgroup_path) + return -ENODATA; + + if ((u->cgroup_realized_mask & CGROUP_MASK_PIDS) == 0) + return -ENODATA; + + r = cg_get_attribute("pids", u->cgroup_path, "pids.current", &v); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + + return safe_atou64(v, ret); +} + static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) { _cleanup_free_ char *v = NULL; uint64_t ns; @@ -1532,6 +1694,32 @@ bool unit_cgroup_delegate(Unit *u) { return c->delegate; } +void unit_invalidate_cgroup(Unit *u, CGroupMask m) { + assert(u); + + if (!UNIT_HAS_CGROUP_CONTEXT(u)) + return; + + if (m == 0) + return; + + if ((u->cgroup_realized_mask & m) == 0) + return; + + u->cgroup_realized_mask &= ~m; + unit_add_to_cgroup_queue(u); +} + +void manager_invalidate_startup_units(Manager *m) { + Iterator i; + Unit *u; + + assert(m); + + SET_FOREACH(u, m->startup_units, i) + unit_invalidate_cgroup(u, CGROUP_MASK_CPU|CGROUP_MASK_BLKIO); +} + static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = { [CGROUP_AUTO] = "auto", [CGROUP_CLOSED] = "closed", diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 438f5bf50f..457544b49f 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -26,6 +26,11 @@ #include "list.h" #include "time-util.h" +/* Maximum value for fixed (manual) net class ID assignment, + * and also the value at which the range of automatic assignments starts + */ +#define CGROUP_NETCLASS_FIXED_MAX UINT32_C(65535) + typedef struct CGroupContext CGroupContext; typedef struct CGroupDeviceAllow CGroupDeviceAllow; typedef struct CGroupBlockIODeviceWeight CGroupBlockIODeviceWeight; @@ -47,6 +52,17 @@ typedef enum CGroupDevicePolicy { _CGROUP_DEVICE_POLICY_INVALID = -1 } CGroupDevicePolicy; +typedef enum CGroupNetClassType { + /* Default - do not assign a net class */ + CGROUP_NETCLASS_TYPE_NONE, + + /* Automatically assign a net class */ + CGROUP_NETCLASS_TYPE_AUTO, + + /* Assign the net class that was provided by the user */ + CGROUP_NETCLASS_TYPE_FIXED, +} CGroupNetClassType; + struct CGroupDeviceAllow { LIST_FIELDS(CGroupDeviceAllow, device_allow); char *path; @@ -58,7 +74,7 @@ struct CGroupDeviceAllow { struct CGroupBlockIODeviceWeight { LIST_FIELDS(CGroupBlockIODeviceWeight, device_weights); char *path; - unsigned long weight; + uint64_t weight; }; struct CGroupBlockIODeviceBandwidth { @@ -72,13 +88,14 @@ struct CGroupContext { bool cpu_accounting; bool blockio_accounting; bool memory_accounting; + bool tasks_accounting; - unsigned long cpu_shares; - unsigned long startup_cpu_shares; + uint64_t cpu_shares; + uint64_t startup_cpu_shares; usec_t cpu_quota_per_sec_usec; - unsigned long blockio_weight; - unsigned long startup_blockio_weight; + uint64_t blockio_weight; + uint64_t startup_blockio_weight; LIST_HEAD(CGroupBlockIODeviceWeight, blockio_device_weights); LIST_HEAD(CGroupBlockIODeviceBandwidth, blockio_device_bandwidths); @@ -87,6 +104,11 @@ struct CGroupContext { CGroupDevicePolicy device_policy; LIST_HEAD(CGroupDeviceAllow, device_allow); + CGroupNetClassType netclass_type; + uint32_t netclass_id; + + uint64_t tasks_max; + bool delegate; }; @@ -96,7 +118,7 @@ struct CGroupContext { void cgroup_context_init(CGroupContext *c); void cgroup_context_done(CGroupContext *c); void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix); -void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, ManagerState state); +void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, uint32_t netclass_id, ManagerState state); CGroupMask cgroup_context_get_mask(CGroupContext *c); @@ -124,6 +146,9 @@ int unit_watch_cgroup(Unit *u); int unit_attach_pids_to_cgroup(Unit *u); +int unit_add_to_netclass_cgroup(Unit *u); +int unit_remove_from_netclass_cgroup(Unit *u); + int manager_setup_cgroup(Manager *m); void manager_shutdown_cgroup(Manager *m, bool delete); @@ -137,6 +162,7 @@ int unit_search_main_pid(Unit *u, pid_t *ret); int unit_watch_all_pids(Unit *u); int unit_get_memory_current(Unit *u, uint64_t *ret); +int unit_get_tasks_current(Unit *u, uint64_t *ret); int unit_get_cpu_usage(Unit *u, nsec_t *ret); int unit_reset_cpu_usage(Unit *u); @@ -145,5 +171,9 @@ bool unit_cgroup_delegate(Unit *u); int unit_notify_cgroup_empty(Unit *u); int manager_notify_cgroup_empty(Manager *m, const char *group); +void unit_invalidate_cgroup(Unit *u, CGroupMask m); + +void manager_invalidate_startup_units(Manager *m); + const char* cgroup_device_policy_to_string(CGroupDevicePolicy i) _const_; CGroupDevicePolicy cgroup_device_policy_from_string(const char *s) _pure_; diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c index ba2f4e53b9..f334dc928d 100644 --- a/src/core/dbus-cgroup.c +++ b/src/core/dbus-cgroup.c @@ -133,34 +133,16 @@ static int property_get_device_allow( return sd_bus_message_close_container(reply); } -static int property_get_ulong_as_u64( - sd_bus *bus, - const char *path, - const char *interface, - const char *property, - sd_bus_message *reply, - void *userdata, - sd_bus_error *error) { - - unsigned long *ul = userdata; - - assert(bus); - assert(reply); - assert(ul); - - return sd_bus_message_append(reply, "t", *ul == (unsigned long) -1 ? (uint64_t) -1 : (uint64_t) *ul); -} - const sd_bus_vtable bus_cgroup_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Delegate", "b", bus_property_get_bool, offsetof(CGroupContext, delegate), 0), SD_BUS_PROPERTY("CPUAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, cpu_accounting), 0), - SD_BUS_PROPERTY("CPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, cpu_shares), 0), - SD_BUS_PROPERTY("StartupCPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_cpu_shares), 0), + SD_BUS_PROPERTY("CPUShares", "t", NULL, offsetof(CGroupContext, cpu_shares), 0), + SD_BUS_PROPERTY("StartupCPUShares", "t", NULL, offsetof(CGroupContext, startup_cpu_shares), 0), SD_BUS_PROPERTY("CPUQuotaPerSecUSec", "t", bus_property_get_usec, offsetof(CGroupContext, cpu_quota_per_sec_usec), 0), SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0), - SD_BUS_PROPERTY("BlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, blockio_weight), 0), - SD_BUS_PROPERTY("StartupBlockIOWeight", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_blockio_weight), 0), + SD_BUS_PROPERTY("BlockIOWeight", "t", NULL, offsetof(CGroupContext, blockio_weight), 0), + SD_BUS_PROPERTY("StartupBlockIOWeight", "t", NULL, offsetof(CGroupContext, startup_blockio_weight), 0), SD_BUS_PROPERTY("BlockIODeviceWeight", "a(st)", property_get_blockio_device_weight, 0, 0), SD_BUS_PROPERTY("BlockIOReadBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0), SD_BUS_PROPERTY("BlockIOWriteBandwidth", "a(st)", property_get_blockio_device_bandwidths, 0, 0), @@ -168,6 +150,8 @@ const sd_bus_vtable bus_cgroup_vtable[] = { SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0), SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0), SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0), + SD_BUS_PROPERTY("TasksAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, tasks_accounting), 0), + SD_BUS_PROPERTY("TasksMax", "t", NULL, offsetof(CGroupContext, tasks_max), 0), SD_BUS_VTABLE_END }; @@ -228,56 +212,52 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->cpu_accounting = b; - u->cgroup_realized_mask &= ~CGROUP_MASK_CPUACCT; + unit_invalidate_cgroup(u, CGROUP_MASK_CPUACCT|CGROUP_MASK_CPU); unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no"); } return 1; } else if (streq(name, "CPUShares")) { - uint64_t u64; - unsigned long ul; + uint64_t shares; - r = sd_bus_message_read(message, "t", &u64); + r = sd_bus_message_read(message, "t", &shares); if (r < 0) return r; - if (u64 == (uint64_t) -1) - ul = (unsigned long) -1; - else { - ul = (unsigned long) u64; - if (ul <= 0 || (uint64_t) ul != u64) - return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range"); - } + if (!CGROUP_CPU_SHARES_IS_OK(shares)) + return sd_bus_error_set_errnof(error, EINVAL, "CPUShares value out of range"); if (mode != UNIT_CHECK) { - c->cpu_shares = ul; - u->cgroup_realized_mask &= ~CGROUP_MASK_CPU; - unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul); + c->cpu_shares = shares; + unit_invalidate_cgroup(u, CGROUP_MASK_CPU); + + if (shares == CGROUP_CPU_SHARES_INVALID) + unit_write_drop_in_private(u, mode, name, "CPUShares="); + else + unit_write_drop_in_private_format(u, mode, name, "CPUShares=%" PRIu64, shares); } return 1; } else if (streq(name, "StartupCPUShares")) { - uint64_t u64; - unsigned long ul; + uint64_t shares; - r = sd_bus_message_read(message, "t", &u64); + r = sd_bus_message_read(message, "t", &shares); if (r < 0) return r; - if (u64 == (uint64_t) -1) - ul = (unsigned long) -1; - else { - ul = (unsigned long) u64; - if (ul <= 0 || (uint64_t) ul != u64) - return sd_bus_error_set_errnof(error, EINVAL, "StartupCPUShares value out of range"); - } + if (!CGROUP_CPU_SHARES_IS_OK(shares)) + return sd_bus_error_set_errnof(error, EINVAL, "StartupCPUShares value out of range"); if (mode != UNIT_CHECK) { - c->startup_cpu_shares = ul; - u->cgroup_realized_mask &= ~CGROUP_MASK_CPU; - unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%lu", ul); + c->startup_cpu_shares = shares; + unit_invalidate_cgroup(u, CGROUP_MASK_CPU); + + if (shares == CGROUP_CPU_SHARES_INVALID) + unit_write_drop_in_private(u, mode, name, "StartupCPUShares="); + else + unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%" PRIu64, shares); } return 1; @@ -294,7 +274,7 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->cpu_quota_per_sec_usec = u64; - u->cgroup_realized_mask &= ~CGROUP_MASK_CPU; + unit_invalidate_cgroup(u, CGROUP_MASK_CPU); unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%0.f%%", (double) (c->cpu_quota_per_sec_usec / 10000)); } @@ -309,56 +289,52 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->blockio_accounting = b; - u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO; + unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no"); } return 1; } else if (streq(name, "BlockIOWeight")) { - uint64_t u64; - unsigned long ul; + uint64_t weight; - r = sd_bus_message_read(message, "t", &u64); + r = sd_bus_message_read(message, "t", &weight); if (r < 0) return r; - if (u64 == (uint64_t) -1) - ul = (unsigned long) -1; - else { - ul = (unsigned long) u64; - if (ul < 10 || ul > 1000) - return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range"); - } + if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight)) + return sd_bus_error_set_errnof(error, EINVAL, "BlockIOWeight value out of range"); if (mode != UNIT_CHECK) { - c->blockio_weight = ul; - u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO; - unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul); + c->blockio_weight = weight; + unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); + + if (weight == CGROUP_BLKIO_WEIGHT_INVALID) + unit_write_drop_in_private(u, mode, name, "BlockIOWeight="); + else + unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%" PRIu64, weight); } return 1; } else if (streq(name, "StartupBlockIOWeight")) { - uint64_t u64; - unsigned long ul; + uint64_t weight; - r = sd_bus_message_read(message, "t", &u64); + r = sd_bus_message_read(message, "t", &weight); if (r < 0) return r; - if (u64 == (uint64_t) -1) - ul = (unsigned long) -1; - else { - ul = (unsigned long) u64; - if (ul < 10 || ul > 1000) - return sd_bus_error_set_errnof(error, EINVAL, "StartupBlockIOWeight value out of range"); - } + if (CGROUP_BLKIO_WEIGHT_IS_OK(weight)) + return sd_bus_error_set_errnof(error, EINVAL, "StartupBlockIOWeight value out of range"); if (mode != UNIT_CHECK) { - c->startup_blockio_weight = ul; - u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO; - unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%lu", ul); + c->startup_blockio_weight = weight; + unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); + + if (weight == CGROUP_BLKIO_WEIGHT_INVALID) + unit_write_drop_in_private(u, mode, name, "StartupBlockIOWeight="); + else + unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%" PRIu64, weight); } return 1; @@ -427,7 +403,7 @@ int bus_cgroup_set_property( cgroup_context_free_blockio_device_bandwidth(c, a); } - u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO; + unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); f = open_memstream(&buf, &size); if (!f) @@ -453,17 +429,16 @@ int bus_cgroup_set_property( } else if (streq(name, "BlockIODeviceWeight")) { const char *path; - uint64_t u64; + uint64_t weight; unsigned n = 0; r = sd_bus_message_enter_container(message, 'a', "(st)"); if (r < 0) return r; - while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) { - unsigned long ul = u64; + while ((r = sd_bus_message_read(message, "(st)", &path, &weight)) > 0) { - if (ul < 10 || ul > 1000) + if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight) || weight == CGROUP_BLKIO_WEIGHT_INVALID) return sd_bus_error_set_errnof(error, EINVAL, "BlockIODeviceWeight out of range"); if (mode != UNIT_CHECK) { @@ -489,7 +464,7 @@ int bus_cgroup_set_property( LIST_PREPEND(device_weights,c->blockio_device_weights, a); } - a->weight = ul; + a->weight = weight; } n++; @@ -510,7 +485,7 @@ int bus_cgroup_set_property( cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights); } - u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO; + unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); f = open_memstream(&buf, &size); if (!f) @@ -518,7 +493,7 @@ int bus_cgroup_set_property( fputs("BlockIODeviceWeight=\n", f); LIST_FOREACH(device_weights, a, c->blockio_device_weights) - fprintf(f, "BlockIODeviceWeight=%s %lu\n", a->path, a->weight); + fprintf(f, "BlockIODeviceWeight=%s %" PRIu64 "\n", a->path, a->weight); fflush(f); unit_write_drop_in_private(u, mode, name, buf); @@ -535,7 +510,7 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->memory_accounting = b; - u->cgroup_realized_mask &= ~CGROUP_MASK_MEMORY; + unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no"); } @@ -550,8 +525,12 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->memory_limit = limit; - u->cgroup_realized_mask &= ~CGROUP_MASK_MEMORY; - unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit); + unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); + + if (limit == (uint64_t) -1) + unit_write_drop_in_private(u, mode, name, "MemoryLimit=infinity"); + else + unit_write_drop_in_private_format(u, mode, name, "MemoryLimit=%" PRIu64, limit); } return 1; @@ -572,7 +551,7 @@ int bus_cgroup_set_property( char *buf; c->device_policy = p; - u->cgroup_realized_mask &= ~CGROUP_MASK_DEVICES; + unit_invalidate_cgroup(u, CGROUP_MASK_DEVICES); buf = strjoina("DevicePolicy=", policy); unit_write_drop_in_private(u, mode, name, buf); @@ -651,7 +630,7 @@ int bus_cgroup_set_property( cgroup_context_free_device_allow(c, c->device_allow); } - u->cgroup_realized_mask &= ~CGROUP_MASK_DEVICES; + unit_invalidate_cgroup(u, CGROUP_MASK_DEVICES); f = open_memstream(&buf, &size); if (!f) @@ -667,6 +646,39 @@ int bus_cgroup_set_property( return 1; + } else if (streq(name, "TasksAccounting")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->tasks_accounting = b; + unit_invalidate_cgroup(u, CGROUP_MASK_PIDS); + unit_write_drop_in_private(u, mode, name, b ? "TasksAccounting=yes" : "TasksAccounting=no"); + } + + return 1; + + } else if (streq(name, "TasksMax")) { + uint64_t limit; + + r = sd_bus_message_read(message, "t", &limit); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->tasks_max = limit; + unit_invalidate_cgroup(u, CGROUP_MASK_PIDS); + + if (limit == (uint64_t) -1) + unit_write_drop_in_private(u, mode, name, "TasksMax=infinity"); + else + unit_write_drop_in_private_format(u, mode, name, "TasksMax=%" PRIu64, limit); + } + + return 1; } if (u->transient && u->load_state == UNIT_STUB) { diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index ed55fcfca2..fd13c6d019 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -933,7 +933,10 @@ int bus_exec_context_set_transient_property( return 1; - } else if (streq(name, "IgnoreSIGPIPE")) { + } else if (STR_IN_SET(name, + "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", + "PrivateTmp", "PrivateDevices", "PrivateNetwork", + "NoNewPrivileges")) { int b; r = sd_bus_message_read(message, "b", &b); @@ -941,39 +944,22 @@ int bus_exec_context_set_transient_property( return r; if (mode != UNIT_CHECK) { - c->ignore_sigpipe = b; - - unit_write_drop_in_private_format(u, mode, name, "IgnoreSIGPIPE=%s\n", yes_no(b)); - } - - return 1; - - } else if (streq(name, "TTYVHangup")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - c->tty_vhangup = b; - - unit_write_drop_in_private_format(u, mode, name, "TTYVHangup=%s\n", yes_no(b)); - } - - return 1; - - } else if (streq(name, "TTYReset")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (mode != UNIT_CHECK) { - c->tty_reset = b; - - unit_write_drop_in_private_format(u, mode, name, "TTYReset=%s\n", yes_no(b)); + if (streq(name, "IgnoreSIGPIPE")) + c->ignore_sigpipe = b; + else if (streq(name, "TTYVHangup")) + c->tty_vhangup = b; + else if (streq(name, "TTYReset")) + c->tty_reset = b; + else if (streq(name, "PrivateTmp")) + c->private_tmp = b; + else if (streq(name, "PrivateDevices")) + c->private_devices = b; + else if (streq(name, "PrivateNetwork")) + c->private_network = b; + else if (streq(name, "NoNewPrivileges")) + c->no_new_privileges = b; + + unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, yes_no(b)); } return 1; diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index f9275ed935..cd88a87340 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -679,6 +679,7 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("Asserts", "a(sbbsi)", property_get_conditions, offsetof(Unit, asserts), 0), SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NetClass", "u", bus_property_get_unsigned, offsetof(Unit, cgroup_netclass_id), 0), SD_BUS_METHOD("Start", "s", "o", method_start, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Stop", "s", "o", method_stop, SD_BUS_VTABLE_UNPRIVILEGED), @@ -736,6 +737,30 @@ static int property_get_current_memory( return sd_bus_message_append(reply, "t", sz); } +static int property_get_current_tasks( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + uint64_t cn = (uint64_t) -1; + Unit *u = userdata; + int r; + + assert(bus); + assert(reply); + assert(u); + + r = unit_get_tasks_current(u, &cn); + if (r < 0 && r != -ENODATA) + log_unit_warning_errno(u, r, "Failed to get pids.current attribute: %m"); + + return sd_bus_message_append(reply, "t", cn); +} + static int property_get_cpu_usage( sd_bus *bus, const char *path, @@ -796,6 +821,7 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = { SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0), SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0), SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0), + SD_BUS_PROPERTY("TasksCurrent", "t", property_get_current_tasks, 0, 0), SD_BUS_VTABLE_END }; diff --git a/src/core/dbus.c b/src/core/dbus.c index 0a2180c6a7..2d6a1ff836 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -1055,12 +1055,10 @@ void bus_done(Manager *m) { while ((b = set_steal_first(m->private_buses))) destroy_bus(m, &b); - set_free(m->private_buses); - m->private_buses = NULL; + m->private_buses = set_free(m->private_buses); m->subscribed = sd_bus_track_unref(m->subscribed); - strv_free(m->deserialized_subscribed); - m->deserialized_subscribed = NULL; + m->deserialized_subscribed = strv_free(m->deserialized_subscribed); if (m->private_listen_event_source) m->private_listen_event_source = sd_event_source_unref(m->private_listen_event_source); @@ -1191,8 +1189,7 @@ int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l) { } } - strv_free(*l); - *l = NULL; + *l = strv_free(*l); return r; } diff --git a/src/core/device.c b/src/core/device.c index 7fdfacfddf..0b54518691 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -594,8 +594,7 @@ static void device_shutdown(Manager *m) { m->udev_monitor = NULL; } - hashmap_free(m->devices_by_sysfs); - m->devices_by_sysfs = NULL; + m->devices_by_sysfs = hashmap_free(m->devices_by_sysfs); } static int device_enumerate(Manager *m) { diff --git a/src/core/execute.c b/src/core/execute.c index 6abb0a5d5d..3c308e3e3e 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -1160,8 +1160,8 @@ static void do_idle_pipe_dance(int idle_pipe[4]) { assert(idle_pipe); - safe_close(idle_pipe[1]); - safe_close(idle_pipe[2]); + idle_pipe[1] = safe_close(idle_pipe[1]); + idle_pipe[2] = safe_close(idle_pipe[2]); if (idle_pipe[0] >= 0) { int r; @@ -1169,18 +1169,20 @@ static void do_idle_pipe_dance(int idle_pipe[4]) { r = fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT_USEC); if (idle_pipe[3] >= 0 && r == 0 /* timeout */) { + ssize_t n; + /* Signal systemd that we are bored and want to continue. */ - r = write(idle_pipe[3], "x", 1); - if (r > 0) + n = write(idle_pipe[3], "x", 1); + if (n > 0) /* Wait for systemd to react to the signal above. */ fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT2_USEC); } - safe_close(idle_pipe[0]); + idle_pipe[0] = safe_close(idle_pipe[0]); } - safe_close(idle_pipe[3]); + idle_pipe[3] = safe_close(idle_pipe[3]); } static int build_environment( @@ -1992,11 +1994,8 @@ void exec_context_done(ExecContext *c) { assert(c); - strv_free(c->environment); - c->environment = NULL; - - strv_free(c->environment_files); - c->environment_files = NULL; + c->environment = strv_free(c->environment); + c->environment_files = strv_free(c->environment_files); for (l = 0; l < ELEMENTSOF(c->rlimit); l++) c->rlimit[l] = mfree(c->rlimit[l]); @@ -2008,8 +2007,7 @@ void exec_context_done(ExecContext *c) { c->user = mfree(c->user); c->group = mfree(c->group); - strv_free(c->supplementary_groups); - c->supplementary_groups = NULL; + c->supplementary_groups = strv_free(c->supplementary_groups); c->pam_name = mfree(c->pam_name); @@ -2018,14 +2016,9 @@ void exec_context_done(ExecContext *c) { c->capabilities = NULL; } - strv_free(c->read_only_dirs); - c->read_only_dirs = NULL; - - strv_free(c->read_write_dirs); - c->read_write_dirs = NULL; - - strv_free(c->inaccessible_dirs); - c->inaccessible_dirs = NULL; + c->read_only_dirs = strv_free(c->read_only_dirs); + c->read_write_dirs = strv_free(c->read_write_dirs); + c->inaccessible_dirs = strv_free(c->inaccessible_dirs); if (c->cpuset) CPU_FREE(c->cpuset); @@ -2034,17 +2027,11 @@ void exec_context_done(ExecContext *c) { c->selinux_context = mfree(c->selinux_context); c->apparmor_profile = mfree(c->apparmor_profile); - set_free(c->syscall_filter); - c->syscall_filter = NULL; + c->syscall_filter = set_free(c->syscall_filter); + c->syscall_archs = set_free(c->syscall_archs); + c->address_families = set_free(c->address_families); - set_free(c->syscall_archs); - c->syscall_archs = NULL; - - set_free(c->address_families); - c->address_families = NULL; - - strv_free(c->runtime_directory); - c->runtime_directory = NULL; + c->runtime_directory = strv_free(c->runtime_directory); bus_endpoint_free(c->bus_endpoint); c->bus_endpoint = NULL; @@ -2079,8 +2066,7 @@ void exec_command_done(ExecCommand *c) { c->path = mfree(c->path); - strv_free(c->argv); - c->argv = NULL; + c->argv = strv_free(c->argv); } void exec_command_done_array(ExecCommand *c, unsigned n) { diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index edd55b9e45..ec744214c1 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -124,7 +124,10 @@ $1.StartupBlockIOWeight, config_parse_blockio_weight, 0, $1.BlockIODeviceWeight, config_parse_blockio_device_weight, 0, offsetof($1, cgroup_context) $1.BlockIOReadBandwidth, config_parse_blockio_bandwidth, 0, offsetof($1, cgroup_context) $1.BlockIOWriteBandwidth, config_parse_blockio_bandwidth, 0, offsetof($1, cgroup_context) -$1.Delegate, config_parse_bool, 0, offsetof($1, cgroup_context.delegate)' +$1.TasksAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.tasks_accounting) +$1.TasksMax, config_parse_tasks_max, 0, offsetof($1, cgroup_context) +$1.Delegate, config_parse_bool, 0, offsetof($1, cgroup_context.delegate) +$1.NetClass, config_parse_netclass, 0, offsetof($1, cgroup_context)' )m4_dnl Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description) Unit.Documentation, config_parse_documentation, 0, offsetof(Unit, documentation) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index b695c57da8..1217b4651e 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -1888,8 +1888,7 @@ int config_parse_unit_env_file(const char *unit, if (isempty(rvalue)) { /* Empty assignment frees the list */ - strv_free(*env); - *env = NULL; + *env = strv_free(*env); return 0; } @@ -1937,8 +1936,7 @@ int config_parse_environ(const char *unit, if (isempty(rvalue)) { /* Empty assignment resets the list */ - strv_free(*env); - *env = NULL; + *env = strv_free(*env); return 0; } @@ -2245,8 +2243,7 @@ int config_parse_documentation(const char *unit, if (isempty(rvalue)) { /* Empty assignment resets the list */ - strv_free(u->documentation); - u->documentation = NULL; + u->documentation = strv_free(u->documentation); return 0; } @@ -2305,8 +2302,7 @@ int config_parse_syscall_filter( if (isempty(rvalue)) { /* Empty assignment resets the list */ - set_free(c->syscall_filter); - c->syscall_filter = NULL; + c->syscall_filter = set_free(c->syscall_filter); c->syscall_whitelist = false; return 0; } @@ -2404,8 +2400,7 @@ int config_parse_syscall_archs( int r; if (isempty(rvalue)) { - set_free(*archs); - *archs = NULL; + *archs = set_free(*archs); return 0; } @@ -2501,8 +2496,7 @@ int config_parse_address_families( if (isempty(rvalue)) { /* Empty assignment resets the list */ - set_free(c->address_families); - c->address_families = NULL; + c->address_families = set_free(c->address_families); c->address_families_whitelist = false; return 0; } @@ -2611,26 +2605,19 @@ int config_parse_cpu_shares( void *data, void *userdata) { - unsigned long *shares = data, lu; + uint64_t *shares = data; int r; assert(filename); assert(lvalue); assert(rvalue); - if (isempty(rvalue)) { - *shares = (unsigned long) -1; - return 0; - } - - r = safe_atolu(rvalue, &lu); - if (r < 0 || lu <= 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "CPU shares '%s' invalid. Ignoring.", rvalue); + r = cg_cpu_shares_parse(rvalue, shares); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "CPU shares '%s' invalid. Ignoring.", rvalue); return 0; } - *shares = lu; return 0; } @@ -2689,24 +2676,51 @@ int config_parse_memory_limit( void *userdata) { CGroupContext *c = data; - off_t bytes; + uint64_t bytes; int r; - if (isempty(rvalue)) { + if (isempty(rvalue) || streq(rvalue, "infinity")) { c->memory_limit = (uint64_t) -1; return 0; } - assert_cc(sizeof(uint64_t) == sizeof(off_t)); - r = parse_size(rvalue, 1024, &bytes); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Memory limit '%s' invalid. Ignoring.", rvalue); + if (r < 0 || bytes < 1) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Memory limit '%s' invalid. Ignoring.", rvalue); + return 0; + } + + c->memory_limit = bytes; + return 0; +} + +int config_parse_tasks_max( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + CGroupContext *c = data; + uint64_t u; + int r; + + if (isempty(rvalue) || streq(rvalue, "infinity")) { + c->tasks_max = (uint64_t) -1; + return 0; + } + + r = safe_atou64(rvalue, &u); + if (r < 0 || u < 1) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Maximum tasks value '%s' invalid. Ignoring.", rvalue); return 0; } - c->memory_limit = (uint64_t) bytes; return 0; } @@ -2784,26 +2798,19 @@ int config_parse_blockio_weight( void *data, void *userdata) { - unsigned long *weight = data, lu; + uint64_t *weight = data; int r; assert(filename); assert(lvalue); assert(rvalue); - if (isempty(rvalue)) { - *weight = (unsigned long) -1; - return 0; - } - - r = safe_atolu(rvalue, &lu); - if (r < 0 || lu < 10 || lu > 1000) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Block IO weight '%s' invalid. Ignoring.", rvalue); + r = cg_blkio_weight_parse(rvalue, weight); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", rvalue); return 0; } - *weight = lu; return 0; } @@ -2822,8 +2829,8 @@ int config_parse_blockio_device_weight( _cleanup_free_ char *path = NULL; CGroupBlockIODeviceWeight *w; CGroupContext *c = data; - unsigned long lu; const char *weight; + uint64_t u; size_t n; int r; @@ -2840,9 +2847,10 @@ int config_parse_blockio_device_weight( n = strcspn(rvalue, WHITESPACE); weight = rvalue + n; - if (!*weight) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Expected block device and device weight. Ignoring."); + weight += strspn(weight, WHITESPACE); + + if (isempty(weight)) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Expected block device and device weight. Ignoring."); return 0; } @@ -2851,19 +2859,18 @@ int config_parse_blockio_device_weight( return log_oom(); if (!path_startswith(path, "/dev")) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Invalid device node path '%s'. Ignoring.", path); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Invalid device node path '%s'. Ignoring.", path); return 0; } - weight += strspn(weight, WHITESPACE); - r = safe_atolu(weight, &lu); - if (r < 0 || lu < 10 || lu > 1000) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Block IO weight '%s' invalid. Ignoring.", rvalue); + r = cg_blkio_weight_parse(weight, &u); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Block IO weight '%s' invalid. Ignoring.", weight); return 0; } + assert(u != CGROUP_BLKIO_WEIGHT_INVALID); + w = new0(CGroupBlockIODeviceWeight, 1); if (!w) return log_oom(); @@ -2871,7 +2878,7 @@ int config_parse_blockio_device_weight( w->path = path; path = NULL; - w->weight = lu; + w->weight = u; LIST_PREPEND(device_weights, c->blockio_device_weights, w); return 0; @@ -2893,7 +2900,7 @@ int config_parse_blockio_bandwidth( CGroupBlockIODeviceBandwidth *b; CGroupContext *c = data; const char *bandwidth; - off_t bytes; + uint64_t bytes; bool read; size_t n; int r; @@ -2947,7 +2954,7 @@ int config_parse_blockio_bandwidth( b->path = path; path = NULL; - b->bandwidth = (uint64_t) bytes; + b->bandwidth = bytes; b->read = read; LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b); @@ -2955,6 +2962,48 @@ int config_parse_blockio_bandwidth( return 0; } +int config_parse_netclass( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + CGroupContext *c = data; + unsigned v; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (streq(rvalue, "auto")) { + c->netclass_type = CGROUP_NETCLASS_TYPE_AUTO; + return 0; + } + + r = safe_atou32(rvalue, &v); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Netclass '%s' invalid. Ignoring.", rvalue); + return 0; + } + + if (v > CGROUP_NETCLASS_FIXED_MAX) + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Fixed netclass %" PRIu32 " out of allowed range (0-%d). Applying anyway.", v, (uint32_t) CGROUP_NETCLASS_FIXED_MAX); + + c->netclass_id = v; + c->netclass_type = CGROUP_NETCLASS_TYPE_FIXED; + + return 0; +} + DEFINE_CONFIG_PARSE_ENUM(config_parse_job_mode, job_mode, JobMode, "Failed to parse job mode"); int config_parse_job_mode_isolate( @@ -3011,8 +3060,7 @@ int config_parse_runtime_directory( if (isempty(rvalue)) { /* Empty assignment resets the list */ - strv_free(*rt); - *rt = NULL; + *rt = strv_free(*rt); return 0; } @@ -3140,8 +3188,7 @@ int config_parse_namespace_path_strv( if (isempty(rvalue)) { /* Empty assignment resets the list */ - strv_free(*sv); - *sv = NULL; + *sv = strv_free(*sv); return 0; } @@ -3622,7 +3669,7 @@ void unit_dump_config_items(FILE *f) { { config_parse_int, "INTEGER" }, { config_parse_unsigned, "UNSIGNED" }, { config_parse_iec_size, "SIZE" }, - { config_parse_iec_off, "SIZE" }, + { config_parse_iec_uint64, "SIZE" }, { config_parse_si_size, "SIZE" }, { config_parse_bool, "BOOLEAN" }, { config_parse_string, "STRING" }, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 1d128716c4..5d0a09249f 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -84,11 +84,13 @@ int config_parse_environ(const char *unit, const char *filename, unsigned line, int config_parse_unit_slice(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_cpu_shares(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_memory_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_tasks_max(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_device_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_device_allow(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_blockio_weight(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_blockio_device_weight(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_blockio_bandwidth(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_netclass(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_job_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_job_mode_isolate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_exec_selinux_context(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/core/main.c b/src/core/main.c index be95dc68b2..200fe740da 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -114,6 +114,7 @@ static FILE* arg_serialization = NULL; static bool arg_default_cpu_accounting = false; static bool arg_default_blockio_accounting = false; static bool arg_default_memory_accounting = false; +static bool arg_default_tasks_accounting = false; static void nop_handler(int sig) {} @@ -676,6 +677,7 @@ static int parse_config_file(void) { { "Manager", "DefaultCPUAccounting", config_parse_bool, 0, &arg_default_cpu_accounting }, { "Manager", "DefaultBlockIOAccounting", config_parse_bool, 0, &arg_default_blockio_accounting }, { "Manager", "DefaultMemoryAccounting", config_parse_bool, 0, &arg_default_memory_accounting }, + { "Manager", "DefaultTasksAccounting", config_parse_bool, 0, &arg_default_tasks_accounting }, {} }; @@ -704,6 +706,7 @@ static void manager_set_defaults(Manager *m) { m->default_cpu_accounting = arg_default_cpu_accounting; m->default_blockio_accounting = arg_default_blockio_accounting; m->default_memory_accounting = arg_default_memory_accounting; + m->default_tasks_accounting = arg_default_tasks_accounting; manager_set_default_rlimits(m, arg_default_rlimit); manager_environment_add(m, NULL, arg_default_environment); @@ -1846,8 +1849,7 @@ finish: arg_default_environment = strv_free(arg_default_environment); - set_free(arg_syscall_archs); - arg_syscall_archs = NULL; + arg_syscall_archs = set_free(arg_syscall_archs); mac_selinux_finish(); diff --git a/src/core/manager.c b/src/core/manager.c index d918007bb8..4e672a8c48 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -317,6 +317,8 @@ static int manager_watch_idle_pipe(Manager *m) { static void manager_close_idle_pipe(Manager *m) { assert(m); + m->idle_pipe_event_source = sd_event_source_unref(m->idle_pipe_event_source); + safe_close_pair(m->idle_pipe); safe_close_pair(m->idle_pipe + 2); } @@ -577,6 +579,8 @@ int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) { m->have_ask_password = -EINVAL; /* we don't know */ m->first_boot = -1; + m->cgroup_netclass_registry_last = CGROUP_NETCLASS_FIXED_MAX; + m->test_run = test_run; /* Reboot immediately if the user hits C-A-D more often than 7x per 2s */ @@ -602,14 +606,6 @@ int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) { if (r < 0) goto fail; - r = set_ensure_allocated(&m->startup_units, NULL); - if (r < 0) - goto fail; - - r = set_ensure_allocated(&m->failed_units, NULL); - if (r < 0) - goto fail; - r = sd_event_default(&m->event); if (r < 0) goto fail; @@ -944,7 +940,6 @@ Manager* manager_free(Manager *m) { sd_event_source_unref(m->notify_event_source); sd_event_source_unref(m->time_change_event_source); sd_event_source_unref(m->jobs_in_progress_event_source); - sd_event_source_unref(m->idle_pipe_event_source); sd_event_source_unref(m->run_queue_event_source); safe_close(m->signal_fd); @@ -967,6 +962,8 @@ Manager* manager_free(Manager *m) { hashmap_free(m->cgroup_unit); set_free_free(m->unit_path_cache); + hashmap_free(m->cgroup_netclass_registry); + free(m->switch_root); free(m->switch_root_init); @@ -1962,7 +1959,6 @@ static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32 m->no_console_output = m->n_on_console > 0; - m->idle_pipe_event_source = sd_event_source_unref(m->idle_pipe_event_source); manager_close_idle_pipe(m); return 0; @@ -2675,9 +2671,6 @@ static void manager_notify_finished(Manager *m) { } void manager_check_finished(Manager *m) { - Unit *u = NULL; - Iterator i; - assert(m); if (m->n_reloading > 0) @@ -2690,11 +2683,9 @@ void manager_check_finished(Manager *m) { return; if (hashmap_size(m->jobs) > 0) { - if (m->jobs_in_progress_event_source) /* Ignore any failure, this is only for feedback */ - (void) sd_event_source_set_time(m->jobs_in_progress_event_source, - now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC); + (void) sd_event_source_set_time(m->jobs_in_progress_event_source, now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC); return; } @@ -2702,7 +2693,6 @@ void manager_check_finished(Manager *m) { manager_flip_auto_status(m, false); /* Notify Type=idle units that we are done now */ - m->idle_pipe_event_source = sd_event_source_unref(m->idle_pipe_event_source); manager_close_idle_pipe(m); /* Turn off confirm spawn now */ @@ -2721,9 +2711,7 @@ void manager_check_finished(Manager *m) { manager_notify_finished(m); - SET_FOREACH(u, m->startup_units, i) - if (u->cgroup_path) - cgroup_context_apply(unit_get_cgroup_context(u), unit_get_own_mask(u), u->cgroup_path, manager_state(m)); + manager_invalidate_startup_units(m); } static int create_generator_dir(Manager *m, char **generator, const char *name) { @@ -3069,8 +3057,9 @@ const char *manager_get_runtime_prefix(Manager *m) { getenv("XDG_RUNTIME_DIR"); } -void manager_update_failed_units(Manager *m, Unit *u, bool failed) { +int manager_update_failed_units(Manager *m, Unit *u, bool failed) { unsigned size; + int r; assert(m); assert(u->manager == m); @@ -3078,13 +3067,19 @@ void manager_update_failed_units(Manager *m, Unit *u, bool failed) { size = set_size(m->failed_units); if (failed) { + r = set_ensure_allocated(&m->failed_units, NULL); + if (r < 0) + return log_oom(); + if (set_put(m->failed_units, u) < 0) - log_oom(); + return log_oom(); } else - set_remove(m->failed_units, u); + (void) set_remove(m->failed_units, u); if (set_size(m->failed_units) != size) bus_manager_send_change_signal(m); + + return 0; } ManagerState manager_state(Manager *m) { diff --git a/src/core/manager.h b/src/core/manager.h index 9956cb7700..b955982100 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -256,6 +256,7 @@ struct Manager { bool default_cpu_accounting; bool default_memory_accounting; bool default_blockio_accounting; + bool default_tasks_accounting; usec_t default_timer_accuracy_usec; @@ -302,6 +303,10 @@ struct Manager { const char *unit_log_format_string; int first_boot; + + /* Used for NetClass=auto units */ + Hashmap *cgroup_netclass_registry; + uint32_t cgroup_netclass_registry_last; }; int manager_new(ManagerRunningAs running_as, bool test_run, Manager **m); @@ -368,7 +373,7 @@ const char *manager_get_runtime_prefix(Manager *m); ManagerState manager_state(Manager *m); -void manager_update_failed_units(Manager *m, Unit *u, bool failed); +int manager_update_failed_units(Manager *m, Unit *u, bool failed); const char *manager_state_to_string(ManagerState m) _const_; ManagerState manager_state_from_string(const char *s) _pure_; diff --git a/src/core/service.c b/src/core/service.c index 248a9e8c62..fc28ba4d07 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -2737,6 +2737,8 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us case SERVICE_RELOAD: log_unit_warning(UNIT(s), "Reload operation timed out. Stopping."); + service_unwatch_control_pid(s); + service_kill_control_processes(s); s->reload_result = SERVICE_FAILURE_TIMEOUT; service_enter_running(s, SERVICE_SUCCESS); break; diff --git a/src/core/swap.c b/src/core/swap.c index 311ce7ee04..bef457069f 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -1254,8 +1254,7 @@ static void swap_shutdown(Manager *m) { m->proc_swaps = safe_fclose(m->proc_swaps); - hashmap_free(m->swaps_by_devnode); - m->swaps_by_devnode = NULL; + m->swaps_by_devnode = hashmap_free(m->swaps_by_devnode); } static int swap_enumerate(Manager *m) { diff --git a/src/core/transaction.c b/src/core/transaction.c index b8f69ec6f3..2d120af4b5 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -736,8 +736,8 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error if (m->idle_pipe[0] < 0 && m->idle_pipe[1] < 0 && m->idle_pipe[2] < 0 && m->idle_pipe[3] < 0) { - pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC); - pipe2(m->idle_pipe + 2, O_NONBLOCK|O_CLOEXEC); + (void) pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC); + (void) pipe2(m->idle_pipe + 2, O_NONBLOCK|O_CLOEXEC); } } diff --git a/src/core/unit.c b/src/core/unit.c index 24a6747b10..3356b97522 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -125,6 +125,7 @@ static void unit_init(Unit *u) { cc->cpu_accounting = u->manager->default_cpu_accounting; cc->blockio_accounting = u->manager->default_blockio_accounting; cc->memory_accounting = u->manager->default_memory_accounting; + cc->tasks_accounting = u->manager->default_tasks_accounting; } ec = unit_get_exec_context(u); @@ -445,13 +446,13 @@ static void unit_free_requires_mounts_for(Unit *u) { } } - strv_free(u->requires_mounts_for); - u->requires_mounts_for = NULL; + u->requires_mounts_for = strv_free(u->requires_mounts_for); } static void unit_done(Unit *u) { ExecContext *ec; CGroupContext *cc; + int r; assert(u); @@ -468,6 +469,10 @@ static void unit_done(Unit *u) { cc = unit_get_cgroup_context(u); if (cc) cgroup_context_done(cc); + + r = unit_remove_from_netclass_cgroup(u); + if (r < 0) + log_warning_errno(r, "Unable to remove unit from netclass group: %m"); } void unit_free(Unit *u) { @@ -528,7 +533,7 @@ void unit_free(Unit *u) { unit_release_cgroup(u); - manager_update_failed_units(u->manager, u, false); + (void) manager_update_failed_units(u->manager, u, false); set_remove(u->manager->startup_units, u); free(u->description); @@ -674,8 +679,7 @@ static void merge_dependencies(Unit *u, Unit *other, const char *other_id, UnitD /* The move cannot fail. The caller must have performed a reservation. */ assert_se(complete_move(&u->dependencies[d], &other->dependencies[d]) == 0); - set_free(other->dependencies[d]); - other->dependencies[d] = NULL; + other->dependencies[d] = set_free(other->dependencies[d]); } int unit_merge(Unit *u, Unit *other) { @@ -1173,15 +1177,20 @@ static int unit_add_mount_dependencies(Unit *u) { static int unit_add_startup_units(Unit *u) { CGroupContext *c; + int r; c = unit_get_cgroup_context(u); if (!c) return 0; - if (c->startup_cpu_shares == (unsigned long) -1 && - c->startup_blockio_weight == (unsigned long) -1) + if (c->startup_cpu_shares == CGROUP_CPU_SHARES_INVALID && + c->startup_blockio_weight == CGROUP_BLKIO_WEIGHT_INVALID) return 0; + r = set_ensure_allocated(&u->manager->startup_units, NULL); + if (r < 0) + return r; + return set_put(u->manager->startup_units, u); } @@ -1237,6 +1246,14 @@ int unit_load(Unit *u) { } unit_update_cgroup_members_masks(u); + + /* If we are reloading, we need to wait for the deserializer + * to restore the net_cls ids that have been set previously */ + if (u->manager->n_reloading <= 0) { + r = unit_add_to_netclass_cgroup(u); + if (r < 0) + return r; + } } assert((u->load_state != UNIT_MERGED) == !u->merged_into); @@ -1808,7 +1825,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su } /* Keep track of failed units */ - manager_update_failed_units(u->manager, u, ns == UNIT_FAILED); + (void) manager_update_failed_units(u->manager, u, ns == UNIT_FAILED); /* Make sure the cgroup is always removed when we become inactive */ if (UNIT_IS_INACTIVE_OR_FAILED(ns)) @@ -2587,6 +2604,9 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { unit_serialize_item(u, f, "cgroup", u->cgroup_path); unit_serialize_item(u, f, "cgroup-realized", yes_no(u->cgroup_realized)); + if (u->cgroup_netclass_id) + unit_serialize_item_format(u, f, "netclass-id", "%" PRIu32, u->cgroup_netclass_id); + if (serialize_jobs) { if (u->job) { fprintf(f, "job\n"); @@ -2774,6 +2794,17 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { u->cgroup_realized = b; continue; + } else if (streq(l, "netclass-id")) { + r = safe_atou32(v, &u->cgroup_netclass_id); + if (r < 0) + log_unit_debug(u, "Failed to parse netclass ID %s, ignoring.", v); + else { + r = unit_add_to_netclass_cgroup(u); + if (r < 0) + log_unit_debug_errno(u, r, "Failed to add unit to netclass cgroup, ignoring: %m"); + } + + continue; } if (unit_can_serialize(u)) { diff --git a/src/core/unit.h b/src/core/unit.h index 3c7684411b..c868d75c79 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -161,6 +161,9 @@ struct Unit { /* CGroup realize members queue */ LIST_FIELDS(Unit, cgroup_queue); + /* Units with the same CGroup netclass */ + LIST_FIELDS(Unit, cgroup_netclass); + /* PIDs we keep an eye on. Note that a unit might have many * more, but these are the ones we care enough about to * process SIGCHLD for */ @@ -189,6 +192,8 @@ struct Unit { CGroupMask cgroup_members_mask; int cgroup_inotify_wd; + uint32_t cgroup_netclass_id; + /* How to start OnFailure units */ JobMode on_failure_job_mode; diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c index 6fad8ad80c..ab91afec4d 100644 --- a/src/cryptsetup/cryptsetup-generator.c +++ b/src/cryptsetup/cryptsetup-generator.c @@ -330,7 +330,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value) { free(d->keyfile); d->keyfile = uuid_value; uuid_value = NULL; - } else if (free_and_strdup(&arg_default_keyfile, value)) + } else if (free_and_strdup(&arg_default_keyfile, value) < 0) return log_oom(); } else if (STR_IN_SET(key, "luks.name", "rd.luks.name") && value) { diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c index 44e029ef98..0e77197e34 100644 --- a/src/import/pull-raw.c +++ b/src/import/pull-raw.c @@ -314,7 +314,7 @@ static int raw_pull_make_local_copy(RawPull *i) { if (r < 0) log_warning_errno(errno, "Failed to set file attributes on %s: %m", tp); - r = copy_bytes(i->raw_job->disk_fd, dfd, (off_t) -1, true); + r = copy_bytes(i->raw_job->disk_fd, dfd, (uint64_t) -1, true); if (r < 0) { unlink(tp); return log_error_errno(r, "Failed to make writable copy of image: %m"); diff --git a/src/journal/compress.c b/src/journal/compress.c index 383f6a6e96..c66043e503 100644 --- a/src/journal/compress.c +++ b/src/journal/compress.c @@ -342,11 +342,10 @@ int decompress_startswith(int compression, return -EBADMSG; } -int compress_stream_xz(int fdf, int fdt, off_t max_bytes) { +int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) { #ifdef HAVE_XZ _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; lzma_ret ret; - uint8_t buf[BUFSIZ], out[BUFSIZ]; lzma_action action = LZMA_RUN; @@ -364,8 +363,8 @@ int compress_stream_xz(int fdf, int fdt, off_t max_bytes) { size_t m = sizeof(buf); ssize_t n; - if (max_bytes != -1 && m > (size_t) max_bytes) - m = max_bytes; + if (max_bytes != (uint64_t) -1 && (uint64_t) m > max_bytes) + m = (size_t) max_bytes; n = read(fdf, buf, m); if (n < 0) @@ -376,8 +375,8 @@ int compress_stream_xz(int fdf, int fdt, off_t max_bytes) { s.next_in = buf; s.avail_in = n; - if (max_bytes != -1) { - assert(max_bytes >= n); + if (max_bytes != (uint64_t) -1) { + assert(max_bytes >= (uint64_t) n); max_bytes -= n; } } @@ -419,7 +418,7 @@ int compress_stream_xz(int fdf, int fdt, off_t max_bytes) { #define LZ4_BUFSIZE (512*1024) -int compress_stream_lz4(int fdf, int fdt, off_t max_bytes) { +int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) { #ifdef HAVE_LZ4 @@ -445,8 +444,8 @@ int compress_stream_lz4(int fdf, int fdt, off_t max_bytes) { int r; m = LZ4_BUFSIZE; - if (max_bytes != -1 && m > (size_t) max_bytes - total_in) - m = max_bytes - total_in; + if (max_bytes != (uint64_t) -1 && (uint64_t) m > (max_bytes - total_in)) + m = (size_t) (max_bytes - total_in); n = read(fdf, buf, m); if (n < 0) @@ -497,7 +496,7 @@ int compress_stream_lz4(int fdf, int fdt, off_t max_bytes) { #endif } -int decompress_stream_xz(int fdf, int fdt, off_t max_bytes) { +int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) { #ifdef HAVE_XZ _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; @@ -546,8 +545,8 @@ int decompress_stream_xz(int fdf, int fdt, off_t max_bytes) { n = sizeof(out) - s.avail_out; - if (max_bytes != -1) { - if (max_bytes < n) + if (max_bytes != (uint64_t) -1) { + if (max_bytes < (uint64_t) n) return -EFBIG; max_bytes -= n; @@ -572,7 +571,7 @@ int decompress_stream_xz(int fdf, int fdt, off_t max_bytes) { #endif } -int decompress_stream_lz4(int fdf, int fdt, off_t max_bytes) { +int decompress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) { #ifdef HAVE_LZ4 _cleanup_free_ char *buf = NULL, *out = NULL; @@ -626,8 +625,8 @@ int decompress_stream_lz4(int fdf, int fdt, off_t max_bytes) { total_out += r; - if (max_bytes != -1 && total_out > (size_t) max_bytes) { - log_debug("Decompressed stream longer than %zd bytes", max_bytes); + if (max_bytes != (uint64_t) -1 && (uint64_t) total_out > max_bytes) { + log_debug("Decompressed stream longer than %" PRIu64 " bytes", max_bytes); return -EFBIG; } @@ -647,7 +646,7 @@ int decompress_stream_lz4(int fdf, int fdt, off_t max_bytes) { #endif } -int decompress_stream(const char *filename, int fdf, int fdt, off_t max_bytes) { +int decompress_stream(const char *filename, int fdf, int fdt, uint64_t max_bytes) { if (endswith(filename, ".lz4")) return decompress_stream_lz4(fdf, fdt, max_bytes); diff --git a/src/journal/compress.h b/src/journal/compress.h index 6294f16faa..9a065eb763 100644 --- a/src/journal/compress.h +++ b/src/journal/compress.h @@ -67,11 +67,11 @@ int decompress_startswith(int compression, const void *prefix, size_t prefix_len, uint8_t extra); -int compress_stream_xz(int fdf, int fdt, off_t max_bytes); -int compress_stream_lz4(int fdf, int fdt, off_t max_bytes); +int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes); +int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes); -int decompress_stream_xz(int fdf, int fdt, off_t max_size); -int decompress_stream_lz4(int fdf, int fdt, off_t max_size); +int decompress_stream_xz(int fdf, int fdt, uint64_t max_size); +int decompress_stream_lz4(int fdf, int fdt, uint64_t max_size); #ifdef HAVE_LZ4 # define compress_stream compress_stream_lz4 @@ -81,4 +81,4 @@ int decompress_stream_lz4(int fdf, int fdt, off_t max_size); # define COMPRESSED_EXT ".xz" #endif -int decompress_stream(const char *filename, int fdf, int fdt, off_t max_bytes); +int decompress_stream(const char *filename, int fdf, int fdt, uint64_t max_bytes); diff --git a/src/journal/coredump-vacuum.c b/src/journal/coredump-vacuum.c index c0347ef569..efe418615a 100644 --- a/src/journal/coredump-vacuum.c +++ b/src/journal/coredump-vacuum.c @@ -28,10 +28,10 @@ #include "coredump-vacuum.h" -#define DEFAULT_MAX_USE_LOWER (off_t) (1ULL*1024ULL*1024ULL) /* 1 MiB */ -#define DEFAULT_MAX_USE_UPPER (off_t) (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */ -#define DEFAULT_KEEP_FREE_UPPER (off_t) (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */ -#define DEFAULT_KEEP_FREE (off_t) (1024ULL*1024ULL) /* 1 MB */ +#define DEFAULT_MAX_USE_LOWER (uint64_t) (1ULL*1024ULL*1024ULL) /* 1 MiB */ +#define DEFAULT_MAX_USE_UPPER (uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */ +#define DEFAULT_KEEP_FREE_UPPER (uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */ +#define DEFAULT_KEEP_FREE (uint64_t) (1024ULL*1024ULL) /* 1 MB */ struct vacuum_candidate { unsigned n_files; @@ -82,8 +82,8 @@ static int uid_from_file_name(const char *filename, uid_t *uid) { return parse_uid(u, uid); } -static bool vacuum_necessary(int fd, off_t sum, off_t keep_free, off_t max_use) { - off_t fs_size = 0, fs_free = (off_t) -1; +static bool vacuum_necessary(int fd, uint64_t sum, uint64_t keep_free, uint64_t max_use) { + uint64_t fs_size = 0, fs_free = (uint64_t) -1; struct statvfs sv; assert(fd >= 0); @@ -93,7 +93,7 @@ static bool vacuum_necessary(int fd, off_t sum, off_t keep_free, off_t max_use) fs_free = sv.f_frsize * sv.f_bfree; } - if (max_use == (off_t) -1) { + if (max_use == (uint64_t) -1) { if (fs_size > 0) { max_use = PAGE_ALIGN(fs_size / 10); /* 10% */ @@ -111,7 +111,7 @@ static bool vacuum_necessary(int fd, off_t sum, off_t keep_free, off_t max_use) if (max_use > 0 && sum > max_use) return true; - if (keep_free == (off_t) -1) { + if (keep_free == (uint64_t) -1) { if (fs_size > 0) { keep_free = PAGE_ALIGN((fs_size * 3) / 20); /* 15% */ @@ -129,7 +129,7 @@ static bool vacuum_necessary(int fd, off_t sum, off_t keep_free, off_t max_use) return false; } -int coredump_vacuum(int exclude_fd, off_t keep_free, off_t max_use) { +int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) { _cleanup_closedir_ DIR *d = NULL; struct stat exclude_st; int r; @@ -161,7 +161,7 @@ int coredump_vacuum(int exclude_fd, off_t keep_free, off_t max_use) { _cleanup_(vacuum_candidate_hasmap_freep) Hashmap *h = NULL; struct vacuum_candidate *worst = NULL; struct dirent *de; - off_t sum = 0; + uint64_t sum = 0; rewinddir(d); diff --git a/src/journal/coredump-vacuum.h b/src/journal/coredump-vacuum.h index 7ad4399305..7779c97574 100644 --- a/src/journal/coredump-vacuum.h +++ b/src/journal/coredump-vacuum.h @@ -21,6 +21,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <inttypes.h> #include <sys/types.h> -int coredump_vacuum(int exclude_fd, off_t keep_free, off_t max_use); +int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use); diff --git a/src/journal/coredump.c b/src/journal/coredump.c index 7d94b145c9..e1e66b9826 100644 --- a/src/journal/coredump.c +++ b/src/journal/coredump.c @@ -51,7 +51,7 @@ #include "process-util.h" /* The maximum size up to which we process coredumps */ -#define PROCESS_SIZE_MAX ((off_t) (2LLU*1024LLU*1024LLU*1024LLU)) +#define PROCESS_SIZE_MAX ((uint64_t) (2LLU*1024LLU*1024LLU*1024LLU)) /* The maximum size up to which we leave the coredump around on * disk */ @@ -97,21 +97,21 @@ static DEFINE_CONFIG_PARSE_ENUM(config_parse_coredump_storage, coredump_storage, static CoredumpStorage arg_storage = COREDUMP_STORAGE_EXTERNAL; static bool arg_compress = true; -static off_t arg_process_size_max = PROCESS_SIZE_MAX; -static off_t arg_external_size_max = EXTERNAL_SIZE_MAX; +static uint64_t arg_process_size_max = PROCESS_SIZE_MAX; +static uint64_t arg_external_size_max = EXTERNAL_SIZE_MAX; static size_t arg_journal_size_max = JOURNAL_SIZE_MAX; -static off_t arg_keep_free = (off_t) -1; -static off_t arg_max_use = (off_t) -1; +static uint64_t arg_keep_free = (uint64_t) -1; +static uint64_t arg_max_use = (uint64_t) -1; static int parse_config(void) { static const ConfigTableItem items[] = { { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage }, { "Coredump", "Compress", config_parse_bool, 0, &arg_compress }, - { "Coredump", "ProcessSizeMax", config_parse_iec_off, 0, &arg_process_size_max }, - { "Coredump", "ExternalSizeMax", config_parse_iec_off, 0, &arg_external_size_max }, + { "Coredump", "ProcessSizeMax", config_parse_iec_uint64, 0, &arg_process_size_max }, + { "Coredump", "ExternalSizeMax", config_parse_iec_uint64, 0, &arg_external_size_max }, { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max }, - { "Coredump", "KeepFree", config_parse_iec_off, 0, &arg_keep_free }, - { "Coredump", "MaxUse", config_parse_iec_off, 0, &arg_max_use }, + { "Coredump", "KeepFree", config_parse_iec_uint64, 0, &arg_keep_free }, + { "Coredump", "MaxUse", config_parse_iec_uint64, 0, &arg_max_use }, {} }; @@ -224,7 +224,7 @@ static int fix_permissions( return 0; } -static int maybe_remove_external_coredump(const char *filename, off_t size) { +static int maybe_remove_external_coredump(const char *filename, uint64_t size) { /* Returns 1 if might remove, 0 if will not remove, < 0 on error. */ @@ -285,7 +285,7 @@ static int save_external_coredump( uid_t uid, char **ret_filename, int *ret_fd, - off_t *ret_size) { + uint64_t *ret_size) { _cleanup_free_ char *fn = NULL, *tmp = NULL; _cleanup_close_ int fd = -1; @@ -372,9 +372,9 @@ static int save_external_coredump( /* OK, this worked, we can get rid of the uncompressed version now */ unlink_noerrno(tmp); - *ret_filename = fn_compressed; /* compressed */ - *ret_fd = fd; /* uncompressed */ - *ret_size = st.st_size; /* uncompressed */ + *ret_filename = fn_compressed; /* compressed */ + *ret_fd = fd; /* uncompressed */ + *ret_size = (uint64_t) st.st_size; /* uncompressed */ fn_compressed = NULL; fd = -1; @@ -393,7 +393,7 @@ uncompressed: *ret_filename = fn; *ret_fd = fd; - *ret_size = st.st_size; + *ret_size = (uint64_t) st.st_size; fn = NULL; fd = -1; @@ -544,7 +544,7 @@ int main(int argc, char* argv[]) { _cleanup_close_ int coredump_fd = -1; struct iovec iovec[26]; - off_t coredump_size; + uint64_t coredump_size; int r, j = 0; uid_t uid, owner_uid; gid_t gid; @@ -840,7 +840,7 @@ log: /* Optionally store the entire coredump in the journal */ if (IN_SET(arg_storage, COREDUMP_STORAGE_JOURNAL, COREDUMP_STORAGE_BOTH) && - coredump_size <= (off_t) arg_journal_size_max) { + coredump_size <= arg_journal_size_max) { size_t sz = 0; /* Store the coredump itself in the journal */ diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 4f94799ce7..73d3a4bb9d 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -2542,7 +2542,7 @@ void journal_file_print_header(JournalFile *f) { le64toh(f->header->n_entry_arrays)); if (fstat(f->fd, &st) >= 0) - printf("Disk usage: %s\n", format_bytes(bytes, sizeof(bytes), (off_t) st.st_blocks * 512ULL)); + printf("Disk usage: %s\n", format_bytes(bytes, sizeof(bytes), (uint64_t) st.st_blocks * 512ULL)); } static int journal_file_warn_btrfs(JournalFile *f) { diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 576e4e4d03..9b483413e7 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -107,7 +107,7 @@ static bool arg_reverse = false; static int arg_journal_type = 0; static const char *arg_root = NULL; static const char *arg_machine = NULL; -static off_t arg_vacuum_size = (off_t) -1; +static uint64_t arg_vacuum_size = (uint64_t) -1; static usec_t arg_vacuum_time = USEC_INFINITY; static enum { diff --git a/src/journal/journald-gperf.gperf b/src/journal/journald-gperf.gperf index 74554c1c34..bf7c773009 100644 --- a/src/journal/journald-gperf.gperf +++ b/src/journal/journald-gperf.gperf @@ -21,12 +21,12 @@ Journal.Seal, config_parse_bool, 0, offsetof(Server, seal) Journal.SyncIntervalSec, config_parse_sec, 0, offsetof(Server, sync_interval_usec) Journal.RateLimitInterval, config_parse_sec, 0, offsetof(Server, rate_limit_interval) Journal.RateLimitBurst, config_parse_unsigned, 0, offsetof(Server, rate_limit_burst) -Journal.SystemMaxUse, config_parse_iec_off, 0, offsetof(Server, system_metrics.max_use) -Journal.SystemMaxFileSize, config_parse_iec_off, 0, offsetof(Server, system_metrics.max_size) -Journal.SystemKeepFree, config_parse_iec_off, 0, offsetof(Server, system_metrics.keep_free) -Journal.RuntimeMaxUse, config_parse_iec_off, 0, offsetof(Server, runtime_metrics.max_use) -Journal.RuntimeMaxFileSize, config_parse_iec_off, 0, offsetof(Server, runtime_metrics.max_size) -Journal.RuntimeKeepFree, config_parse_iec_off, 0, offsetof(Server, runtime_metrics.keep_free) +Journal.SystemMaxUse, config_parse_iec_uint64, 0, offsetof(Server, system_metrics.max_use) +Journal.SystemMaxFileSize, config_parse_iec_uint64, 0, offsetof(Server, system_metrics.max_size) +Journal.SystemKeepFree, config_parse_iec_uint64, 0, offsetof(Server, system_metrics.keep_free) +Journal.RuntimeMaxUse, config_parse_iec_uint64, 0, offsetof(Server, runtime_metrics.max_use) +Journal.RuntimeMaxFileSize, config_parse_iec_uint64, 0, offsetof(Server, runtime_metrics.max_size) +Journal.RuntimeKeepFree, config_parse_iec_uint64, 0, offsetof(Server, runtime_metrics.keep_free) Journal.MaxRetentionSec, config_parse_sec, 0, offsetof(Server, max_retention_usec) Journal.MaxFileSec, config_parse_sec, 0, offsetof(Server, max_file_usec) Journal.ForwardToSyslog, config_parse_bool, 0, offsetof(Server, forward_to_syslog) diff --git a/src/journal/test-compress.c b/src/journal/test-compress.c index 41a566d714..f17c00e60d 100644 --- a/src/journal/test-compress.c +++ b/src/journal/test-compress.c @@ -44,8 +44,8 @@ typedef int (decompress_sw_t)(const void *src, uint64_t src_size, const void *prefix, size_t prefix_len, uint8_t extra); -typedef int (compress_stream_t)(int fdf, int fdt, off_t max_bytes); -typedef int (decompress_stream_t)(int fdf, int fdt, off_t max_size); +typedef int (compress_stream_t)(int fdf, int fdt, uint64_t max_bytes); +typedef int (decompress_stream_t)(int fdf, int fdt, uint64_t max_size); static void test_compress_decompress(int compression, compress_blob_t compress, diff --git a/src/journal/test-coredump-vacuum.c b/src/journal/test-coredump-vacuum.c index a4dd00125d..514dadc1dc 100644 --- a/src/journal/test-coredump-vacuum.c +++ b/src/journal/test-coredump-vacuum.c @@ -25,7 +25,7 @@ int main(int argc, char *argv[]) { - if (coredump_vacuum(-1, (off_t) -1, 70 * 1024) < 0) + if (coredump_vacuum(-1, (uint64_t) -1, 70 * 1024) < 0) return EXIT_FAILURE; return EXIT_SUCCESS; diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c index 6826e21665..3e8cb0b7d0 100644 --- a/src/libsystemd/sd-bus/bus-creds.c +++ b/src/libsystemd/sd-bus/bus-creds.c @@ -109,8 +109,7 @@ _public_ sd_bus_creds *sd_bus_creds_unref(sd_bus_creds *c) { c->supplementary_gids = mfree(c->supplementary_gids); - strv_free(c->well_known_names); - c->well_known_names = NULL; + c->well_known_names = strv_free(c->well_known_names); bus_creds_done(c); diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h index 7af61a9433..e399701beb 100644 --- a/src/libsystemd/sd-bus/bus-internal.h +++ b/src/libsystemd/sd-bus/bus-internal.h @@ -396,6 +396,6 @@ int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error); #define bus_assert_return(expr, r, error) \ do { \ - if (!assert_log(expr)) \ + if (!assert_log(expr, #expr)) \ return sd_bus_error_set_errno(error, r); \ } while (false) diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c index 735a775cb4..d0b1e3d7dc 100644 --- a/src/libsystemd/sd-bus/bus-socket.c +++ b/src/libsystemd/sd-bus/bus-socket.c @@ -985,7 +985,7 @@ int bus_socket_read_message(sd_bus *bus) { return -EIO; } - f = realloc(bus->fds, sizeof(int) + (bus->n_fds + n)); + f = realloc(bus->fds, sizeof(int) * (bus->n_fds + n)); if (!f) { close_many((int*) CMSG_DATA(cmsg), n); return -ENOMEM; diff --git a/src/libsystemd/sd-bus/busctl.c b/src/libsystemd/sd-bus/busctl.c index a1f0f30d6c..9a6d338231 100644 --- a/src/libsystemd/sd-bus/busctl.c +++ b/src/libsystemd/sd-bus/busctl.c @@ -1823,20 +1823,20 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_SIZE: { - off_t o; + uint64_t sz; - r = parse_size(optarg, 1024, &o); + r = parse_size(optarg, 1024, &sz); if (r < 0) { log_error("Failed to parse size: %s", optarg); return r; } - if ((off_t) (size_t) o != o) { + if ((uint64_t) (size_t) sz != sz) { log_error("Size out of range."); return -E2BIG; } - arg_snaplen = (size_t) o; + arg_snaplen = (size_t) sz; break; } diff --git a/src/login/loginctl.c b/src/login/loginctl.c index a7e64071cf..405df49a7c 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -686,19 +686,165 @@ static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) return 0; } +static int print_property(const char *name, sd_bus_message *m, const char *contents) { + int r; + + assert(name); + assert(m); + assert(contents); + + if (arg_property && !strv_find(arg_property, name)) + /* skip what we didn't read */ + return sd_bus_message_skip(m, contents); + + switch (contents[0]) { + + case SD_BUS_TYPE_STRUCT_BEGIN: + + if (contents[1] == SD_BUS_TYPE_STRING && STR_IN_SET(name, "Display", "Seat", "ActiveSession")) { + const char *s; + + r = sd_bus_message_read(m, "(so)", &s, NULL); + if (r < 0) + return bus_log_parse_error(r); + + if (arg_all || !isempty(s)) + printf("%s=%s\n", name, s); + + return 0; + + } else if (contents[1] == SD_BUS_TYPE_UINT32 && streq(name, "User")) { + uint32_t uid; + + r = sd_bus_message_read(m, "(uo)", &uid, NULL); + if (r < 0) + return bus_log_parse_error(r); + + if (UID_IS_INVALID(uid)) { + log_error("Invalid user ID: " UID_FMT, uid); + return -EINVAL; + } + + printf("%s=" UID_FMT "\n", name, uid); + + return 0; + } + + break; + + case SD_BUS_TYPE_ARRAY: + + if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Sessions")) { + const char *s; + bool space = false; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(so)"); + if (r < 0) + return bus_log_parse_error(r); + + printf("%s=", name); + + while ((r = sd_bus_message_read(m, "(so)", &s, NULL)) > 0) { + printf("%s%s", space ? " " : "", s); + space = true; + } + + printf("\n"); + + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + return 0; + } + + break; + } + + r = bus_print_property(name, m, arg_all); + if (r < 0) + return bus_log_parse_error(r); + + if (r == 0) { + r = sd_bus_message_skip(m, contents); + if (r < 0) + return bus_log_parse_error(r); + + if (arg_all) + printf("%s=[unprintable]\n", name); + } + + return 0; +} + static int show_properties(sd_bus *bus, const char *path, bool *new_line) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; int r; + assert(bus); + assert(path); + assert(new_line); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + path, + "org.freedesktop.DBus.Properties", + "GetAll", + &error, + &reply, + "s", ""); + if (r < 0) + return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r)); + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}"); + if (r < 0) + return bus_log_parse_error(r); + if (*new_line) printf("\n"); *new_line = true; - r = bus_print_all_properties(bus, "org.freedesktop.login1", path, arg_property, arg_all); + while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) { + const char *name, *contents; + + r = sd_bus_message_read(reply, "s", &name); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_peek_type(reply, NULL, &contents); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents); + if (r < 0) + return bus_log_parse_error(r); + + r = print_property(name, reply, contents); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + } if (r < 0) - log_error_errno(r, "Could not get properties: %m"); + return bus_log_parse_error(r); - return r; + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + return 0; } static int show_session(int argc, char *argv[], void *userdata) { diff --git a/src/login/logind-user.c b/src/login/logind-user.c index f2c89e3653..47669afdef 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -879,15 +879,15 @@ int config_parse_tmpfs_size( *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100)); } else { - off_t o; + uint64_t k; - r = parse_size(rvalue, 1024, &o); - if (r < 0 || (off_t) (size_t) o != o) { - log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue); + r = parse_size(rvalue, 1024, &k); + if (r < 0 || (uint64_t) (size_t) k != k) { + log_syntax(unit, LOG_ERR, filename, line, r < 0 ? r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue); return 0; } - *sz = PAGE_ALIGN((size_t) o); + *sz = PAGE_ALIGN((size_t) k); } return 0; diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index cc38116704..6aaaa8aa31 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -423,7 +423,7 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s _exit(EXIT_FAILURE); } - r = copy_bytes(fd, pair[1], (off_t) -1, false); + r = copy_bytes(fd, pair[1], (uint64_t) -1, false); if (r < 0) _exit(EXIT_FAILURE); diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 7b8f6d1fab..ab113efb28 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -2385,13 +2385,9 @@ static int set_limit(int argc, char *argv[], void *userdata) { if (streq(argv[argc-1], "-")) limit = (uint64_t) -1; else { - off_t off; - - r = parse_size(argv[argc-1], 1024, &off); + r = parse_size(argv[argc-1], 1024, &limit); if (r < 0) return log_error("Failed to parse size: %s", argv[argc-1]); - - limit = (uint64_t) off; } if (argc > 2) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 33943a4b2f..5702df8ab4 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -3601,7 +3601,7 @@ finish: /* Try to flush whatever is still queued in the pty */ if (master >= 0) - (void) copy_bytes(master, STDOUT_FILENO, (off_t) -1, false); + (void) copy_bytes(master, STDOUT_FILENO, (uint64_t) -1, false); loop_remove(loop_nr, &image_fd); diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 12c17003e9..bf1b7c8ab4 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -191,7 +191,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { /* This has a cname? Then update the query with the * new cname. */ - r = dns_query_cname_redirect(q, cname->cname.name); + r = dns_query_cname_redirect(q, cname); if (r < 0) { if (r == -ELOOP) r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_CNAME_LOOP, "CNAME loop on '%s'", q->request_hostname); @@ -220,8 +220,6 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { added++; } - // what about the cache? - /* If we didn't find anything, then let's restart the * query, this time with the cname */ if (added <= 0) { diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c index 13ad4ca6bd..89b9b0e1ea 100644 --- a/src/resolve/resolved-dns-answer.c +++ b/src/resolve/resolved-dns-answer.c @@ -149,6 +149,19 @@ int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key) { return 0; } +int dns_answer_match_soa(DnsResourceKey *key, DnsResourceKey *soa) { + if (soa->class != DNS_CLASS_IN) + return 0; + + if (soa->type != DNS_TYPE_SOA) + return 0; + + if (!dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(soa))) + return 0; + + return 1; +} + int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret) { unsigned i; @@ -164,13 +177,7 @@ int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **r for (i = 0; i < a->n_rrs; i++) { - if (a->items[i].rr->key->class != DNS_CLASS_IN) - continue; - - if (a->items[i].rr->key->type != DNS_TYPE_SOA) - continue; - - if (dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(a->items[i].rr->key))) { + if (dns_answer_match_soa(key, a->items[i].rr->key)) { *ret = a->items[i].rr; return 1; } diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h index 0757dd60d0..044d73b19c 100644 --- a/src/resolve/resolved-dns-answer.h +++ b/src/resolve/resolved-dns-answer.h @@ -49,6 +49,7 @@ DnsAnswer *dns_answer_unref(DnsAnswer *a); int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex); int dns_answer_add_soa(DnsAnswer *a, const char *name, uint32_t ttl); int dns_answer_contains(DnsAnswer *a, DnsResourceKey *key); +int dns_answer_match_soa(DnsResourceKey *key, DnsResourceKey *soa); int dns_answer_find_soa(DnsAnswer *a, DnsResourceKey *key, DnsResourceRecord **ret); DnsAnswer *dns_answer_merge(DnsAnswer *a, DnsAnswer *b); diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index cbbbed8c8a..ab13636bc1 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -277,13 +277,14 @@ static int dns_cache_put_positive( /* New TTL is 0? Delete the entry... */ if (rr->ttl <= 0) { - if (dns_cache_remove(c, rr->key)) { - r = dns_resource_key_to_string(rr->key, &key_str); - if (r < 0) - return r; + r = dns_resource_key_to_string(rr->key, &key_str); + if (r < 0) + return r; + if (dns_cache_remove(c, rr->key)) log_debug("Removed zero TTL entry from cache: %s", key_str); - } + else + log_debug("Not caching zero TTL cache entry: %s", key_str); return 0; } @@ -361,7 +362,7 @@ static int dns_cache_put_negative( if (r < 0) return r; - log_debug("Ignored negative cache entry with zero SOA TTL: %s", key_str); + log_debug("Not caching negative entry with zero SOA TTL: %s", key_str); return 0; } @@ -402,7 +403,7 @@ static int dns_cache_put_negative( int dns_cache_put( DnsCache *c, - DnsQuestion *q, + DnsResourceKey *key, int rcode, DnsAnswer *answer, unsigned max_rrs, @@ -410,16 +411,16 @@ int dns_cache_put( int owner_family, const union in_addr_union *owner_address) { + DnsResourceRecord *soa = NULL; unsigned cache_keys, i; int r; assert(c); - if (q) { - /* First, if we were passed a question, delete all matching old RRs, + if (key) { + /* First, if we were passed a key, delete all matching old RRs, * so that we only keep complete by_key in place. */ - for (i = 0; i < q->n_keys; i++) - dns_cache_remove(c, q->keys[i]); + dns_cache_remove(c, key); } if (!answer) @@ -437,8 +438,8 @@ int dns_cache_put( cache_keys = answer->n_rrs; - if (q) - cache_keys += q->n_keys; + if (key) + cache_keys ++; /* Make some space for our new entries */ dns_cache_make_space(c, cache_keys); @@ -453,44 +454,63 @@ int dns_cache_put( goto fail; } - if (!q) + if (!key) return 0; - /* Third, add in negative entries for all keys with no RR */ - for (i = 0; i < q->n_keys; i++) { - DnsResourceRecord *soa = NULL; + /* Third, add in negative entries if the key has no RR */ + r = dns_answer_contains(answer, key); + if (r < 0) + goto fail; + if (r > 0) + return 0; - r = dns_answer_contains(answer, q->keys[i]); - if (r < 0) - goto fail; - if (r > 0) - continue; + /* See https://tools.ietf.org/html/rfc2308, which + * say that a matching SOA record in the packet + * is used to to enable negative caching. */ - /* See https://tools.ietf.org/html/rfc2308, which - * say that a matching SOA record in the packet - * is used to to enable negative caching. */ + r = dns_answer_find_soa(answer, key, &soa); + if (r < 0) + goto fail; + if (r == 0) + return 0; - r = dns_answer_find_soa(answer, q->keys[i], &soa); - if (r < 0) - goto fail; - if (r == 0) - continue; + /* Also, if the requested key is an alias, the negative response should + be cached for each name in the redirect chain. Any CNAME record in + the response is from the redirection chain, though only the final one + is guaranteed to be included. This means that we cannot verify the + chain and that we need to cache them all as it may be incomplete. */ + for (i = 0; i < answer->n_rrs; i++) { + DnsResourceRecord *answer_rr = answer->items[i].rr; - r = dns_cache_put_negative(c, q->keys[i], rcode, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address); - if (r < 0) - goto fail; + if (answer_rr->key->type == DNS_TYPE_CNAME) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *canonical_key = NULL; + + canonical_key = dns_resource_key_new_redirect(key, answer_rr); + if (!canonical_key) + goto fail; + + /* Let's not add negative cache entries for records outside the current zone. */ + if (!dns_answer_match_soa(canonical_key, soa->key)) + continue; + + r = dns_cache_put_negative(c, canonical_key, rcode, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address); + if (r < 0) + goto fail; + } } + r = dns_cache_put_negative(c, key, rcode, timestamp, MIN(soa->soa.minimum, soa->ttl), owner_family, owner_address); + if (r < 0) + goto fail; + return 0; fail: /* Adding all RRs failed. Let's clean up what we already * added, just in case */ - if (q) { - for (i = 0; i < q->n_keys; i++) - dns_cache_remove(c, q->keys[i]); - } + if (key) + dns_cache_remove(c, key); for (i = 0; i < answer->n_rrs; i++) dns_cache_remove(c, answer->items[i].rr->key); @@ -498,6 +518,29 @@ fail: return r; } +static DnsCacheItem *dns_cache_get_by_key_follow_cname(DnsCache *c, DnsResourceKey *k) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *cname_key = NULL; + DnsCacheItem *i, *j; + + assert(c); + assert(k); + + i = hashmap_get(c->by_key, k); + if (i || k->type == DNS_TYPE_CNAME) + return i; + + /* check if we have a CNAME record instead */ + cname_key = dns_resource_key_new_cname(k); + if (!cname_key) + return NULL; + + j = hashmap_get(c->by_key, cname_key); + if (j) + return j; + + return i; +} + int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret) { _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; unsigned n = 0; @@ -527,7 +570,7 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r return 0; } - first = hashmap_get(c->by_key, key); + first = dns_cache_get_by_key_follow_cname(c, key); if (!first) { /* If one question cannot be answered we need to refresh */ diff --git a/src/resolve/resolved-dns-cache.h b/src/resolve/resolved-dns-cache.h index 1225e58de4..60cf6a4784 100644 --- a/src/resolve/resolved-dns-cache.h +++ b/src/resolve/resolved-dns-cache.h @@ -39,7 +39,7 @@ typedef struct DnsCache { void dns_cache_flush(DnsCache *c); void dns_cache_prune(DnsCache *c); -int dns_cache_put(DnsCache *c, DnsQuestion *q, int rcode, DnsAnswer *answer, unsigned max_rrs, usec_t timestamp, int owner_family, const union in_addr_union *owner_address); +int dns_cache_put(DnsCache *c, DnsResourceKey *key, int rcode, DnsAnswer *answer, unsigned max_rrs, usec_t timestamp, int owner_family, const union in_addr_union *owner_address); int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **answer); int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address); diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index c0b4c8ba81..4b1d18b2ef 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -831,7 +831,7 @@ void dns_query_ready(DnsQuery *q) { dns_query_complete(q, state); } -int dns_query_cname_redirect(DnsQuery *q, const char *name) { +int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) { _cleanup_(dns_question_unrefp) DnsQuestion *nq = NULL; int r; @@ -840,7 +840,7 @@ int dns_query_cname_redirect(DnsQuery *q, const char *name) { if (q->n_cname_redirects > CNAME_MAX) return -ELOOP; - r = dns_question_cname_redirect(q->question, name, &nq); + r = dns_question_cname_redirect(q->question, cname, &nq); if (r < 0) return r; diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h index 93d49301fa..e7063d9678 100644 --- a/src/resolve/resolved-dns-query.h +++ b/src/resolve/resolved-dns-query.h @@ -72,7 +72,7 @@ DnsQuery *dns_query_free(DnsQuery *q); int dns_query_go(DnsQuery *q); void dns_query_ready(DnsQuery *q); -int dns_query_cname_redirect(DnsQuery *q, const char *name); +int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname); int dns_query_bus_track(DnsQuery *q, sd_bus_message *m); diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c index c94928d725..1507f22da0 100644 --- a/src/resolve/resolved-dns-question.c +++ b/src/resolve/resolved-dns-question.c @@ -242,13 +242,13 @@ int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b) { return 1; } -int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **ret) { +int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret) { _cleanup_(dns_question_unrefp) DnsQuestion *n = NULL; bool same = true; unsigned i; int r; - assert(name); + assert(cname); assert(ret); if (!q) { @@ -262,7 +262,7 @@ int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion ** } for (i = 0; i < q->n_keys; i++) { - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name); + r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), cname->cname.name); if (r < 0) return r; @@ -286,7 +286,7 @@ int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion ** for (i = 0; i < q->n_keys; i++) { _cleanup_(dns_resource_key_unrefp) DnsResourceKey *k = NULL; - k = dns_resource_key_new(q->keys[i]->class, q->keys[i]->type, name); + k = dns_resource_key_new_redirect(q->keys[i], cname); if (!k) return -ENOMEM; diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h index 77de0c7a2c..13cd1f20f3 100644 --- a/src/resolve/resolved-dns-question.h +++ b/src/resolve/resolved-dns-question.h @@ -46,6 +46,6 @@ int dns_question_is_superset(DnsQuestion *q, DnsQuestion *other); int dns_question_contains(DnsQuestion *a, DnsResourceKey *k); int dns_question_is_equal(DnsQuestion *a, DnsQuestion *b); -int dns_question_cname_redirect(DnsQuestion *q, const char *name, DnsQuestion **ret); +int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, DnsQuestion **ret); DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuestion*, dns_question_unref); diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index f31644eebc..fd2f53f40b 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -48,6 +48,19 @@ DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char * return k; } +DnsResourceKey* dns_resource_key_new_cname(const DnsResourceKey *key) { + assert(key); + + return dns_resource_key_new(key->class, DNS_TYPE_CNAME, DNS_RESOURCE_KEY_NAME(key)); +} + +DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname) { + assert(key); + assert(cname); + + return dns_resource_key_new(key->class, key->type, cname->cname.name); +} + DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name) { DnsResourceKey *k; diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index 8986a298af..9e2207c0aa 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -177,6 +177,8 @@ static inline const char* DNS_RESOURCE_KEY_NAME(const DnsResourceKey *key) { } DnsResourceKey* dns_resource_key_new(uint16_t class, uint16_t type, const char *name); +DnsResourceKey* dns_resource_key_new_cname(const DnsResourceKey *key); +DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const DnsResourceRecord *cname); DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name); DnsResourceKey* dns_resource_key_ref(DnsResourceKey *key); DnsResourceKey* dns_resource_key_unref(DnsResourceKey *key); diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 8092bb514d..b30473dd7e 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -458,7 +458,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { } /* According to RFC 4795, section 2.9. only the RRs from the answer section shall be cached */ - dns_cache_put(&t->scope->cache, p->question, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender); + dns_cache_put(&t->scope->cache, t->key, DNS_PACKET_RCODE(p), p->answer, DNS_PACKET_ANCOUNT(p), 0, p->family, &p->sender); if (DNS_PACKET_RCODE(p) == DNS_RCODE_SUCCESS) dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS); diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index 674bb6af28..8a59bd1c3c 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -90,11 +90,8 @@ void dns_zone_flush(DnsZone *z) { assert(hashmap_size(z->by_key) == 0); assert(hashmap_size(z->by_name) == 0); - hashmap_free(z->by_key); - z->by_key = NULL; - - hashmap_free(z->by_name); - z->by_name = NULL; + z->by_key = hashmap_free(z->by_key); + z->by_name = hashmap_free(z->by_name); } static DnsZoneItem* dns_zone_get(DnsZone *z, DnsResourceRecord *rr) { diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index 47f461a37d..b9fd8e3dbc 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -238,8 +238,7 @@ static int link_update_domains(Link *l) { if (!l->unicast_scope) return 0; - strv_free(l->unicast_scope->domains); - l->unicast_scope->domains = NULL; + l->unicast_scope->domains = strv_free(l->unicast_scope->domains); r = sd_network_link_get_domains(l->ifindex, &l->unicast_scope->domains); diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 36c44227c5..fdf41cec19 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -23,22 +23,24 @@ #include "sd-daemon.h" #include "sd-event.h" -#include "util.h" -#include "strv.h" -#include "macro.h" +#include "sd-bus.h" + +#include "bus-error.h" +#include "bus-internal.h" +#include "bus-label.h" +#include "bus-message.h" +#include "cgroup-util.h" #include "def.h" -#include "path-util.h" +#include "macro.h" #include "missing.h" +#include "path-util.h" #include "set.h" #include "signal-util.h" +#include "strv.h" #include "unit-name.h" +#include "util.h" -#include "sd-bus.h" -#include "bus-error.h" -#include "bus-label.h" -#include "bus-message.h" #include "bus-util.h" -#include "bus-internal.h" static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { sd_event *e = userdata; @@ -1421,9 +1423,10 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen return bus_log_create_error(r); if (STR_IN_SET(field, - "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", + "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", - "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit")) { + "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", + "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges")) { r = parse_boolean(eq); if (r < 0) { @@ -1434,20 +1437,50 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "v", "b", r); } else if (streq(field, "MemoryLimit")) { - off_t bytes; + uint64_t bytes; - r = parse_size(eq, 1024, &bytes); + if (isempty(eq) || streq(eq, "infinity")) + bytes = (uint64_t) -1; + else { + r = parse_size(eq, 1024, &bytes); + if (r < 0) { + log_error("Failed to parse bytes specification %s", assignment); + return -EINVAL; + } + } + + r = sd_bus_message_append(m, "v", "t", bytes); + + } else if (streq(field, "TasksMax")) { + uint64_t n; + + if (isempty(eq) || streq(eq, "infinity")) + n = (uint64_t) -1; + else { + r = safe_atou64(eq, &n); + if (r < 0) { + log_error("Failed to parse maximum tasks specification %s", assignment); + return -EINVAL; + } + } + + r = sd_bus_message_append(m, "v", "t", n); + + } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) { + uint64_t u; + + r = cg_cpu_shares_parse(eq, &u); if (r < 0) { - log_error("Failed to parse bytes specification %s", assignment); + log_error("Failed to parse %s value %s.", field, eq); return -EINVAL; } - r = sd_bus_message_append(m, "v", "t", (uint64_t) bytes); + r = sd_bus_message_append(m, "v", "t", u); - } else if (STR_IN_SET(field, "CPUShares", "BlockIOWeight")) { + } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) { uint64_t u; - r = safe_atou64(eq, &u); + r = cg_cpu_shares_parse(eq, &u); if (r < 0) { log_error("Failed to parse %s value %s.", field, eq); return -EINVAL; @@ -1492,7 +1525,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "v", "a(st)", 0); else { const char *path, *bandwidth, *e; - off_t bytes; + uint64_t bytes; e = strchr(eq, ' '); if (e) { @@ -1514,7 +1547,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen return -EINVAL; } - r = sd_bus_message_append(m, "v", "a(st)", 1, path, (uint64_t) bytes); + r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes); } } else if (streq(field, "BlockIODeviceWeight")) { diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index 23512f0d35..946eac6823 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -469,7 +469,7 @@ int config_parse_iec_size(const char* unit, void *userdata) { size_t *sz = data; - off_t o; + uint64_t v; int r; assert(filename); @@ -477,13 +477,13 @@ int config_parse_iec_size(const char* unit, assert(rvalue); assert(data); - r = parse_size(rvalue, 1024, &o); - if (r < 0 || (off_t) (size_t) o != o) { - log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue); + r = parse_size(rvalue, 1024, &v); + if (r < 0 || (uint64_t) (size_t) v != v) { + log_syntax(unit, LOG_ERR, filename, line, r < 0 ? r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue); return 0; } - *sz = (size_t) o; + *sz = (size_t) v; return 0; } @@ -499,7 +499,7 @@ int config_parse_si_size(const char* unit, void *userdata) { size_t *sz = data; - off_t o; + uint64_t v; int r; assert(filename); @@ -507,17 +507,17 @@ int config_parse_si_size(const char* unit, assert(rvalue); assert(data); - r = parse_size(rvalue, 1000, &o); - if (r < 0 || (off_t) (size_t) o != o) { - log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue); + r = parse_size(rvalue, 1000, &v); + if (r < 0 || (uint64_t) (size_t) v != v) { + log_syntax(unit, LOG_ERR, filename, line, r < 0 ? r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue); return 0; } - *sz = (size_t) o; + *sz = (size_t) v; return 0; } -int config_parse_iec_off(const char* unit, +int config_parse_iec_uint64(const char* unit, const char *filename, unsigned line, const char *section, @@ -528,7 +528,7 @@ int config_parse_iec_off(const char* unit, void *data, void *userdata) { - off_t *bytes = data; + uint64_t *bytes = data; int r; assert(filename); @@ -536,11 +536,9 @@ int config_parse_iec_off(const char* unit, assert(rvalue); assert(data); - assert_cc(sizeof(off_t) == sizeof(uint64_t)); - r = parse_size(rvalue, 1024, bytes); if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse size value, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue); return 0; } diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index 34e3815782..4efed138c9 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -109,7 +109,7 @@ int config_parse_uint64(const char *unit, const char *filename, unsigned line, c int config_parse_double(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_iec_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_si_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_iec_off(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_iec_uint64(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_bool(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_tristate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_string(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index 70220bdd14..9c1e4d5e13 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -19,16 +19,18 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/statfs.h> -#include <linux/fs.h> #include <fcntl.h> +#include <linux/fs.h> +#include <sys/statfs.h> -#include "utf8.h" #include "btrfs-util.h" -#include "path-util.h" #include "copy.h" #include "mkdir.h" +#include "path-util.h" #include "rm-rf.h" +#include "strv.h" +#include "utf8.h" + #include "machine-image.h" static const char image_search_path[] = @@ -47,6 +49,38 @@ Image *image_unref(Image *i) { return NULL; } +static char **image_settings_path(Image *image) { + _cleanup_strv_free_ char **l = NULL; + char **ret; + const char *fn, *s; + unsigned i = 0; + + assert(image); + + l = new0(char*, 4); + if (!l) + return NULL; + + fn = strjoina(image->name, ".nspawn"); + + FOREACH_STRING(s, "/etc/systemd/nspawn/", "/run/systemd/nspawn/") { + l[i] = strappend(s, fn); + if (!l[i]) + return NULL; + + i++; + } + + l[i] = file_in_same_dir(image->path, fn); + if (!l[i]) + return NULL; + + ret = l; + l = NULL; + + return ret; +} + static int image_new( ImageType t, const char *pretty, @@ -341,6 +375,8 @@ void image_hashmap_free(Hashmap *map) { int image_remove(Image *i) { _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT; + _cleanup_strv_free_ char **settings = NULL; + char **j; int r; assert(i); @@ -349,6 +385,10 @@ int image_remove(Image *i) { path_startswith(i->path, "/usr")) return -EROFS; + settings = image_settings_path(i); + if (!settings) + return -ENOMEM; + /* Make sure we don't interfere with a running nspawn */ r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock); if (r < 0) @@ -357,28 +397,56 @@ int image_remove(Image *i) { switch (i->type) { case IMAGE_SUBVOLUME: - return btrfs_subvol_remove(i->path, true); + r = btrfs_subvol_remove(i->path, true); + if (r < 0) + return r; + break; case IMAGE_DIRECTORY: /* Allow deletion of read-only directories */ (void) chattr_path(i->path, false, FS_IMMUTABLE_FL); - return rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); + r = rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); + if (r < 0) + return r; + + break; case IMAGE_RAW: if (unlink(i->path) < 0) return -errno; - - return 0; + break; default: return -EOPNOTSUPP; } + + STRV_FOREACH(j, settings) { + if (unlink(*j) < 0 && errno != ENOENT) + log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", *j); + } + + return 0; +} + +static int rename_settings_file(const char *path, const char *new_name) { + _cleanup_free_ char *rs = NULL; + const char *fn; + + fn = strjoina(new_name, ".nspawn"); + + rs = file_in_same_dir(path, fn); + if (!rs) + return -ENOMEM; + + return rename_noreplace(AT_FDCWD, path, AT_FDCWD, rs); } int image_rename(Image *i, const char *new_name) { _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT, name_lock = LOCK_FILE_INIT; _cleanup_free_ char *new_path = NULL, *nn = NULL; + _cleanup_strv_free_ char **settings = NULL; unsigned file_attr = 0; + char **j; int r; assert(i); @@ -390,6 +458,10 @@ int image_rename(Image *i, const char *new_name) { path_startswith(i->path, "/usr")) return -EROFS; + settings = image_settings_path(i); + if (!settings) + return -ENOMEM; + /* Make sure we don't interfere with a running nspawn */ r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock); if (r < 0) @@ -458,12 +530,33 @@ int image_rename(Image *i, const char *new_name) { i->name = nn; nn = NULL; + STRV_FOREACH(j, settings) { + r = rename_settings_file(*j, new_name); + if (r < 0 && r != -ENOENT) + log_debug_errno(r, "Failed to rename settings file %s, ignoring: %m", *j); + } + return 0; } +static int clone_settings_file(const char *path, const char *new_name) { + _cleanup_free_ char *rs = NULL; + const char *fn; + + fn = strjoina(new_name, ".nspawn"); + + rs = file_in_same_dir(path, fn); + if (!rs) + return -ENOMEM; + + return copy_file_atomic(path, rs, 0664, false, 0); +} + int image_clone(Image *i, const char *new_name, bool read_only) { _cleanup_release_lock_file_ LockFile name_lock = LOCK_FILE_INIT; + _cleanup_strv_free_ char **settings = NULL; const char *new_path; + char **j; int r; assert(i); @@ -471,6 +564,10 @@ int image_clone(Image *i, const char *new_name, bool read_only) { if (!image_name_is_valid(new_name)) return -EINVAL; + settings = image_settings_path(i); + if (!settings) + return -ENOMEM; + /* Make sure nobody takes the new name, between the time we * checked it is currently unused in all search paths, and the * time we take possesion of it */ @@ -506,6 +603,12 @@ int image_clone(Image *i, const char *new_name, bool read_only) { if (r < 0) return r; + STRV_FOREACH(j, settings) { + r = clone_settings_file(*j, new_name); + if (r < 0 && r != -ENOENT) + log_debug_errno(r, "Failed to clone settings %s, ignoring: %m", *j); + } + return 0; } diff --git a/src/shared/pager.c b/src/shared/pager.c index 479a9d5e8d..41da820938 100644 --- a/src/shared/pager.c +++ b/src/shared/pager.c @@ -38,7 +38,7 @@ static pid_t pager_pid = 0; noreturn static void pager_fallback(void) { int r; - r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (off_t) -1, false); + r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (uint64_t) -1, false); if (r < 0) { log_error_errno(r, "Internal pager failed: %m"); _exit(EXIT_FAILURE); diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c index f6a127174c..d803bbe07e 100644 --- a/src/shared/path-lookup.c +++ b/src/shared/path-lookup.c @@ -333,8 +333,7 @@ int lookup_paths_init( log_debug("Looking for unit files in (higher priority first):\n\t%s", t); } else { log_debug("Ignoring unit files."); - strv_free(p->unit_path); - p->unit_path = NULL; + p->unit_path = strv_free(p->unit_path); } if (running_as == MANAGER_SYSTEM) { @@ -390,8 +389,7 @@ int lookup_paths_init( log_debug("Looking for SysV init scripts in:\n\t%s", t); } else { log_debug("Ignoring SysV init scripts."); - strv_free(p->sysvinit_path); - p->sysvinit_path = NULL; + p->sysvinit_path = strv_free(p->sysvinit_path); } if (!strv_isempty(p->sysvrcnd_path)) { @@ -403,8 +401,7 @@ int lookup_paths_init( log_debug("Looking for SysV rcN.d links in:\n\t%s", t); } else { log_debug("Ignoring SysV rcN.d links."); - strv_free(p->sysvrcnd_path); - p->sysvrcnd_path = NULL; + p->sysvrcnd_path = strv_free(p->sysvrcnd_path); } #else log_debug("SysV init scripts and rcN.d links support disabled"); @@ -417,8 +414,7 @@ int lookup_paths_init( void lookup_paths_free(LookupPaths *p) { assert(p); - strv_free(p->unit_path); - p->unit_path = NULL; + p->unit_path = strv_free(p->unit_path); #ifdef HAVE_SYSV_COMPAT strv_free(p->sysvinit_path); diff --git a/src/shared/pty.c b/src/shared/pty.c deleted file mode 100644 index 35d9ff5f4d..0000000000 --- a/src/shared/pty.c +++ /dev/null @@ -1,633 +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/>. -***/ - -/* - * PTY - * A PTY object represents a single PTY connection between a master and a - * child. The child process is fork()ed so the caller controls what program - * will be run. - * - * Programs like /bin/login tend to perform a vhangup() on their TTY - * before running the login procedure. This also causes the pty master - * to get a EPOLLHUP event as long as no client has the TTY opened. - * This means, we cannot use the TTY connection as reliable way to track - * the client. Instead, we _must_ rely on the PID of the client to track - * them. - * However, this has the side effect that if the client forks and the - * parent exits, we loose them and restart the client. But this seems to - * be the expected behavior so we implement it here. - * - * Unfortunately, epoll always polls for EPOLLHUP so as long as the - * vhangup() is ongoing, we will _always_ get EPOLLHUP and cannot sleep. - * This gets worse if the client closes the TTY but doesn't exit. - * Therefore, the fd must be edge-triggered in the epoll-set so we - * only get the events once they change. - */ - -#include <errno.h> -#include <fcntl.h> -#include <signal.h> -#include <stdbool.h> -#include <stdint.h> -#include <stdlib.h> -#include <sys/epoll.h> -#include <sys/ioctl.h> -#include <sys/uio.h> -#include <sys/wait.h> -#include <termios.h> -#include <unistd.h> - -#include "barrier.h" -#include "macro.h" -#include "ring.h" -#include "util.h" -#include "signal-util.h" -#include "pty.h" - -#define PTY_BUFSIZE 4096 - -enum { - PTY_ROLE_UNKNOWN, - PTY_ROLE_PARENT, - PTY_ROLE_CHILD, -}; - -struct Pty { - unsigned long ref; - Barrier barrier; - int fd; - pid_t child; - sd_event_source *fd_source; - sd_event_source *child_source; - - char in_buf[PTY_BUFSIZE]; - Ring out_buf; - - pty_event_t event_fn; - void *event_fn_userdata; - - bool needs_requeue : 1; - unsigned int role : 2; -}; - -int pty_new(Pty **out) { - _pty_unref_ Pty *pty = NULL; - int r; - - assert_return(out, -EINVAL); - - pty = new0(Pty, 1); - if (!pty) - return -ENOMEM; - - pty->ref = 1; - pty->fd = -1; - pty->barrier = (Barrier) BARRIER_NULL; - - pty->fd = posix_openpt(O_RDWR | O_NOCTTY | O_CLOEXEC | O_NONBLOCK); - if (pty->fd < 0) - return -errno; - - /* - * The slave-node is initialized to uid/gid of the caller of - * posix_openpt(). Only if devpts is mounted with fixed uid/gid this is - * skipped. In that case, grantpt() can overwrite these, but then you - * have to be root to use chown() (or a pt_chown helper has to be - * present). In those cases grantpt() really does something, - * otherwise it's a no-op. We call grantpt() here to try supporting - * those cases, even though no-one uses that, I guess. If you need other - * access-rights, set them yourself after this call returns (no, this is - * not racy, it looks racy, but races regarding your own UID are never - * important as an attacker could ptrace you; and the slave-pty is also - * still locked). - */ - r = grantpt(pty->fd); - if (r < 0) - return -errno; - - r = barrier_create(&pty->barrier); - if (r < 0) - return r; - - *out = pty; - pty = NULL; - return 0; -} - -Pty *pty_ref(Pty *pty) { - if (!pty || pty->ref < 1) - return NULL; - - ++pty->ref; - return pty; -} - -Pty *pty_unref(Pty *pty) { - if (!pty || pty->ref < 1 || --pty->ref > 0) - return NULL; - - pty_close(pty); - pty->child_source = sd_event_source_unref(pty->child_source); - barrier_destroy(&pty->barrier); - ring_clear(&pty->out_buf); - free(pty); - - return NULL; -} - -Barrier *pty_get_barrier(Pty *pty) { - assert(pty); - return &pty->barrier; -} - -bool pty_is_unknown(Pty *pty) { - return pty && pty->role == PTY_ROLE_UNKNOWN; -} - -bool pty_is_parent(Pty *pty) { - return pty && pty->role == PTY_ROLE_PARENT; -} - -bool pty_is_child(Pty *pty) { - return pty && pty->role == PTY_ROLE_CHILD; -} - -bool pty_has_child(Pty *pty) { - return pty_is_parent(pty) && pty->child > 0; -} - -pid_t pty_get_child(Pty *pty) { - return pty_has_child(pty) ? pty->child : -ECHILD; -} - -bool pty_is_open(Pty *pty) { - return pty && pty->fd >= 0; -} - -int pty_get_fd(Pty *pty) { - assert_return(pty, -EINVAL); - - return pty_is_open(pty) ? pty->fd : -EPIPE; -} - -int pty_make_child(Pty *pty) { - _cleanup_free_ char *slave_name = NULL; - int r, fd; - - assert_return(pty, -EINVAL); - assert_return(pty_is_unknown(pty), -EALREADY); - - r = ptsname_malloc(pty->fd, &slave_name); - if (r < 0) - return -errno; - - fd = open(slave_name, O_RDWR | O_CLOEXEC | O_NOCTTY); - if (fd < 0) - return -errno; - - safe_close(pty->fd); - pty->fd = fd; - pty->child = getpid(); - pty->role = PTY_ROLE_CHILD; - barrier_set_role(&pty->barrier, BARRIER_CHILD); - - return 0; -} - -int pty_make_parent(Pty *pty, pid_t child) { - assert_return(pty, -EINVAL); - assert_return(pty_is_unknown(pty), -EALREADY); - - pty->child = child; - pty->role = PTY_ROLE_PARENT; - - return 0; -} - -int pty_unlock(Pty *pty) { - assert_return(pty, -EINVAL); - assert_return(pty_is_unknown(pty) || pty_is_parent(pty), -EINVAL); - assert_return(pty_is_open(pty), -ENODEV); - - return unlockpt(pty->fd) < 0 ? -errno : 0; -} - -int pty_setup_child(Pty *pty) { - struct termios attr; - pid_t pid; - int r; - - assert_return(pty, -EINVAL); - assert_return(pty_is_child(pty), -EINVAL); - assert_return(pty_is_open(pty), -EALREADY); - - r = reset_signal_mask(); - if (r < 0) - return r; - - r = reset_all_signal_handlers(); - if (r < 0) - return r; - - pid = setsid(); - if (pid < 0 && errno != EPERM) - return -errno; - - r = ioctl(pty->fd, TIOCSCTTY, 0); - if (r < 0) - return -errno; - - r = tcgetattr(pty->fd, &attr); - if (r < 0) - return -errno; - - /* erase character should be normal backspace, PLEASEEE! */ - attr.c_cc[VERASE] = 010; - /* always set UTF8 flag */ - attr.c_iflag |= IUTF8; - - r = tcsetattr(pty->fd, TCSANOW, &attr); - if (r < 0) - return -errno; - - if (dup2(pty->fd, STDIN_FILENO) != STDIN_FILENO || - dup2(pty->fd, STDOUT_FILENO) != STDOUT_FILENO || - dup2(pty->fd, STDERR_FILENO) != STDERR_FILENO) - return -errno; - - /* only close FD if it's not a std-fd */ - pty->fd = (pty->fd > 2) ? safe_close(pty->fd) : -1; - - return 0; -} - -void pty_close(Pty *pty) { - if (!pty_is_open(pty)) - return; - - pty->fd_source = sd_event_source_unref(pty->fd_source); - pty->fd = safe_close(pty->fd); -} - -/* - * Drain input-queue and dispatch data via the event-handler. Returns <0 on - * error, 0 if queue is empty and 1 if we couldn't empty the input queue fast - * enough and there's still data left. - */ -static int pty_dispatch_read(Pty *pty) { - unsigned int i; - ssize_t len; - int r; - - /* - * We're edge-triggered, means we need to read the whole queue. This, - * however, might cause us to stall if the writer is faster than we - * are. Therefore, try reading as much as 8 times (32KiB) and only - * bail out then. - */ - - for (i = 0; i < 8; ++i) { - len = read(pty->fd, pty->in_buf, sizeof(pty->in_buf) - 1); - if (len < 0) { - if (errno == EINTR) - continue; - - return (errno == EAGAIN) ? 0 : -errno; - } else if (len == 0) - continue; - - /* set terminating zero for debugging safety */ - pty->in_buf[len] = 0; - r = pty->event_fn(pty, pty->event_fn_userdata, PTY_DATA, pty->in_buf, len); - if (r < 0) - return r; - } - - /* still data left, make sure we're queued again */ - pty->needs_requeue = true; - - return 1; -} - -/* - * Drain output-queue by writing data to the pty. Returns <0 on error, 0 if the - * output queue is empty now and 1 if we couldn't empty the output queue fast - * enough and there's still data left. - */ -static int pty_dispatch_write(Pty *pty) { - struct iovec vec[2]; - unsigned int i; - ssize_t len; - size_t num; - - /* - * Same as pty_dispatch_read(), we're edge-triggered so we need to call - * write() until either all data is written or it returns EAGAIN. We - * call it twice and if it still writes successfully, we reschedule. - */ - - for (i = 0; i < 2; ++i) { - num = ring_peek(&pty->out_buf, vec); - if (num < 1) - return 0; - - len = writev(pty->fd, vec, (int)num); - if (len < 0) { - if (errno == EINTR) - continue; - - return (errno == EAGAIN) ? 1 : -errno; - } else if (len == 0) - continue; - - ring_pull(&pty->out_buf, (size_t)len); - } - - /* still data left, make sure we're queued again */ - if (ring_get_size(&pty->out_buf) > 0) { - pty->needs_requeue = true; - return 1; - } - - return 0; -} - -static int pty_fd_fn(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - Pty *pty = userdata; - int r_hup = 0, r_write = 0, r_read = 0, r; - - /* - * Whenever we encounter I/O errors, we have to make sure to drain the - * input queue first, before we handle any HUP. A child might send us - * a message and immediately close the queue. We must not handle the - * HUP first or we loose data. - * Therefore, if we read a message successfully, we always return - * success and wait for the next event-loop iteration. Furthermore, - * whenever there is a write-error, we must try reading from the input - * queue even if EPOLLIN is not set. The input might have arrived in - * between epoll_wait() and write(). Therefore, write-errors are only - * ever handled if the input-queue is empty. In all other cases they - * are ignored until either reading fails or the input queue is empty. - */ - - if (revents & (EPOLLHUP | EPOLLERR)) - r_hup = -EPIPE; - - if (revents & EPOLLOUT) - r_write = pty_dispatch_write(pty); - - /* Awesome! Kernel signals HUP without IN but queues are not empty.. */ - if ((revents & EPOLLIN) || r_hup < 0 || r_write < 0) { - r_read = pty_dispatch_read(pty); - if (r_read > 0) - return 0; /* still data left to fetch next round */ - } - - if (r_hup < 0 || r_write < 0 || r_read < 0) { - /* PTY closed and input-queue drained */ - pty_close(pty); - r = pty->event_fn(pty, pty->event_fn_userdata, PTY_HUP, NULL, 0); - if (r < 0) - return r; - } - - return 0; -} - -static int pty_fd_prepare_fn(sd_event_source *source, void *userdata) { - Pty *pty = userdata; - int r; - - if (pty->needs_requeue) { - /* - * We're edge-triggered. In case we couldn't handle all events - * or in case new write-data is queued, we set needs_requeue. - * Before going asleep, we set the io-events *again*. sd-event - * notices that we're edge-triggered and forwards the call to - * the kernel even if the events didn't change. The kernel will - * check the events and re-queue us on the ready queue in case - * an event is pending. - */ - r = sd_event_source_set_io_events(source, EPOLLHUP | EPOLLERR | EPOLLIN | EPOLLOUT | EPOLLET); - if (r >= 0) - pty->needs_requeue = false; - } - - return 0; -} - -static int pty_child_fn(sd_event_source *source, const siginfo_t *si, void *userdata) { - Pty *pty = userdata; - int r; - - pty->child = 0; - - r = pty->event_fn(pty, pty->event_fn_userdata, PTY_CHILD, si, sizeof(*si)); - if (r < 0) - return r; - - return 0; -} - -int pty_attach_event(Pty *pty, sd_event *event, pty_event_t event_fn, void *event_fn_userdata) { - int r; - - assert_return(pty, -EINVAL); - assert_return(event, -EINVAL); - assert_return(event_fn, -EINVAL); - assert_return(pty_is_parent(pty), -EINVAL); - - pty_detach_event(pty); - - if (pty_is_open(pty)) { - r = sd_event_add_io(event, - &pty->fd_source, - pty->fd, - EPOLLHUP | EPOLLERR | EPOLLIN | EPOLLOUT | EPOLLET, - pty_fd_fn, - pty); - if (r < 0) - goto error; - - r = sd_event_source_set_prepare(pty->fd_source, pty_fd_prepare_fn); - if (r < 0) - goto error; - } - - if (pty_has_child(pty)) { - r = sd_event_add_child(event, - &pty->child_source, - pty->child, - WEXITED, - pty_child_fn, - pty); - if (r < 0) - goto error; - } - - pty->event_fn = event_fn; - pty->event_fn_userdata = event_fn_userdata; - - return 0; - -error: - pty_detach_event(pty); - return r; -} - -void pty_detach_event(Pty *pty) { - if (!pty) - return; - - pty->child_source = sd_event_source_unref(pty->child_source); - pty->fd_source = sd_event_source_unref(pty->fd_source); - pty->event_fn = NULL; - pty->event_fn_userdata = NULL; -} - -int pty_write(Pty *pty, const void *buf, size_t size) { - bool was_empty; - int r; - - assert_return(pty, -EINVAL); - assert_return(pty_is_open(pty), -ENODEV); - assert_return(pty_is_parent(pty), -ENODEV); - - if (size < 1) - return 0; - - /* - * Push @buf[0..@size] into the output ring-buffer. In case the - * ring-buffer wasn't empty beforehand, we're already waiting for - * EPOLLOUT and we're done. If it was empty, we have to re-queue the - * FD for EPOLLOUT as we're edge-triggered and wouldn't get any new - * EPOLLOUT event. - */ - - was_empty = ring_get_size(&pty->out_buf) < 1; - - r = ring_push(&pty->out_buf, buf, size); - if (r < 0) - return r; - - if (was_empty) - pty->needs_requeue = true; - - return 0; -} - -int pty_signal(Pty *pty, int sig) { - assert_return(pty, -EINVAL); - assert_return(pty_is_open(pty), -ENODEV); - assert_return(pty_is_parent(pty), -ENODEV); - - return ioctl(pty->fd, TIOCSIG, sig) < 0 ? -errno : 0; -} - -int pty_resize(Pty *pty, unsigned short term_width, unsigned short term_height) { - struct winsize ws = { - .ws_col = term_width, - .ws_row = term_height, - }; - - assert_return(pty, -EINVAL); - assert_return(pty_is_open(pty), -ENODEV); - assert_return(pty_is_parent(pty), -ENODEV); - - /* - * This will send SIGWINCH to the pty slave foreground process group. - * We will also get one, but we don't need it. - */ - return ioctl(pty->fd, TIOCSWINSZ, &ws) < 0 ? -errno : 0; -} - -pid_t pty_fork(Pty **out, sd_event *event, pty_event_t event_fn, void *event_fn_userdata, unsigned short initial_term_width, unsigned short initial_term_height) { - _pty_unref_ Pty *pty = NULL; - int r; - pid_t pid; - - assert_return(out, -EINVAL); - assert_return((event && event_fn) || (!event && !event_fn), -EINVAL); - - r = pty_new(&pty); - if (r < 0) - return r; - - r = pty_unlock(pty); - if (r < 0) - return r; - - pid = fork(); - if (pid < 0) - return -errno; - - if (pid == 0) { - /* child */ - - r = pty_make_child(pty); - if (r < 0) - _exit(-r); - - r = pty_setup_child(pty); - if (r < 0) - _exit(-r); - - /* sync with parent */ - if (!barrier_place_and_sync(&pty->barrier)) - _exit(1); - - /* fallthrough and return the child's PTY object */ - } else { - /* parent */ - - r = pty_make_parent(pty, pid); - if (r < 0) - goto parent_error; - - r = pty_resize(pty, initial_term_width, initial_term_height); - if (r < 0) - goto parent_error; - - if (event) { - r = pty_attach_event(pty, event, event_fn, event_fn_userdata); - if (r < 0) - goto parent_error; - } - - /* sync with child */ - if (!barrier_place_and_sync(&pty->barrier)) { - r = -ECHILD; - goto parent_error; - } - - /* fallthrough and return the parent's PTY object */ - } - - *out = pty; - pty = NULL; - return pid; - -parent_error: - barrier_abort(&pty->barrier); - waitpid(pty->child, NULL, 0); - pty->child = 0; - return r; -} diff --git a/src/shared/pty.h b/src/shared/pty.h deleted file mode 100644 index 63c7db2833..0000000000 --- a/src/shared/pty.h +++ /dev/null @@ -1,72 +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/>. -***/ - -#include <stdbool.h> -#include <unistd.h> - -#include "barrier.h" -#include "macro.h" -#include "sd-event.h" - -typedef struct Pty Pty; - -enum { - PTY_CHILD, - PTY_HUP, - PTY_DATA, -}; - -typedef int (*pty_event_t) (Pty *pty, void *userdata, unsigned int event, const void *ptr, size_t size); - -int pty_new(Pty **out); -Pty *pty_ref(Pty *pty); -Pty *pty_unref(Pty *pty); - -#define _pty_unref_ _cleanup_(pty_unrefp) -DEFINE_TRIVIAL_CLEANUP_FUNC(Pty*, pty_unref); - -Barrier *pty_get_barrier(Pty *pty); - -bool pty_is_unknown(Pty *pty); -bool pty_is_parent(Pty *pty); -bool pty_is_child(Pty *pty); -bool pty_has_child(Pty *pty); -pid_t pty_get_child(Pty *pty); - -bool pty_is_open(Pty *pty); -int pty_get_fd(Pty *pty); - -int pty_make_child(Pty *pty); -int pty_make_parent(Pty *pty, pid_t child); -int pty_unlock(Pty *pty); -int pty_setup_child(Pty *pty); -void pty_close(Pty *pty); - -int pty_attach_event(Pty *pty, sd_event *event, pty_event_t event_fn, void *event_fn_userdata); -void pty_detach_event(Pty *pty); - -int pty_write(Pty *pty, const void *buf, size_t size); -int pty_signal(Pty *pty, int sig); -int pty_resize(Pty *pty, unsigned short term_width, unsigned short term_height); - -pid_t pty_fork(Pty **out, sd_event *event, pty_event_t event_fn, void *event_fn_userdata, unsigned short initial_term_width, unsigned short initial_term_height); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 74d7fc2b3b..d21ba9a566 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -20,59 +20,60 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/reboot.h> -#include <linux/reboot.h> -#include <stdio.h> +#include <errno.h> +#include <fcntl.h> #include <getopt.h> +#include <linux/reboot.h> #include <locale.h> #include <stdbool.h> +#include <stddef.h> +#include <stdio.h> #include <string.h> -#include <errno.h> -#include <unistd.h> -#include <fcntl.h> +#include <sys/reboot.h> #include <sys/socket.h> -#include <stddef.h> +#include <unistd.h> +#include "sd-bus.h" #include "sd-daemon.h" #include "sd-login.h" -#include "sd-bus.h" -#include "log.h" -#include "util.h" -#include "macro.h" -#include "set.h" -#include "utmp-wtmp.h" -#include "special.h" -#include "initreq.h" -#include "path-util.h" -#include "strv.h" + +#include "build.h" +#include "bus-common-errors.h" +#include "bus-error.h" +#include "bus-message.h" +#include "bus-util.h" #include "cgroup-show.h" #include "cgroup-util.h" -#include "list.h" -#include "path-lookup.h" -#include "exit-status.h" -#include "build.h" -#include "unit-name.h" -#include "pager.h" -#include "spawn-ask-password-agent.h" -#include "spawn-polkit-agent.h" -#include "install.h" -#include "logs-show.h" -#include "socket-util.h" -#include "fileio.h" #include "copy.h" -#include "env-util.h" -#include "bus-util.h" -#include "bus-message.h" -#include "bus-error.h" -#include "bus-common-errors.h" -#include "mkdir.h" #include "dropin.h" #include "efivars.h" +#include "env-util.h" +#include "exit-status.h" +#include "fileio.h" #include "formats-util.h" -#include "process-util.h" -#include "terminal-util.h" #include "hostname-util.h" +#include "initreq.h" +#include "install.h" +#include "list.h" +#include "log.h" +#include "logs-show.h" +#include "macro.h" +#include "mkdir.h" +#include "pager.h" +#include "path-lookup.h" +#include "path-util.h" +#include "process-util.h" +#include "set.h" #include "signal-util.h" +#include "socket-util.h" +#include "spawn-ask-password-agent.h" +#include "spawn-polkit-agent.h" +#include "special.h" +#include "strv.h" +#include "terminal-util.h" +#include "unit-name.h" +#include "util.h" +#include "utmp-wtmp.h" static char **arg_types = NULL; static char **arg_states = NULL; @@ -100,7 +101,7 @@ static bool arg_quiet = false; static bool arg_full = false; static bool arg_recursive = false; static int arg_force = 0; -static bool arg_ask_password = true; +static bool arg_ask_password = false; static bool arg_runtime = false; static UnitFilePresetMode arg_preset_mode = UNIT_FILE_PRESET_FULL; static char **arg_wall = NULL; @@ -1299,10 +1300,11 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { const char *on, *off; const char *id; - if (u->state == UNIT_FILE_MASKED || - u->state == UNIT_FILE_MASKED_RUNTIME || - u->state == UNIT_FILE_DISABLED || - u->state == UNIT_FILE_INVALID) { + if (IN_SET(u->state, + UNIT_FILE_MASKED, + UNIT_FILE_MASKED_RUNTIME, + UNIT_FILE_DISABLED, + UNIT_FILE_INVALID)) { on = ansi_highlight_red(); off = ansi_highlight_off(); } else if (u->state == UNIT_FILE_ENABLED) { @@ -1476,6 +1478,8 @@ static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, cha "BindsTo\0", [DEPENDENCY_REVERSE] = "RequiredBy\0" "RequiredByOverridable\0" + "RequisiteOf\0" + "RequisiteOfOverridable\0" "WantedBy\0" "PartOf\0" "BoundBy\0", @@ -2762,7 +2766,7 @@ static int start_unit(sd_bus *bus, char **args) { static int reboot_with_logind(sd_bus *bus, enum action a) { #ifdef HAVE_LOGIND _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - const char *method; + const char *method, *description; int r; if (!bus) @@ -2774,22 +2778,27 @@ static int reboot_with_logind(sd_bus *bus, enum action a) { case ACTION_REBOOT: method = "Reboot"; + description = "reboot system"; break; case ACTION_POWEROFF: method = "PowerOff"; + description = "power off system"; break; case ACTION_SUSPEND: method = "Suspend"; + description = "suspend system"; break; case ACTION_HIBERNATE: method = "Hibernate"; + description = "hibernate system"; break; case ACTION_HYBRID_SLEEP: method = "HybridSleep"; + description = "put system into hybrid sleep"; break; default: @@ -2833,7 +2842,7 @@ static int reboot_with_logind(sd_bus *bus, enum action a) { NULL, "b", arg_ask_password); if (r < 0) - log_error("Failed to execute operation: %s", bus_error_message(&error, r)); + log_error("Failed to %s via logind: %s", description, bus_error_message(&error, r)); return r; #else @@ -2898,10 +2907,11 @@ static int check_inhibitors(sd_bus *bus, enum action a) { return log_error_errno(ERANGE, "Bad PID %"PRIu32": %m", pid); if (!strv_contains(sv, - a == ACTION_HALT || - a == ACTION_POWEROFF || - a == ACTION_REBOOT || - a == ACTION_KEXEC ? "shutdown" : "sleep")) + IN_SET(a, + ACTION_HALT, + ACTION_POWEROFF, + ACTION_REBOOT, + ACTION_KEXEC) ? "shutdown" : "sleep")) continue; get_process_comm(pid, &comm); @@ -3022,29 +3032,36 @@ static int start_special(sd_bus *bus, char **args) { } if (arg_force >= 2 && - (a == ACTION_HALT || - a == ACTION_POWEROFF || - a == ACTION_REBOOT)) + IN_SET(a, + ACTION_HALT, + ACTION_POWEROFF, + ACTION_REBOOT)) return halt_now(a); if (arg_force >= 1 && - (a == ACTION_HALT || - a == ACTION_POWEROFF || - a == ACTION_REBOOT || - a == ACTION_KEXEC || - a == ACTION_EXIT)) + IN_SET(a, + ACTION_HALT, + ACTION_POWEROFF, + ACTION_REBOOT, + ACTION_KEXEC, + ACTION_EXIT)) return daemon_reload(bus, args); /* first try logind, to allow authentication with polkit */ if (geteuid() != 0 && - (a == ACTION_POWEROFF || - a == ACTION_REBOOT || - a == ACTION_SUSPEND || - a == ACTION_HIBERNATE || - a == ACTION_HYBRID_SLEEP)) { + IN_SET(a, + ACTION_POWEROFF, + ACTION_REBOOT, + ACTION_SUSPEND, + ACTION_HIBERNATE, + ACTION_HYBRID_SLEEP)) { r = reboot_with_logind(bus, a); - if (r >= 0 || IN_SET(r, -EOPNOTSUPP, -EINPROGRESS)) + if (r >= 0) + return r; + if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS)) + /* requested operation is not supported or already in progress */ return r; + /* on all other errors, try low-level operation */ } r = start_unit(bus, args); @@ -3283,6 +3300,8 @@ typedef struct UnitStatusInfo { uint64_t memory_current; uint64_t memory_limit; uint64_t cpu_usage_nsec; + uint64_t tasks_current; + uint64_t tasks_max; LIST_HEAD(ExecStatusInfo, exec); } UnitStatusInfo; @@ -3541,6 +3560,15 @@ static void print_status_info( if (i->status_errno > 0) printf(" Error: %i (%s)\n", i->status_errno, strerror(i->status_errno)); + if (i->tasks_current != (uint64_t) -1) { + printf(" Tasks: %" PRIu64, i->tasks_current); + + if (i->tasks_max != (uint64_t) -1) + printf(" (limit: %" PRIi64 ")\n", i->tasks_max); + else + printf("\n"); + } + if (i->memory_current != (uint64_t) -1) { char buf[FORMAT_BYTES_MAX]; @@ -3559,12 +3587,14 @@ static void print_status_info( if (i->control_group && (i->main_pid > 0 || i->control_pid > 0 || - ((arg_transport != BUS_TRANSPORT_LOCAL && arg_transport != BUS_TRANSPORT_MACHINE) || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group) == 0))) { + (!IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE) || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group) == 0))) { unsigned c; printf(" CGroup: %s\n", i->control_group); - if (arg_transport == BUS_TRANSPORT_LOCAL || arg_transport == BUS_TRANSPORT_MACHINE) { + if (IN_SET(arg_transport, + BUS_TRANSPORT_LOCAL, + BUS_TRANSPORT_MACHINE)) { unsigned k = 0; pid_t extra[2]; static const char prefix[] = " "; @@ -3774,6 +3804,10 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo * i->memory_current = u; else if (streq(name, "MemoryLimit")) i->memory_limit = u; + else if (streq(name, "TasksCurrent")) + i->tasks_current = u; + else if (streq(name, "TasksMax")) + i->tasks_max = u; else if (streq(name, "CPUUsageNSec")) i->cpu_usage_nsec = u; @@ -4250,6 +4284,8 @@ static int show_one( .memory_current = (uint64_t) -1, .memory_limit = (uint64_t) -1, .cpu_usage_nsec = (uint64_t) -1, + .tasks_current = (uint64_t) -1, + .tasks_max = (uint64_t) -1, }; ExecStatusInfo *p; int r; @@ -4467,7 +4503,9 @@ static int show_system_status(sd_bus *bus) { format_timestamp_relative(since1, sizeof(since1), mi.timestamp)); printf(" CGroup: %s\n", mi.control_group ?: "/"); - if (arg_transport == BUS_TRANSPORT_LOCAL || arg_transport == BUS_TRANSPORT_MACHINE) { + if (IN_SET(arg_transport, + BUS_TRANSPORT_LOCAL, + BUS_TRANSPORT_MACHINE)) { static const char prefix[] = " "; unsigned c; @@ -4623,7 +4661,7 @@ static int cat_file(const char *filename, bool newline) { ansi_highlight_off()); fflush(stdout); - return copy_bytes(fd, STDOUT_FILENO, (off_t) -1, false); + return copy_bytes(fd, STDOUT_FILENO, (uint64_t) -1, false); } static int cat(sd_bus *bus, char **args) { @@ -5134,9 +5172,10 @@ static int enable_sysv_units(const char *verb, char **args) { if (arg_scope != UNIT_FILE_SYSTEM) return 0; - if (!streq(verb, "enable") && - !streq(verb, "disable") && - !streq(verb, "is-enabled")) + if (!STR_IN_SET(verb, + "enable", + "disable", + "is-enabled")) return 0; /* Processes all SysV units, and reshuffles the array so that @@ -5636,10 +5675,11 @@ static int unit_is_enabled(sd_bus *bus, char **args) { if (state < 0) return log_error_errno(state, "Failed to get unit file state for %s: %m", *name); - if (state == UNIT_FILE_ENABLED || - state == UNIT_FILE_ENABLED_RUNTIME || - state == UNIT_FILE_STATIC || - state == UNIT_FILE_INDIRECT) + if (IN_SET(state, + UNIT_FILE_ENABLED, + UNIT_FILE_ENABLED_RUNTIME, + UNIT_FILE_STATIC, + UNIT_FILE_INDIRECT)) enabled = true; if (!arg_quiet) @@ -6340,6 +6380,9 @@ static int systemctl_parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); + /* we default to allowing interactive authorization only in systemctl (not in the legacy commands) */ + arg_ask_password = true; + while ((c = getopt_long(argc, argv, "ht:p:alqfs:H:M:n:o:ir", options, NULL)) >= 0) switch (c) { @@ -7353,21 +7396,27 @@ static int halt_main(sd_bus *bus) { return r; if (geteuid() != 0) { + if (arg_when > 0 || + arg_dry || + arg_force > 0) { + log_error("Must be root."); + return -EPERM; + } + /* Try logind if we are a normal user and no special * mode applies. Maybe PolicyKit allows us to shutdown * the machine. */ - - if (arg_when <= 0 && - arg_force <= 0 && - (arg_action == ACTION_POWEROFF || - arg_action == ACTION_REBOOT)) { + if (IN_SET(arg_action, + ACTION_POWEROFF, + ACTION_REBOOT)) { r = reboot_with_logind(bus, arg_action); if (r >= 0) return r; + if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS)) + /* requested operation is not supported or already in progress */ + return r; + /* on all other errors, try low-level operation */ } - - log_error("Must be root."); - return -EPERM; } if (arg_when > 0) { @@ -7376,6 +7425,8 @@ static int halt_main(sd_bus *bus) { _cleanup_free_ char *m = NULL; const char *action; + assert(geteuid() == 0); + if (avoid_bus()) { log_error("Unable to perform operation without bus connection."); return -ENOSYS; @@ -7451,6 +7502,8 @@ static int halt_main(sd_bus *bus) { if (!arg_dry && !arg_force) return start_with_fallback(bus); + assert(geteuid() == 0); + if (!arg_no_wtmp) { if (sd_booted() > 0) log_debug("Not writing utmp record, assuming that systemd-update-utmp is used."); diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index b5e09cad26..aaa33354f4 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -207,7 +207,7 @@ static int make_backup(const char *target, const char *x) { if (r < 0) return r; - r = copy_bytes(src, fileno(dst), (off_t) -1, true); + r = copy_bytes(src, fileno(dst), (uint64_t) -1, true); if (r < 0) goto fail; diff --git a/src/test/test-copy.c b/src/test/test-copy.c index b73c958ec5..a03a68bd43 100644 --- a/src/test/test-copy.c +++ b/src/test/test-copy.c @@ -146,7 +146,7 @@ static void test_copy_bytes(void) { assert_se(pipe2(pipefd, O_CLOEXEC) == 0); - r = copy_bytes(infd, pipefd[1], (off_t) -1, false); + r = copy_bytes(infd, pipefd[1], (uint64_t) -1, false); assert_se(r == 0); r = read(pipefd[0], buf, sizeof(buf)); diff --git a/src/test/test-pty.c b/src/test/test-pty.c deleted file mode 100644 index fbab3d4ebe..0000000000 --- a/src/test/test-pty.c +++ /dev/null @@ -1,142 +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 <locale.h> -#include <string.h> -#include <sys/wait.h> -#include <unistd.h> - -#include "pty.h" -#include "util.h" -#include "signal-util.h" - -static const char sndmsg[] = "message\n"; -static const char rcvmsg[] = "message\r\n"; -static char rcvbuf[128]; -static size_t rcvsiz = 0; -static sd_event *event; - -static void run_child(Pty *pty) { - ssize_t r, l; - char buf[512]; - - r = read(0, buf, sizeof(buf)); - assert_se((size_t)r == strlen(sndmsg)); - assert_se(!strncmp(buf, sndmsg, r)); - - l = write(1, buf, r); - assert_se(l == r); -} - -static int pty_fn(Pty *pty, void *userdata, unsigned int ev, const void *ptr, size_t size) { - switch (ev) { - case PTY_DATA: - assert_se(rcvsiz < strlen(rcvmsg) * 2); - assert_se(rcvsiz + size < sizeof(rcvbuf)); - - memcpy(&rcvbuf[rcvsiz], ptr, size); - rcvsiz += size; - - if (rcvsiz >= strlen(rcvmsg) * 2) { - assert_se(rcvsiz == strlen(rcvmsg) * 2); - assert_se(!memcmp(rcvbuf, rcvmsg, strlen(rcvmsg))); - assert_se(!memcmp(&rcvbuf[strlen(rcvmsg)], rcvmsg, strlen(rcvmsg))); - } - - break; - case PTY_HUP: - /* This is guaranteed to appear _after_ the input queues are - * drained! */ - assert_se(rcvsiz == strlen(rcvmsg) * 2); - break; - case PTY_CHILD: - /* this may appear at any time */ - break; - default: - assert_se(0); - break; - } - - /* if we got HUP _and_ CHILD, exit */ - if (pty_get_fd(pty) < 0 && pty_get_child(pty) < 0) - sd_event_exit(event, 0); - - return 0; -} - -static void run_parent(Pty *pty) { - int r; - - /* write message to pty, ECHO mode guarantees that we get it back - * twice: once via ECHO, once from the run_child() fn */ - assert_se(pty_write(pty, sndmsg, strlen(sndmsg)) >= 0); - - r = sd_event_loop(event); - assert_se(r >= 0); -} - -static void test_pty(void) { - pid_t pid; - Pty *pty = NULL; - - rcvsiz = 0; - zero(rcvbuf); - - assert_se(sd_event_default(&event) >= 0); - - pid = pty_fork(&pty, event, pty_fn, NULL, 80, 25); - assert_se(pid >= 0); - - if (pid == 0) { - /* child */ - run_child(pty); - exit(0); - } - - /* parent */ - run_parent(pty); - - /* Make sure the PTY recycled the child; yeah, this is racy if the - * PID was already reused; but that seems fine for a test. */ - assert_se(waitpid(pid, NULL, WNOHANG) < 0 && errno == ECHILD); - - pty_unref(pty); - sd_event_unref(event); -} - -int main(int argc, char *argv[]) { - unsigned int i; - - log_parse_environment(); - log_open(); - - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0); - - /* Oh, there're ugly races in the TTY layer regarding HUP vs IN. Turns - * out they appear only 10% of the time. I fixed all of them and - * don't see them, anymore. But let's be safe and run this 1000 times - * so we catch any new ones, in case they appear again. */ - for (i = 0; i < 1000; ++i) - test_pty(); - - return 0; -} diff --git a/src/test/test-ring.c b/src/test/test-ring.c deleted file mode 100644 index cb8a5d4e9e..0000000000 --- a/src/test/test-ring.c +++ /dev/null @@ -1,130 +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 <string.h> - -#include "def.h" -#include "ring.h" - -static void test_ring(void) { - static const char buf[8192]; - Ring r; - size_t l; - struct iovec vec[2]; - int s; - - zero(r); - - l = ring_peek(&r, vec); - assert_se(l == 0); - - s = ring_push(&r, buf, 2048); - assert_se(!s); - assert_se(ring_get_size(&r) == 2048); - - l = ring_peek(&r, vec); - assert_se(l == 1); - assert_se(vec[0].iov_len == 2048); - assert_se(!memcmp(vec[0].iov_base, buf, vec[0].iov_len)); - assert_se(ring_get_size(&r) == 2048); - - ring_pull(&r, 2048); - assert_se(ring_get_size(&r) == 0); - - l = ring_peek(&r, vec); - assert_se(l == 0); - assert_se(ring_get_size(&r) == 0); - - s = ring_push(&r, buf, 2048); - assert_se(!s); - assert_se(ring_get_size(&r) == 2048); - - l = ring_peek(&r, vec); - assert_se(l == 1); - assert_se(vec[0].iov_len == 2048); - assert_se(!memcmp(vec[0].iov_base, buf, vec[0].iov_len)); - assert_se(ring_get_size(&r) == 2048); - - s = ring_push(&r, buf, 1); - assert_se(!s); - assert_se(ring_get_size(&r) == 2049); - - l = ring_peek(&r, vec); - assert_se(l == 2); - assert_se(vec[0].iov_len == 2048); - assert_se(vec[1].iov_len == 1); - assert_se(!memcmp(vec[0].iov_base, buf, vec[0].iov_len)); - assert_se(!memcmp(vec[1].iov_base, buf, vec[1].iov_len)); - assert_se(ring_get_size(&r) == 2049); - - ring_pull(&r, 2048); - assert_se(ring_get_size(&r) == 1); - - l = ring_peek(&r, vec); - assert_se(l == 1); - assert_se(vec[0].iov_len == 1); - assert_se(!memcmp(vec[0].iov_base, buf, vec[0].iov_len)); - assert_se(ring_get_size(&r) == 1); - - ring_pull(&r, 1); - assert_se(ring_get_size(&r) == 0); - - s = ring_push(&r, buf, 2048); - assert_se(!s); - assert_se(ring_get_size(&r) == 2048); - - s = ring_push(&r, buf, 2049); - assert_se(!s); - assert_se(ring_get_size(&r) == 4097); - - l = ring_peek(&r, vec); - assert_se(l == 1); - assert_se(vec[0].iov_len == 4097); - assert_se(!memcmp(vec[0].iov_base, buf, vec[0].iov_len)); - assert_se(ring_get_size(&r) == 4097); - - ring_pull(&r, 1); - assert_se(ring_get_size(&r) == 4096); - - s = ring_push(&r, buf, 4096); - assert_se(!s); - assert_se(ring_get_size(&r) == 8192); - - l = ring_peek(&r, vec); - assert_se(l == 2); - assert_se(vec[0].iov_len == 8191); - assert_se(vec[1].iov_len == 1); - assert_se(!memcmp(vec[0].iov_base, buf, vec[0].iov_len)); - assert_se(!memcmp(vec[1].iov_base, buf, vec[1].iov_len)); - assert_se(ring_get_size(&r) == 8192); - - ring_clear(&r); - assert_se(ring_get_size(&r) == 0); -} - -int main(int argc, char *argv[]) { - log_parse_environment(); - log_open(); - - test_ring(); - - return 0; -} diff --git a/src/test/test-util.c b/src/test/test-util.c index 8ceb71f22a..7935442dbb 100644 --- a/src/test/test-util.c +++ b/src/test/test-util.c @@ -893,7 +893,7 @@ static void test_protect_errno(void) { } static void test_parse_size(void) { - off_t bytes; + uint64_t bytes; assert_se(parse_size("111", 1024, &bytes) == 0); assert_se(bytes == 111); @@ -960,12 +960,12 @@ static void test_parse_size(void) { assert_se(parse_size("-10B 20K", 1024, &bytes) == -ERANGE); } -static void test_config_parse_iec_off(void) { - off_t offset = 0; - assert_se(config_parse_iec_off(NULL, "/this/file", 11, "Section", 22, "Size", 0, "4M", &offset, NULL) == 0); +static void test_config_parse_iec_uint64(void) { + uint64_t offset = 0; + assert_se(config_parse_iec_uint64(NULL, "/this/file", 11, "Section", 22, "Size", 0, "4M", &offset, NULL) == 0); assert_se(offset == 4 * 1024 * 1024); - assert_se(config_parse_iec_off(NULL, "/this/file", 11, "Section", 22, "Size", 0, "4.5M", &offset, NULL) == 0); + assert_se(config_parse_iec_uint64(NULL, "/this/file", 11, "Section", 22, "Size", 0, "4.5M", &offset, NULL) == 0); } static void test_strextend(void) { @@ -2250,7 +2250,7 @@ int main(int argc, char *argv[]) { test_u64log2(); test_protect_errno(); test_parse_size(); - test_config_parse_iec_off(); + test_config_parse_iec_uint64(); test_strextend(); test_strrep(); test_split_pair(); diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c index 7b4178c993..3cb7d435cd 100644 --- a/src/timesync/timesyncd.c +++ b/src/timesync/timesyncd.c @@ -131,7 +131,7 @@ int main(int argc, char *argv[]) { if (r < 0) log_warning_errno(r, "Failed to parse configuration file: %m"); - log_debug("systemd-timesyncd running as pid %lu", (unsigned long) getpid()); + log_debug("systemd-timesyncd running as pid " PID_FMT, getpid()); sd_notify(false, "READY=1\n" "STATUS=Daemon is running"); |