diff options
Diffstat (limited to 'src')
51 files changed, 791 insertions, 506 deletions
diff --git a/src/basic/copy.c b/src/basic/copy.c index 33427c6a73..cc5faa80a1 100644 --- a/src/basic/copy.c +++ b/src/basic/copy.c @@ -30,7 +30,7 @@ #define COPY_BUFFER_SIZE (16*1024) int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) { - bool try_sendfile = true; + bool try_sendfile = true, try_splice = true; int r; assert(fdf >= 0); @@ -69,7 +69,23 @@ int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) { } else if (n == 0) /* EOF */ break; else if (n > 0) - /* Succcess! */ + /* Success! */ + goto next; + } + + /* The try splice, unless we already tried */ + if (try_splice) { + n = splice(fdf, NULL, fdt, NULL, m, 0); + if (n < 0) { + if (errno != EINVAL && errno != ENOSYS) + return -errno; + + try_splice = false; + /* use fallback below */ + } else if (n == 0) /* EOF */ + break; + else if (n > 0) + /* Success! */ goto next; } diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 61f188467f..cff2d2a034 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -181,10 +181,10 @@ int is_kernel_thread(pid_t pid) { bool eof; FILE *f; - if (pid == 0) + if (pid == 0 || pid == 1) /* pid 1, and we ourselves certainly aren't a kernel thread */ return 0; - assert(pid > 0); + assert(pid > 1); p = procfs_file_alloca(pid, "cmdline"); f = fopen(p, "re"); diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 3d8d5d7568..afc6a6eb24 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -37,6 +37,14 @@ usec_t now(clockid_t clock_id) { return timespec_load(&ts); } +nsec_t now_nsec(clockid_t clock_id) { + struct timespec ts; + + assert_se(clock_gettime(clock_id, &ts) == 0); + + return timespec_load_nsec(&ts); +} + dual_timestamp* dual_timestamp_get(dual_timestamp *ts) { assert(ts); @@ -130,6 +138,18 @@ usec_t timespec_load(const struct timespec *ts) { (usec_t) ts->tv_nsec / NSEC_PER_USEC; } +nsec_t timespec_load_nsec(const struct timespec *ts) { + assert(ts); + + if (ts->tv_sec == (time_t) -1 && + ts->tv_nsec == (long) -1) + return NSEC_INFINITY; + + return + (nsec_t) ts->tv_sec * NSEC_PER_SEC + + (nsec_t) ts->tv_nsec; +} + struct timespec *timespec_store(struct timespec *ts, usec_t u) { assert(ts); diff --git a/src/basic/time-util.h b/src/basic/time-util.h index 03a47f310d..de881e8fe1 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -70,6 +70,7 @@ typedef struct dual_timestamp { #define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) { 0ULL, 0ULL }) usec_t now(clockid_t clock); +nsec_t now_nsec(clockid_t clock); dual_timestamp* dual_timestamp_get(dual_timestamp *ts); dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u); @@ -87,6 +88,8 @@ struct timespec *timespec_store(struct timespec *ts, usec_t u); usec_t timeval_load(const struct timeval *tv) _pure_; struct timeval *timeval_store(struct timeval *tv, usec_t u); +nsec_t timespec_load_nsec(const struct timespec *ts) _pure_; + char *format_timestamp(char *buf, size_t l, usec_t t); char *format_timestamp_utc(char *buf, size_t l, usec_t t); char *format_timestamp_us(char *buf, size_t l, usec_t t); diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c index fa530da456..8742ee757f 100644 --- a/src/basic/unit-name.c +++ b/src/basic/unit-name.c @@ -586,6 +586,42 @@ int unit_name_from_dbus_path(const char *path, char **name) { return 0; } +const char* unit_dbus_interface_from_type(UnitType t) { + + static const char *const table[_UNIT_TYPE_MAX] = { + [UNIT_SERVICE] = "org.freedesktop.systemd1.Service", + [UNIT_SOCKET] = "org.freedesktop.systemd1.Socket", + [UNIT_BUSNAME] = "org.freedesktop.systemd1.BusName", + [UNIT_TARGET] = "org.freedesktop.systemd1.Target", + [UNIT_SNAPSHOT] = "org.freedesktop.systemd1.Snapshot", + [UNIT_DEVICE] = "org.freedesktop.systemd1.Device", + [UNIT_MOUNT] = "org.freedesktop.systemd1.Mount", + [UNIT_AUTOMOUNT] = "org.freedesktop.systemd1.Automount", + [UNIT_SWAP] = "org.freedesktop.systemd1.Swap", + [UNIT_TIMER] = "org.freedesktop.systemd1.Timer", + [UNIT_PATH] = "org.freedesktop.systemd1.Path", + [UNIT_SLICE] = "org.freedesktop.systemd1.Slice", + [UNIT_SCOPE] = "org.freedesktop.systemd1.Scope", + }; + + if (t < 0) + return NULL; + if (t >= _UNIT_TYPE_MAX) + return NULL; + + return table[t]; +} + +const char *unit_dbus_interface_from_name(const char *name) { + UnitType t; + + t = unit_name_to_type(name); + if (t < 0) + return NULL; + + return unit_dbus_interface_from_type(t); +} + static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) { const char *valid_chars; @@ -787,7 +823,7 @@ static const char* const unit_type_table[_UNIT_TYPE_MAX] = { [UNIT_TIMER] = "timer", [UNIT_PATH] = "path", [UNIT_SLICE] = "slice", - [UNIT_SCOPE] = "scope" + [UNIT_SCOPE] = "scope", }; DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType); diff --git a/src/basic/unit-name.h b/src/basic/unit-name.h index b2043d0870..28b3a555f3 100644 --- a/src/basic/unit-name.h +++ b/src/basic/unit-name.h @@ -152,6 +152,9 @@ int unit_name_to_path(const char *name, char **ret); char *unit_dbus_path_from_name(const char *name); int unit_name_from_dbus_path(const char *path, char **name); +const char* unit_dbus_interface_from_type(UnitType t); +const char *unit_dbus_interface_from_name(const char *name); + typedef enum UnitNameMangle { UNIT_NAME_NOGLOB, UNIT_NAME_GLOB, diff --git a/src/basic/util.c b/src/basic/util.c index f752595ca1..737f2a221c 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -4273,7 +4273,7 @@ bool is_locale_utf8(void) { /* Check result, but ignore the result if C was set * explicitly. */ cached_answer = - streq(set, "C") && + STR_IN_SET(set, "C", "POSIX") && !getenv("LC_ALL") && !getenv("LC_CTYPE") && !getenv("LANG"); @@ -5754,40 +5754,39 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra switch (state) { case START: - if (c == 0) { - if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) - if (!GREEDY_REALLOC(s, allocated, sz+1)) - return -ENOMEM; + if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) + if (!GREEDY_REALLOC(s, allocated, sz+1)) + return -ENOMEM; + + if (c == 0) goto finish_force_terminate; - } else if (strchr(separators, c)) { + else if (strchr(separators, c)) { if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) { - if (!GREEDY_REALLOC(s, allocated, sz+1)) - return -ENOMEM; (*p) ++; goto finish_force_next; } break; } + /* We found a non-blank character, so we will always + * want to return a string (even if it is empty), + * allocate it here. */ + if (!GREEDY_REALLOC(s, allocated, sz+1)) + return -ENOMEM; + state = VALUE; /* fallthrough */ case VALUE: if (c == 0) goto finish_force_terminate; - else if (c == '\'' && (flags & EXTRACT_QUOTES)) { - if (!GREEDY_REALLOC(s, allocated, sz+1)) - return -ENOMEM; - + else if (c == '\'' && (flags & EXTRACT_QUOTES)) state = SINGLE_QUOTE; - } else if (c == '\\') + else if (c == '\\') state = VALUE_ESCAPE; - else if (c == '\"' && (flags & EXTRACT_QUOTES)) { - if (!GREEDY_REALLOC(s, allocated, sz+1)) - return -ENOMEM; - + else if (c == '\"' && (flags & EXTRACT_QUOTES)) state = DOUBLE_QUOTE; - } else if (strchr(separators, c)) { + else if (strchr(separators, c)) { if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) { (*p) ++; goto finish_force_next; @@ -5891,8 +5890,6 @@ end_escape: case SEPARATOR: if (c == 0) goto finish_force_terminate; - if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) - goto finish_force_next; if (!strchr(separators, c)) goto finish; break; diff --git a/src/basic/util.h b/src/basic/util.h index 1ead7b5419..1484ef58e5 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -83,7 +83,7 @@ int strcmp_ptr(const char *a, const char *b) _pure_; #define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n))) -#define malloc0(n) (calloc((n), 1)) +#define malloc0(n) (calloc(1, (n))) static inline void *mfree(void *memory) { free(memory); diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c index b8d1d2ccaf..a8d910d532 100644 --- a/src/cgls/cgls.c +++ b/src/cgls/cgls.c @@ -54,7 +54,7 @@ static void help(void) { " -a --all Show all groups, including empty\n" " -l --full Do not ellipsize output\n" " -k Include kernel threads in output\n" - " -M --machine Show container\n" + " -M --machine= Show container\n" , program_invocation_short_name); } @@ -123,146 +123,158 @@ static int parse_argv(int argc, char *argv[]) { return 1; } -int main(int argc, char *argv[]) { - int r = 0, retval = EXIT_FAILURE; - int output_flags; - _cleanup_free_ char *root = NULL; +static int get_cgroup_root(char **ret) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL; + _cleanup_free_ char *unit = NULL, *path = NULL; + const char *m; + int r; + + if (!arg_machine) { + r = cg_get_root_path(ret); + if (r < 0) + return log_error_errno(r, "Failed to get root control group path: %m"); + + return 0; + } + + m = strjoina("/run/systemd/machines/", arg_machine); + r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL); + if (r < 0) + return log_error_errno(r, "Failed to load machine data: %m"); + + path = unit_dbus_path_from_name(unit); + if (!path) + return log_oom(); + + r = bus_open_transport(BUS_TRANSPORT_LOCAL, NULL, false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + path, + unit_dbus_interface_from_name(unit), + "ControlGroup", + &error, + ret); + if (r < 0) + return log_error_errno(r, "Failed to query unit control group path: %s", bus_error_message(&error, r)); + + return 0; +} + +int main(int argc, char *argv[]) { + int r, output_flags; log_parse_environment(); log_open(); r = parse_argv(argc, argv); - if (r < 0) + if (r <= 0) goto finish; - else if (r == 0) { - retval = EXIT_SUCCESS; - goto finish; - } if (!arg_no_pager) { r = pager_open(false); - if (r > 0) { - if (arg_full == -1) - arg_full = true; - } + if (r > 0 && arg_full < 0) + arg_full = true; } output_flags = arg_all * OUTPUT_SHOW_ALL | (arg_full > 0) * OUTPUT_FULL_WIDTH; - r = bus_open_transport(BUS_TRANSPORT_LOCAL, NULL, false, &bus); - if (r < 0) { - log_error_errno(r, "Failed to create bus connection: %m"); - goto finish; - } - if (optind < argc) { + _cleanup_free_ char *root = NULL; int i; + r = get_cgroup_root(&root); + if (r < 0) + goto finish; + for (i = optind; i < argc; i++) { int q; - fprintf(stdout, "%s:\n", argv[i]); - fflush(stdout); - - if (arg_machine) - root = strjoin("machine/", arg_machine, "/", argv[i], NULL); - else - root = strdup(argv[i]); - if (!root) - return log_oom(); - - q = show_cgroup_by_path(root, NULL, 0, - arg_kernel_threads, output_flags); - if (q < 0) - r = q; - } + if (path_startswith(argv[i], "/sys/fs/cgroup")) { - } else { - _cleanup_free_ char *p; + printf("Directory %s:\n", argv[i]); + fflush(stdout); - p = get_current_dir_name(); - if (!p) { - log_error_errno(errno, "Cannot determine current working directory: %m"); - goto finish; - } + q = show_cgroup_by_path(argv[i], NULL, 0, arg_kernel_threads, output_flags); + } else { + _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL; + const char *controller, *path; - if (path_startswith(p, "/sys/fs/cgroup") && !arg_machine) { - printf("Working Directory %s:\n", p); - r = show_cgroup_by_path(p, NULL, 0, - arg_kernel_threads, output_flags); - } else { - if (arg_machine) { - char *m; - const char *cgroup; - _cleanup_free_ char *unit = NULL; - _cleanup_free_ char *path = NULL; - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - - m = strjoina("/run/systemd/machines/", arg_machine); - r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL); + r = cg_split_spec(argv[i], &c, &p); if (r < 0) { - log_error_errno(r, "Failed to get machine path: %m"); + log_error_errno(r, "Failed to split argument %s: %m", argv[i]); goto finish; } - path = unit_dbus_path_from_name(unit); - if (!path) { - log_oom(); - goto finish; - } + controller = c ?: SYSTEMD_CGROUP_CONTROLLER; + if (p) { + j = strjoin(root, "/", p, NULL); + if (!j) { + r = log_oom(); + goto finish; + } - r = sd_bus_get_property( - bus, - "org.freedesktop.systemd1", - path, - endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service", - "ControlGroup", - &error, - &reply, - "s"); + path_kill_slashes(j); + path = j; + } else + path = root; - if (r < 0) { - log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r)); - goto finish; - } + printf("Controller %s; control group %s:\n", controller, path); + fflush(stdout); - r = sd_bus_message_read(reply, "s", &cgroup); - if (r < 0) { - bus_log_parse_error(r); - goto finish; - } + q = show_cgroup(controller, path, NULL, 0, arg_kernel_threads, output_flags); + } - root = strdup(cgroup); - if (!root) { - log_oom(); - goto finish; - } + if (q < 0) + r = q; + } + + } else { + bool done = false; - } else - r = cg_get_root_path(&root); - if (r < 0) { - log_error_errno(r, "Failed to get %s path: %m", - arg_machine ? "machine" : "root"); + if (!arg_machine) { + _cleanup_free_ char *cwd = NULL; + + cwd = get_current_dir_name(); + if (!cwd) { + r = log_error_errno(errno, "Cannot determine current working directory: %m"); goto finish; } - r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, - arg_kernel_threads, output_flags); + if (path_startswith(cwd, "/sys/fs/cgroup")) { + printf("Working directory %s:\n", cwd); + fflush(stdout); + + r = show_cgroup_by_path(cwd, NULL, 0, arg_kernel_threads, output_flags); + done = true; + } + } + + if (!done) { + _cleanup_free_ char *root = NULL; + + r = get_cgroup_root(&root); + if (r < 0) + goto finish; + + printf("Control group %s:\n", isempty(root) ? "/" : root); + fflush(stdout); + + r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, arg_kernel_threads, output_flags); } } - if (r < 0) { - log_error_errno(r, "Failed to list cgroup tree %s: %m", root); - retval = EXIT_FAILURE; - } else - retval = EXIT_SUCCESS; + if (r < 0) + log_error_errno(r, "Failed to list cgroup tree: %m"); finish: pager_close(); - return retval; + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index f953c9e624..06a43d15e4 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -19,7 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#define __STDC_FORMAT_MACROS #include <errno.h> #include <string.h> #include <stdlib.h> @@ -31,6 +30,7 @@ #include "path-util.h" #include "terminal-util.h" +#include "process-util.h" #include "util.h" #include "hashmap.h" #include "cgroup-util.h" @@ -48,23 +48,25 @@ typedef struct Group { unsigned n_tasks; unsigned cpu_iteration; - uint64_t cpu_usage; - struct timespec cpu_timestamp; + nsec_t cpu_usage; + nsec_t cpu_timestamp; double cpu_fraction; uint64_t memory; unsigned io_iteration; uint64_t io_input, io_output; - struct timespec io_timestamp; + nsec_t io_timestamp; uint64_t io_input_bps, io_output_bps; } Group; static unsigned arg_depth = 3; -static unsigned arg_iterations = (unsigned)-1; +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; +static bool arg_recursive = true; static enum { ORDER_PATH, @@ -108,12 +110,16 @@ static const char *maybe_format_bytes(char *buf, size_t l, bool is_valid, off_t return format_bytes(buf, l, t); } -static int process(const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration) { +static int process( + const char *controller, + const char *path, + Hashmap *a, + Hashmap *b, + unsigned iteration, + Group **ret) { + Group *g; int r; - FILE *f = NULL; - pid_t pid; - unsigned n; assert(controller); assert(path); @@ -142,84 +148,86 @@ static int process(const char *controller, const char *path, Hashmap *a, Hashmap r = hashmap_move_one(a, b, path); if (r < 0) return r; + g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false; } } - /* Regardless which controller, let's find the maximum number - * of processes in any of it */ + if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { + _cleanup_fclose_ FILE *f = NULL; + pid_t pid; - r = cg_enumerate_processes(controller, path, &f); - if (r < 0) - return r; + r = cg_enumerate_processes(controller, path, &f); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; - n = 0; - while (cg_read_pid(f, &pid) > 0) - n++; - fclose(f); + g->n_tasks = 0; + while (cg_read_pid(f, &pid) > 0) { - if (n > 0) { - if (g->n_tasks_valid) - g->n_tasks = MAX(g->n_tasks, n); - else - g->n_tasks = n; + if (!arg_kernel_threads && is_kernel_thread(pid) > 0) + continue; - g->n_tasks_valid = true; - } + g->n_tasks++; + } - if (streq(controller, "cpuacct")) { + if (g->n_tasks > 0) + g->n_tasks_valid = true; + + } else if (streq(controller, "cpuacct")) { + _cleanup_free_ char *p = NULL, *v = NULL; uint64_t new_usage; - char *p, *v; - struct timespec ts; + nsec_t timestamp; r = cg_get_path(controller, path, "cpuacct.usage", &p); if (r < 0) return r; r = read_one_line_file(p, &v); - free(p); + if (r == -ENOENT) + return 0; if (r < 0) return r; r = safe_atou64(v, &new_usage); - free(v); if (r < 0) return r; - assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); + timestamp = now_nsec(CLOCK_MONOTONIC); - if (g->cpu_iteration == iteration - 1) { - uint64_t x, y; + if (g->cpu_iteration == iteration - 1 && + (nsec_t) new_usage > g->cpu_usage) { - x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) - - ((uint64_t) g->cpu_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->cpu_timestamp.tv_nsec); + nsec_t x, y; - y = new_usage - g->cpu_usage; + x = timestamp - g->cpu_timestamp; + if (x < 1) + x = 1; - if (y > 0) { - g->cpu_fraction = (double) y / (double) x; - g->cpu_valid = true; - } + y = (nsec_t) new_usage - g->cpu_usage; + g->cpu_fraction = (double) y / (double) x; + g->cpu_valid = true; } - g->cpu_usage = new_usage; - g->cpu_timestamp = ts; + g->cpu_usage = (nsec_t) new_usage; + g->cpu_timestamp = timestamp; g->cpu_iteration = iteration; } else if (streq(controller, "memory")) { - char *p, *v; + _cleanup_free_ char *p = NULL, *v = NULL; r = cg_get_path(controller, path, "memory.usage_in_bytes", &p); if (r < 0) return r; r = read_one_line_file(p, &v); - free(p); + if (r == -ENOENT) + return 0; if (r < 0) return r; r = safe_atou64(v, &g->memory); - free(v); if (r < 0) return r; @@ -227,19 +235,21 @@ static int process(const char *controller, const char *path, Hashmap *a, Hashmap g->memory_valid = true; } else if (streq(controller, "blkio")) { - char *p; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *p = NULL; uint64_t wr = 0, rd = 0; - struct timespec ts; + nsec_t timestamp; r = cg_get_path(controller, path, "blkio.io_service_bytes", &p); if (r < 0) return r; f = fopen(p, "re"); - free(p); - - if (!f) + if (!f) { + if (errno == ENOENT) + return 0; return -errno; + } for (;;) { char line[LINE_MAX], *l; @@ -269,20 +279,26 @@ static int process(const char *controller, const char *path, Hashmap *a, Hashmap *q += k; } - fclose(f); - - assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); + timestamp = now_nsec(CLOCK_MONOTONIC); if (g->io_iteration == iteration - 1) { uint64_t x, yr, yw; - x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) - - ((uint64_t) g->io_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->io_timestamp.tv_nsec); + x = (uint64_t) (timestamp - g->io_timestamp); + if (x < 1) + x = 1; + + if (rd > g->io_input) + yr = rd - g->io_input; + else + yr = 0; - yr = rd - g->io_input; - yw = wr - g->io_output; + if (wr > g->io_output) + yw = wr - g->io_output; + else + yw = 0; - if (g->io_input > 0 || g->io_output > 0) { + if (yr > 0 || yw > 0) { g->io_input_bps = (yr * 1000000000ULL) / x; g->io_output_bps = (yw * 1000000000ULL) / x; g->io_valid = true; @@ -291,10 +307,13 @@ static int process(const char *controller, const char *path, Hashmap *a, Hashmap g->io_input = rd; g->io_output = wr; - g->io_timestamp = ts; + g->io_timestamp = timestamp; g->io_iteration = iteration; } + if (ret) + *ret = g; + return 0; } @@ -304,9 +323,11 @@ static int refresh_one( Hashmap *a, Hashmap *b, unsigned iteration, - unsigned depth) { + unsigned depth, + Group **ret) { - DIR *d = NULL; + _cleanup_closedir_ DIR *d = NULL; + Group *ours; int r; assert(controller); @@ -316,83 +337,100 @@ static int refresh_one( if (depth > arg_depth) return 0; - r = process(controller, path, a, b, iteration); + r = process(controller, path, a, b, iteration, &ours); if (r < 0) return r; r = cg_enumerate_subgroups(controller, path, &d); - if (r < 0) { - if (r == -ENOENT) - return 0; - + if (r == -ENOENT) + return 0; + if (r < 0) return r; - } for (;;) { - char *fn, *p; + _cleanup_free_ char *fn = NULL, *p = NULL; + Group *child = NULL; r = cg_read_subgroup(d, &fn); - if (r <= 0) - goto finish; + if (r < 0) + return r; + if (r == 0) + break; p = strjoin(path, "/", fn, NULL); - free(fn); - - if (!p) { - r = -ENOMEM; - goto finish; - } + if (!p) + return -ENOMEM; path_kill_slashes(p); - r = refresh_one(controller, p, a, b, iteration, depth + 1); - free(p); - + r = refresh_one(controller, p, a, b, iteration, depth + 1, &child); if (r < 0) - goto finish; + return r; + + if (arg_recursive && + child && + child->n_tasks_valid && + streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { + + /* Recursively sum up processes */ + + if (ours->n_tasks_valid) + ours->n_tasks += child->n_tasks; + else { + ours->n_tasks = child->n_tasks; + ours->n_tasks_valid = true; + } + } } -finish: - if (d) - closedir(d); + if (ret) + *ret = ours; - return r; + return 1; } -static int refresh(Hashmap *a, Hashmap *b, unsigned iteration) { +static int refresh(const char *root, Hashmap *a, Hashmap *b, unsigned iteration) { int r; assert(a); - r = refresh_one("name=systemd", "/", a, b, iteration, 0); + r = refresh_one(SYSTEMD_CGROUP_CONTROLLER, root, a, b, iteration, 0, NULL); if (r < 0) - if (r != -ENOENT) - return r; - r = refresh_one("cpuacct", "/", a, b, iteration, 0); + return r; + r = refresh_one("cpuacct", root, a, b, iteration, 0, NULL); if (r < 0) - if (r != -ENOENT) - return r; - r = refresh_one("memory", "/", a, b, iteration, 0); + return r; + r = refresh_one("memory", root, a, b, iteration, 0, NULL); if (r < 0) - if (r != -ENOENT) - return r; - - r = refresh_one("blkio", "/", a, b, iteration, 0); + return r; + r = refresh_one("blkio", root, a, b, iteration, 0, NULL); if (r < 0) - if (r != -ENOENT) - return r; + return r; + return 0; } static int group_compare(const void*a, const void *b) { const Group *x = *(Group**)a, *y = *(Group**)b; - if (path_startswith(y->path, x->path)) - return -1; - if (path_startswith(x->path, y->path)) - return 1; + if (arg_order != ORDER_TASKS || arg_recursive) { + /* Let's make sure that the parent is always before + * the child. Except when ordering by tasks and + * recursive summing is off, since that is actually + * not accumulative for all children. */ + + if (path_startswith(y->path, x->path)) + return -1; + if (path_startswith(x->path, y->path)) + return 1; + } + + switch (arg_order) { - if (arg_order == ORDER_CPU) { + case ORDER_PATH: + break; + + case ORDER_CPU: if (arg_cpu_type == CPU_PERCENT) { if (x->cpu_valid && y->cpu_valid) { if (x->cpu_fraction > y->cpu_fraction) @@ -409,10 +447,10 @@ static int group_compare(const void*a, const void *b) { else if (x->cpu_usage < y->cpu_usage) return 1; } - } - if (arg_order == ORDER_TASKS) { + break; + case ORDER_TASKS: if (x->n_tasks_valid && y->n_tasks_valid) { if (x->n_tasks > y->n_tasks) return -1; @@ -422,9 +460,10 @@ static int group_compare(const void*a, const void *b) { return -1; else if (y->n_tasks_valid) return 1; - } - if (arg_order == ORDER_MEMORY) { + break; + + case ORDER_MEMORY: if (x->memory_valid && y->memory_valid) { if (x->memory > y->memory) return -1; @@ -434,9 +473,10 @@ static int group_compare(const void*a, const void *b) { return -1; else if (y->memory_valid) return 1; - } - if (arg_order == ORDER_IO) { + break; + + case ORDER_IO: if (x->io_valid && y->io_valid) { if (x->io_input_bps + x->io_output_bps > y->io_input_bps + y->io_output_bps) return -1; @@ -448,13 +488,13 @@ static int group_compare(const void*a, const void *b) { return 1; } - return strcmp(x->path, y->path); + return path_compare(x->path, y->path); } #define ON ANSI_HIGHLIGHT_ON #define OFF ANSI_HIGHLIGHT_OFF -static int display(Hashmap *a) { +static void display(Hashmap *a) { Iterator i; Group *g; Group **array; @@ -481,9 +521,10 @@ static int display(Hashmap *a) { for (j = 0; j < n; j++) { unsigned cputlen, pathtlen; - format_timespan(buffer, sizeof(buffer), (nsec_t) (array[j]->cpu_usage / NSEC_PER_USEC), 0); + format_timespan(buffer, sizeof(buffer), (usec_t) (array[j]->cpu_usage / NSEC_PER_USEC), 0); cputlen = strlen(buffer); maxtcpu = MAX(maxtcpu, cputlen); + pathtlen = strlen(array[j]->path); maxtpath = MAX(maxtpath, pathtlen); } @@ -503,7 +544,7 @@ static int display(Hashmap *a) { path_columns = 10; 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, "Path", + arg_order == ORDER_PATH ? ON : "", path_columns, "Control Group", arg_order == ORDER_PATH ? OFF : "", arg_order == ORDER_TASKS ? ON : "", "Tasks", arg_order == ORDER_TASKS ? OFF : "", @@ -519,7 +560,7 @@ static int display(Hashmap *a) { path_columns = maxtpath; for (j = 0; j < n; j++) { - char *p; + _cleanup_free_ char *p = NULL; if (on_tty() && j + 5 > rows) break; @@ -527,8 +568,7 @@ static int display(Hashmap *a) { g = array[j]; p = ellipsize(g->path, path_columns, 33); - printf("%-*s", path_columns, p ? p : g->path); - free(p); + printf("%-*s", path_columns, p ?: g->path); if (g->n_tasks_valid) printf(" %7u", g->n_tasks); @@ -541,7 +581,7 @@ static int display(Hashmap *a) { else fputs(" -", stdout); } else - printf(" %*s", maxtcpu, format_timespan(buffer, sizeof(buffer), (nsec_t) (g->cpu_usage / NSEC_PER_USEC), 0)); + printf(" %*s", maxtcpu, format_timespan(buffer, sizeof(buffer), (usec_t) (g->cpu_usage / NSEC_PER_USEC), 0)); printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->memory_valid, g->memory)); printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->io_valid, g->io_input_bps)); @@ -549,22 +589,23 @@ static int display(Hashmap *a) { putchar('\n'); } - - return 0; } static void help(void) { printf("%s [OPTIONS...]\n\n" "Show top control groups by their resource usage.\n\n" " -h --help Show this help\n" - " --version Print version and exit\n" - " -p Order by path\n" - " -t Order by number of tasks\n" - " -c Order by CPU load\n" - " -m Order by memory load\n" - " -i Order by IO load\n" + " --version Show package version\n" + " -p --order=path Order by path\n" + " -t --order=tasks Order by number of tasks\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[=TYPE] Show CPU usage as time or percentage (default)\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" " -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" @@ -577,28 +618,31 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, ARG_DEPTH, - ARG_CPU_TYPE + ARG_CPU_TYPE, + ARG_ORDER, + ARG_RECURSIVE, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "delay", required_argument, NULL, 'd' }, - { "iterations", required_argument, NULL, 'n' }, - { "batch", no_argument, NULL, 'b' }, - { "raw", no_argument, NULL, 'r' }, - { "depth", required_argument, NULL, ARG_DEPTH }, - { "cpu", optional_argument, NULL, ARG_CPU_TYPE}, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "delay", required_argument, NULL, 'd' }, + { "iterations", required_argument, NULL, 'n' }, + { "batch", no_argument, NULL, 'b' }, + { "raw", no_argument, NULL, 'r' }, + { "depth", required_argument, NULL, ARG_DEPTH }, + { "cpu", optional_argument, NULL, ARG_CPU_TYPE }, + { "order", required_argument, NULL, ARG_ORDER }, + { "recursive", required_argument, NULL, ARG_RECURSIVE }, {} }; - int c; - int r; + int c, r; assert(argc >= 1); assert(argv); - while ((c = getopt_long(argc, argv, "hptcmin:brd:", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "hptcmin:brd:k", options, NULL)) >= 0) switch (c) { @@ -613,13 +657,17 @@ static int parse_argv(int argc, char *argv[]) { case ARG_CPU_TYPE: if (optarg) { - if (strcmp(optarg, "time") == 0) + if (streq(optarg, "time")) arg_cpu_type = CPU_TIME; - else if (strcmp(optarg, "percentage") == 0) + else if (streq(optarg, "percentage")) arg_cpu_type = CPU_PERCENT; - else + else { + log_error("Unknown argument to --cpu=: %s", optarg); return -EINVAL; - } + } + } else + arg_cpu_type = CPU_TIME; + break; case ARG_DEPTH: @@ -677,6 +725,37 @@ static int parse_argv(int argc, char *argv[]) { arg_order = ORDER_IO; break; + case ARG_ORDER: + if (streq(optarg, "path")) + arg_order = ORDER_PATH; + else if (streq(optarg, "tasks")) + arg_order = ORDER_TASKS; + else if (streq(optarg, "cpu")) + arg_order = ORDER_CPU; + else if (streq(optarg, "memory")) + arg_order = ORDER_MEMORY; + else if (streq(optarg, "io")) + arg_order = ORDER_IO; + else { + log_error("Invalid argument to --order=: %s", optarg); + return -EINVAL; + } + break; + + case 'k': + arg_kernel_threads = true; + break; + + case ARG_RECURSIVE: + r = parse_boolean(optarg); + if (r < 0) { + log_error("Failed to parse --recursive= argument: %s", optarg); + return r; + } + + arg_recursive = r; + break; + case '?': return -EINVAL; @@ -698,6 +777,7 @@ int main(int argc, char *argv[]) { unsigned iteration = 0; usec_t last_refresh = 0; bool quit = false, immediate_refresh = false; + _cleanup_free_ char *root = NULL; log_parse_environment(); log_open(); @@ -706,6 +786,12 @@ int main(int argc, char *argv[]) { if (r <= 0) goto finish; + r = cg_get_root_path(&root); + if (r < 0) { + log_error_errno(r, "Failed to get root control group path: %m"); + goto finish; + } + a = hashmap_new(&string_hash_ops); b = hashmap_new(&string_hash_ops); if (!a || !b) { @@ -715,7 +801,7 @@ int main(int argc, char *argv[]) { signal(SIGWINCH, columns_lines_cache_reset); - if (arg_iterations == (unsigned)-1) + if (arg_iterations == (unsigned) -1) arg_iterations = on_tty() ? 0 : 1; while (!quit) { @@ -728,9 +814,11 @@ int main(int argc, char *argv[]) { if (t >= last_refresh + arg_delay || immediate_refresh) { - r = refresh(a, b, iteration++); - if (r < 0) + r = refresh(root, a, b, iteration++); + if (r < 0) { + log_error_errno(r, "Failed to refresh: %m"); goto finish; + } group_hashmap_clear(b); @@ -742,9 +830,7 @@ int main(int argc, char *argv[]) { immediate_refresh = false; } - r = display(b); - if (r < 0) - goto finish; + display(b); if (arg_iterations && iteration >= arg_iterations) break; @@ -753,11 +839,10 @@ int main(int argc, char *argv[]) { fputs("\n", stdout); fflush(stdout); - if (arg_batch) { - usleep(last_refresh + arg_delay - t); - } else { - r = read_one_char(stdin, &key, - last_refresh + arg_delay - t, NULL); + if (arg_batch) + (void) usleep(last_refresh + arg_delay - t); + else { + r = read_one_char(stdin, &key, last_refresh + arg_delay - t, NULL); if (r == -ETIMEDOUT) continue; if (r < 0) { @@ -808,6 +893,20 @@ int main(int argc, char *argv[]) { arg_cpu_type = arg_cpu_type == CPU_TIME ? CPU_PERCENT : CPU_TIME; break; + case 'k': + arg_kernel_threads = !arg_kernel_threads; + fprintf(stdout, "\nCounting kernel threads: %s.", yes_no(arg_kernel_threads)); + fflush(stdout); + sleep(1); + break; + + case 'r': + arg_recursive = !arg_recursive; + fprintf(stdout, "\nRecursive task counting: %s", yes_no(arg_recursive)); + fflush(stdout); + sleep(1); + break; + case '+': if (arg_delay < USEC_PER_SEC) arg_delay += USEC_PER_MSEC*250; @@ -836,14 +935,17 @@ int main(int argc, char *argv[]) { 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 "+" OFF "> Increase delay; <" ON "-" OFF "> Decrease delay; <" ON "%%" OFF "> Toggle time\n" - "\t<" ON "q" OFF "> Quit; <" ON "SPACE" OFF "> Refresh"); + "\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"); fflush(stdout); sleep(3); break; default: - fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key); + if (key < ' ') + fprintf(stdout, "\nUnknown key '\\x%x'. Ignoring.", key); + else + fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key); fflush(stdout); sleep(1); break; @@ -856,10 +958,5 @@ finish: group_hashmap_free(a); group_hashmap_free(b); - if (r < 0) { - log_error_errno(r, "Exiting with failure: %m"); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/core/automount.c b/src/core/automount.c index 4af381b4b6..b8171ddad7 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -1075,7 +1075,6 @@ const UnitVTable automount_vtable = { .reset_failed = automount_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Automount", .bus_vtable = bus_automount_vtable, .shutdown = automount_shutdown, diff --git a/src/core/busname.c b/src/core/busname.c index 9530a87311..d3c1282239 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -1058,7 +1058,6 @@ const UnitVTable busname_vtable = { .supported = busname_supported, - .bus_interface = "org.freedesktop.systemd1.BusName", .bus_vtable = bus_busname_vtable, .status_message_formats = { diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 6474e08bd2..c26807ba2b 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -806,12 +806,9 @@ static void unit_queue_siblings(Unit *u) { } int unit_realize_cgroup(Unit *u) { - CGroupContext *c; - assert(u); - c = unit_get_cgroup_context(u); - if (!c) + if (!UNIT_HAS_CGROUP_CONTEXT(u)) return 0; /* So, here's the deal: when realizing the cgroups for this diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 0a9effda71..1e6291e762 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -783,7 +783,7 @@ static int send_changed_signal(sd_bus *bus, void *userdata) { r = sd_bus_emit_properties_changed_strv( bus, p, - UNIT_VTABLE(u)->bus_interface, + unit_dbus_interface_from_type(u->type), NULL); if (r < 0) return r; @@ -965,38 +965,39 @@ static int bus_unit_set_transient_property( return 1; - } else if (streq(name, "Slice") && unit_get_cgroup_context(u)) { + } else if (streq(name, "Slice")) { + Unit *slice; const char *s; + if (!UNIT_HAS_CGROUP_CONTEXT(u)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "The slice property is only available for units with control groups."); + if (u->type == UNIT_SLICE) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Slice may not be set for slice units."); + r = sd_bus_message_read(message, "s", &s); if (r < 0) return r; - if (!unit_name_is_valid(s, UNIT_NAME_PLAIN) || !endswith(s, ".slice")) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid slice name %s", s); + if (!unit_name_is_valid(s, UNIT_NAME_PLAIN)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name '%s'", s); - if (isempty(s)) { - if (mode != UNIT_CHECK) { - unit_ref_unset(&u->slice); - unit_remove_drop_in(u, mode, name); - } - } else { - Unit *slice; + r = manager_load_unit(u->manager, s, NULL, error, &slice); + if (r < 0) + return r; + + if (slice->type != UNIT_SLICE) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit name '%s' is not a slice", s); - r = manager_load_unit(u->manager, s, NULL, error, &slice); + if (mode != UNIT_CHECK) { + r = unit_set_slice(u, slice); if (r < 0) return r; - if (slice->type != UNIT_SLICE) - return -EINVAL; - - if (mode != UNIT_CHECK) { - unit_ref_set(&u->slice, slice); - unit_write_drop_in_private_format(u, mode, name, "Slice=%s\n", s); - } + unit_write_drop_in_private_format(u, mode, name, "Slice=%s\n", s); } return 1; + } else if (STR_IN_SET(name, "Requires", "RequiresOverridable", "Requisite", "RequisiteOverridable", diff --git a/src/core/dbus.c b/src/core/dbus.c index 44bf5cab28..7ad16aa42b 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -356,7 +356,7 @@ static int bus_unit_interface_find(sd_bus *bus, const char *path, const char *in if (r <= 0) return r; - if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface)) + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) return 0; *found = u; @@ -378,10 +378,10 @@ static int bus_unit_cgroup_find(sd_bus *bus, const char *path, const char *inter if (r <= 0) return r; - if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface)) + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) return 0; - if (!unit_get_cgroup_context(u)) + if (!UNIT_HAS_CGROUP_CONTEXT(u)) return 0; *found = u; @@ -404,7 +404,7 @@ static int bus_cgroup_context_find(sd_bus *bus, const char *path, const char *in if (r <= 0) return r; - if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface)) + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) return 0; c = unit_get_cgroup_context(u); @@ -431,7 +431,7 @@ static int bus_exec_context_find(sd_bus *bus, const char *path, const char *inte if (r <= 0) return r; - if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface)) + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) return 0; c = unit_get_exec_context(u); @@ -458,7 +458,7 @@ static int bus_kill_context_find(sd_bus *bus, const char *path, const char *inte if (r <= 0) return r; - if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface)) + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) return 0; c = unit_get_kill_context(u); @@ -555,30 +555,34 @@ static int bus_setup_api_vtables(Manager *m, sd_bus *bus) { return log_error_errno(r, "Failed to add job enumerator: %m"); for (t = 0; t < _UNIT_TYPE_MAX; t++) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, unit_vtable[t]->bus_vtable, bus_unit_interface_find, m); + const char *interface; + + assert_se(interface = unit_dbus_interface_from_type(t)); + + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, unit_vtable[t]->bus_vtable, bus_unit_interface_find, m); if (r < 0) - return log_error_errno(r, "Failed to register type specific vtable for %s: %m", unit_vtable[t]->bus_interface); + return log_error_errno(r, "Failed to register type specific vtable for %s: %m", interface); if (unit_vtable[t]->cgroup_context_offset > 0) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_unit_cgroup_vtable, bus_unit_cgroup_find, m); + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_unit_cgroup_vtable, bus_unit_cgroup_find, m); if (r < 0) - return log_error_errno(r, "Failed to register control group unit vtable for %s: %m", unit_vtable[t]->bus_interface); + return log_error_errno(r, "Failed to register control group unit vtable for %s: %m", interface); - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_cgroup_vtable, bus_cgroup_context_find, m); + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_cgroup_vtable, bus_cgroup_context_find, m); if (r < 0) - return log_error_errno(r, "Failed to register control group vtable for %s: %m", unit_vtable[t]->bus_interface); + return log_error_errno(r, "Failed to register control group vtable for %s: %m", interface); } if (unit_vtable[t]->exec_context_offset > 0) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_exec_vtable, bus_exec_context_find, m); + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_exec_vtable, bus_exec_context_find, m); if (r < 0) - return log_error_errno(r, "Failed to register execute vtable for %s: %m", unit_vtable[t]->bus_interface); + return log_error_errno(r, "Failed to register execute vtable for %s: %m", interface); } if (unit_vtable[t]->kill_context_offset > 0) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_kill_vtable, bus_kill_context_find, m); + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_kill_vtable, bus_kill_context_find, m); if (r < 0) - return log_error_errno(r, "Failed to register kill vtable for %s: %m", unit_vtable[t]->bus_interface); + return log_error_errno(r, "Failed to register kill vtable for %s: %m", interface); } } diff --git a/src/core/device.c b/src/core/device.c index e7efcf0f0a..3f574b1832 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -849,7 +849,6 @@ const UnitVTable device_vtable = { .active_state = device_active_state, .sub_state_to_string = device_sub_state_to_string, - .bus_interface = "org.freedesktop.systemd1.Device", .bus_vtable = bus_device_vtable, .following = device_following, diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index b3bf8bdb40..745291c5c6 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -2602,7 +2602,7 @@ int config_parse_unit_slice( void *userdata) { _cleanup_free_ char *k = NULL; - Unit *u = userdata, *slice; + Unit *u = userdata, *slice = NULL; int r; assert(filename); @@ -2611,29 +2611,23 @@ int config_parse_unit_slice( assert(u); r = unit_name_printf(u, rvalue, &k); - if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); - if (!k) { - k = strdup(rvalue); - if (!k) - return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); + return 0; } r = manager_load_unit(u->manager, k, NULL, NULL, &slice); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to load slice unit %s. Ignoring.", k); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load slice unit %s. Ignoring.", k); return 0; } - if (slice->type != UNIT_SLICE) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Slice unit %s is not a slice. Ignoring.", k); + r = unit_set_slice(u, slice); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to assign slice %s to unit %s. Ignoring.", slice->id, u->id); return 0; } - unit_ref_set(&u->slice, slice); return 0; } @@ -3603,6 +3597,11 @@ int unit_load_fragment(Unit *u) { assert(u->load_state == UNIT_STUB); assert(u->id); + if (u->transient) { + u->load_state = UNIT_LOADED; + return 0; + } + /* First, try to find the unit under its id. We always look * for unit files in the default directories, to make it easy * to override things by placing things in /etc/systemd/system */ diff --git a/src/core/main.c b/src/core/main.c index 87b97aa883..e232be88c0 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1391,8 +1391,7 @@ int main(int argc, char *argv[]) { /* clear the kernel timestamp, * because we are not PID 1 */ - kernel_timestamp.monotonic = 0ULL; - kernel_timestamp.realtime = 0ULL; + kernel_timestamp = DUAL_TIMESTAMP_NULL; } /* Initialize default unit */ diff --git a/src/core/manager.c b/src/core/manager.c index ecea89c377..ede2a9910d 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -250,8 +250,8 @@ static int manager_dispatch_ask_password_fd(sd_event_source *source, static void manager_close_ask_password(Manager *m) { assert(m); - m->ask_password_inotify_fd = safe_close(m->ask_password_inotify_fd); m->ask_password_event_source = sd_event_source_unref(m->ask_password_event_source); + m->ask_password_inotify_fd = safe_close(m->ask_password_inotify_fd); m->have_ask_password = -EINVAL; } diff --git a/src/core/mount.c b/src/core/mount.c index c0d1cdfbd4..2b81d17b9c 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -521,7 +521,7 @@ static int mount_add_extras(Mount *m) { if (r < 0) return r; - r = unit_add_default_slice(u, &m->cgroup_context); + r = unit_set_default_slice(u); if (r < 0) return r; @@ -1871,7 +1871,6 @@ const UnitVTable mount_vtable = { .reset_failed = mount_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Mount", .bus_vtable = bus_mount_vtable, .bus_set_property = bus_mount_set_property, .bus_commit_properties = bus_mount_commit_properties, diff --git a/src/core/path.c b/src/core/path.c index 20995d920c..e9111d0612 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -770,6 +770,5 @@ const UnitVTable path_vtable = { .reset_failed = path_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Path", .bus_vtable = bus_path_vtable }; diff --git a/src/core/scope.c b/src/core/scope.c index ab1769b46b..c594ab5294 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -164,7 +164,7 @@ static int scope_load(Unit *u) { if (r < 0) return r; - r = unit_add_default_slice(u, &s->cgroup_context); + r = unit_set_default_slice(u); if (r < 0) return r; @@ -561,7 +561,6 @@ const UnitVTable scope_vtable = { .notify_cgroup_empty = scope_notify_cgroup_empty_event, - .bus_interface = "org.freedesktop.systemd1.Scope", .bus_vtable = bus_scope_vtable, .bus_set_property = bus_scope_set_property, .bus_commit_properties = bus_scope_commit_properties, diff --git a/src/core/selinux-setup.c b/src/core/selinux-setup.c index a4678500e6..e5b457643b 100644 --- a/src/core/selinux-setup.c +++ b/src/core/selinux-setup.c @@ -34,6 +34,7 @@ #include "log.h" #ifdef HAVE_SELINUX +_printf_(2,3) static int null_log(int type, const char *fmt, ...) { return 0; } diff --git a/src/core/service.c b/src/core/service.c index b790ec98be..3c4232417d 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -556,7 +556,7 @@ static int service_add_extras(Service *s) { if (r < 0) return r; - r = unit_add_default_slice(UNIT(s), &s->cgroup_context); + r = unit_set_default_slice(UNIT(s)); if (r < 0) return r; @@ -3214,7 +3214,6 @@ const UnitVTable service_vtable = { .bus_name_owner_change = service_bus_name_owner_change, - .bus_interface = "org.freedesktop.systemd1.Service", .bus_vtable = bus_service_vtable, .bus_set_property = bus_service_set_property, .bus_commit_properties = bus_service_commit_properties, diff --git a/src/core/slice.c b/src/core/slice.c index 064eb5d933..7442d23391 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -289,7 +289,6 @@ const UnitVTable slice_vtable = { .active_state = slice_active_state, .sub_state_to_string = slice_sub_state_to_string, - .bus_interface = "org.freedesktop.systemd1.Slice", .bus_vtable = bus_slice_vtable, .bus_set_property = bus_slice_set_property, .bus_commit_properties = bus_slice_commit_properties, diff --git a/src/core/snapshot.c b/src/core/snapshot.c index 9518e21f36..336ff20f84 100644 --- a/src/core/snapshot.c +++ b/src/core/snapshot.c @@ -302,6 +302,5 @@ const UnitVTable snapshot_vtable = { .active_state = snapshot_active_state, .sub_state_to_string = snapshot_sub_state_to_string, - .bus_interface = "org.freedesktop.systemd1.Snapshot", .bus_vtable = bus_snapshot_vtable }; diff --git a/src/core/socket.c b/src/core/socket.c index a387057473..1014fad626 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -345,7 +345,7 @@ static int socket_add_extras(Socket *s) { if (r < 0) return r; - r = unit_add_default_slice(u, &s->cgroup_context); + r = unit_set_default_slice(u); if (r < 0) return r; } @@ -839,7 +839,7 @@ static void socket_apply_socket_options(Socket *s, int fd) { if (s->keep_alive_cnt) { int value = s->keep_alive_cnt; - if (setsockopt(fd, SOL_SOCKET, TCP_KEEPCNT, &value, sizeof(value)) < 0) + if (setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &value, sizeof(value)) < 0) log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPCNT failed: %m"); } @@ -2709,7 +2709,6 @@ const UnitVTable socket_vtable = { .reset_failed = socket_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Socket", .bus_vtable = bus_socket_vtable, .bus_set_property = bus_socket_set_property, .bus_commit_properties = bus_socket_commit_properties, diff --git a/src/core/swap.c b/src/core/swap.c index 0bc3827ff0..4f3ddc9f04 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -326,7 +326,7 @@ static int swap_load(Unit *u) { if (r < 0) return r; - r = unit_add_default_slice(u, &s->cgroup_context); + r = unit_set_default_slice(u); if (r < 0) return r; @@ -1485,7 +1485,6 @@ const UnitVTable swap_vtable = { .reset_failed = swap_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Swap", .bus_vtable = bus_swap_vtable, .bus_set_property = bus_swap_set_property, .bus_commit_properties = bus_swap_commit_properties, diff --git a/src/core/target.c b/src/core/target.c index b492a7c4c7..f714cb31c2 100644 --- a/src/core/target.c +++ b/src/core/target.c @@ -221,7 +221,6 @@ const UnitVTable target_vtable = { .active_state = target_active_state, .sub_state_to_string = target_sub_state_to_string, - .bus_interface = "org.freedesktop.systemd1.Target", .bus_vtable = bus_target_vtable, .status_message_formats = { diff --git a/src/core/timer.c b/src/core/timer.c index 89758c6b19..eb6567bbfa 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -772,7 +772,6 @@ const UnitVTable timer_vtable = { .reset_failed = timer_reset_failed, .time_change = timer_time_change, - .bus_interface = "org.freedesktop.systemd1.Timer", .bus_vtable = bus_timer_vtable, .bus_set_property = bus_timer_set_property, diff --git a/src/core/unit.c b/src/core/unit.c index 43a5ca1064..5f602bdf5f 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -405,17 +405,17 @@ static void unit_remove_transient(Unit *u) { return; if (u->fragment_path) - unlink(u->fragment_path); + (void) unlink(u->fragment_path); STRV_FOREACH(i, u->dropin_paths) { _cleanup_free_ char *p = NULL; int r; - unlink(*i); + (void) unlink(*i); r = path_get_parent(*i, &p); if (r >= 0) - rmdir(p); + (void) rmdir(p); } } @@ -1122,7 +1122,7 @@ static int unit_add_target_dependencies(Unit *u) { static int unit_add_slice_dependencies(Unit *u) { assert(u); - if (!unit_get_cgroup_context(u)) + if (!UNIT_HAS_CGROUP_CONTEXT(u)) return 0; if (UNIT_ISSET(u->slice)) @@ -2424,14 +2424,42 @@ char *unit_default_cgroup_path(Unit *u) { return strjoin(u->manager->cgroup_root, "/", escaped, NULL); } -int unit_add_default_slice(Unit *u, CGroupContext *c) { +int unit_set_slice(Unit *u, Unit *slice) { + assert(u); + assert(slice); + + /* Sets the unit slice if it has not been set before. Is extra + * careful, to only allow this for units that actually have a + * cgroup context. Also, we don't allow to set this for slices + * (since the parent slice is derived from the name). Make + * sure the unit we set is actually a slice. */ + + if (!UNIT_HAS_CGROUP_CONTEXT(u)) + return -EOPNOTSUPP; + + if (u->type == UNIT_SLICE) + return -EINVAL; + + if (slice->type != UNIT_SLICE) + return -EINVAL; + + if (UNIT_DEREF(u->slice) == slice) + return 0; + + if (UNIT_ISSET(u->slice)) + return -EBUSY; + + unit_ref_set(&u->slice, slice); + return 1; +} + +int unit_set_default_slice(Unit *u) { _cleanup_free_ char *b = NULL; const char *slice_name; Unit *slice; int r; assert(u); - assert(c); if (UNIT_ISSET(u->slice)) return 0; @@ -2471,8 +2499,7 @@ int unit_add_default_slice(Unit *u, CGroupContext *c) { if (r < 0) return r; - unit_ref_set(&u->slice, slice); - return 0; + return unit_set_slice(u, slice); } const char *unit_slice_name(Unit *u) { @@ -3108,17 +3135,17 @@ int unit_kill_common( int r = 0; - if (who == KILL_MAIN && main_pid <= 0) { + if (who == KILL_MAIN) { if (main_pid < 0) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type)); - else + else if (main_pid == 0) return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill"); } - if (who == KILL_CONTROL && control_pid <= 0) { + if (who == KILL_CONTROL) { if (control_pid < 0) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no control processes", unit_type_to_string(u->type)); - else + else if (control_pid == 0) return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); } @@ -3317,6 +3344,8 @@ ExecRuntime *unit_get_exec_runtime(Unit *u) { } static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient, char **dir) { + assert(u); + if (u->manager->running_as == MANAGER_USER) { int r; @@ -3324,9 +3353,9 @@ static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient, r = user_config_home(dir); else r = user_runtime_dir(dir); - if (r == 0) return -ENOENT; + return r; } @@ -3340,8 +3369,7 @@ static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient, return 0; } -static int unit_drop_in_file(Unit *u, - UnitSetPropertiesMode mode, const char *name, char **p, char **q) { +static int unit_drop_in_file(Unit *u, UnitSetPropertiesMode mode, const char *name, char **p, char **q) { _cleanup_free_ char *dir = NULL; int r; @@ -3475,40 +3503,17 @@ int unit_remove_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name) { } int unit_make_transient(Unit *u) { - int r; - assert(u); + if (!UNIT_VTABLE(u)->can_transient) + return -EOPNOTSUPP; + u->load_state = UNIT_STUB; u->load_error = 0; u->transient = true; + u->fragment_path = mfree(u->fragment_path); - free(u->fragment_path); - u->fragment_path = NULL; - - if (u->manager->running_as == MANAGER_USER) { - _cleanup_free_ char *c = NULL; - - r = user_runtime_dir(&c); - if (r < 0) - return r; - if (r == 0) - return -ENOENT; - - u->fragment_path = strjoin(c, "/", u->id, NULL); - if (!u->fragment_path) - return -ENOMEM; - - mkdir_p(c, 0755); - } else { - u->fragment_path = strappend("/run/systemd/system/", u->id); - if (!u->fragment_path) - return -ENOMEM; - - mkdir_p("/run/systemd/system", 0755); - } - - return write_string_file_atomic_label(u->fragment_path, "# Transient stub"); + return 0; } int unit_kill_context( diff --git a/src/core/unit.h b/src/core/unit.h index 9df5a7e6fb..bc26653247 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -404,9 +404,6 @@ struct UnitVTable { * of this type will immediately fail. */ bool (*supported)(void); - /* The interface name */ - const char *bus_interface; - /* The bus vtable */ const sd_bus_vtable *bus_vtable; @@ -442,6 +439,10 @@ extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX]; /* For casting the various unit types into a unit */ #define UNIT(u) (&(u)->meta) +#define UNIT_HAS_EXEC_CONTEXT(u) (UNIT_VTABLE(u)->exec_context_offset > 0) +#define UNIT_HAS_CGROUP_CONTEXT(u) (UNIT_VTABLE(u)->cgroup_context_offset > 0) +#define UNIT_HAS_KILL_CONTEXT(u) (UNIT_VTABLE(u)->kill_context_offset > 0) + #define UNIT_TRIGGER(u) ((Unit*) set_first((u)->dependencies[UNIT_TRIGGERS])) DEFINE_CAST(SERVICE, Service); @@ -493,7 +494,8 @@ int unit_load_fragment_and_dropin(Unit *u); int unit_load_fragment_and_dropin_optional(Unit *u); int unit_load(Unit *unit); -int unit_add_default_slice(Unit *u, CGroupContext *c); +int unit_set_slice(Unit *u, Unit *slice); +int unit_set_default_slice(Unit *u); const char *unit_description(Unit *u) _pure_; diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h index 6cc794c937..5dc3c7aa26 100644 --- a/src/libsystemd-network/dhcp-server-internal.h +++ b/src/libsystemd-network/dhcp-server-internal.h @@ -57,9 +57,9 @@ struct sd_dhcp_server { int ifindex; be32_t address; be32_t netmask; - be32_t pool_start; - size_t pool_size; - size_t next_offer; + be32_t subnet; + uint32_t pool_offset; + uint32_t pool_size; char *timezone; @@ -68,6 +68,7 @@ struct sd_dhcp_server { Hashmap *leases_by_client_id; DHCPLease **bound_leases; + DHCPLease invalid_lease; uint32_t max_lease_time, default_lease_time; }; diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index a46858258b..7a8b298b51 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -22,6 +22,7 @@ #include <sys/ioctl.h> +#include "in-addr-util.h" #include "siphash24.h" #include "sd-dhcp-server.h" @@ -31,38 +32,63 @@ #define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR #define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12) -int sd_dhcp_server_set_lease_pool(sd_dhcp_server *server, - struct in_addr *address, - size_t size) { +/* configures the server's address and subnet, and optionally the pool's size and offset into the subnet + * the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address + * moreover, the server's own address may be in the pool, and is in that case reserved in order not to + * accidentally hand it out */ +int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size) { + struct in_addr netmask_addr; + be32_t netmask; + uint32_t server_off, broadcast_off, size_max; + assert_return(server, -EINVAL); assert_return(address, -EINVAL); - assert_return(address->s_addr, -EINVAL); - assert_return(size, -EINVAL); - assert_return(server->pool_start == htobe32(INADDR_ANY), -EBUSY); - assert_return(!server->pool_size, -EBUSY); - assert_return(!server->bound_leases, -EBUSY); + assert_return(address->s_addr != INADDR_ANY, -EINVAL); + assert_return(prefixlen <= 32, -ERANGE); + assert_return(server->address == INADDR_ANY, -EBUSY); + + assert_se(in_addr_prefixlen_to_netmask(&netmask_addr, prefixlen)); + netmask = netmask_addr.s_addr; + + server_off = be32toh(address->s_addr & ~netmask); + broadcast_off = be32toh(~netmask); + + /* the server address cannot be the subnet address */ + assert_return(server_off != 0, -ERANGE); + + /* nor the broadcast address */ + assert_return(server_off != broadcast_off, -ERANGE); + + /* 0 offset means we should set a default, we skip the first (subnet) address + and take the next one */ + if (offset == 0) + offset = 1; + + size_max = (broadcast_off + 1) /* the number of addresses in the subnet */ + - offset /* exclude the addresses before the offset */ + - 1; /* exclude the last (broadcast) address */ + + /* The pool must contain at least one address */ + assert_return(size_max >= 1, -ERANGE); + + if (size != 0) + assert_return(size <= size_max, -ERANGE); + else + size = size_max; server->bound_leases = new0(DHCPLease*, size); if (!server->bound_leases) return -ENOMEM; - server->pool_start = address->s_addr; + server->pool_offset = offset; server->pool_size = size; - return 0; -} - -int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address, - unsigned char prefixlen) { - assert_return(server, -EINVAL); - assert_return(address, -EINVAL); - assert_return(address->s_addr, -EINVAL); - assert_return(prefixlen <= 32, -ERANGE); - assert_return(server->address == htobe32(INADDR_ANY), -EBUSY); - assert_return(server->netmask == htobe32(INADDR_ANY), -EBUSY); - server->address = address->s_addr; - server->netmask = htobe32(0xfffffffflu << (32 - prefixlen)); + server->netmask = netmask; + server->subnet = address->s_addr & netmask; + + if (server_off >= offset && server_off - offset < size) + server->bound_leases[server_off - offset] = &server->invalid_lease; return 0; } @@ -661,14 +687,15 @@ static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) { if (!server->pool_size) return -EINVAL; - if (be32toh(requested_ip) < be32toh(server->pool_start) || - be32toh(requested_ip) >= be32toh(server->pool_start) + - + server->pool_size) - return -EINVAL; + if (be32toh(requested_ip) < (be32toh(server->subnet) | server->pool_offset) || + be32toh(requested_ip) >= (be32toh(server->subnet) | (server->pool_offset + server->pool_size))) + return -ERANGE; - return be32toh(requested_ip) - be32toh(server->pool_start); + return be32toh(requested_ip & ~server->netmask) - server->pool_offset; } +#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30) + int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, size_t length) { _cleanup_dhcp_request_free_ DHCPRequest *req = NULL; @@ -716,12 +743,20 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, if (existing_lease) address = existing_lease->address; else { + uint32_t next_offer; + + /* even with no persistence of leases, we try to offer the same client + the same IP address. we do this by using the hash of the client id + as the offset into the pool of leases when finding the next free one */ + + next_offer = client_id_hash_func(&req->client_id, HASH_KEY.bytes) % server->pool_size; + for (i = 0; i < server->pool_size; i++) { - if (!server->bound_leases[server->next_offer]) { - address = htobe32(be32toh(server->pool_start) + server->next_offer); + if (!server->bound_leases[next_offer]) { + address = server->subnet | htobe32(server->pool_offset + next_offer); break; } else - server->next_offer = (server->next_offer + 1) % server->pool_size; + next_offer = (next_offer + 1) % server->pool_size; } } @@ -1012,7 +1047,7 @@ int sd_dhcp_server_forcerenew(sd_dhcp_server *server) { for (i = 0; i < server->pool_size; i++) { DHCPLease *lease = server->bound_leases[i]; - if (!lease) + if (!lease || lease == &server->invalid_lease) continue; r = server_send_forcerenew(server, lease->address, diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c index 9f60ab761e..7d8a1f6bd9 100644 --- a/src/libsystemd-network/test-dhcp-server.c +++ b/src/libsystemd-network/test-dhcp-server.c @@ -28,6 +28,14 @@ #include "sd-dhcp-server.h" #include "dhcp-server-internal.h" +static void test_pool(struct in_addr *address, unsigned size, int ret) { + _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL; + + assert_se(sd_dhcp_server_new(&server, 1) >= 0); + + assert_se(sd_dhcp_server_configure_pool(server, address, 8, 0, size) == ret); +} + static int test_basic(sd_event *event) { _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL; struct in_addr address_lo = { @@ -54,15 +62,14 @@ static int test_basic(sd_event *event) { assert_se(!sd_dhcp_server_unref(server)); assert_se(sd_dhcp_server_start(server) == -EUNATCH); - assert_se(sd_dhcp_server_set_address(server, &address_any, 28) == -EINVAL); - assert_se(sd_dhcp_server_set_address(server, &address_lo, 38) == -ERANGE); - assert_se(sd_dhcp_server_set_address(server, &address_lo, 8) >= 0); - assert_se(sd_dhcp_server_set_address(server, &address_lo, 8) == -EBUSY); - assert_se(sd_dhcp_server_set_lease_pool(server, &address_any, 1) == -EINVAL); - assert_se(sd_dhcp_server_set_lease_pool(server, &address_lo, 0) == -EINVAL); - assert_se(sd_dhcp_server_set_lease_pool(server, &address_lo, 1) >= 0); - assert_se(sd_dhcp_server_set_lease_pool(server, &address_lo, 1) == -EBUSY); + assert_se(sd_dhcp_server_configure_pool(server, &address_any, 28, 0, 0) == -EINVAL); + assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 38, 0, 0) == -ERANGE); + assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0); + assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) == -EBUSY); + + test_pool(&address_any, 1, -EINVAL); + test_pool(&address_lo, 1, 0); r = sd_dhcp_server_start(server); @@ -119,12 +126,10 @@ static void test_message_handler(void) { }; assert_se(sd_dhcp_server_new(&server, 1) >= 0); - assert_se(sd_dhcp_server_set_address(server, &address_lo, 8) >= 0); + assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0); assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0); assert_se(sd_dhcp_server_start(server) >= 0); - assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); - assert_se(sd_dhcp_server_set_lease_pool(server, &address_lo, 10) >= 0); assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); test.end = 0; diff --git a/src/libsystemd/sd-bus/bus-match.c b/src/libsystemd/sd-bus/bus-match.c index 2b83f439a7..7234e7926a 100644 --- a/src/libsystemd/sd-bus/bus-match.c +++ b/src/libsystemd/sd-bus/bus-match.c @@ -932,7 +932,7 @@ fail: } char *bus_match_to_string(struct bus_match_component *components, unsigned n_components) { - _cleanup_free_ FILE *f = NULL; + _cleanup_fclose_ FILE *f = NULL; char *buffer = NULL; size_t size = 0; unsigned i; diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index 0e33ced342..c419be820a 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -481,7 +481,8 @@ static void source_io_unregister(sd_event_source *s) { return; r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->io.fd, NULL); - assert_log(r >= 0); + if (r < 0) + log_debug_errno(errno, "Failed to remove source %s from epoll: %m", strna(s->description)); s->io.registered = false; } diff --git a/src/locale/localed.c b/src/locale/localed.c index e9489f04c2..4fa84df8c0 100644 --- a/src/locale/localed.c +++ b/src/locale/localed.c @@ -1092,6 +1092,7 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro } #ifdef HAVE_XKBCOMMON +_printf_(3, 0) static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) { const char *fmt; diff --git a/src/login/70-power-switch.rules b/src/login/70-power-switch.rules index 71f9fe6c72..36d2a3eb40 100644 --- a/src/login/70-power-switch.rules +++ b/src/login/70-power-switch.rules @@ -11,6 +11,7 @@ SUBSYSTEM=="input", KERNEL=="event*", SUBSYSTEMS=="acpi", TAG+="power-switch" SUBSYSTEM=="input", KERNEL=="event*", KERNELS=="thinkpad_acpi", TAG+="power-switch" SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="twl4030_pwrbutton", TAG+="power-switch" SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="tps65217_pwr_but", TAG+="power-switch" +SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="* WMI hotkeys", TAG+="power-switch" SUBSYSTEM=="input", KERNEL=="event*", \ SUBSYSTEMS=="platform", DRIVERS=="gpio-keys", ATTRS{keys}=="116", TAG+="power-switch" diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index f83d18b035..71c84d8e0a 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -179,24 +179,37 @@ static int export_legacy_dbus_address( const char *runtime) { _cleanup_free_ char *s = NULL; - int r; + int r = PAM_BUF_ERR; - /* skip export if kdbus is not active */ - if (!is_kdbus_available()) - return PAM_SUCCESS; + if (is_kdbus_available()) { + if (asprintf(&s, KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT, uid, runtime) < 0) + goto error; + } else { + /* FIXME: We *realy* should move the access() check into the + * daemons that spawn dbus-daemon, instead of forcing + * DBUS_SESSION_BUS_ADDRESS= here. */ + + s = strjoin(runtime, "/bus", NULL); + if (!s) + goto error; + + if (access(s, F_OK) < 0) + return PAM_SUCCESS; - if (asprintf(&s, KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT, uid, runtime) < 0) { - pam_syslog(handle, LOG_ERR, "Failed to set bus variable."); - return PAM_BUF_ERR; + s = mfree(s); + if (asprintf(&s, UNIX_USER_BUS_ADDRESS_FMT, runtime) < 0) + goto error; } r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", s, 0); - if (r != PAM_SUCCESS) { - pam_syslog(handle, LOG_ERR, "Failed to set bus variable."); - return r; - } + if (r != PAM_SUCCESS) + goto error; return PAM_SUCCESS; + +error: + pam_syslog(handle, LOG_ERR, "Failed to set bus variable."); + return r; } _public_ PAM_EXTERN int pam_sm_open_session( diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 926035d185..8bd0ed756b 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -361,8 +361,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { bus, "org.freedesktop.systemd1", path, - endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : - endswith(unit, ".slice") ? "org.freedesktop.systemd1.Slice" : "org.freedesktop.systemd1.Service", + unit_dbus_interface_from_name(unit), "ControlGroup", &error, &reply, diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index ff693ed0fd..979f3115f6 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -730,7 +730,6 @@ static int link_enter_set_addresses(Link *link) { /* now that we can figure out a default address for the dhcp server, start it */ if (link_dhcp4_server_enabled(link)) { - struct in_addr pool_start; Address *address; Link *uplink = NULL; bool acquired_uplink = false; @@ -742,16 +741,9 @@ static int link_enter_set_addresses(Link *link) { return 0; } - r = sd_dhcp_server_set_address(link->dhcp_server, - &address->in_addr.in, - address->prefixlen); - if (r < 0) - return r; - - /* offer 32 addresses starting from the address following the server address */ - pool_start.s_addr = htobe32(be32toh(address->in_addr.in.s_addr) + 1); - r = sd_dhcp_server_set_lease_pool(link->dhcp_server, - &pool_start, 32); + /* use the server address' subnet as the pool */ + r = sd_dhcp_server_configure_pool(link->dhcp_server, &address->in_addr.in, address->prefixlen, + link->network->dhcp_server_pool_offset, link->network->dhcp_server_pool_size); if (r < 0) return r; @@ -760,11 +752,6 @@ static int link_enter_set_addresses(Link *link) { &main_address->in_addr.in); if (r < 0) return r; - - r = sd_dhcp_server_set_prefixlen(link->dhcp_server, - main_address->prefixlen); - if (r < 0) - return r; */ if (link->network->dhcp_server_max_lease_time_usec > 0) { @@ -823,7 +810,7 @@ static int link_enter_set_addresses(Link *link) { if (link->network->dhcp_server_emit_timezone) { _cleanup_free_ char *buffer = NULL; - const char *tz; + const char *tz = NULL; if (link->network->dhcp_server_timezone) tz = link->network->dhcp_server_timezone; diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 108e892fb8..10ca9dae35 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -82,6 +82,8 @@ DHCPServer.EmitNTP, config_parse_bool, 0 DHCPServer.NTP, config_parse_dhcp_server_ntp, 0, 0 DHCPServer.EmitTimezone, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_timezone) DHCPServer.Timezone, config_parse_timezone, 0, offsetof(Network, dhcp_server_timezone) +DHCPServer.PoolOffset, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_offset) +DHCPServer.PoolSize, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_size) Bridge.Cost, config_parse_unsigned, 0, offsetof(Network, cost) Bridge.UseBPDU, config_parse_bool, 0, offsetof(Network, use_bpdu) Bridge.HairPin, config_parse_bool, 0, offsetof(Network, hairpin) diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index d691cc3a45..c3439a70ba 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -102,6 +102,8 @@ struct Network { bool dhcp_server_emit_timezone; char *dhcp_server_timezone; usec_t dhcp_server_default_lease_time_usec, dhcp_server_max_lease_time_usec; + uint32_t dhcp_server_pool_offset; + uint32_t dhcp_server_pool_size; /* IPV4LL Support */ AddressFamilyBoolean link_local; diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 837947ee28..8039847a72 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -257,9 +257,11 @@ static void help(void) { " try-guest, try-host\n" " -j Equivalent to --link-journal=try-guest\n" " --read-only Mount the root directory read-only\n" - " --bind=PATH[:PATH] Bind mount a file or directory from the host into\n" + " --bind=PATH[:PATH[:OPTIONS]]\n" + " Bind mount a file or directory from the host into\n" " the container\n" - " --bind-ro=PATH[:PATH] Similar, but creates a read-only bind mount\n" + " --bind-ro=PATH[:PATH[:OPTIONS]\n" + " Similar, but creates a read-only bind mount\n" " --tmpfs=PATH:[OPTIONS] Mount an empty tmpfs to the specified directory\n" " --overlay=PATH[:PATH...]:PATH\n" " Create an overlay mount from the host to \n" @@ -656,14 +658,15 @@ static int parse_argv(int argc, char *argv[]) { case ARG_BIND: case ARG_BIND_RO: { const char *current = optarg; - _cleanup_free_ char *source = NULL, *destination = NULL; + _cleanup_free_ char *source = NULL, *destination = NULL, *opts = NULL; CustomMount *m; - r = extract_many_words(¤t, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination, NULL); + r = extract_many_words(¤t, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination, &opts, NULL); switch (r) { case 1: destination = strdup(source); case 2: + case 3: break; case -ENOMEM: return log_oom(); @@ -687,8 +690,9 @@ static int parse_argv(int argc, char *argv[]) { m->source = source; m->destination = destination; m->read_only = c == ARG_BIND_RO; + m->options = opts; - source = destination = NULL; + source = destination = opts = NULL; break; } @@ -1158,13 +1162,53 @@ static int mount_all(const char *dest, bool userns) { return 0; } +static int parse_mount_bind_options(const char *options, unsigned long *mount_flags, char **mount_opts) { + const char *p = options; + unsigned long flags = *mount_flags; + char *opts = NULL; + + assert(options); + + for (;;) { + _cleanup_free_ char *word = NULL; + int r = extract_first_word(&p, &word, ",", 0); + if (r < 0) + return log_error_errno(r, "Failed to extract mount option: %m"); + if (r == 0) + break; + + if (streq(word, "rbind")) + flags |= MS_REC; + else if (streq(word, "norbind")) + flags &= ~MS_REC; + else { + log_error("Invalid bind mount option: %s", word); + return -EINVAL; + } + } + + *mount_flags = flags; + /* in the future mount_opts will hold string options for mount(2) */ + *mount_opts = opts; + + return 0; +} + static int mount_bind(const char *dest, CustomMount *m) { struct stat source_st, dest_st; const char *where; + unsigned long mount_flags = MS_BIND | MS_REC; + _cleanup_free_ char *mount_opts = NULL; int r; assert(m); + if (m->options) { + r = parse_mount_bind_options(m->options, &mount_flags, &mount_opts); + if (r < 0) + return r; + } + if (stat(m->source, &source_st) < 0) return log_error_errno(errno, "Failed to stat %s: %m", m->source); @@ -1201,7 +1245,7 @@ static int mount_bind(const char *dest, CustomMount *m) { if (r < 0 && r != -EEXIST) return log_error_errno(r, "Failed to create mount point %s: %m", where); - if (mount(m->source, where, NULL, MS_BIND, NULL) < 0) + if (mount(m->source, where, NULL, mount_flags, mount_opts) < 0) return log_error_errno(errno, "mount(%s) failed: %m", where); if (m->read_only) { diff --git a/src/shared/Makefile b/src/shared/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/shared/Makefile @@ -0,0 +1 @@ +../Makefile
\ No newline at end of file diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c index 1a2c4b28cd..3abccdb49a 100644 --- a/src/shared/cgroup-show.c +++ b/src/shared/cgroup-show.c @@ -161,8 +161,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns } if (last) { - printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_BRANCH), - basename(last)); + printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_BRANCH), cg_unescape(basename(last))); if (!p1) { p1 = strappend(prefix, draw_special_char(DRAW_TREE_VERTICAL)); @@ -185,8 +184,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns show_cgroup_one_by_path(path, prefix, n_columns, !!last, kernel_threads, flags); if (last) { - printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_RIGHT), - basename(last)); + printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_RIGHT), cg_unescape(basename(last))); if (!p2) { p2 = strappend(prefix, " "); diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index d99aa1d6e9..1f4aea6d6b 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -450,6 +450,7 @@ int config_parse_many(const char *conf_file, DEFINE_PARSER(int, int, safe_atoi) DEFINE_PARSER(long, long, safe_atoli) +DEFINE_PARSER(uint32, uint32_t, safe_atou32) DEFINE_PARSER(uint64, uint64_t, safe_atou64) DEFINE_PARSER(unsigned, unsigned, safe_atou) DEFINE_PARSER(double, double, safe_atod) diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index 6152ee33b9..66c80890d3 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -104,6 +104,7 @@ int config_parse_many(const char *conf_file, /* possibly NULL */ int config_parse_int(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_unsigned(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_long(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_uint32(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_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_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); diff --git a/src/shared/pager.c b/src/shared/pager.c index 13f03e798b..3992f9c837 100644 --- a/src/shared/pager.c +++ b/src/shared/pager.c @@ -31,18 +31,16 @@ #include "macro.h" #include "terminal-util.h" #include "signal-util.h" +#include "copy.h" static pid_t pager_pid = 0; noreturn static void pager_fallback(void) { - ssize_t n; - - do { - n = splice(STDIN_FILENO, NULL, STDOUT_FILENO, NULL, 64*1024, 0); - } while (n > 0); + int r; - if (n < 0) { - log_error_errno(errno, "Internal pager failed: %m"); + r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (off_t) -1, false); + if (r < 0) { + log_error_errno(r, "Internal pager failed: %m"); _exit(EXIT_FAILURE); } @@ -84,7 +82,7 @@ int pager_open(bool jump_to_end) { /* In the child start the pager */ if (pager_pid == 0) { - const char* less_opts; + const char* less_opts, *less_charset; (void) reset_all_signal_handlers(); (void) reset_signal_mask(); @@ -92,6 +90,7 @@ int pager_open(bool jump_to_end) { dup2(fd[0], STDIN_FILENO); safe_close_pair(fd); + /* Initialize a good set of less options */ less_opts = getenv("SYSTEMD_LESS"); if (!less_opts) less_opts = "FRSXMK"; @@ -99,6 +98,15 @@ int pager_open(bool jump_to_end) { less_opts = strjoina(less_opts, " +G"); setenv("LESS", less_opts, 1); + /* Initialize a good charset for less. This is + * particularly important if we output UTF-8 + * characters. */ + less_charset = getenv("SYSTEMD_LESSCHARSET"); + if (!less_charset && is_locale_utf8()) + less_charset = "utf-8"; + if (less_charset) + setenv("LESSCHARSET", less_charset, 1); + /* Make sure the pager goes away when the parent dies */ if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) _exit(EXIT_FAILURE); @@ -131,6 +139,8 @@ int pager_open(bool jump_to_end) { /* Return in the parent */ if (dup2(fd[1], STDOUT_FILENO) < 0) return log_error_errno(errno, "Failed to duplicate pager pipe: %m"); + if (dup2(fd[1], STDERR_FILENO) < 0) + return log_error_errno(errno, "Failed to duplicate pager pipe: %m"); safe_close_pair(fd); return 1; @@ -143,6 +153,11 @@ void pager_close(void) { /* Inform pager that we are done */ fclose(stdout); + stdout = NULL; + + fclose(stderr); + stderr = NULL; + kill(pager_pid, SIGCONT); (void) wait_for_terminate(pager_pid, NULL); pager_pid = 0; diff --git a/src/systemd/sd-dhcp-server.h b/src/systemd/sd-dhcp-server.h index 7e4f2ffb30..4b0c7a1852 100644 --- a/src/systemd/sd-dhcp-server.h +++ b/src/systemd/sd-dhcp-server.h @@ -44,8 +44,7 @@ bool sd_dhcp_server_is_running(sd_dhcp_server *server); int sd_dhcp_server_start(sd_dhcp_server *server); int sd_dhcp_server_stop(sd_dhcp_server *server); -int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen); -int sd_dhcp_server_set_lease_pool(sd_dhcp_server *server, struct in_addr *start, size_t size); +int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size); int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *timezone); int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n); |