diff options
Diffstat (limited to 'src')
62 files changed, 1027 insertions, 385 deletions
diff --git a/src/basic/architecture.c b/src/basic/architecture.c index a9ecfc1cd6..8e2c2b02d2 100644 --- a/src/basic/architecture.c +++ b/src/basic/architecture.c @@ -121,6 +121,8 @@ int uname_architecture(void) { { "tilegx", ARCHITECTURE_TILEGX }, #elif defined(__cris__) { "crisv32", ARCHITECTURE_CRIS }, +#elif defined(__nios2__) + { "nios2", ARCHITECTURE_NIOS2 }, #else #error "Please register your architecture here!" #endif @@ -171,6 +173,7 @@ static const char *const architecture_table[_ARCHITECTURE_MAX] = { [ARCHITECTURE_M68K] = "m68k", [ARCHITECTURE_TILEGX] = "tilegx", [ARCHITECTURE_CRIS] = "cris", + [ARCHITECTURE_NIOS2] = "nios2", }; DEFINE_STRING_TABLE_LOOKUP(architecture, int); diff --git a/src/basic/architecture.h b/src/basic/architecture.h index c22cbc8279..91ec108e04 100644 --- a/src/basic/architecture.h +++ b/src/basic/architecture.h @@ -57,6 +57,7 @@ enum { ARCHITECTURE_M68K, ARCHITECTURE_TILEGX, ARCHITECTURE_CRIS, + ARCHITECTURE_NIOS2, _ARCHITECTURE_MAX, _ARCHITECTURE_INVALID = -1 }; @@ -187,6 +188,9 @@ int uname_architecture(void); #elif defined(__cris__) # define native_architecture() ARCHITECTURE_CRIS # error "Missing LIB_ARCH_TUPLE for CRIS" +#elif defined(__nios2__) +# define native_architecture() ARCHITECTURE_NIOS2 +# define LIB_ARCH_TUPLE "nios2-linux-gnu" #else # error "Please register your architecture here!" #endif diff --git a/src/basic/copy.c b/src/basic/copy.c index 3001234a01..c3586728d0 100644 --- a/src/basic/copy.c +++ b/src/basic/copy.c @@ -305,6 +305,8 @@ static int fd_copy_directory( fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); else fdf = fcntl(df, F_DUPFD_CLOEXEC, 3); + if (fdf < 0) + return -errno; d = fdopendir(fdf); if (!d) @@ -325,22 +327,6 @@ static int fd_copy_directory( r = 0; - if (created) { - struct timespec ut[2] = { - st->st_atim, - st->st_mtim - }; - - if (fchown(fdt, st->st_uid, st->st_gid) < 0) - r = -errno; - - if (fchmod(fdt, st->st_mode & 07777) < 0) - r = -errno; - - (void) futimens(fdt, ut); - (void) copy_xattr(dirfd(d), fdt); - } - FOREACH_DIRENT_ALL(de, d, return -errno) { struct stat buf; int q; @@ -376,6 +362,22 @@ static int fd_copy_directory( r = q; } + if (created) { + struct timespec ut[2] = { + st->st_atim, + st->st_mtim + }; + + if (fchown(fdt, st->st_uid, st->st_gid) < 0) + r = -errno; + + if (fchmod(fdt, st->st_mode & 07777) < 0) + r = -errno; + + (void) copy_xattr(dirfd(d), fdt); + (void) futimens(fdt, ut); + } + return r; } @@ -407,7 +409,6 @@ int copy_tree(const char *from, const char *to, bool merge) { } int copy_directory_fd(int dirfd, const char *to, bool merge) { - struct stat st; assert(dirfd >= 0); @@ -422,6 +423,21 @@ int copy_directory_fd(int dirfd, const char *to, bool merge) { return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge); } +int copy_directory(const char *from, const char *to, bool merge) { + struct stat st; + + assert(from); + assert(to); + + if (lstat(from, &st) < 0) + return -errno; + + if (!S_ISDIR(st.st_mode)) + return -ENOTDIR; + + return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge); +} + int copy_file_fd(const char *from, int fdt, bool try_reflink) { _cleanup_close_ int fdf = -1; int r; diff --git a/src/basic/copy.h b/src/basic/copy.h index 3e5eb52506..b5d08ebafe 100644 --- a/src/basic/copy.h +++ b/src/basic/copy.h @@ -30,6 +30,7 @@ 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_directory(const char *from, const char *to, bool merge); int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink); int copy_times(int fdf, int fdt); int copy_xattr(int fdf, int fdt); diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 2a9b6e46ad..29f5374222 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -1337,7 +1337,7 @@ int link_tmpfile(int fd, const char *path, const char *target) { * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported * on the directory, and renameat2() is used instead. * - * Note that in both cases we will not replace existing files. This is because linkat() dos not support this + * Note that in both cases we will not replace existing files. This is because linkat() does not support this * operation currently (renameat2() does), and there is no nice way to emulate this. */ if (path) { diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c index 85b8d812b3..49a0479592 100644 --- a/src/basic/hashmap.c +++ b/src/basic/hashmap.c @@ -1773,20 +1773,18 @@ int set_consume(Set *s, void *value) { int set_put_strdup(Set *s, const char *p) { char *c; - int r; assert(s); assert(p); + if (set_contains(s, (char*) p)) + return 0; + c = strdup(p); if (!c) return -ENOMEM; - r = set_consume(s, c); - if (r == -EEXIST) - return 0; - - return r; + return set_consume(s, c); } int set_put_strdupv(Set *s, char **l) { diff --git a/src/basic/process-util.c b/src/basic/process-util.c index f2cea01979..4a7367cc92 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -528,14 +528,20 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_cod return -EPROTO; } -void sigkill_wait(pid_t *pid) { +void sigkill_wait(pid_t pid) { + assert(pid > 1); + + if (kill(pid, SIGKILL) > 0) + (void) wait_for_terminate(pid, NULL); +} + +void sigkill_waitp(pid_t *pid) { if (!pid) return; if (*pid <= 1) return; - if (kill(*pid, SIGKILL) > 0) - (void) wait_for_terminate(*pid, NULL); + sigkill_wait(*pid); } int kill_and_sigcont(pid_t pid, int sig) { diff --git a/src/basic/process-util.h b/src/basic/process-util.h index ffd4bcb0ff..9f75088796 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -58,8 +58,8 @@ int get_process_ppid(pid_t pid, pid_t *ppid); int wait_for_terminate(pid_t pid, siginfo_t *status); int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code); -void sigkill_wait(pid_t *pid); -#define _cleanup_sigkill_wait_ _cleanup_(sigkill_wait) +void sigkill_wait(pid_t pid); +void sigkill_waitp(pid_t *pid); int kill_and_sigcont(pid_t pid, int sig); diff --git a/src/basic/strbuf.c b/src/basic/strbuf.c index 797f00cf71..4bef87d3c2 100644 --- a/src/basic/strbuf.c +++ b/src/basic/strbuf.c @@ -156,12 +156,13 @@ ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len) { return off; } + c = s[len - 1 - depth]; + /* bsearch is not allowed on a NULL sequence */ if (node->children_count == 0) break; /* lookup child node */ - c = s[len - 1 - depth]; search.c = c; child = bsearch(&search, node->children, node->children_count, sizeof(struct strbuf_child_entry), diff --git a/src/basic/virt.c b/src/basic/virt.c index e6c5a095a0..dace1f4328 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -280,7 +280,7 @@ static int detect_vm_uml(void) { return VIRTUALIZATION_UML; } - log_debug("No virtualization found in /proc/cpuinfo (%s)", cpuinfo_contents); + log_debug("No virtualization found in /proc/cpuinfo."); return VIRTUALIZATION_NONE; } diff --git a/src/core/automount.c b/src/core/automount.c index 7c55d7bc49..f06d837e30 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -75,6 +75,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct expire_data*, expire_data_free); static int open_dev_autofs(Manager *m); static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, void *userdata); +static int automount_start_expire(Automount *a); +static void automount_stop_expire(Automount *a); +static int automount_send_ready(Automount *a, Set *tokens, int status); static void automount_init(Unit *u) { Automount *a = AUTOMOUNT(u); @@ -87,8 +90,6 @@ static void automount_init(Unit *u) { UNIT(a)->ignore_on_isolate = true; } -static int automount_send_ready(Automount *a, Set *tokens, int status); - static void unmount_autofs(Automount *a) { int r; @@ -235,6 +236,9 @@ static void automount_set_state(Automount *a, AutomountState state) { old_state = a->state; a->state = state; + if (state != AUTOMOUNT_RUNNING) + automount_stop_expire(a); + if (state != AUTOMOUNT_WAITING && state != AUTOMOUNT_RUNNING) unmount_autofs(a); @@ -408,7 +412,7 @@ static int autofs_send_ready(int dev_autofs_fd, int ioctl_fd, uint32_t token, in init_autofs_dev_ioctl(¶m); param.ioctlfd = ioctl_fd; - if (status) { + if (status != 0) { param.fail.token = token; param.fail.status = status; } else @@ -435,7 +439,7 @@ static int automount_send_ready(Automount *a, Set *tokens, int status) { if (ioctl_fd < 0) return ioctl_fd; - if (status) + if (status != 0) log_unit_debug_errno(UNIT(a), status, "Sending failure: %m"); else log_unit_debug(UNIT(a), "Sending success."); @@ -462,59 +466,54 @@ static int automount_send_ready(Automount *a, Set *tokens, int status) { return r; } -static int automount_start_expire(Automount *a); - -int automount_update_mount(Automount *a, MountState old_state, MountState state) { +static void automount_trigger_notify(Unit *u, Unit *other) { + Automount *a = AUTOMOUNT(u); int r; assert(a); + assert(other); + + /* Filter out invocations with bogus state */ + if (other->load_state != UNIT_LOADED || other->type != UNIT_MOUNT) + return; + + /* Don't propagate state changes from the mount if we are already down */ + if (!IN_SET(a->state, AUTOMOUNT_WAITING, AUTOMOUNT_RUNNING)) + return; + + /* Propagate start limit hit state */ + if (other->start_limit_hit) { + automount_enter_dead(a, AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT); + return; + } + + /* Don't propagate anything if there's still a job queued */ + if (other->job) + return; + + /* The mount is successfully established */ + if (IN_SET(MOUNT(other)->state, MOUNT_MOUNTED, MOUNT_REMOUNTING)) { + (void) automount_send_ready(a, a->tokens, 0); - switch (state) { - case MOUNT_MOUNTED: - case MOUNT_REMOUNTING: - automount_send_ready(a, a->tokens, 0); r = automount_start_expire(a); if (r < 0) log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m"); - break; - case MOUNT_DEAD: - case MOUNT_UNMOUNTING: - case MOUNT_MOUNTING_SIGTERM: - case MOUNT_MOUNTING_SIGKILL: - case MOUNT_REMOUNTING_SIGTERM: - case MOUNT_REMOUNTING_SIGKILL: - case MOUNT_UNMOUNTING_SIGTERM: - case MOUNT_UNMOUNTING_SIGKILL: - case MOUNT_FAILED: - if (old_state != state) - automount_send_ready(a, a->tokens, -ENODEV); - (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF); - break; - default: - break; - } - switch (state) { - case MOUNT_DEAD: - automount_send_ready(a, a->expire_tokens, 0); - break; - case MOUNT_MOUNTING: - case MOUNT_MOUNTING_DONE: - case MOUNT_MOUNTING_SIGTERM: - case MOUNT_MOUNTING_SIGKILL: - case MOUNT_REMOUNTING_SIGTERM: - case MOUNT_REMOUNTING_SIGKILL: - case MOUNT_UNMOUNTING_SIGTERM: - case MOUNT_UNMOUNTING_SIGKILL: - case MOUNT_FAILED: - if (old_state != state) - automount_send_ready(a, a->expire_tokens, -ENODEV); - break; - default: - break; + automount_set_state(a, AUTOMOUNT_RUNNING); } - return 0; + /* The mount is in some unhappy state now, let's unfreeze any waiting clients */ + if (IN_SET(MOUNT(other)->state, + MOUNT_DEAD, MOUNT_UNMOUNTING, + MOUNT_MOUNTING_SIGTERM, MOUNT_MOUNTING_SIGKILL, + MOUNT_REMOUNTING_SIGTERM, MOUNT_REMOUNTING_SIGKILL, + MOUNT_UNMOUNTING_SIGTERM, MOUNT_UNMOUNTING_SIGKILL, + MOUNT_FAILED)) { + + (void) automount_send_ready(a, a->tokens, -ENODEV); + + automount_set_state(a, AUTOMOUNT_WAITING); + } } static void automount_enter_waiting(Automount *a) { @@ -699,6 +698,15 @@ static int automount_start_expire(Automount *a) { return 0; } +static void automount_stop_expire(Automount *a) { + assert(a); + + if (!a->expire_event_source) + return; + + (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF); +} + static void automount_enter_runnning(Automount *a) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; struct stat st; @@ -751,6 +759,7 @@ fail: static int automount_start(Unit *u) { Automount *a = AUTOMOUNT(u); Unit *trigger; + int r; assert(a); assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED); @@ -766,6 +775,12 @@ static int automount_start(Unit *u) { return -ENOENT; } + r = unit_start_limit_test(u); + if (r < 0) { + automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT); + return r; + } + a->result = AUTOMOUNT_SUCCESS; automount_enter_waiting(a); return 1; @@ -958,7 +973,7 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo case autofs_ptype_expire_direct: log_unit_debug(UNIT(a), "Got direct umount request on %s", a->where); - (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF); + automount_stop_expire(a); r = set_ensure_allocated(&a->expire_tokens, NULL); if (r < 0) { @@ -1037,7 +1052,9 @@ static bool automount_supported(void) { static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = { [AUTOMOUNT_SUCCESS] = "success", - [AUTOMOUNT_FAILURE_RESOURCES] = "resources" + [AUTOMOUNT_FAILURE_RESOURCES] = "resources", + [AUTOMOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit", + [AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT] = "mount-start-limit-hit", }; DEFINE_STRING_TABLE_LOOKUP(automount_result, AutomountResult); @@ -1050,9 +1067,6 @@ const UnitVTable automount_vtable = { "Automount\0" "Install\0", - .no_alias = true, - .no_instances = true, - .init = automount_init, .load = automount_load, .done = automount_done, @@ -1072,6 +1086,8 @@ const UnitVTable automount_vtable = { .check_gc = automount_check_gc, + .trigger_notify = automount_trigger_notify, + .reset_failed = automount_reset_failed, .bus_vtable = bus_automount_vtable, diff --git a/src/core/automount.h b/src/core/automount.h index cf5b1cf994..76a201178e 100644 --- a/src/core/automount.h +++ b/src/core/automount.h @@ -26,6 +26,8 @@ typedef struct Automount Automount; typedef enum AutomountResult { AUTOMOUNT_SUCCESS, AUTOMOUNT_FAILURE_RESOURCES, + AUTOMOUNT_FAILURE_START_LIMIT_HIT, + AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT, _AUTOMOUNT_RESULT_MAX, _AUTOMOUNT_RESULT_INVALID = -1 } AutomountResult; @@ -53,7 +55,5 @@ struct Automount { extern const UnitVTable automount_vtable; -int automount_update_mount(Automount *a, MountState old_state, MountState state); - const char* automount_result_to_string(AutomountResult i) _const_; AutomountResult automount_result_from_string(const char *s) _pure_; diff --git a/src/core/busname.c b/src/core/busname.c index f4f433340c..f03a95c24e 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -607,6 +607,7 @@ fail: static int busname_start(Unit *u) { BusName *n = BUSNAME(u); + int r; assert(n); @@ -632,6 +633,12 @@ static int busname_start(Unit *u) { assert(IN_SET(n->state, BUSNAME_DEAD, BUSNAME_FAILED)); + r = unit_start_limit_test(u); + if (r < 0) { + busname_enter_dead(n, BUSNAME_FAILURE_START_LIMIT_HIT); + return r; + } + n->result = BUSNAME_SUCCESS; busname_enter_making(n); @@ -1014,6 +1021,7 @@ static const char* const busname_result_table[_BUSNAME_RESULT_MAX] = { [BUSNAME_FAILURE_EXIT_CODE] = "exit-code", [BUSNAME_FAILURE_SIGNAL] = "signal", [BUSNAME_FAILURE_CORE_DUMP] = "core-dump", + [BUSNAME_FAILURE_START_LIMIT_HIT] = "start-limit-hit", [BUSNAME_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit", }; @@ -1028,9 +1036,6 @@ const UnitVTable busname_vtable = { "Install\0", .private_section = "BusName", - .no_alias = true, - .no_instances = true, - .init = busname_init, .done = busname_done, .load = busname_load, diff --git a/src/core/busname.h b/src/core/busname.h index 52c4055dbb..a8562db458 100644 --- a/src/core/busname.h +++ b/src/core/busname.h @@ -32,6 +32,7 @@ typedef enum BusNameResult { BUSNAME_FAILURE_EXIT_CODE, BUSNAME_FAILURE_SIGNAL, BUSNAME_FAILURE_CORE_DUMP, + BUSNAME_FAILURE_START_LIMIT_HIT, BUSNAME_FAILURE_SERVICE_START_LIMIT_HIT, _BUSNAME_RESULT_MAX, _BUSNAME_RESULT_INVALID = -1 diff --git a/src/core/device.c b/src/core/device.c index d01bec53d8..16e56efcc3 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -841,8 +841,6 @@ const UnitVTable device_vtable = { "Device\0" "Install\0", - .no_instances = true, - .init = device_init, .done = device_done, .load = unit_load_fragment_and_dropin_optional, diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index c4566f7709..1a8c03904c 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -3427,10 +3427,10 @@ int config_parse_protect_system( #define FOLLOW_MAX 8 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { + char *id = NULL; unsigned c = 0; int fd, r; FILE *f; - char *id = NULL; assert(filename); assert(*filename); @@ -3452,7 +3452,6 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { * the names of this unit, but only if it is a valid * unit name. */ name = basename(*filename); - if (unit_name_is_valid(name, UNIT_NAME_ANY)) { id = set_get(names, name); @@ -3492,6 +3491,7 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { *_f = f; *_final = id; + return 0; } @@ -3552,13 +3552,13 @@ static int merge_by_names(Unit **u, Set *names, const char *id) { } static int load_from_path(Unit *u, const char *path) { - int r; _cleanup_set_free_free_ Set *symlink_names = NULL; _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *filename = NULL; char *id = NULL; Unit *merged; struct stat st; + int r; assert(u); assert(path); @@ -3597,18 +3597,14 @@ static int load_from_path(Unit *u, const char *path) { r = -ENOENT; else r = open_follow(&filename, &f, symlink_names, &id); + if (r >= 0) + break; + filename = mfree(filename); + if (r != -ENOENT) + return r; - if (r < 0) { - filename = mfree(filename); - if (r != -ENOENT) - return r; - - /* Empty the symlink names for the next run */ - set_clear_free(symlink_names); - continue; - } - - break; + /* Empty the symlink names for the next run */ + set_clear_free(symlink_names); } } @@ -3616,6 +3612,11 @@ static int load_from_path(Unit *u, const char *path) { /* Hmm, no suitable file found? */ return 0; + if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) { + log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id); + return -ELOOP; + } + merged = u; r = merge_by_names(&merged, symlink_names, id); if (r < 0) diff --git a/src/core/mount.c b/src/core/mount.c index cc07873b24..5a8c26b9e1 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -584,23 +584,6 @@ static int mount_load(Unit *u) { return mount_verify(m); } -static int mount_notify_automount(Mount *m, MountState old_state, MountState state) { - Unit *p; - int r; - Iterator i; - - assert(m); - - SET_FOREACH(p, UNIT(m)->dependencies[UNIT_TRIGGERED_BY], i) - if (p->type == UNIT_AUTOMOUNT) { - r = automount_update_mount(AUTOMOUNT(p), old_state, state); - if (r < 0) - return r; - } - - return 0; -} - static void mount_set_state(Mount *m, MountState state) { MountState old_state; assert(m); @@ -624,8 +607,6 @@ static void mount_set_state(Mount *m, MountState state) { m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID; } - mount_notify_automount(m, old_state, state); - if (state != old_state) log_unit_debug(UNIT(m), "Changed %s -> %s", mount_state_to_string(old_state), mount_state_to_string(state)); @@ -984,6 +965,7 @@ fail: static int mount_start(Unit *u) { Mount *m = MOUNT(u); + int r; assert(m); @@ -1002,6 +984,12 @@ static int mount_start(Unit *u) { assert(m->state == MOUNT_DEAD || m->state == MOUNT_FAILED); + r = unit_start_limit_test(u); + if (r < 0) { + mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT); + return r; + } + m->result = MOUNT_SUCCESS; m->reload_result = MOUNT_SUCCESS; m->reset_cpu_usage = true; @@ -1821,7 +1809,8 @@ static const char* const mount_result_table[_MOUNT_RESULT_MAX] = { [MOUNT_FAILURE_TIMEOUT] = "timeout", [MOUNT_FAILURE_EXIT_CODE] = "exit-code", [MOUNT_FAILURE_SIGNAL] = "signal", - [MOUNT_FAILURE_CORE_DUMP] = "core-dump" + [MOUNT_FAILURE_CORE_DUMP] = "core-dump", + [MOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit", }; DEFINE_STRING_TABLE_LOOKUP(mount_result, MountResult); @@ -1839,9 +1828,6 @@ const UnitVTable mount_vtable = { "Install\0", .private_section = "Mount", - .no_alias = true, - .no_instances = true, - .init = mount_init, .load = mount_load, .done = mount_done, diff --git a/src/core/mount.h b/src/core/mount.h index 3b343c6b1f..da529c44f4 100644 --- a/src/core/mount.h +++ b/src/core/mount.h @@ -39,6 +39,7 @@ typedef enum MountResult { MOUNT_FAILURE_EXIT_CODE, MOUNT_FAILURE_SIGNAL, MOUNT_FAILURE_CORE_DUMP, + MOUNT_FAILURE_START_LIMIT_HIT, _MOUNT_RESULT_MAX, _MOUNT_RESULT_INVALID = -1 } MountResult; diff --git a/src/core/path.c b/src/core/path.c index 5e7b3eb234..0dd0d375d8 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -560,6 +560,7 @@ static void path_mkdir(Path *p) { static int path_start(Unit *u) { Path *p = PATH(u); Unit *trigger; + int r; assert(p); assert(p->state == PATH_DEAD || p->state == PATH_FAILED); @@ -570,6 +571,12 @@ static int path_start(Unit *u) { return -ENOENT; } + r = unit_start_limit_test(u); + if (r < 0) { + path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT); + return r; + } + path_mkdir(p); p->result = PATH_SUCCESS; @@ -739,6 +746,7 @@ DEFINE_STRING_TABLE_LOOKUP(path_type, PathType); static const char* const path_result_table[_PATH_RESULT_MAX] = { [PATH_SUCCESS] = "success", [PATH_FAILURE_RESOURCES] = "resources", + [PATH_FAILURE_START_LIMIT_HIT] = "start-limit-hit", }; DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult); diff --git a/src/core/path.h b/src/core/path.h index bbbcebd78e..4230c8fb99 100644 --- a/src/core/path.h +++ b/src/core/path.h @@ -62,6 +62,7 @@ static inline bool path_spec_owns_inotify_fd(PathSpec *s, int fd) { typedef enum PathResult { PATH_SUCCESS, PATH_FAILURE_RESOURCES, + PATH_FAILURE_START_LIMIT_HIT, _PATH_RESULT_MAX, _PATH_RESULT_INVALID = -1 } PathResult; diff --git a/src/core/scope.c b/src/core/scope.c index 7078d1f7e9..238f63a729 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -569,8 +569,6 @@ const UnitVTable scope_vtable = { "Install\0", .private_section = "Scope", - .no_alias = true, - .no_instances = true, .can_transient = true, .init = scope_init, diff --git a/src/core/service.c b/src/core/service.c index f7a3fcf2b9..7ebabca5d6 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1957,6 +1957,7 @@ fail: static int service_start(Unit *u) { Service *s = SERVICE(u); + int r; assert(s); @@ -1983,6 +1984,13 @@ static int service_start(Unit *u) { assert(IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED)); + /* Make sure we don't enter a busy loop of some kind. */ + r = unit_start_limit_test(u); + if (r < 0) { + service_enter_dead(s, SERVICE_FAILURE_START_LIMIT_HIT, false); + return r; + } + s->result = SERVICE_SUCCESS; s->reload_result = SERVICE_SUCCESS; s->main_pid_known = false; @@ -3266,6 +3274,7 @@ static const char* const service_result_table[_SERVICE_RESULT_MAX] = { [SERVICE_FAILURE_SIGNAL] = "signal", [SERVICE_FAILURE_CORE_DUMP] = "core-dump", [SERVICE_FAILURE_WATCHDOG] = "watchdog", + [SERVICE_FAILURE_START_LIMIT_HIT] = "start-limit-hit", }; DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult); diff --git a/src/core/service.h b/src/core/service.h index c7f1e81bdb..4af3d40439 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -86,6 +86,7 @@ typedef enum ServiceResult { SERVICE_FAILURE_SIGNAL, SERVICE_FAILURE_CORE_DUMP, SERVICE_FAILURE_WATCHDOG, + SERVICE_FAILURE_START_LIMIT_HIT, _SERVICE_RESULT_MAX, _SERVICE_RESULT_INVALID = -1 } ServiceResult; diff --git a/src/core/slice.c b/src/core/slice.c index 63a77c9bca..c7700b8857 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -309,8 +309,6 @@ const UnitVTable slice_vtable = { "Install\0", .private_section = "Slice", - .no_alias = true, - .no_instances = true, .can_transient = true, .init = slice_init, diff --git a/src/core/socket.c b/src/core/socket.c index 7eeed068bd..d3d4866fe6 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -2057,6 +2057,7 @@ fail: static int socket_start(Unit *u) { Socket *s = SOCKET(u); + int r; assert(s); @@ -2101,6 +2102,12 @@ static int socket_start(Unit *u) { assert(s->state == SOCKET_DEAD || s->state == SOCKET_FAILED); + r = unit_start_limit_test(u); + if (r < 0) { + socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT); + return r; + } + s->result = SOCKET_SUCCESS; s->reset_cpu_usage = true; @@ -2735,17 +2742,26 @@ static void socket_trigger_notify(Unit *u, Unit *other) { assert(u); assert(other); - /* Don't propagate state changes from the service if we are - already down or accepting connections */ - if (!IN_SET(s->state, SOCKET_RUNNING, SOCKET_LISTENING) || s->accept) + /* Filter out invocations with bogus state */ + if (other->load_state != UNIT_LOADED || other->type != UNIT_SERVICE) + return; + + /* Don't propagate state changes from the service if we are already down */ + if (!IN_SET(s->state, SOCKET_RUNNING, SOCKET_LISTENING)) + return; + + /* We don't care for the service state if we are in Accept=yes mode */ + if (s->accept) return; + /* Propagate start limit hit state */ if (other->start_limit_hit) { socket_enter_stop_pre(s, SOCKET_FAILURE_SERVICE_START_LIMIT_HIT); return; } - if (other->load_state != UNIT_LOADED || other->type != UNIT_SERVICE) + /* Don't propagate anything if there's still a job queued */ + if (other->job) return; if (IN_SET(SERVICE(other)->state, @@ -2818,6 +2834,7 @@ static const char* const socket_result_table[_SOCKET_RESULT_MAX] = { [SOCKET_FAILURE_EXIT_CODE] = "exit-code", [SOCKET_FAILURE_SIGNAL] = "signal", [SOCKET_FAILURE_CORE_DUMP] = "core-dump", + [SOCKET_FAILURE_START_LIMIT_HIT] = "start-limit-hit", [SOCKET_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit", [SOCKET_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit" }; diff --git a/src/core/socket.h b/src/core/socket.h index 2a4b1bb674..0f1ac69c6f 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -52,6 +52,7 @@ typedef enum SocketResult { SOCKET_FAILURE_EXIT_CODE, SOCKET_FAILURE_SIGNAL, SOCKET_FAILURE_CORE_DUMP, + SOCKET_FAILURE_START_LIMIT_HIT, SOCKET_FAILURE_TRIGGER_LIMIT_HIT, SOCKET_FAILURE_SERVICE_START_LIMIT_HIT, _SOCKET_RESULT_MAX, diff --git a/src/core/swap.c b/src/core/swap.c index d8802470d2..a532b15be8 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -814,6 +814,7 @@ fail: static int swap_start(Unit *u) { Swap *s = SWAP(u), *other; + int r; assert(s); @@ -842,6 +843,12 @@ static int swap_start(Unit *u) { if (UNIT(other)->job && UNIT(other)->job->state == JOB_RUNNING) return -EAGAIN; + r = unit_start_limit_test(u); + if (r < 0) { + swap_enter_dead(s, SWAP_FAILURE_START_LIMIT_HIT); + return r; + } + s->result = SWAP_SUCCESS; s->reset_cpu_usage = true; @@ -1447,7 +1454,8 @@ static const char* const swap_result_table[_SWAP_RESULT_MAX] = { [SWAP_FAILURE_TIMEOUT] = "timeout", [SWAP_FAILURE_EXIT_CODE] = "exit-code", [SWAP_FAILURE_SIGNAL] = "signal", - [SWAP_FAILURE_CORE_DUMP] = "core-dump" + [SWAP_FAILURE_CORE_DUMP] = "core-dump", + [SWAP_FAILURE_START_LIMIT_HIT] = "start-limit-hit", }; DEFINE_STRING_TABLE_LOOKUP(swap_result, SwapResult); @@ -1465,9 +1473,6 @@ const UnitVTable swap_vtable = { "Install\0", .private_section = "Swap", - .no_alias = true, - .no_instances = true, - .init = swap_init, .load = swap_load, .done = swap_done, diff --git a/src/core/swap.h b/src/core/swap.h index ac7a63d81b..fbf66debdc 100644 --- a/src/core/swap.h +++ b/src/core/swap.h @@ -38,6 +38,7 @@ typedef enum SwapResult { SWAP_FAILURE_EXIT_CODE, SWAP_FAILURE_SIGNAL, SWAP_FAILURE_CORE_DUMP, + SWAP_FAILURE_START_LIMIT_HIT, _SWAP_RESULT_MAX, _SWAP_RESULT_INVALID = -1 } SwapResult; diff --git a/src/core/timer.c b/src/core/timer.c index f8f5f4b2e4..3206296f09 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -599,6 +599,7 @@ static int timer_start(Unit *u) { Timer *t = TIMER(u); TimerValue *v; Unit *trigger; + int r; assert(t); assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED); @@ -609,6 +610,12 @@ static int timer_start(Unit *u) { return -ENOENT; } + r = unit_start_limit_test(u); + if (r < 0) { + timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT); + return r; + } + t->last_trigger = DUAL_TIMESTAMP_NULL; /* Reenable all timers that depend on unit activation time */ @@ -808,7 +815,8 @@ DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase); static const char* const timer_result_table[_TIMER_RESULT_MAX] = { [TIMER_SUCCESS] = "success", - [TIMER_FAILURE_RESOURCES] = "resources" + [TIMER_FAILURE_RESOURCES] = "resources", + [TIMER_FAILURE_START_LIMIT_HIT] = "start-limit-hit", }; DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult); diff --git a/src/core/timer.h b/src/core/timer.h index 698e6da2f5..9c4b64f898 100644 --- a/src/core/timer.h +++ b/src/core/timer.h @@ -48,6 +48,7 @@ typedef struct TimerValue { typedef enum TimerResult { TIMER_SUCCESS, TIMER_FAILURE_RESOURCES, + TIMER_FAILURE_START_LIMIT_HIT, _TIMER_RESULT_MAX, _TIMER_RESULT_INVALID = -1 } TimerResult; diff --git a/src/core/unit.c b/src/core/unit.c index 81cd7ee2b8..8153515e89 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -193,7 +193,7 @@ int unit_add_name(Unit *u, const char *text) { if (r < 0) return r; - if (i && unit_vtable[t]->no_instances) + if (i && !unit_type_may_template(t)) return -EINVAL; /* Ensure that this unit is either instanced or not instanced, @@ -202,7 +202,7 @@ int unit_add_name(Unit *u, const char *text) { if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i) return -EINVAL; - if (unit_vtable[t]->no_alias && !set_isempty(u->names)) + if (!unit_type_may_alias(t) && !set_isempty(u->names)) return -EEXIST; if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES) @@ -720,6 +720,9 @@ int unit_merge(Unit *u, Unit *other) { if (!u->instance != !other->instance) return -EINVAL; + if (!unit_type_may_alias(u->type)) /* Merging only applies to unit names that support aliases */ + return -EEXIST; + if (other->load_state != UNIT_STUB && other->load_state != UNIT_NOT_FOUND) return -EEXIST; @@ -776,9 +779,9 @@ int unit_merge(Unit *u, Unit *other) { } int unit_merge_by_name(Unit *u, const char *name) { + _cleanup_free_ char *s = NULL; Unit *other; int r; - _cleanup_free_ char *s = NULL; assert(u); assert(name); @@ -1459,7 +1462,7 @@ void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t) { unit_status_print_starting_stopping(u, t); } -static int unit_start_limit_test(Unit *u) { +int unit_start_limit_test(Unit *u) { assert(u); if (ratelimit_test(&u->start_limit)) { @@ -1485,7 +1488,6 @@ static int unit_start_limit_test(Unit *u) { int unit_start(Unit *u) { UnitActiveState state; Unit *following; - int r; assert(u); @@ -1538,11 +1540,6 @@ int unit_start(Unit *u) { if (!UNIT_VTABLE(u)->start) return -EBADR; - /* Make sure we don't enter a busy loop of some kind. */ - r = unit_start_limit_test(u); - if (r < 0) - return r; - /* We don't suppress calls to ->start() here when we are * already starting, to allow this request to be used as a * "hurry up" call, for example when the unit is in some "auto @@ -2948,7 +2945,7 @@ int unit_coldplug(Unit *u) { return 0; } -static bool fragment_mtime_changed(const char *path, usec_t mtime) { +static bool fragment_mtime_newer(const char *path, usec_t mtime) { struct stat st; if (!path) @@ -2960,7 +2957,7 @@ static bool fragment_mtime_changed(const char *path, usec_t mtime) { if (mtime > 0) /* For non-empty files check the mtime */ - return timespec_load(&st.st_mtim) != mtime; + return timespec_load(&st.st_mtim) > mtime; else if (!null_or_empty(&st)) /* For masked files check if they are still so */ return true; @@ -2971,32 +2968,24 @@ static bool fragment_mtime_changed(const char *path, usec_t mtime) { bool unit_need_daemon_reload(Unit *u) { _cleanup_strv_free_ char **t = NULL; char **path; - unsigned loaded_cnt, current_cnt; assert(u); - if (fragment_mtime_changed(u->fragment_path, u->fragment_mtime) || - fragment_mtime_changed(u->source_path, u->source_mtime)) + if (fragment_mtime_newer(u->fragment_path, u->fragment_mtime)) return true; - (void) unit_find_dropin_paths(u, &t); - loaded_cnt = strv_length(t); - current_cnt = strv_length(u->dropin_paths); - - if (loaded_cnt == current_cnt) { - if (loaded_cnt == 0) - return false; + if (fragment_mtime_newer(u->source_path, u->source_mtime)) + return true; - if (strv_overlap(u->dropin_paths, t)) { - STRV_FOREACH(path, u->dropin_paths) - if (fragment_mtime_changed(*path, u->dropin_mtime)) - return true; + (void) unit_find_dropin_paths(u, &t); + if (!strv_equal(u->dropin_paths, t)) + return true; - return false; - } - } + STRV_FOREACH(path, u->dropin_paths) + if (fragment_mtime_newer(*path, u->dropin_mtime)) + return true; - return true; + return false; } void unit_reset_failed(Unit *u) { diff --git a/src/core/unit.h b/src/core/unit.h index 5909652976..f14972728e 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -416,12 +416,6 @@ struct UnitVTable { /* The strings to print in status messages */ UnitStatusMessageFormats status_message_formats; - /* Can units of this type have multiple names? */ - bool no_alias:1; - - /* Instances make no sense for this type */ - bool no_instances:1; - /* True if transient units of this type are OK */ bool can_transient:1; }; @@ -617,6 +611,8 @@ static inline bool unit_supported(Unit *u) { void unit_warn_if_dir_nonempty(Unit *u, const char* where); int unit_fail_if_symlink(Unit *u, const char* where); +int unit_start_limit_test(Unit *u); + /* Macros which append UNIT= or USER_UNIT= to the message */ #define log_unit_full(unit, level, error, ...) \ diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 6f576b5ecf..343e3b1817 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -379,6 +379,7 @@ static int add_mount( } fprintf(f, + "\n" "[Automount]\n" "Where=%s\n", where); diff --git a/src/import/pull-common.c b/src/import/pull-common.c index d301d4d79e..dc4e4667a9 100644 --- a/src/import/pull-common.c +++ b/src/import/pull-common.c @@ -330,7 +330,7 @@ int pull_verify(PullJob *main_job, _cleanup_close_ int sig_file = -1; const char *p, *line; char sig_file_path[] = "/tmp/sigXXXXXX", gpg_home[] = "/tmp/gpghomeXXXXXX"; - _cleanup_sigkill_wait_ pid_t pid = 0; + _cleanup_(sigkill_waitp) pid_t pid = 0; bool gpg_home_created = false; int r; diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index c9ce5c73be..7504326bff 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -3160,7 +3160,7 @@ int journal_file_open( goto fail; } - /* The file is opened now successfully, thus we take possesion of any passed in fd. */ + /* The file is opened now successfully, thus we take possession of any passed in fd. */ f->close_fd = true; *ret = f; @@ -3293,7 +3293,7 @@ int journal_file_open_reliably( /* btrfs doesn't cope well with our write pattern and * fragments heavily. Let's defrag all files we rotate */ - (void) chattr_path(p, false, FS_NOCOW_FL); + (void) chattr_path(p, 0, FS_NOCOW_FL); (void) btrfs_defrag(p); log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname); diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 27c1dd346f..1cea68ad42 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -1942,7 +1942,7 @@ _public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fd return 0; fail: - /* If we fail, make sure we don't take possession of the files we managed to make use of successfuly, and they + /* If we fail, make sure we don't take possession of the files we managed to make use of successfully, and they * remain open */ ORDERED_HASHMAP_FOREACH(f, j->files, iterator) f->close_fd = false; diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c index f56798674c..86d8dee867 100644 --- a/src/libsystemd/sd-netlink/netlink-message.c +++ b/src/libsystemd/sd-netlink/netlink-message.c @@ -207,11 +207,11 @@ static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *da * and gives us too little data (so don't do that) */ padding = mempcpy(RTA_DATA(rta), data, data_length); - else { + + else /* if no data was passed, make sure we still initialize the padding note that we can have data_length > 0 (used by some containers) */ padding = RTA_DATA(rta); - } /* make sure also the padding at the end of the message is initialized */ padding_length = (uint8_t*)m->hdr + message_length - (uint8_t*)padding; @@ -343,7 +343,7 @@ int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, c assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); - r = add_rtattr(m, type, &data, len); + r = add_rtattr(m, type, data, len); if (r < 0) return r; diff --git a/src/libsystemd/sd-netlink/rtnl-message.c b/src/libsystemd/sd-netlink/rtnl-message.c index f251536a89..09240c7b2a 100644 --- a/src/libsystemd/sd-netlink/rtnl-message.c +++ b/src/libsystemd/sd-netlink/rtnl-message.c @@ -111,6 +111,20 @@ int sd_rtnl_message_route_get_flags(sd_netlink_message *m, unsigned *flags) { return 0; } +int sd_rtnl_message_route_set_table(sd_netlink_message *m, unsigned char table) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + rtm->rtm_table = table; + + return 0; +} + int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family) { struct rtmsg *rtm; @@ -126,6 +140,20 @@ int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family) { return 0; } +int sd_rtnl_message_route_set_family(sd_netlink_message *m, int family) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + rtm->rtm_family = family; + + return 0; +} + int sd_rtnl_message_route_get_protocol(sd_netlink_message *m, unsigned char *protocol) { struct rtmsg *rtm; diff --git a/src/locale/language-fallback-map b/src/locale/language-fallback-map index 6aadda091a..d0b02a6b98 100644 --- a/src/locale/language-fallback-map +++ b/src/locale/language-fallback-map @@ -3,6 +3,10 @@ en_AU en_AU:en_GB en_IE en_IE:en_GB en_NZ en_NZ:en_GB en_ZA en_ZA:en_GB +fr_BE fr_BE:fr_FR +fr_CA fr_CA:fr_FR +fr_CH fr_CH:fr_FR +fr_LU fr_LU:fr_FR it_CH it_CH:it_IT mai_IN mai:hi nds_DE nds:de diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c index b764bc43a0..0eed9b81bb 100644 --- a/src/machine/image-dbus.c +++ b/src/machine/image-dbus.c @@ -20,9 +20,11 @@ #include "alloc-util.h" #include "bus-label.h" #include "bus-util.h" +#include "fd-util.h" #include "image-dbus.h" #include "io-util.h" #include "machine-image.h" +#include "process-util.h" #include "strv.h" #include "user-util.h" @@ -33,13 +35,18 @@ int bus_image_method_remove( void *userdata, sd_bus_error *error) { + _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; Image *image = userdata; Manager *m = image->userdata; + pid_t child; int r; assert(message); assert(image); + if (m->n_operations >= OPERATIONS_MAX) + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations."); + r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, @@ -54,11 +61,35 @@ int bus_image_method_remove( if (r == 0) return 1; /* Will call us back */ - r = image_remove(image); - if (r < 0) + if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); + + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + if (child == 0) { + errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); + + r = image_remove(image); + if (r < 0) { + (void) write(errno_pipe_fd[1], &r, sizeof(r)); + _exit(EXIT_FAILURE); + } + + _exit(EXIT_SUCCESS); + } + + errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); + + r = operation_new(m, NULL, child, message, errno_pipe_fd[0]); + if (r < 0) { + (void) sigkill_wait(child); return r; + } - return sd_bus_reply_method_return(message, NULL); + errno_pipe_fd[0] = -1; + + return 1; } int bus_image_method_rename( @@ -107,13 +138,19 @@ int bus_image_method_clone( void *userdata, sd_bus_error *error) { + _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; Image *image = userdata; Manager *m = image->userdata; const char *new_name; int r, read_only; + pid_t child; assert(message); assert(image); + assert(m); + + if (m->n_operations >= OPERATIONS_MAX) + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations."); r = sd_bus_message_read(message, "sb", &new_name, &read_only); if (r < 0) @@ -136,13 +173,35 @@ int bus_image_method_clone( if (r == 0) return 1; /* Will call us back */ - r = image_clone(image, new_name, read_only); - if (r == -EOPNOTSUPP) - return sd_bus_reply_method_errnof(message, r, "Image cloning is currently only supported on btrfs file systems."); - if (r < 0) + if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); + + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + if (child == 0) { + errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); + + r = image_clone(image, new_name, read_only); + if (r < 0) { + (void) write(errno_pipe_fd[1], &r, sizeof(r)); + _exit(EXIT_FAILURE); + } + + _exit(EXIT_SUCCESS); + } + + errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); + + r = operation_new(m, NULL, child, message, errno_pipe_fd[0]); + if (r < 0) { + (void) sigkill_wait(child); return r; + } - return sd_bus_reply_method_return(message, NULL); + errno_pipe_fd[0] = -1; + + return 1; } int bus_image_method_mark_read_only( diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index 5121bfdd18..7b9aa66d63 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -1085,52 +1085,11 @@ finish: return r; } -static int machine_operation_done(sd_event_source *s, const siginfo_t *si, void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - MachineOperation *o = userdata; - int r; - - assert(o); - assert(si); - - o->pid = 0; - - if (si->si_code != CLD_EXITED) { - r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child died abnormally."); - goto fail; - } - - if (si->si_status != EXIT_SUCCESS) { - if (read(o->errno_fd, &r, sizeof(r)) == sizeof(r)) - r = sd_bus_error_set_errnof(&error, r, "%m"); - else - r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed."); - - goto fail; - } - - r = sd_bus_reply_method_return(o->message, NULL); - if (r < 0) - log_error_errno(r, "Failed to reply to message: %m"); - - machine_operation_unref(o); - return 0; - -fail: - r = sd_bus_reply_method_error(o->message, &error); - if (r < 0) - log_error_errno(r, "Failed to reply to message: %m"); - - machine_operation_unref(o); - return 0; -} - int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) { const char *src, *dest, *host_path, *container_path, *host_basename, *host_dirname, *container_basename, *container_dirname; _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; _cleanup_close_ int hostfd = -1; Machine *m = userdata; - MachineOperation *o; bool copy_from; pid_t child; char *t; @@ -1139,7 +1098,7 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro assert(message); assert(m); - if (m->n_operations >= MACHINE_OPERATIONS_MAX) + if (m->manager->n_operations >= OPERATIONS_MAX) return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing copies."); if (m->class != MACHINE_CONTAINER) @@ -1249,27 +1208,14 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); - /* Copying might take a while, hence install a watch the - * child, and return */ + /* Copying might take a while, hence install a watch on the child, and return */ - o = new0(MachineOperation, 1); - if (!o) - return log_oom(); - - o->pid = child; - o->message = sd_bus_message_ref(message); - o->errno_fd = errno_pipe_fd[0]; - errno_pipe_fd[0] = -1; - - r = sd_event_add_child(m->manager->event, &o->event_source, child, WEXITED, machine_operation_done, o); + r = operation_new(m->manager, m, child, message, errno_pipe_fd[0]); if (r < 0) { - machine_operation_unref(o); - return log_oom(); + (void) sigkill_wait(child); + return r; } - - LIST_PREPEND(operations, m->operations, o); - m->n_operations++; - o->machine = m; + errno_pipe_fd[0] = -1; return 1; } diff --git a/src/machine/machine.c b/src/machine/machine.c index 7d4270a8ff..c1fae57084 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -89,7 +89,7 @@ void machine_free(Machine *m) { assert(m); while (m->operations) - machine_operation_unref(m->operations); + operation_free(m->operations); if (m->in_gc_queue) LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m); @@ -596,28 +596,6 @@ int machine_open_terminal(Machine *m, const char *path, int mode) { } } -MachineOperation *machine_operation_unref(MachineOperation *o) { - if (!o) - return NULL; - - sd_event_source_unref(o->event_source); - - safe_close(o->errno_fd); - - if (o->pid > 1) - (void) kill(o->pid, SIGKILL); - - sd_bus_message_unref(o->message); - - if (o->machine) { - LIST_REMOVE(operations, o->machine->operations, o); - o->machine->n_operations--; - } - - free(o); - return NULL; -} - void machine_release_unit(Machine *m) { assert(m); diff --git a/src/machine/machine.h b/src/machine/machine.h index 1d8cc5911a..e5d75361a9 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -20,11 +20,11 @@ ***/ typedef struct Machine Machine; -typedef struct MachineOperation MachineOperation; typedef enum KillWho KillWho; #include "list.h" #include "machined.h" +#include "operation.h" typedef enum MachineState { MACHINE_OPENING, /* Machine is being registered */ @@ -49,17 +49,6 @@ enum KillWho { _KILL_WHO_INVALID = -1 }; -#define MACHINE_OPERATIONS_MAX 64 - -struct MachineOperation { - Machine *machine; - pid_t pid; - sd_bus_message *message; - int errno_fd; - sd_event_source *event_source; - LIST_FIELDS(MachineOperation, operations); -}; - struct Machine { Manager *manager; @@ -88,10 +77,9 @@ struct Machine { int *netif; unsigned n_netif; - LIST_FIELDS(Machine, gc_queue); + LIST_HEAD(Operation, operations); - MachineOperation *operations; - unsigned n_operations; + LIST_FIELDS(Machine, gc_queue); }; Machine* machine_new(Manager *manager, MachineClass class, const char *name); @@ -109,8 +97,6 @@ void machine_release_unit(Machine *m); MachineState machine_get_state(Machine *u); -MachineOperation *machine_operation_unref(MachineOperation *o); - const char* machine_class_to_string(MachineClass t) _const_; MachineClass machine_class_from_string(const char *s) _pure_; diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 5a68c4ceb2..1165ab5afa 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -1076,6 +1076,7 @@ static int terminate_machine(int argc, char *argv[], void *userdata) { static int copy_files(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_free_ char *abs_host_path = NULL; char *dest, *host_path, *container_path; sd_bus *bus = userdata; @@ -1099,19 +1100,28 @@ static int copy_files(int argc, char *argv[], void *userdata) { host_path = abs_host_path; } - r = sd_bus_call_method( + r = sd_bus_message_new_method_call( bus, + &m, "org.freedesktop.machine1", "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", - copy_from ? "CopyFromMachine" : "CopyToMachine", - &error, - NULL, + copy_from ? "CopyFromMachine" : "CopyToMachine"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append( + m, "sss", argv[1], copy_from ? container_path : host_path, copy_from ? host_path : container_path); if (r < 0) + return bus_log_create_error(r); + + /* This is a slow operation, hence turn off any method call timeouts */ + r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL); + if (r < 0) return log_error_errno(r, "Failed to copy: %s", bus_error_message(&error, r)); return 0; @@ -1393,7 +1403,6 @@ static int shell_machine(int argc, char *argv[], void *userdata) { } static int remove_image(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = userdata; int r, i; @@ -1402,19 +1411,27 @@ static int remove_image(int argc, char *argv[], void *userdata) { polkit_agent_open_if_enabled(); for (i = 1; i < argc; i++) { - r = sd_bus_call_method( + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + + r = sd_bus_message_new_method_call( bus, + &m, "org.freedesktop.machine1", "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", - "RemoveImage", - &error, - NULL, - "s", argv[i]); - if (r < 0) { - log_error("Could not remove image: %s", bus_error_message(&error, -r)); - return r; - } + "RemoveImage"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "s", argv[i]); + if (r < 0) + return bus_log_create_error(r); + + /* This is a slow operation, hence turn off any method call timeouts */ + r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL); + if (r < 0) + return log_error_errno(r, "Could not remove image: %s", bus_error_message(&error, r)); } return 0; @@ -1446,24 +1463,30 @@ static int rename_image(int argc, char *argv[], void *userdata) { static int clone_image(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; sd_bus *bus = userdata; int r; polkit_agent_open_if_enabled(); - r = sd_bus_call_method( + r = sd_bus_message_new_method_call( bus, + &m, "org.freedesktop.machine1", "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", - "CloneImage", - &error, - NULL, - "ssb", argv[1], argv[2], arg_read_only); - if (r < 0) { - log_error("Could not clone image: %s", bus_error_message(&error, -r)); - return r; - } + "CloneImage"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "ssb", argv[1], argv[2], arg_read_only); + if (r < 0) + return bus_log_create_error(r); + + /* This is a slow operation, hence turn off any method call timeouts */ + r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL); + if (r < 0) + return log_error_errno(r, "Could not clone image: %s", bus_error_message(&error, r)); return 0; } diff --git a/src/machine/machined.c b/src/machine/machined.c index f2c1966a6b..f7ceb5e603 100644 --- a/src/machine/machined.c +++ b/src/machine/machined.c @@ -70,6 +70,11 @@ void manager_free(Manager *m) { assert(m); + while (m->operations) + operation_free(m->operations); + + assert(m->n_operations == 0); + while ((machine = hashmap_first(m->machines))) machine_free(machine); @@ -336,6 +341,9 @@ int manager_startup(Manager *m) { static bool check_idle(void *userdata) { Manager *m = userdata; + if (m->operations) + return false; + manager_gc(m, true); return hashmap_isempty(m->machines); diff --git a/src/machine/machined.h b/src/machine/machined.h index e7d7dfdceb..7b9b148044 100644 --- a/src/machine/machined.h +++ b/src/machine/machined.h @@ -32,6 +32,7 @@ typedef struct Manager Manager; #include "image-dbus.h" #include "machine-dbus.h" #include "machine.h" +#include "operation.h" struct Manager { sd_event *event; @@ -49,6 +50,9 @@ struct Manager { LIST_HEAD(Machine, machine_gc_queue); Machine *host_machine; + + LIST_HEAD(Operation, operations); + unsigned n_operations; }; Manager *manager_new(void); diff --git a/src/machine/operation.c b/src/machine/operation.c new file mode 100644 index 0000000000..e6ddc41a55 --- /dev/null +++ b/src/machine/operation.c @@ -0,0 +1,131 @@ +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "alloc-util.h" +#include "fd-util.h" +#include "operation.h" +#include "process-util.h" + +static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + Operation *o = userdata; + int r; + + assert(o); + assert(si); + + log_debug("Operating " PID_FMT " is now complete with with code=%s status=%i", + o->pid, + sigchld_code_to_string(si->si_code), si->si_status); + + o->pid = 0; + + if (si->si_code != CLD_EXITED) { + r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child died abnormally."); + goto fail; + } + + if (si->si_status != EXIT_SUCCESS) { + if (read(o->errno_fd, &r, sizeof(r)) == sizeof(r)) + r = sd_bus_error_set_errnof(&error, r, "%m"); + else + r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed."); + + goto fail; + } + + r = sd_bus_reply_method_return(o->message, NULL); + if (r < 0) + log_error_errno(r, "Failed to reply to message: %m"); + + operation_free(o); + return 0; + +fail: + r = sd_bus_reply_method_error(o->message, &error); + if (r < 0) + log_error_errno(r, "Failed to reply to message: %m"); + + operation_free(o); + return 0; +} + +int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd) { + Operation *o; + int r; + + assert(manager); + assert(child > 1); + assert(message); + assert(errno_fd >= 0); + + o = new0(Operation, 1); + if (!o) + return -ENOMEM; + + r = sd_event_add_child(manager->event, &o->event_source, child, WEXITED, operation_done, o); + if (r < 0) { + free(o); + return r; + } + + o->pid = child; + o->message = sd_bus_message_ref(message); + o->errno_fd = errno_fd; + + LIST_PREPEND(operations, manager->operations, o); + manager->n_operations++; + o->manager = manager; + + if (machine) { + LIST_PREPEND(operations_by_machine, machine->operations, o); + o->machine = machine; + } + + log_debug("Started new operation " PID_FMT ".", child); + + /* At this point we took ownership of both the child and the errno file descriptor! */ + + return 0; +} + +Operation *operation_free(Operation *o) { + if (!o) + return NULL; + + sd_event_source_unref(o->event_source); + + safe_close(o->errno_fd); + + if (o->pid > 1) + (void) sigkill_wait(o->pid); + + sd_bus_message_unref(o->message); + + if (o->manager) { + LIST_REMOVE(operations, o->manager->operations, o); + o->manager->n_operations--; + } + + if (o->machine) + LIST_REMOVE(operations_by_machine, o->machine->operations, o); + + free(o); + return NULL; +} diff --git a/src/machine/operation.h b/src/machine/operation.h new file mode 100644 index 0000000000..7ca47bc3af --- /dev/null +++ b/src/machine/operation.h @@ -0,0 +1,47 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <sys/types.h> + +#include "sd-bus.h" +#include "sd-event.h" + +#include "list.h" + +typedef struct Operation Operation; + +#include "machined.h" + +#define OPERATIONS_MAX 64 + +struct Operation { + Manager *manager; + Machine *machine; + pid_t pid; + sd_bus_message *message; + int errno_fd; + sd_event_source *event_source; + LIST_FIELDS(Operation, operations); + LIST_FIELDS(Operation, operations_by_machine); +}; + +int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd); +Operation *operation_free(Operation *o); diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 429319da6b..8b52a1f742 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -774,6 +774,54 @@ int config_parse_label(const char *unit, return 0; } +int config_parse_lifetime(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) { + Network *network = userdata; + _cleanup_address_free_ Address *n = NULL; + unsigned k; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = address_new_static(network, section_line, &n); + if (r < 0) + return r; + + if (STR_IN_SET(rvalue, "forever", "infinity")) { + n->cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME; + n = NULL; + + return 0; + } + + r = safe_atou(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse PreferredLifetime, ignoring: %s", rvalue); + return 0; + } + + if (k != 0) + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid PreferredLifetime value, ignoring: %d", k); + else { + n->cinfo.ifa_prefered = k; + n = NULL; + } + + return 0; +} + bool address_is_ready(const Address *a) { assert(a); diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 3b5285abf0..784ab18b27 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -76,3 +76,4 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free); int config_parse_address(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_broadcast(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_label(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_lifetime(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/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 6bf57cdf99..51e750b299 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -65,12 +65,14 @@ Address.Address, config_parse_address, Address.Peer, config_parse_address, 0, 0 Address.Broadcast, config_parse_broadcast, 0, 0 Address.Label, config_parse_label, 0, 0 +Address.PreferredLifetime, config_parse_lifetime, 0, 0 Route.Gateway, config_parse_gateway, 0, 0 Route.Destination, config_parse_destination, 0, 0 Route.Source, config_parse_destination, 0, 0 Route.Metric, config_parse_route_priority, 0, 0 Route.Scope, config_parse_route_scope, 0, 0 Route.PreferredSource, config_parse_preferred_src, 0, 0 +Route.Table, config_parse_route_table, 0, 0 DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier) DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns) DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp) diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index bda2707e6d..01094b20bd 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -451,6 +451,10 @@ int route_configure(Route *route, Link *link, r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m"); + + r = sd_rtnl_message_route_set_family(req, route->family); + if (r < 0) + return log_error_errno(r, "Could not set route family: %m"); } if (route->dst_prefixlen) { @@ -494,7 +498,26 @@ int route_configure(Route *route, Link *link, r = sd_rtnl_message_route_set_flags(req, route->flags); if (r < 0) - return log_error_errno(r, "Colud not set flags: %m"); + return log_error_errno(r, "Could not set flags: %m"); + + if (route->table != RT_TABLE_DEFAULT) { + + if (route->table < 256) { + r = sd_rtnl_message_route_set_table(req, route->table); + if (r < 0) + return log_error_errno(r, "Could not set route table: %m"); + } else { + + r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC); + if (r < 0) + return log_error_errno(r, "Could not set route table: %m"); + + /* Table attribute to allow allow more than 256. */ + r = sd_netlink_message_append_data(req, RTA_TABLE, &route->table, sizeof(route->table)); + if (r < 0) + return log_error_errno(r, "Could not append RTA_TABLE attribute: %m"); + } + } r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority); if (r < 0) @@ -777,3 +800,42 @@ int config_parse_route_scope(const char *unit, return 0; } + +int config_parse_route_table(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) { + _cleanup_route_free_ Route *n = NULL; + Network *network = userdata; + uint32_t k; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = route_new_static(network, section_line, &n); + if (r < 0) + return r; + + r = safe_atou32(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue); + return 0; + } + + n->table = k; + + n = NULL; + + return 0; +} diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 59843162f0..84d74992c9 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -36,7 +36,7 @@ struct Route { unsigned char protocol; /* RTPROT_* */ unsigned char tos; uint32_t priority; /* note that ip(8) calls this 'metric' */ - unsigned char table; + uint32_t table; unsigned char pref; unsigned flags; @@ -73,3 +73,4 @@ int config_parse_preferred_src(const char *unit, const char *filename, unsigned int config_parse_destination(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_route_priority(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_route_scope(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_route_table(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/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index 706f8c14ed..ea04e58d61 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -810,7 +810,7 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) { switch (t->state) { case DNS_TRANSACTION_SUCCESS: { - /* We found a successfuly reply, merge it into the answer */ + /* We found a successfully reply, merge it into the answer */ r = dns_answer_extend(&q->answer, t->answer); if (r < 0) goto fail; diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 081131ede0..a4a67623e7 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -1804,7 +1804,8 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { * - For unsigned SOA/NS we get the matching DS * - For unsigned CNAME/DNAME/DS we get the parent SOA RR * - For other unsigned RRs we get the matching SOA RR - * - For SOA/NS/DS queries with no matching response RRs, and no NSEC/NSEC3, the parent's SOA RR + * - For SOA/NS queries with no matching response RR, and no NSEC/NSEC3, the DS RR + * - For DS queries with no matching response RRs, and no NSEC/NSEC3, the parent's SOA RR * - For other queries with no matching response RRs, and no NSEC/NSEC3, the SOA RR */ @@ -2038,32 +2039,42 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { return r; if (r > 0) { const char *name; + uint16_t type = 0; name = dns_resource_key_name(t->key); - /* If this was a SOA or NS request, then this - * indicates that we are not at a zone apex, hence ask - * the parent name instead. If this was a DS request, - * then it's signed when the parent zone is signed, - * hence ask the parent in that case, too. */ + /* If this was a SOA or NS request, then check if there's a DS RR for the same domain. Note that this + * could also be used as indication that we are not at a zone apex, but in real world setups there are + * too many broken DNS servers (Hello, incapdns.net!) where non-terminal zones return NXDOMAIN even + * though they have further children. If this was a DS request, then it's signed when the parent zone + * is signed, hence ask the parent SOA in that case. If this was any other RR then ask for the SOA RR, + * to see if that is signed. */ - if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS, DNS_TYPE_DS)) { + if (t->key->type == DNS_TYPE_DS) { r = dns_name_parent(&name); - if (r < 0) - return r; - if (r > 0) - log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS/DS response).", + if (r > 0) { + type = DNS_TYPE_SOA; + log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned empty DS response).", t->id, dns_resource_key_name(t->key)); - else + } else name = NULL; - } else + + } else if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS)) { + + type = DNS_TYPE_DS; + log_debug("Requesting DS to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS response).", + t->id, dns_resource_key_name(t->key)); + + } else { + type = DNS_TYPE_SOA; log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned empty non-SOA/NS/DS response).", t->id, dns_resource_key_name(t->key)); + } if (name) { _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL; - soa = dns_resource_key_new(t->key->class, DNS_TYPE_SOA, name); + soa = dns_resource_key_new(t->key->class, type, name); if (!soa) return -ENOMEM; @@ -2317,11 +2328,12 @@ static int dns_transaction_in_private_tld(DnsTransaction *t, const DnsResourceKe } static int dns_transaction_requires_nsec(DnsTransaction *t) { + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; DnsTransaction *dt; const char *name; + uint16_t type = 0; Iterator i; int r; - char key_str[DNS_RESOURCE_KEY_STRING_MAX]; assert(t); @@ -2355,22 +2367,25 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) { name = dns_resource_key_name(t->key); - if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS, DNS_TYPE_DS)) { + if (t->key->type == DNS_TYPE_DS) { - /* We got a negative reply for this SOA/NS lookup? If - * so, then we are not at a zone apex, and thus should - * look at the result of the parent SOA lookup. - * - * We got a negative reply for this DS lookup? DS RRs - * are signed when their parent zone is signed, hence - * also check the parent SOA in this case. */ + /* We got a negative reply for this DS lookup? DS RRs are signed when their parent zone is signed, + * hence check the parent SOA in this case. */ r = dns_name_parent(&name); if (r < 0) return r; if (r == 0) return true; - } + + type = DNS_TYPE_SOA; + + } else if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS)) + /* We got a negative reply for this SOA/NS lookup? If so, check if there's a DS RR for this */ + type = DNS_TYPE_DS; + else + /* For all other negative replies, check for the SOA lookup */ + type = DNS_TYPE_SOA; /* For all other RRs we check the SOA on the same level to see * if it's signed. */ @@ -2379,7 +2394,7 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) { if (dt->key->class != t->key->class) continue; - if (dt->key->type != DNS_TYPE_SOA) + if (dt->key->type != type) continue; r = dns_name_equal(dns_resource_key_name(dt->key), name); diff --git a/src/shared/install.c b/src/shared/install.c index 931d3e2907..cc36da1853 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -66,8 +66,56 @@ typedef struct { OrderedHashmap *have_processed; } InstallContext; +typedef enum { + PRESET_UNKNOWN, + PRESET_ENABLE, + PRESET_DISABLE, +} PresetAction; + +typedef struct { + char *pattern; + PresetAction action; +} PresetRule; + +typedef struct { + PresetRule *rules; + size_t n_rules; +} Presets; + +static inline void presets_freep(Presets *p) { + size_t i; + + if (!p) + return; + + for (i = 0; i < p->n_rules; i++) + free(p->rules[i].pattern); + + free(p->rules); + p->n_rules = 0; +} + static int unit_file_lookup_state(UnitFileScope scope, const LookupPaths *paths, const char *name, UnitFileState *ret); +bool unit_type_may_alias(UnitType type) { + return IN_SET(type, + UNIT_SERVICE, + UNIT_SOCKET, + UNIT_TARGET, + UNIT_DEVICE, + UNIT_TIMER, + UNIT_PATH); +} + +bool unit_type_may_template(UnitType type) { + return IN_SET(type, + UNIT_SERVICE, + UNIT_SOCKET, + UNIT_TARGET, + UNIT_TIMER, + UNIT_PATH); +} + static int in_search_path(const LookupPaths *p, const char *path) { _cleanup_free_ char *parent = NULL; char **i; @@ -898,6 +946,36 @@ fail: return r; } +static int config_parse_alias( + 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) { + + const char *name; + UnitType type; + + assert(filename); + assert(lvalue); + assert(rvalue); + + name = basename(filename); + type = unit_name_to_type(name); + if (!unit_type_may_alias(type)) + return log_syntax(unit, LOG_WARNING, filename, line, 0, + "Aliases are not allowed for %s units, ignoring.", + unit_type_to_string(type)); + + return config_parse_strv(unit, filename, line, section, section_line, + lvalue, ltype, rvalue, data, userdata); +} + static int config_parse_also( const char *unit, const char *filename, @@ -954,6 +1032,7 @@ static int config_parse_default_instance( void *userdata) { UnitFileInstallInfo *i = data; + const char *name; char *printed; int r; @@ -961,6 +1040,15 @@ static int config_parse_default_instance( assert(lvalue); assert(rvalue); + name = basename(filename); + if (unit_name_is_valid(name, UNIT_NAME_INSTANCE)) + /* When enabling an instance, we might be using a template unit file, + * but we should ignore DefaultInstance silently. */ + return 0; + if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) + return log_syntax(unit, LOG_WARNING, filename, line, 0, + "DefaultInstance only makes sense for template units, ignoring."); + r = install_full_printf(i, rvalue, &printed); if (r < 0) return r; @@ -983,7 +1071,7 @@ static int unit_file_load( SearchFlags flags) { const ConfigTableItem items[] = { - { "Install", "Alias", config_parse_strv, 0, &info->aliases }, + { "Install", "Alias", config_parse_alias, 0, &info->aliases }, { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by }, { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by }, { "Install", "DefaultInstance", config_parse_default_instance, 0, info }, @@ -991,6 +1079,8 @@ static int unit_file_load( {} }; + const char *name; + UnitType type; _cleanup_fclose_ FILE *f = NULL; _cleanup_close_ int fd = -1; struct stat st; @@ -1000,6 +1090,12 @@ static int unit_file_load( assert(info); assert(path); + name = basename(path); + type = unit_name_to_type(name); + if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && + !unit_type_may_template(type)) + return log_error_errno(EINVAL, "Unit type %s cannot be templated.", unit_type_to_string(type)); + if (!(flags & SEARCH_LOAD)) { r = lstat(path, &st); if (r < 0) @@ -2367,17 +2463,16 @@ int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char * return 1; } -int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) { +static int read_presets(UnitFileScope scope, const char *root_dir, Presets *presets) { + _cleanup_(presets_freep) Presets ps = {}; + size_t n_allocated = 0; _cleanup_strv_free_ char **files = NULL; char **p; int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(name); - - if (!unit_name_is_valid(name, UNIT_NAME_ANY)) - return -EINVAL; + assert(presets); if (scope == UNIT_FILE_SYSTEM) r = conf_files_list(&files, ".preset", root_dir, @@ -2394,8 +2489,11 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char "/usr/local/lib/systemd/user-preset", "/usr/lib/systemd/user-preset", NULL); - else - return 1; /* Default is "enable" */ + else { + *presets = (Presets){}; + + return 0; + } if (r < 0) return r; @@ -2403,6 +2501,7 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char STRV_FOREACH(p, files) { _cleanup_fclose_ FILE *f; char line[LINE_MAX]; + int n = 0; f = fopen(*p, "re"); if (!f) { @@ -2413,10 +2512,12 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char } FOREACH_LINE(line, f, return -errno) { + PresetRule rule = {}; const char *parameter; char *l; l = strstrip(line); + n++; if (isempty(l)) continue; @@ -2425,31 +2526,87 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char parameter = first_word(l, "enable"); if (parameter) { - if (fnmatch(parameter, name, FNM_NOESCAPE) == 0) { - log_debug("Preset file says enable %s.", name); - return 1; - } + char *pattern; - continue; + pattern = strdup(parameter); + if (!pattern) + return -ENOMEM; + + rule = (PresetRule) { + .pattern = pattern, + .action = PRESET_ENABLE, + }; } parameter = first_word(l, "disable"); if (parameter) { - if (fnmatch(parameter, name, FNM_NOESCAPE) == 0) { - log_debug("Preset file says disable %s.", name); - return 0; - } + char *pattern; + + pattern = strdup(parameter); + if (!pattern) + return -ENOMEM; + + rule = (PresetRule) { + .pattern = pattern, + .action = PRESET_DISABLE, + }; + } + if (rule.action) { + if (!GREEDY_REALLOC(ps.rules, n_allocated, ps.n_rules + 1)) + return -ENOMEM; + + ps.rules[ps.n_rules++] = rule; continue; } - log_debug("Couldn't parse line '%s'", l); + log_syntax(NULL, LOG_WARNING, *p, n, 0, "Couldn't parse line '%s'. Ignoring.", line); } } - /* Default is "enable" */ - log_debug("Preset file doesn't say anything about %s, enabling.", name); - return 1; + *presets = ps; + ps = (Presets){}; + + return 0; +} + +static int query_presets(const char *name, const Presets presets) { + PresetAction action = PRESET_UNKNOWN; + size_t i; + + if (!unit_name_is_valid(name, UNIT_NAME_ANY)) + return -EINVAL; + + for (i = 0; i < presets.n_rules; i++) + if (fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) { + action = presets.rules[i].action; + break; + } + + switch (action) { + case PRESET_UNKNOWN: + log_debug("Preset files don't specify rule for %s. Enabling.", name); + return 1; + case PRESET_ENABLE: + log_debug("Preset files say enable %s.", name); + return 1; + case PRESET_DISABLE: + log_debug("Preset files say disable %s.", name); + return 0; + default: + assert_not_reached("invalid preset action"); + } +} + +int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) { + _cleanup_(presets_freep) Presets presets = {}; + int r; + + r = read_presets(scope, root_dir, &presets); + if (r < 0) + return r; + + return query_presets(name, presets); } static int execute_preset( @@ -2505,6 +2662,7 @@ static int preset_prepare_one( LookupPaths *paths, UnitFilePresetMode mode, const char *name, + Presets presets, UnitFileChange **changes, unsigned *n_changes) { @@ -2515,7 +2673,7 @@ static int preset_prepare_one( install_info_find(minus, name)) return 0; - r = unit_file_query_preset(scope, paths->root_dir, name); + r = query_presets(name, presets); if (r < 0) return r; @@ -2545,6 +2703,7 @@ int unit_file_preset( _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; _cleanup_lookup_paths_free_ LookupPaths paths = {}; + _cleanup_(presets_freep) Presets presets = {}; const char *config_path; char **i; int r; @@ -2559,11 +2718,12 @@ int unit_file_preset( config_path = runtime ? paths.runtime_config : paths.persistent_config; - STRV_FOREACH(i, files) { - if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) - return -EINVAL; + r = read_presets(scope, root_dir, &presets); + if (r < 0) + return r; - r = preset_prepare_one(scope, &plus, &minus, &paths, mode, *i, changes, n_changes); + STRV_FOREACH(i, files) { + r = preset_prepare_one(scope, &plus, &minus, &paths, mode, *i, presets, changes, n_changes); if (r < 0) return r; } @@ -2582,6 +2742,7 @@ int unit_file_preset_all( _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; _cleanup_lookup_paths_free_ LookupPaths paths = {}; + _cleanup_(presets_freep) Presets presets = {}; const char *config_path = NULL; char **i; int r; @@ -2596,6 +2757,10 @@ int unit_file_preset_all( config_path = runtime ? paths.runtime_config : paths.persistent_config; + r = read_presets(scope, root_dir, &presets); + if (r < 0) + return r; + STRV_FOREACH(i, paths.search_path) { _cleanup_closedir_ DIR *d = NULL; struct dirent *de; @@ -2619,7 +2784,7 @@ int unit_file_preset_all( continue; /* we don't pass changes[] in, because we want to handle errors on our own */ - r = preset_prepare_one(scope, &plus, &minus, &paths, mode, de->d_name, NULL, 0); + r = preset_prepare_one(scope, &plus, &minus, &paths, mode, de->d_name, presets, NULL, 0); if (r == -ERFKILL) r = unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, de->d_name, NULL); diff --git a/src/shared/install.h b/src/shared/install.h index 4ffc5a21f2..5812447c5b 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -138,6 +138,9 @@ static inline bool UNIT_FILE_INSTALL_INFO_HAS_ALSO(UnitFileInstallInfo *i) { return !strv_isempty(i->also); } +bool unit_type_may_alias(UnitType type) _const_; +bool unit_type_may_template(UnitType type) _const_; + int unit_file_enable( UnitFileScope scope, bool runtime, diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index bebfc40efe..529d89ee2a 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -423,7 +423,7 @@ int image_remove(Image *i) { case IMAGE_DIRECTORY: /* Allow deletion of read-only directories */ - (void) chattr_path(i->path, false, FS_IMMUTABLE_FL); + (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL); r = rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); if (r < 0) return r; @@ -487,7 +487,7 @@ int image_rename(Image *i, const char *new_name) { /* 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 */ + * time we take possession of it */ r = image_name_lock(new_name, LOCK_EX|LOCK_NB, &name_lock); if (r < 0) return r; @@ -505,7 +505,7 @@ int image_rename(Image *i, const char *new_name) { (void) read_attr_path(i->path, &file_attr); if (file_attr & FS_IMMUTABLE_FL) - (void) chattr_path(i->path, false, FS_IMMUTABLE_FL); + (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL); /* fall through */ @@ -538,7 +538,7 @@ int image_rename(Image *i, const char *new_name) { /* Restore the immutable bit, if it was set before */ if (file_attr & FS_IMMUTABLE_FL) - (void) chattr_path(new_path, true, FS_IMMUTABLE_FL); + (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL); free(i->path); i->path = new_path; @@ -588,7 +588,7 @@ int image_clone(Image *i, const char *new_name, bool read_only) { /* 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 */ + * time we take possession of it */ r = image_name_lock(new_name, LOCK_EX|LOCK_NB, &name_lock); if (r < 0) return r; @@ -603,13 +603,21 @@ int image_clone(Image *i, const char *new_name, bool read_only) { case IMAGE_SUBVOLUME: case IMAGE_DIRECTORY: + /* If we can we'll always try to create a new btrfs subvolume here, even if the source is a plain + * directory.*/ + new_path = strjoina("/var/lib/machines/", new_name); r = btrfs_subvol_snapshot(i->path, new_path, (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE | BTRFS_SNAPSHOT_QUOTA); + if (r == -EOPNOTSUPP) { + /* No btrfs snapshots supported, create a normal directory then. */ - /* Enable "subtree" quotas for the copy, if we didn't - * copy any quota from the source. */ - (void) btrfs_subvol_auto_qgroup(i->path, 0, true); + r = copy_directory(i->path, new_path, false); + if (r >= 0) + (void) chattr_path(new_path, read_only ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL); + } else if (r >= 0) + /* Enable "subtree" quotas for the copy, if we didn't copy any quota from the source. */ + (void) btrfs_subvol_auto_qgroup(new_path, 0, true); break; @@ -670,7 +678,7 @@ int image_read_only(Image *i, bool b) { a read-only subvolume, but at least something, and we can read the value back.*/ - r = chattr_path(i->path, b, FS_IMMUTABLE_FL); + r = chattr_path(i->path, b ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL); if (r < 0) return r; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 9af25e22a4..bec4f31b39 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -2334,6 +2334,8 @@ static int need_daemon_reload(sd_bus *bus, const char *unit) { } static void warn_unit_file_changed(const char *name) { + assert(name); + log_warning("%sWarning:%s %s changed on disk. Run 'systemctl%s daemon-reload' to reload units.", ansi_highlight_red(), ansi_normal(), diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h index af7a797567..3ae110c080 100644 --- a/src/systemd/sd-netlink.h +++ b/src/systemd/sd-netlink.h @@ -137,8 +137,10 @@ int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char int sd_rtnl_message_route_set_src_prefixlen(sd_netlink_message *m, unsigned char prefixlen); int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope); int sd_rtnl_message_route_set_flags(sd_netlink_message *m, unsigned flags); +int sd_rtnl_message_route_set_table(sd_netlink_message *m, unsigned char table); int sd_rtnl_message_route_get_flags(sd_netlink_message *m, unsigned *flags); int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family); +int sd_rtnl_message_route_set_family(sd_netlink_message *m, int family); int sd_rtnl_message_route_get_protocol(sd_netlink_message *m, unsigned char *protocol); int sd_rtnl_message_route_get_scope(sd_netlink_message *m, unsigned char *scope); int sd_rtnl_message_route_get_tos(sd_netlink_message *m, unsigned char *tos); diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c index 4680b0336d..4b9a74fca4 100644 --- a/src/test/test-install-root.c +++ b/src/test/test-install-root.c @@ -681,6 +681,53 @@ static void test_revert(const char *root) { changes = NULL; n_changes = 0; } +static void test_preset_order(const char *root) { + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + const char *p; + UnitFileState state; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-1.service", &state) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) == -ENOENT); + + p = strjoina(root, "/usr/lib/systemd/system/prefix-1.service"); + assert_se(write_string_file(p, + "[Install]\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system/prefix-2.service"); + assert_se(write_string_file(p, + "[Install]\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system-preset/test.preset"); + assert_se(write_string_file(p, + "enable prefix-1.service\n" + "disable prefix-*.service\n" + "enable prefix-2.service\n", WRITE_STRING_FILE_CREATE) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_preset(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("prefix-1.service"), UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/usr/lib/systemd/system/prefix-1.service")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/prefix-1.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_preset(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("prefix-2.service"), UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0); + assert_se(n_changes == 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); +} + int main(int argc, char *argv[]) { char root[] = "/tmp/rootXXXXXX"; const char *p; @@ -709,6 +756,7 @@ int main(int argc, char *argv[]) { test_template_enable(root); test_indirect(root); test_preset_and_list(root); + test_preset_order(root); test_revert(root); assert_se(rm_rf(root, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index 475856db6f..26fa52cf6c 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -329,8 +329,8 @@ static void dump_token(struct udev_rules *rules, struct token *token) { enum token_type type = token->type; enum operation_type op = token->key.op; enum string_glob_type glob = token->key.glob; - const char *value = str(rules, token->key.value_off); - const char *attr = &rules->buf[token->key.attr_off]; + const char *value = rules_str(rules, token->key.value_off); + const char *attr = &rules->strbuf->buf[token->key.attr_off]; switch (type) { case TK_RULE: @@ -340,9 +340,9 @@ static void dump_token(struct udev_rules *rules, struct token *token) { unsigned int idx = (tk_ptr - tks_ptr) / sizeof(struct token); log_debug("* RULE %s:%u, token: %u, count: %u, label: '%s'", - &rules->buf[token->rule.filename_off], token->rule.filename_line, + &rules->strbuf->buf[token->rule.filename_off], token->rule.filename_line, idx, token->rule.token_count, - &rules->buf[token->rule.label_off]); + &rules->strbuf->buf[token->rule.label_off]); break; } case TK_M_ACTION: @@ -439,11 +439,11 @@ static void dump_token(struct udev_rules *rules, struct token *token) { static void dump_rules(struct udev_rules *rules) { unsigned int i; - log_debug("dumping %u (%zu bytes) tokens, %u (%zu bytes) strings", + log_debug("dumping %u (%zu bytes) tokens, %zu (%zu bytes) strings", rules->token_cur, rules->token_cur * sizeof(struct token), - rules->buf_count, - rules->buf_cur); + rules->strbuf->nodes_count, + rules->strbuf->len); for (i = 0; i < rules->token_cur; i++) dump_token(rules, &rules->tokens[i]); } |