diff options
Diffstat (limited to 'src')
231 files changed, 6550 insertions, 2815 deletions
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 51d881c5fb..ac0470b20d 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -461,6 +461,7 @@ static int acquire_host_info(sd_bus *bus, struct host_info **hi) { "org.freedesktop.hostname1", "/org/freedesktop/hostname1", hostname_map, + &error, host); if (r < 0) log_debug_errno(r, "Failed to get host information from systemd-hostnamed: %s", bus_error_message(&error, r)); @@ -469,6 +470,7 @@ static int acquire_host_info(sd_bus *bus, struct host_info **hi) { "org.freedesktop.systemd1", "/org/freedesktop/systemd1", manager_map, + &error, host); if (r < 0) return log_error_errno(r, "Failed to get host information from systemd: %s", bus_error_message(&error, r)); @@ -1360,7 +1362,7 @@ static void help(void) { " blame Print list of running units ordered by time to init\n" " critical-chain Print a tree of the time critical chain of units\n" " plot Output SVG graphic showing service initialization\n" - " dot Output dependency graph in dot(1) format\n" + " dot Output dependency graph in man:dot(1) format\n" " set-log-level LEVEL Set logging threshold for manager\n" " set-log-target TARGET Set logging target for manager\n" " dump Output state serialization of service manager\n" diff --git a/src/basic/architecture.c b/src/basic/architecture.c index b74dc0db78..5a3dc08a4a 100644 --- a/src/basic/architecture.c +++ b/src/basic/architecture.c @@ -123,7 +123,8 @@ int uname_architecture(void) { { "crisv32", ARCHITECTURE_CRIS }, #elif defined(__nios2__) { "nios2", ARCHITECTURE_NIOS2 }, -#elif defined(__riscv__) +#elif defined(__riscv__) || defined(__riscv) + /* __riscv__ is obsolete, remove in 2018 */ { "riscv32", ARCHITECTURE_RISCV32 }, { "riscv64", ARCHITECTURE_RISCV64 }, # if __SIZEOF_POINTER__ == 4 diff --git a/src/basic/architecture.h b/src/basic/architecture.h index b329df2f6d..46883719d1 100644 --- a/src/basic/architecture.h +++ b/src/basic/architecture.h @@ -124,13 +124,21 @@ int uname_architecture(void); #elif defined(__sparc__) # define native_architecture() ARCHITECTURE_SPARC # define LIB_ARCH_TUPLE "sparc-linux-gnu" -#elif defined(__mips64__) +#elif defined(__mips64) && defined(__LP64__) # if __BYTE_ORDER == __BIG_ENDIAN # define native_architecture() ARCHITECTURE_MIPS64 -# error "Missing LIB_ARCH_TUPLE for MIPS64" +# define LIB_ARCH_TUPLE "mips64-linux-gnuabi64" # else # define native_architecture() ARCHITECTURE_MIPS64_LE -# error "Missing LIB_ARCH_TUPLE for MIPS64_LE" +# define LIB_ARCH_TUPLE "mips64el-linux-gnuabi64" +# endif +#elif defined(__mips64) +# if __BYTE_ORDER == __BIG_ENDIAN +# define native_architecture() ARCHITECTURE_MIPS64 +# define LIB_ARCH_TUPLE "mips64-linux-gnuabin32" +# else +# define native_architecture() ARCHITECTURE_MIPS64_LE +# define LIB_ARCH_TUPLE "mips64el-linux-gnuabin32" # endif #elif defined(__mips__) # if __BYTE_ORDER == __BIG_ENDIAN @@ -187,14 +195,15 @@ int uname_architecture(void); # define LIB_ARCH_TUPLE "m68k-linux-gnu" #elif defined(__tilegx__) # define native_architecture() ARCHITECTURE_TILEGX -# error "Missing LIB_ARCH_TUPLE for TILEGX" +# define LIB_ARCH_TUPLE "tilegx-linux-gnu" #elif defined(__cris__) # define native_architecture() ARCHITECTURE_CRIS # error "Missing LIB_ARCH_TUPLE for CRIS" #elif defined(__nios2__) # define native_architecture() ARCHITECTURE_NIOS2 # define LIB_ARCH_TUPLE "nios2-linux-gnu" -#elif defined(__riscv__) +#elif defined(__riscv__) || defined(__riscv) + /* __riscv__ is obsolete, remove in 2018 */ # if __SIZEOF_POINTER__ == 4 # define native_architecture() ARCHITECTURE_RISCV32 # define LIB_ARCH_TUPLE "riscv32-linux-gnu" diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c index 5f9e21dcba..5505499312 100644 --- a/src/basic/btrfs-util.c +++ b/src/basic/btrfs-util.c @@ -1737,7 +1737,7 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag } else if (r < 0) return r; - r = copy_directory_fd(old_fd, new_path, true); + r = copy_directory_fd(old_fd, new_path, COPY_MERGE|COPY_REFLINK); if (r < 0) goto fallback_fail; diff --git a/src/basic/build.h b/src/basic/build.h index 633c2aaccb..91312bd2a3 100644 --- a/src/basic/build.h +++ b/src/basic/build.h @@ -133,6 +133,8 @@ #define _IDN_FEATURE_ "-IDN" #endif +#define _CGROUP_HIEARCHY_ "default-hierarchy=" DEFAULT_HIERARCHY_NAME + #define SYSTEMD_FEATURES \ _PAM_FEATURE_ " " \ _AUDIT_FEATURE_ " " \ @@ -152,4 +154,5 @@ _BLKID_FEATURE_ " " \ _ELFUTILS_FEATURE_ " " \ _KMOD_FEATURE_ " " \ - _IDN_FEATURE_ + _IDN_FEATURE_ " " \ + _CGROUP_HIEARCHY_ diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c index 3fa1c51ace..2323eb8555 100644 --- a/src/basic/calendarspec.c +++ b/src/basic/calendarspec.c @@ -116,8 +116,7 @@ static void normalize_chain(CalendarComponent **c) { /* Drop non-unique entries */ for (k = n-1; k > 0; k--) { - if (b[k-1]->start == next->start && - b[k-1]->repeat == next->repeat) { + if (component_compare(&b[k-1], &next) == 0) { free(b[k-1]); continue; } diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 6948ed3931..bda5c555ad 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -208,6 +208,18 @@ int cg_rmdir(const char *controller, const char *path) { if (r < 0 && errno != ENOENT) return -errno; + r = cg_hybrid_unified(); + if (r < 0) + return r; + if (r == 0) + return 0; + + if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { + r = cg_rmdir(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path); + if (r < 0) + log_warning_errno(r, "Failed to remove compat systemd cgroup %s: %m", path); + } + return 0; } @@ -542,6 +554,13 @@ static const char *controller_to_dirname(const char *controller) { * just cuts off the name= prefixed used for named * hierarchies, if it is specified. */ + if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { + if (cg_hybrid_unified() > 0) + controller = SYSTEMD_CGROUP_CONTROLLER_HYBRID; + else + controller = SYSTEMD_CGROUP_CONTROLLER_LEGACY; + } + e = startswith(controller, "name="); if (e) return e; @@ -594,7 +613,7 @@ static int join_path_unified(const char *path, const char *suffix, char **fs) { } int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) { - int unified, r; + int r; assert(fs); @@ -623,11 +642,10 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch if (!cg_controller_is_valid(controller)) return -EINVAL; - unified = cg_all_unified(); - if (unified < 0) - return unified; - - if (unified > 0) + r = cg_all_unified(); + if (r < 0) + return r; + if (r > 0) r = join_path_unified(path, suffix, fs); else r = join_path_legacy(controller, path, suffix, fs); @@ -639,7 +657,7 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch } static int controller_is_accessible(const char *controller) { - int unified; + int r; assert(controller); @@ -651,10 +669,10 @@ static int controller_is_accessible(const char *controller) { if (!cg_controller_is_valid(controller)) return -EINVAL; - unified = cg_all_unified(); - if (unified < 0) - return unified; - if (unified > 0) { + r = cg_all_unified(); + if (r < 0) + return r; + if (r > 0) { /* We don't support named hierarchies if we are using * the unified hierarchy. */ @@ -708,7 +726,7 @@ static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct int cg_trim(const char *controller, const char *path, bool delete_root) { _cleanup_free_ char *fs = NULL; - int r = 0; + int r = 0, q; assert(path); @@ -731,6 +749,15 @@ int cg_trim(const char *controller, const char *path, bool delete_root) { return -errno; } + q = cg_hybrid_unified(); + if (q < 0) + return q; + if (q > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { + q = cg_trim(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, delete_root); + if (q < 0) + log_warning_errno(q, "Failed to trim compat systemd cgroup %s: %m", path); + } + return r; } @@ -754,6 +781,16 @@ int cg_create(const char *controller, const char *path) { return -errno; } + r = cg_hybrid_unified(); + if (r < 0) + return r; + + if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { + r = cg_create(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path); + if (r < 0) + log_warning_errno(r, "Failed to create compat systemd cgroup %s: %m", path); + } + return 1; } @@ -791,7 +828,21 @@ int cg_attach(const char *controller, const char *path, pid_t pid) { xsprintf(c, PID_FMT "\n", pid); - return write_string_file(fs, c, 0); + r = write_string_file(fs, c, 0); + if (r < 0) + return r; + + r = cg_hybrid_unified(); + if (r < 0) + return r; + + if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { + r = cg_attach(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, pid); + if (r < 0) + log_warning_errno(r, "Failed to attach %d to compat systemd cgroup %s: %m", pid, path); + } + + return 0; } int cg_attach_fallback(const char *controller, const char *path, pid_t pid) { @@ -840,7 +891,20 @@ int cg_set_group_access( if (r < 0) return r; - return chmod_and_chown(fs, mode, uid, gid); + r = chmod_and_chown(fs, mode, uid, gid); + if (r < 0) + return r; + + r = cg_hybrid_unified(); + if (r < 0) + return r; + if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { + r = cg_set_group_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, mode, uid, gid); + if (r < 0) + log_warning_errno(r, "Failed to set group access on compat systemd cgroup %s: %m", path); + } + + return 0; } int cg_set_task_access( @@ -851,7 +915,7 @@ int cg_set_task_access( gid_t gid) { _cleanup_free_ char *fs = NULL, *procs = NULL; - int r, unified; + int r; assert(path); @@ -869,16 +933,24 @@ int cg_set_task_access( if (r < 0) return r; - unified = cg_unified(controller); - if (unified < 0) - return unified; - if (unified) - return 0; + r = cg_unified_controller(controller); + if (r < 0) + return r; + if (r == 0) { + /* Compatibility, Always keep values for "tasks" in sync with + * "cgroup.procs" */ + if (cg_get_path(controller, path, "tasks", &procs) >= 0) + (void) chmod_and_chown(procs, mode, uid, gid); + } - /* Compatibility, Always keep values for "tasks" in sync with - * "cgroup.procs" */ - if (cg_get_path(controller, path, "tasks", &procs) >= 0) - (void) chmod_and_chown(procs, mode, uid, gid); + r = cg_hybrid_unified(); + if (r < 0) + return r; + if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { + r = cg_set_task_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, mode, uid, gid); + if (r < 0) + log_warning_errno(r, "Failed to set task access on compat systemd cgroup %s: %m", path); + } return 0; } @@ -923,7 +995,7 @@ int cg_get_xattr(const char *controller, const char *path, const char *name, voi int cg_pid_get_path(const char *controller, pid_t pid, char **path) { _cleanup_fclose_ FILE *f = NULL; char line[LINE_MAX]; - const char *fs; + const char *fs, *controller_str; size_t cs = 0; int unified; @@ -936,11 +1008,17 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) { } else controller = SYSTEMD_CGROUP_CONTROLLER; - unified = cg_unified(controller); + unified = cg_unified_controller(controller); if (unified < 0) return unified; - if (unified == 0) - cs = strlen(controller); + if (unified == 0) { + if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) + controller_str = SYSTEMD_CGROUP_CONTROLLER_LEGACY; + else + controller_str = controller; + + cs = strlen(controller_str); + } fs = procfs_file_alloca(pid, "cgroup"); f = fopen(fs, "re"); @@ -977,7 +1055,7 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) { *e = 0; FOREACH_WORD_SEPARATOR(word, k, l, ",", state) { - if (k == cs && memcmp(word, controller, cs) == 0) { + if (k == cs && memcmp(word, controller_str, cs) == 0) { found = true; break; } @@ -1001,14 +1079,14 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) { int cg_install_release_agent(const char *controller, const char *agent) { _cleanup_free_ char *fs = NULL, *contents = NULL; const char *sc; - int r, unified; + int r; assert(agent); - unified = cg_unified(controller); - if (unified < 0) - return unified; - if (unified) /* doesn't apply to unified hierarchy */ + r = cg_unified_controller(controller); + if (r < 0) + return r; + if (r > 0) /* doesn't apply to unified hierarchy */ return -EOPNOTSUPP; r = cg_get_path(controller, NULL, "release_agent", &fs); @@ -1054,12 +1132,12 @@ int cg_install_release_agent(const char *controller, const char *agent) { int cg_uninstall_release_agent(const char *controller) { _cleanup_free_ char *fs = NULL; - int r, unified; + int r; - unified = cg_unified(controller); - if (unified < 0) - return unified; - if (unified) /* Doesn't apply to unified hierarchy */ + r = cg_unified_controller(controller); + if (r < 0) + return r; + if (r > 0) /* Doesn't apply to unified hierarchy */ return -EOPNOTSUPP; r = cg_get_path(controller, NULL, "notify_on_release", &fs); @@ -1104,7 +1182,7 @@ int cg_is_empty(const char *controller, const char *path) { } int cg_is_empty_recursive(const char *controller, const char *path) { - int unified, r; + int r; assert(path); @@ -1112,11 +1190,10 @@ int cg_is_empty_recursive(const char *controller, const char *path) { if (controller && (isempty(path) || path_equal(path, "/"))) return false; - unified = cg_unified(controller); - if (unified < 0) - return unified; - - if (unified > 0) { + r = cg_unified_controller(controller); + if (r < 0) + return r; + if (r > 0) { _cleanup_free_ char *t = NULL; /* On the unified hierarchy we can check empty state @@ -1833,6 +1910,9 @@ bool cg_controller_is_valid(const char *p) { if (!p) return false; + if (streq(p, SYSTEMD_CGROUP_CONTROLLER)) + return true; + s = startswith(p, "name="); if (s) p = s; @@ -1986,7 +2066,7 @@ int cg_get_keyed_attribute(const char *controller, const char *path, const char int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) { CGroupController c; - int r, unified; + int r; /* This one will create a cgroup in our private tree, but also * duplicate it in the trees specified in mask, and remove it @@ -1998,10 +2078,10 @@ int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path return r; /* If we are in the unified hierarchy, we are done now */ - unified = cg_all_unified(); - if (unified < 0) - return unified; - if (unified > 0) + r = cg_all_unified(); + if (r < 0) + return r; + if (r > 0) return 0; /* Otherwise, do the same in the other hierarchies */ @@ -2022,16 +2102,16 @@ int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) { CGroupController c; - int r, unified; + int r; r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid); if (r < 0) return r; - unified = cg_all_unified(); - if (unified < 0) - return unified; - if (unified > 0) + r = cg_all_unified(); + if (r < 0) + return r; + if (r > 0) return 0; for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { @@ -2072,7 +2152,7 @@ int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) { CGroupController c; - int r = 0, unified; + int r = 0, q; if (!path_equal(from, to)) { r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, CGROUP_REMOVE); @@ -2080,10 +2160,10 @@ int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to return r; } - unified = cg_all_unified(); - if (unified < 0) - return unified; - if (unified > 0) + q = cg_all_unified(); + if (q < 0) + return q; + if (q > 0) return r; for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { @@ -2107,16 +2187,16 @@ int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) { CGroupController c; - int r, unified; + int r, q; r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root); if (r < 0) return r; - unified = cg_all_unified(); - if (unified < 0) - return unified; - if (unified > 0) + q = cg_all_unified(); + if (q < 0) + return q; + if (q > 0) return r; for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { @@ -2133,16 +2213,16 @@ int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) int cg_mask_supported(CGroupMask *ret) { CGroupMask mask = 0; - int r, unified; + int r; /* Determines the mask of supported cgroup controllers. Only * includes controllers we can make sense of and that are * actually accessible. */ - unified = cg_all_unified(); - if (unified < 0) - return unified; - if (unified > 0) { + r = cg_all_unified(); + if (r < 0) + return r; + if (r > 0) { _cleanup_free_ char *root = NULL, *controllers = NULL, *path = NULL; const char *c; @@ -2262,7 +2342,18 @@ int cg_kernel_controllers(Set *controllers) { static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN; -static int cg_update_unified(void) { +/* The hybrid mode was initially implemented in v232 and simply mounted cgroup v2 on /sys/fs/cgroup/systemd. This + * unfortunately broke other tools (such as docker) which expected the v1 "name=systemd" hierarchy on + * /sys/fs/cgroup/systemd. From v233 and on, the hybrid mode mountnbs v2 on /sys/fs/cgroup/unified and maintains + * "name=systemd" hierarchy on /sys/fs/cgroup/systemd for compatibility with other tools. + * + * To keep live upgrade working, we detect and support v232 layout. When v232 layout is detected, to keep cgroup v2 + * process management but disable the compat dual layout, we return %true on + * cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) and %false on cg_hybrid_unified(). + */ +static thread_local bool unified_systemd_v232; + +static int cg_unified_update(void) { struct statfs fs; @@ -2280,54 +2371,83 @@ static int cg_update_unified(void) { if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) unified_cache = CGROUP_UNIFIED_ALL; else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) { - if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0) - return -errno; - - unified_cache = F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC) ? - CGROUP_UNIFIED_SYSTEMD : CGROUP_UNIFIED_NONE; + if (statfs("/sys/fs/cgroup/unified/", &fs) == 0 && + F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) { + unified_cache = CGROUP_UNIFIED_SYSTEMD; + unified_systemd_v232 = false; + } else if (statfs("/sys/fs/cgroup/systemd/", &fs) == 0 && + F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) { + unified_cache = CGROUP_UNIFIED_SYSTEMD; + unified_systemd_v232 = true; + } else { + if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0) + return -errno; + if (!F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC)) + return -ENOMEDIUM; + unified_cache = CGROUP_UNIFIED_NONE; + } } else return -ENOMEDIUM; return 0; } -int cg_unified(const char *controller) { - +int cg_unified_controller(const char *controller) { int r; - r = cg_update_unified(); + r = cg_unified_update(); if (r < 0) return r; - if (streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER)) - return unified_cache >= CGROUP_UNIFIED_SYSTEMD; - else - return unified_cache >= CGROUP_UNIFIED_ALL; + if (unified_cache == CGROUP_UNIFIED_NONE) + return false; + + if (unified_cache >= CGROUP_UNIFIED_ALL) + return true; + + return streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER); } int cg_all_unified(void) { + int r; + + r = cg_unified_update(); + if (r < 0) + return r; - return cg_unified(NULL); + return unified_cache >= CGROUP_UNIFIED_ALL; } -void cg_unified_flush(void) { +int cg_hybrid_unified(void) { + int r; + + r = cg_unified_update(); + if (r < 0) + return r; + + return unified_cache == CGROUP_UNIFIED_SYSTEMD && !unified_systemd_v232; +} + +int cg_unified_flush(void) { unified_cache = CGROUP_UNIFIED_UNKNOWN; + + return cg_unified_update(); } int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) { _cleanup_free_ char *fs = NULL; CGroupController c; - int r, unified; + int r; assert(p); if (supported == 0) return 0; - unified = cg_all_unified(); - if (unified < 0) - return unified; - if (!unified) /* on the legacy hiearchy there's no joining of controllers defined */ + r = cg_all_unified(); + if (r < 0) + return r; + if (r == 0) /* on the legacy hiearchy there's no joining of controllers defined */ return 0; r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs); @@ -2359,63 +2479,69 @@ int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) { bool cg_is_unified_wanted(void) { static thread_local int wanted = -1; - int r, unified; + int r; bool b; + const bool is_default = DEFAULT_HIERARCHY == CGROUP_UNIFIED_ALL; - /* If the hierarchy is already mounted, then follow whatever - * was chosen for it. */ - unified = cg_all_unified(); - if (unified >= 0) - return unified; - - /* Otherwise, let's see what the kernel command line has to - * say. Since checking that is expensive, let's cache the - * result. */ + /* If we have a cached value, return that. */ if (wanted >= 0) return wanted; + /* If the hierarchy is already mounted, then follow whatever + * was chosen for it. */ + if (cg_unified_flush() >= 0) + return (wanted = unified_cache >= CGROUP_UNIFIED_ALL); + + /* Otherwise, let's see what the kernel command line has to say. + * Since checking is expensive, cache a non-error result. */ r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b); - if (r < 0) - return false; - return (wanted = r > 0 ? b : false); + return (wanted = r > 0 ? b : is_default); } bool cg_is_legacy_wanted(void) { - return !cg_is_unified_wanted(); + static thread_local int wanted = -1; + + /* If we have a cached value, return that. */ + if (wanted >= 0) + return wanted; + + /* Check if we have cgroups2 already mounted. */ + if (cg_unified_flush() >= 0 && + unified_cache == CGROUP_UNIFIED_ALL) + return (wanted = false); + + /* Otherwise, assume that at least partial legacy is wanted, + * since cgroups2 should already be mounted at this point. */ + return (wanted = true); } -bool cg_is_unified_systemd_controller_wanted(void) { +bool cg_is_hybrid_wanted(void) { static thread_local int wanted = -1; - int r, unified; + int r; bool b; + const bool is_default = DEFAULT_HIERARCHY >= CGROUP_UNIFIED_SYSTEMD; + /* We default to true if the default is "hybrid", obviously, + * but also when the default is "unified", because if we get + * called, it means that unified hierarchy was not mounted. */ - /* If the unified hierarchy is requested in full, no need to - * bother with this. */ - if (cg_is_unified_wanted()) - return 0; + /* If we have a cached value, return that. */ + if (wanted >= 0) + return wanted; /* If the hierarchy is already mounted, then follow whatever * was chosen for it. */ - unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER); - if (unified >= 0) - return unified; - - /* Otherwise, let's see what the kernel command line has to - * say. Since checking that is expensive, let's cache the - * result. */ - if (wanted >= 0) - return wanted; + if (cg_unified_flush() >= 0 && + unified_cache == CGROUP_UNIFIED_ALL) + return (wanted = false); + /* Otherwise, let's see what the kernel command line has to say. + * Since checking is expensive, cache a non-error result. */ r = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b); - if (r < 0) - return false; - - return (wanted = r > 0 ? b : false); -} -bool cg_is_legacy_systemd_controller_wanted(void) { - return cg_is_legacy_wanted() && !cg_is_unified_systemd_controller_wanted(); + /* The meaning of the kernel option is reversed wrt. to the return value + * of this function, hence the negation. */ + return (wanted = r > 0 ? !b : is_default); } int cg_weight_parse(const char *s, uint64_t *ret) { diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h index 0aa27c4cd7..a522095d95 100644 --- a/src/basic/cgroup-util.h +++ b/src/basic/cgroup-util.h @@ -241,13 +241,13 @@ int cg_kernel_controllers(Set *controllers); bool cg_ns_supported(void); int cg_all_unified(void); -int cg_unified(const char *controller); -void cg_unified_flush(void); +int cg_hybrid_unified(void); +int cg_unified_controller(const char *controller); +int cg_unified_flush(void); bool cg_is_unified_wanted(void); bool cg_is_legacy_wanted(void); -bool cg_is_unified_systemd_controller_wanted(void); -bool cg_is_legacy_systemd_controller_wanted(void); +bool cg_is_hybrid_wanted(void); const char* cgroup_controller_to_string(CGroupController c) _const_; CGroupController cgroup_controller_from_string(const char *s) _pure_; diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c index b5780194df..b8f0f5d03d 100644 --- a/src/basic/conf-files.c +++ b/src/basic/conf-files.c @@ -137,7 +137,6 @@ int conf_files_list(char ***strv, const char *suffix, const char *root, const ch va_list ap; assert(strv); - assert(suffix); va_start(ap, dir); dirs = strv_new_ap(dir, ap); @@ -153,7 +152,6 @@ int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, c _cleanup_strv_free_ char **dirs = NULL; assert(strv); - assert(suffix); dirs = strv_split_nulstr(d); if (!dirs) diff --git a/src/basic/copy.c b/src/basic/copy.c index e9a7efd232..e120b9eb4e 100644 --- a/src/basic/copy.c +++ b/src/basic/copy.c @@ -45,6 +45,7 @@ #include "strv.h" #include "time-util.h" #include "umask-util.h" +#include "user-util.h" #include "xattr-util.h" #define COPY_BUFFER_SIZE (16*1024u) @@ -68,7 +69,7 @@ static ssize_t try_copy_file_range(int fd_in, loff_t *off_in, return -errno; } -int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { +int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags) { bool try_cfr = true, try_sendfile = true, try_splice = true; int r; size_t m = SSIZE_MAX; /* that is the maximum that sendfile and c_f_r accept */ @@ -77,7 +78,7 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { assert(fdt >= 0); /* Try btrfs reflinks first. */ - if (try_reflink && + if ((copy_flags & COPY_REFLINK) && max_bytes == (uint64_t) -1 && lseek(fdf, 0, SEEK_CUR) == 0 && lseek(fdt, 0, SEEK_CUR) == 0) { @@ -176,7 +177,16 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { return 0; /* return 0 if we hit EOF earlier than the size limit */ } -static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) { +static int fd_copy_symlink( + int df, + const char *from, + const struct stat *st, + int dt, + const char *to, + uid_t override_uid, + gid_t override_gid, + CopyFlags copy_flags) { + _cleanup_free_ char *target = NULL; int r; @@ -191,13 +201,25 @@ static int fd_copy_symlink(int df, const char *from, const struct stat *st, int if (symlinkat(target, dt, to) < 0) return -errno; - if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0) + if (fchownat(dt, to, + uid_is_valid(override_uid) ? override_uid : st->st_uid, + gid_is_valid(override_gid) ? override_gid : st->st_gid, + AT_SYMLINK_NOFOLLOW) < 0) return -errno; return 0; } -static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) { +static int fd_copy_regular( + int df, + const char *from, + const struct stat *st, + int dt, + const char *to, + uid_t override_uid, + gid_t override_gid, + CopyFlags copy_flags) { + _cleanup_close_ int fdf = -1, fdt = -1; struct timespec ts[2]; int r, q; @@ -214,13 +236,15 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int if (fdt < 0) return -errno; - r = copy_bytes(fdf, fdt, (uint64_t) -1, true); + r = copy_bytes(fdf, fdt, (uint64_t) -1, copy_flags); if (r < 0) { unlinkat(dt, to, 0); return r; } - if (fchown(fdt, st->st_uid, st->st_gid) < 0) + if (fchown(fdt, + uid_is_valid(override_uid) ? override_uid : st->st_uid, + gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0) r = -errno; if (fchmod(fdt, st->st_mode & 07777) < 0) @@ -229,7 +253,6 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int ts[0] = st->st_atim; ts[1] = st->st_mtim; (void) futimens(fdt, ts); - (void) copy_xattr(fdf, fdt); q = close(fdt); @@ -243,7 +266,15 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int return r; } -static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) { +static int fd_copy_fifo( + int df, + const char *from, + const struct stat *st, + int dt, + const char *to, + uid_t override_uid, + gid_t override_gid, + CopyFlags copy_flags) { int r; assert(from); @@ -254,7 +285,10 @@ static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, if (r < 0) return -errno; - if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0) + if (fchownat(dt, to, + uid_is_valid(override_uid) ? override_uid : st->st_uid, + gid_is_valid(override_gid) ? override_gid : st->st_gid, + AT_SYMLINK_NOFOLLOW) < 0) r = -errno; if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0) @@ -263,7 +297,15 @@ static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, return r; } -static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) { +static int fd_copy_node( + int df, + const char *from, + const struct stat *st, + int dt, + const char *to, + uid_t override_uid, + gid_t override_gid, + CopyFlags copy_flags) { int r; assert(from); @@ -274,7 +316,10 @@ static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, if (r < 0) return -errno; - if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0) + if (fchownat(dt, to, + uid_is_valid(override_uid) ? override_uid : st->st_uid, + gid_is_valid(override_gid) ? override_gid : st->st_gid, + AT_SYMLINK_NOFOLLOW) < 0) r = -errno; if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0) @@ -290,7 +335,9 @@ static int fd_copy_directory( int dt, const char *to, dev_t original_device, - bool merge) { + uid_t override_uid, + gid_t override_gid, + CopyFlags copy_flags) { _cleanup_close_ int fdf = -1, fdt = -1; _cleanup_closedir_ DIR *d = NULL; @@ -316,7 +363,7 @@ static int fd_copy_directory( r = mkdirat(dt, to, st->st_mode & 07777); if (r >= 0) created = true; - else if (errno == EEXIST && merge) + else if (errno == EEXIST && (copy_flags & COPY_MERGE)) created = false; else return -errno; @@ -343,19 +390,19 @@ static int fd_copy_directory( continue; if (S_ISREG(buf.st_mode)) - q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name); + q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags); else if (S_ISDIR(buf.st_mode)) - q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge); + q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, override_uid, override_gid, copy_flags); else if (S_ISLNK(buf.st_mode)) - q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name); + q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags); else if (S_ISFIFO(buf.st_mode)) - q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name); + q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags); else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode) || S_ISSOCK(buf.st_mode)) - q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name); + q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags); else q = -EOPNOTSUPP; - if (q == -EEXIST && merge) + if (q == -EEXIST && (copy_flags & COPY_MERGE)) q = 0; if (q < 0) @@ -368,7 +415,9 @@ static int fd_copy_directory( st->st_mtim }; - if (fchown(fdt, st->st_uid, st->st_gid) < 0) + if (fchown(fdt, + uid_is_valid(override_uid) ? override_uid : st->st_uid, + gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0) r = -errno; if (fchmod(fdt, st->st_mode & 07777) < 0) @@ -381,7 +430,7 @@ static int fd_copy_directory( return r; } -int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) { +int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) { struct stat st; assert(from); @@ -391,24 +440,24 @@ int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) return -errno; if (S_ISREG(st.st_mode)) - return fd_copy_regular(fdf, from, &st, fdt, to); + return fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags); else if (S_ISDIR(st.st_mode)) - return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge); + return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, override_uid, override_gid, copy_flags); else if (S_ISLNK(st.st_mode)) - return fd_copy_symlink(fdf, from, &st, fdt, to); + return fd_copy_symlink(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags); else if (S_ISFIFO(st.st_mode)) - return fd_copy_fifo(fdf, from, &st, fdt, to); + return fd_copy_fifo(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags); else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode)) - return fd_copy_node(fdf, from, &st, fdt, to); + return fd_copy_node(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags); else return -EOPNOTSUPP; } -int copy_tree(const char *from, const char *to, bool merge) { - return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge); +int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) { + return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags); } -int copy_directory_fd(int dirfd, const char *to, bool merge) { +int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags) { struct stat st; assert(dirfd >= 0); @@ -420,10 +469,10 @@ int copy_directory_fd(int dirfd, const char *to, bool merge) { if (!S_ISDIR(st.st_mode)) return -ENOTDIR; - return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge); + return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, UID_INVALID, GID_INVALID, copy_flags); } -int copy_directory(const char *from, const char *to, bool merge) { +int copy_directory(const char *from, const char *to, CopyFlags copy_flags) { struct stat st; assert(from); @@ -435,10 +484,10 @@ int copy_directory(const char *from, const char *to, bool merge) { if (!S_ISDIR(st.st_mode)) return -ENOTDIR; - return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge); + return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, UID_INVALID, GID_INVALID, copy_flags); } -int copy_file_fd(const char *from, int fdt, bool try_reflink) { +int copy_file_fd(const char *from, int fdt, CopyFlags copy_flags) { _cleanup_close_ int fdf = -1; int r; @@ -449,7 +498,7 @@ int copy_file_fd(const char *from, int fdt, bool try_reflink) { if (fdf < 0) return -errno; - r = copy_bytes(fdf, fdt, (uint64_t) -1, try_reflink); + r = copy_bytes(fdf, fdt, (uint64_t) -1, copy_flags); (void) copy_times(fdf, fdt); (void) copy_xattr(fdf, fdt); @@ -457,7 +506,7 @@ int copy_file_fd(const char *from, int fdt, bool try_reflink) { return r; } -int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) { +int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags) { int fdt = -1, r; assert(from); @@ -472,7 +521,7 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned if (chattr_flags != 0) (void) chattr_fd(fdt, chattr_flags, (unsigned) -1); - r = copy_file_fd(from, fdt, true); + r = copy_file_fd(from, fdt, copy_flags); if (r < 0) { close(fdt); unlink(to); @@ -487,7 +536,7 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned return 0; } -int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) { +int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags) { _cleanup_free_ char *t = NULL; int r; @@ -498,18 +547,18 @@ int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace if (r < 0) return r; - r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags); + r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags, copy_flags); if (r < 0) return r; - if (replace) { + if (copy_flags & COPY_REPLACE) { r = renameat(AT_FDCWD, t, AT_FDCWD, to); if (r < 0) r = -errno; } else r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to); if (r < 0) { - (void) unlink_noerrno(t); + (void) unlink(t); return r; } diff --git a/src/basic/copy.h b/src/basic/copy.h index b5d08ebafe..4f3e11423e 100644 --- a/src/basic/copy.h +++ b/src/basic/copy.h @@ -24,13 +24,19 @@ #include <stdint.h> #include <sys/types.h> -int copy_file_fd(const char *from, int to, bool try_reflink); -int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags); -int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags); -int copy_tree(const char *from, const char *to, bool merge); -int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge); -int copy_directory_fd(int dirfd, const char *to, bool merge); -int copy_directory(const char *from, const char *to, bool merge); -int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink); +typedef enum CopyFlags { + COPY_REFLINK = 0x1, /* try to reflink */ + COPY_MERGE = 0x2, /* merge existing trees with our new one to copy */ + COPY_REPLACE = 0x4, /* replace an existing file if there's one */ +} CopyFlags; + +int copy_file_fd(const char *from, int to, CopyFlags copy_flags); +int copy_file(const char *from, const char *to, int open_flags, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags); +int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags); +int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags); +int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags); +int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags); +int copy_directory(const char *from, const char *to, CopyFlags copy_flags); +int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags); int copy_times(int fdf, int fdt); int copy_xattr(int fdf, int fdt); diff --git a/src/basic/def.h b/src/basic/def.h index 2266eff650..200ea973c1 100644 --- a/src/basic/def.h +++ b/src/basic/def.h @@ -36,7 +36,9 @@ /* The default value for the net.unix.max_dgram_qlen sysctl */ #define DEFAULT_UNIX_MAX_DGRAM_QLEN 512UL -#define SYSTEMD_CGROUP_CONTROLLER "name=systemd" +#define SYSTEMD_CGROUP_CONTROLLER_LEGACY "name=systemd" +#define SYSTEMD_CGROUP_CONTROLLER_HYBRID "name=unified" +#define SYSTEMD_CGROUP_CONTROLLER "_systemd" #define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT #define SIGNALS_IGNORE SIGPIPE @@ -73,18 +75,18 @@ #define NOTIFY_BUFFER_MAX PIPE_BUF #ifdef HAVE_SPLIT_USR -#define _CONF_PATHS_SPLIT_USR(n) "/lib/" n "\0" +# define _CONF_PATHS_SPLIT_USR(n) "/lib/" n "\0" #else -#define _CONF_PATHS_SPLIT_USR(n) +# define _CONF_PATHS_SPLIT_USR(n) #endif /* Return a nulstr for a standard cascade of configuration paths, * suitable to pass to conf_files_list_nulstr() or config_parse_many_nulstr() * to implement drop-in directories for extending configuration * files. */ -#define CONF_PATHS_NULSTR(n) \ - "/etc/" n "\0" \ - "/run/" n "\0" \ - "/usr/local/lib/" n "\0" \ - "/usr/lib/" n "\0" \ +#define CONF_PATHS_NULSTR(n) \ + "/etc/" n "\0" \ + "/run/" n "\0" \ + "/usr/local/lib/" n "\0" \ + "/usr/lib/" n "\0" \ _CONF_PATHS_SPLIT_USR(n) diff --git a/src/basic/env-util.c b/src/basic/env-util.c index 96da38d45e..1ec574e8a0 100644 --- a/src/basic/env-util.c +++ b/src/basic/env-util.c @@ -26,6 +26,7 @@ #include "alloc-util.h" #include "env-util.h" +#include "escape.h" #include "extract-word.h" #include "macro.h" #include "parse-util.h" @@ -247,7 +248,7 @@ fail: return NULL; } -_pure_ static bool env_match(const char *t, const char *pattern) { +static bool env_match(const char *t, const char *pattern) { assert(t); assert(pattern); @@ -273,6 +274,19 @@ _pure_ static bool env_match(const char *t, const char *pattern) { return false; } +static bool env_entry_has_name(const char *entry, const char *name) { + const char *t; + + assert(entry); + assert(name); + + t = startswith(entry, name); + if (!t) + return false; + + return *t == '='; +} + char **strv_env_delete(char **x, unsigned n_lists, ...) { size_t n, i = 0; char **k, **r; @@ -386,18 +400,24 @@ char **strv_env_unset_many(char **l, ...) { int strv_env_replace(char ***l, char *p) { char **f; + const char *t, *name; assert(p); /* Replace first occurrence of the env var or add a new one in the * string list. Drop other occurences. Edits in-place. Does not copy p. + * p must be a valid key=value assignment. */ + t = strchr(p, '='); + assert(t); + + name = strndupa(p, t - p); + for (f = *l; f && *f; f++) - if (env_match(*f, p)) { - free(*f); - *f = p; - strv_env_unset(f + 1, p); + if (env_entry_has_name(*f, name)) { + free_and_replace(*f, p); + strv_env_unset(f + 1, *f); return 0; } @@ -434,7 +454,7 @@ fail: return NULL; } -char *strv_env_get_n(char **l, const char *name, size_t k) { +char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) { char **i; assert(name); @@ -442,18 +462,25 @@ char *strv_env_get_n(char **l, const char *name, size_t k) { if (k <= 0) return NULL; - STRV_FOREACH(i, l) + STRV_FOREACH_BACKWARDS(i, l) if (strneq(*i, name, k) && (*i)[k] == '=') return *i + k + 1; + if (flags & REPLACE_ENV_USE_ENVIRONMENT) { + const char *t; + + t = strndupa(name, k); + return getenv(t); + }; + return NULL; } char *strv_env_get(char **l, const char *name) { assert(name); - return strv_env_get_n(l, name, strlen(name)); + return strv_env_get_n(l, name, strlen(name), 0); } char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) { @@ -492,19 +519,26 @@ char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const cha return e; } -char *replace_env(const char *format, char **env) { +char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) { enum { WORD, CURLY, - VARIABLE + VARIABLE, + VARIABLE_RAW, + TEST, + DEFAULT_VALUE, + ALTERNATE_VALUE, } state = WORD; - const char *e, *word = format; - char *r = NULL, *k; + const char *e, *word = format, *test_value; + char *k; + _cleanup_free_ char *r = NULL; + size_t i, len; + int nest = 0; assert(format); - for (e = format; *e; e ++) { + for (e = format, i = 0; *e && i < n; e ++, i ++) { switch (state) { @@ -517,24 +551,36 @@ char *replace_env(const char *format, char **env) { if (*e == '{') { k = strnappend(r, word, e-word-1); if (!k) - goto fail; + return NULL; free(r); r = k; word = e-1; state = VARIABLE; - + nest++; } else if (*e == '$') { k = strnappend(r, word, e-word); if (!k) - goto fail; + return NULL; free(r); r = k; word = e+1; state = WORD; + + } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_CHARS_ENV_NAME, *e)) { + k = strnappend(r, word, e-word-1); + if (!k) + return NULL; + + free(r); + r = k; + + word = e-1; + state = VARIABLE_RAW; + } else state = WORD; break; @@ -543,31 +589,109 @@ char *replace_env(const char *format, char **env) { if (*e == '}') { const char *t; - t = strempty(strv_env_get_n(env, word+2, e-word-2)); + t = strv_env_get_n(env, word+2, e-word-2, flags); k = strappend(r, t); if (!k) - goto fail; + return NULL; free(r); r = k; word = e+1; state = WORD; + } else if (*e == ':') { + if (!(flags & REPLACE_ENV_ALLOW_EXTENDED)) + /* Treat this as unsupported syntax, i.e. do no replacement */ + state = WORD; + else { + len = e-word-2; + state = TEST; + } + } + break; + + case TEST: + if (*e == '-') + state = DEFAULT_VALUE; + else if (*e == '+') + state = ALTERNATE_VALUE; + else { + state = WORD; + break; + } + + test_value = e+1; + break; + + case DEFAULT_VALUE: /* fall through */ + case ALTERNATE_VALUE: + assert(flags & REPLACE_ENV_ALLOW_EXTENDED); + + if (*e == '{') { + nest++; + break; + } + + if (*e != '}') + break; + + nest--; + if (nest == 0) { + const char *t; + _cleanup_free_ char *v = NULL; + + t = strv_env_get_n(env, word+2, len, flags); + + if (t && state == ALTERNATE_VALUE) + t = v = replace_env_n(test_value, e-test_value, env, flags); + else if (!t && state == DEFAULT_VALUE) + t = v = replace_env_n(test_value, e-test_value, env, flags); + + k = strappend(r, t); + if (!k) + return NULL; + + free(r); + r = k; + + word = e+1; + state = WORD; + } + break; + + case VARIABLE_RAW: + assert(flags & REPLACE_ENV_ALLOW_BRACELESS); + + if (!strchr(VALID_CHARS_ENV_NAME, *e)) { + const char *t; + + t = strv_env_get_n(env, word+1, e-word-1, flags); + + k = strappend(r, t); + if (!k) + return NULL; + + free(r); + r = k; + + word = e--; + i--; + state = WORD; } break; } } - k = strnappend(r, word, e-word); - if (!k) - goto fail; + if (state == VARIABLE_RAW) { + const char *t; - free(r); - return k; + assert(flags & REPLACE_ENV_ALLOW_BRACELESS); -fail: - return mfree(r); + t = strv_env_get_n(env, word+1, e-word-1, flags); + return strappend(r, t); + } else + return strnappend(r, word, e-word); } char **replace_env_argv(char **argv, char **env) { @@ -623,7 +747,7 @@ char **replace_env_argv(char **argv, char **env) { } /* If ${FOO} appears as part of a word, replace it by the variable as-is */ - ret[k] = replace_env(*i, env); + ret[k] = replace_env(*i, env, 0); if (!ret[k]) { strv_free(ret); return NULL; @@ -644,3 +768,39 @@ int getenv_bool(const char *p) { return parse_boolean(e); } + +int serialize_environment(FILE *f, char **environment) { + char **e; + + STRV_FOREACH(e, environment) { + _cleanup_free_ char *ce; + + ce = cescape(*e); + if (!ce) + return -ENOMEM; + + fprintf(f, "env=%s\n", *e); + } + + /* caller should call ferror() */ + + return 0; +} + +int deserialize_environment(char ***environment, const char *line) { + char *uce = NULL; + int r; + + assert(line); + assert(environment); + + assert(startswith(line, "env=")); + r = cunescape(line + 4, UNESCAPE_RELAX, &uce); + if (r < 0) + return r; + + if (!env_assignment_is_valid(uce)) + return -EINVAL; + + return strv_env_replace(environment, uce); +} diff --git a/src/basic/env-util.h b/src/basic/env-util.h index 8cb0fc2131..e88fa6aac0 100644 --- a/src/basic/env-util.h +++ b/src/basic/env-util.h @@ -21,6 +21,7 @@ #include <stdbool.h> #include <stddef.h> +#include <stdio.h> #include "macro.h" @@ -28,9 +29,19 @@ bool env_name_is_valid(const char *e); bool env_value_is_valid(const char *e); bool env_assignment_is_valid(const char *e); -char *replace_env(const char *format, char **env); +enum { + REPLACE_ENV_USE_ENVIRONMENT = 1u, + REPLACE_ENV_ALLOW_BRACELESS = 2u, + REPLACE_ENV_ALLOW_EXTENDED = 4u, +}; + +char *replace_env_n(const char *format, size_t n, char **env, unsigned flags); char **replace_env_argv(char **argv, char **env); +static inline char *replace_env(const char *format, char **env, unsigned flags) { + return replace_env_n(format, strlen(format), env, flags); +} + bool strv_env_is_valid(char **e); #define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL) char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata); @@ -46,7 +57,10 @@ char **strv_env_unset(char **l, const char *p); /* In place ... */ char **strv_env_unset_many(char **l, ...) _sentinel_; int strv_env_replace(char ***l, char *p); /* In place ... */ -char *strv_env_get_n(char **l, const char *name, size_t k) _pure_; +char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_; char *strv_env_get(char **x, const char *n) _pure_; int getenv_bool(const char *p); + +int serialize_environment(FILE *f, char **environment); +int deserialize_environment(char ***environment, const char *line); diff --git a/src/basic/exec-util.c b/src/basic/exec-util.c new file mode 100644 index 0000000000..aced9e8e3d --- /dev/null +++ b/src/basic/exec-util.c @@ -0,0 +1,360 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <dirent.h> +#include <errno.h> +#include <sys/prctl.h> +#include <sys/types.h> +#include <unistd.h> +#include <stdio.h> + +#include "alloc-util.h" +#include "conf-files.h" +#include "env-util.h" +#include "exec-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "hashmap.h" +#include "macro.h" +#include "process-util.h" +#include "set.h" +#include "signal-util.h" +#include "stat-util.h" +#include "string-util.h" +#include "strv.h" +#include "terminal-util.h" +#include "util.h" + +/* Put this test here for a lack of better place */ +assert_cc(EAGAIN == EWOULDBLOCK); + +static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) { + + pid_t _pid; + + if (null_or_empty_path(path)) { + log_debug("%s is empty (a mask).", path); + return 0; + } + + _pid = fork(); + if (_pid < 0) + return log_error_errno(errno, "Failed to fork: %m"); + if (_pid == 0) { + char *_argv[2]; + + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); + + if (stdout_fd >= 0) { + /* If the fd happens to be in the right place, go along with that */ + if (stdout_fd != STDOUT_FILENO && + dup2(stdout_fd, STDOUT_FILENO) < 0) + return -errno; + + fd_cloexec(STDOUT_FILENO, false); + } + + if (!argv) { + _argv[0] = (char*) path; + _argv[1] = NULL; + argv = _argv; + } else + argv[0] = (char*) path; + + execv(path, argv); + log_error_errno(errno, "Failed to execute %s: %m", path); + _exit(EXIT_FAILURE); + } + + log_debug("Spawned %s as " PID_FMT ".", path, _pid); + *pid = _pid; + return 1; +} + +static int do_execute( + char **directories, + usec_t timeout, + gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX], + void* const callback_args[_STDOUT_CONSUME_MAX], + int output_fd, + char *argv[]) { + + _cleanup_hashmap_free_free_ Hashmap *pids = NULL; + _cleanup_strv_free_ char **paths = NULL; + char **path; + int r; + + /* We fork this all off from a child process so that we can somewhat cleanly make + * use of SIGALRM to set a time limit. + * + * If callbacks is nonnull, execution is serial. Otherwise, we default to parallel. + */ + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + + assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); + + r = conf_files_list_strv(&paths, NULL, NULL, (const char* const*) directories); + if (r < 0) + return r; + + if (!callbacks) { + pids = hashmap_new(NULL); + if (!pids) + return log_oom(); + } + + /* Abort execution of this process after the timout. We simply rely on SIGALRM as + * default action terminating the process, and turn on alarm(). */ + + if (timeout != USEC_INFINITY) + alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC); + + STRV_FOREACH(path, paths) { + _cleanup_free_ char *t = NULL; + _cleanup_close_ int fd = -1; + pid_t pid; + + t = strdup(*path); + if (!t) + return log_oom(); + + if (callbacks) { + fd = open_serialization_fd(basename(*path)); + if (fd < 0) + return log_error_errno(fd, "Failed to open serialization file: %m"); + } + + r = do_spawn(t, argv, fd, &pid); + if (r <= 0) + continue; + + if (pids) { + r = hashmap_put(pids, PID_TO_PTR(pid), t); + if (r < 0) + return log_oom(); + t = NULL; + } else { + r = wait_for_terminate_and_warn(t, pid, true); + if (r < 0) + continue; + + if (lseek(fd, 0, SEEK_SET) < 0) + return log_error_errno(errno, "Failed to seek on serialization fd: %m"); + + r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]); + fd = -1; + if (r < 0) + return log_error_errno(r, "Failed to process output from %s: %m", *path); + } + } + + if (callbacks) { + r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]); + if (r < 0) + return log_error_errno(r, "Callback two failed: %m"); + } + + while (!hashmap_isempty(pids)) { + _cleanup_free_ char *t = NULL; + pid_t pid; + + pid = PTR_TO_PID(hashmap_first_key(pids)); + assert(pid > 0); + + t = hashmap_remove(pids, PID_TO_PTR(pid)); + assert(t); + + wait_for_terminate_and_warn(t, pid, true); + } + + return 0; +} + +int execute_directories( + const char* const* directories, + usec_t timeout, + gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX], + void* const callback_args[_STDOUT_CONSUME_MAX], + char *argv[]) { + + pid_t executor_pid; + char *name; + char **dirs = (char**) directories; + _cleanup_close_ int fd = -1; + int r; + + assert(!strv_isempty(dirs)); + + name = basename(dirs[0]); + assert(!isempty(name)); + + if (callbacks) { + assert(callback_args); + assert(callbacks[STDOUT_GENERATE]); + assert(callbacks[STDOUT_COLLECT]); + assert(callbacks[STDOUT_CONSUME]); + + fd = open_serialization_fd(name); + if (fd < 0) + return log_error_errno(fd, "Failed to open serialization file: %m"); + } + + /* Executes all binaries in the directories serially or in parallel and waits for + * them to finish. Optionally a timeout is applied. If a file with the same name + * exists in more than one directory, the earliest one wins. */ + + executor_pid = fork(); + if (executor_pid < 0) + return log_error_errno(errno, "Failed to fork: %m"); + + if (executor_pid == 0) { + r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv); + _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); + } + + r = wait_for_terminate_and_warn(name, executor_pid, true); + if (r < 0) + return log_error_errno(r, "Execution failed: %m"); + if (r > 0) { + /* non-zero return code from child */ + log_error("Forker process failed."); + return -EREMOTEIO; + } + + if (!callbacks) + return 0; + + if (lseek(fd, 0, SEEK_SET) < 0) + return log_error_errno(errno, "Failed to rewind serialization fd: %m"); + + r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]); + fd = -1; + if (r < 0) + return log_error_errno(r, "Failed to parse returned data: %m"); + return 0; +} + +static int gather_environment_generate(int fd, void *arg) { + char ***env = arg, **x, **y; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_strv_free_ char **new; + int r; + + /* Read a series of VAR=value assignments from fd, use them to update the list of + * variables in env. Also update the exported environment. + * + * fd is always consumed, even on error. + */ + + assert(env); + + f = fdopen(fd, "r"); + if (!f) { + safe_close(fd); + return -errno; + } + + r = load_env_file_pairs(f, NULL, NULL, &new); + if (r < 0) + return r; + + STRV_FOREACH_PAIR(x, y, new) { + char *p; + + if (!env_name_is_valid(*x)) { + log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x); + continue; + } + + p = strjoin(*x, "=", *y); + if (!p) + return -ENOMEM; + + r = strv_env_replace(env, p); + if (r < 0) + return r; + + if (setenv(*x, *y, true) < 0) + return -errno; + } + + return r; +} + +static int gather_environment_collect(int fd, void *arg) { + char ***env = arg; + _cleanup_fclose_ FILE *f = NULL; + int r; + + /* Write out a series of env=cescape(VAR=value) assignments to fd. */ + + assert(env); + + f = fdopen(fd, "w"); + if (!f) { + safe_close(fd); + return -errno; + } + + r = serialize_environment(f, *env); + if (r < 0) + return r; + + if (ferror(f)) + return errno > 0 ? -errno : -EIO; + + return 0; +} + +static int gather_environment_consume(int fd, void *arg) { + char ***env = arg; + _cleanup_fclose_ FILE *f = NULL; + char line[LINE_MAX]; + int r = 0, k; + + /* Read a series of env=cescape(VAR=value) assignments from fd into env. */ + + assert(env); + + f = fdopen(fd, "r"); + if (!f) { + safe_close(fd); + return -errno; + } + + FOREACH_LINE(line, f, return -EIO) { + truncate_nl(line); + + k = deserialize_environment(env, line); + if (k < 0) + log_error_errno(k, "Invalid line \"%s\": %m", line); + if (k < 0 && r == 0) + r = k; + } + + return r; +} + +const gather_stdout_callback_t gather_environment[] = { + gather_environment_generate, + gather_environment_collect, + gather_environment_consume, +}; diff --git a/src/basic/exec-util.h b/src/basic/exec-util.h new file mode 100644 index 0000000000..72009799b2 --- /dev/null +++ b/src/basic/exec-util.h @@ -0,0 +1,40 @@ +/*** + This file is part of systemd. + + Copyright 2017 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdbool.h> + +#include "time-util.h" + +typedef int (*gather_stdout_callback_t) (int fd, void *arg); + +enum { + STDOUT_GENERATE, /* from generators to helper process */ + STDOUT_COLLECT, /* from helper process to main process */ + STDOUT_CONSUME, /* process data in main process */ + _STDOUT_CONSUME_MAX, +}; + +int execute_directories( + const char* const* directories, + usec_t timeout, + gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX], + void* const callback_args[_STDOUT_CONSUME_MAX], + char *argv[]); + +extern const gather_stdout_callback_t gather_environment[_STDOUT_CONSUME_MAX]; diff --git a/src/basic/fileio.c b/src/basic/fileio.c index c43b0583a4..7c2c2b38f5 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -30,6 +30,7 @@ #include "alloc-util.h" #include "ctype.h" +#include "env-util.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" @@ -553,13 +554,14 @@ static int parse_env_file_internal( } } - if (state == PRE_VALUE || - state == VALUE || - state == VALUE_ESCAPE || - state == SINGLE_QUOTE_VALUE || - state == SINGLE_QUOTE_VALUE_ESCAPE || - state == DOUBLE_QUOTE_VALUE || - state == DOUBLE_QUOTE_VALUE_ESCAPE) { + if (IN_SET(state, + PRE_VALUE, + VALUE, + VALUE_ESCAPE, + SINGLE_QUOTE_VALUE, + SINGLE_QUOTE_VALUE_ESCAPE, + DOUBLE_QUOTE_VALUE, + DOUBLE_QUOTE_VALUE_ESCAPE)) { key[n_key] = 0; @@ -586,14 +588,9 @@ fail: return r; } -static int parse_env_file_push( +static int check_utf8ness_and_warn( const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - - const char *k; - va_list aq, *ap = userdata; + const char *key, char *value) { if (!utf8_is_valid(key)) { _cleanup_free_ char *p = NULL; @@ -611,6 +608,23 @@ static int parse_env_file_push( return -EINVAL; } + return 0; +} + +static int parse_env_file_push( + const char *filename, unsigned line, + const char *key, char *value, + void *userdata, + int *n_pushed) { + + const char *k; + va_list aq, *ap = userdata; + int r; + + r = check_utf8ness_and_warn(filename, line, key, value); + if (r < 0) + return r; + va_copy(aq, *ap); while ((k = va_arg(aq, const char *))) { @@ -662,27 +676,19 @@ static int load_env_file_push( char *p; int r; - if (!utf8_is_valid(key)) { - _cleanup_free_ char *t = utf8_escape_invalid(key); - - log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t); - return -EINVAL; - } - - if (value && !utf8_is_valid(value)) { - _cleanup_free_ char *t = utf8_escape_invalid(value); - - log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t); - return -EINVAL; - } + r = check_utf8ness_and_warn(filename, line, key, value); + if (r < 0) + return r; - p = strjoin(key, "=", strempty(value)); + p = strjoin(key, "=", value); if (!p) return -ENOMEM; - r = strv_consume(m, p); - if (r < 0) + r = strv_env_replace(m, p); + if (r < 0) { + free(p); return r; + } if (n_pushed) (*n_pushed)++; @@ -716,19 +722,9 @@ static int load_env_file_push_pairs( char ***m = userdata; int r; - if (!utf8_is_valid(key)) { - _cleanup_free_ char *t = utf8_escape_invalid(key); - - log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t); - return -EINVAL; - } - - if (value && !utf8_is_valid(value)) { - _cleanup_free_ char *t = utf8_escape_invalid(value); - - log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t); - return -EINVAL; - } + r = check_utf8ness_and_warn(filename, line, key, value); + if (r < 0) + return r; r = strv_extend(m, key); if (r < 0) @@ -767,6 +763,52 @@ int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char ** return 0; } +static int merge_env_file_push( + const char *filename, unsigned line, + const char *key, char *value, + void *userdata, + int *n_pushed) { + + char ***env = userdata; + char *expanded_value; + + assert(env); + + if (!value) { + log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key); + return 0; + } + + if (!env_name_is_valid(key)) { + log_error("%s:%u: invalid variable name \"%s\", ignoring.", strna(filename), line, key); + free(value); + return 0; + } + + expanded_value = replace_env(value, *env, + REPLACE_ENV_USE_ENVIRONMENT| + REPLACE_ENV_ALLOW_BRACELESS| + REPLACE_ENV_ALLOW_EXTENDED); + if (!expanded_value) + return -ENOMEM; + + free_and_replace(value, expanded_value); + + return load_env_file_push(filename, line, key, value, env, n_pushed); +} + +int merge_env_file( + char ***env, + FILE *f, + const char *fname) { + + /* NOTE: this function supports braceful and braceless variable expansions, + * plus "extended" substitutions, unlike other exported parsing functions. + */ + + return parse_env_file_internal(f, fname, NEWLINE, merge_env_file_push, env, NULL); +} + static void write_env_var(FILE *f, const char *v) { const char *p; @@ -1342,6 +1384,25 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) { return fd; } +int open_serialization_fd(const char *ident) { + int fd = -1; + + fd = memfd_create(ident, MFD_CLOEXEC); + if (fd < 0) { + const char *path; + + path = getpid() == 1 ? "/run/systemd" : "/tmp"; + fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC); + if (fd < 0) + return fd; + + log_debug("Serializing %s to %s.", ident, path); + } else + log_debug("Serializing %s to memfd.", ident); + + return fd; +} + int link_tmpfile(int fd, const char *path, const char *target) { assert(fd >= 0); diff --git a/src/basic/fileio.h b/src/basic/fileio.h index 17b38a5d60..e547614cc4 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -48,6 +48,8 @@ int parse_env_file(const char *fname, const char *separator, ...) _sentinel_; int load_env_file(FILE *f, const char *fname, const char *separator, char ***l); int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l); +int merge_env_file(char ***env, FILE *f, const char *fname); + int write_env_file(const char *fname, char **l); int executable_is_script(const char *path, char **interpreter); @@ -84,6 +86,7 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) int open_tmpfile_unlinkable(const char *directory, int flags); int open_tmpfile_linkable(const char *target, int flags, char **ret_path); +int open_serialization_fd(const char *ident); int link_tmpfile(int fd, const char *path, const char *target); diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 5fe5c71ff0..094acf1799 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -91,3 +91,9 @@ static inline void rmdir_and_free(char *p) { free(p); } DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free); + +static inline void unlink_and_free(char *p) { + (void) unlink(p); + free(p); +} +DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free); diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c index e44a357287..a94037b303 100644 --- a/src/basic/hostname-util.c +++ b/src/basic/hostname-util.c @@ -55,7 +55,7 @@ char* gethostname_malloc(void) { assert_se(uname(&u) >= 0); if (isempty(u.nodename) || streq(u.nodename, "(none)")) - return strdup(u.sysname); + return strdup(FALLBACK_HOSTNAME); return strdup(u.nodename); } diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c index 3b06cb00ad..3927df2955 100644 --- a/src/basic/in-addr-util.c +++ b/src/basic/in-addr-util.c @@ -66,6 +66,18 @@ int in_addr_is_link_local(int family, const union in_addr_union *u) { return -EAFNOSUPPORT; } +int in_addr_is_multicast(int family, const union in_addr_union *u) { + assert(u); + + if (family == AF_INET) + return IN_MULTICAST(be32toh(u->in.s_addr)); + + if (family == AF_INET6) + return IN6_IS_ADDR_MULTICAST(&u->in6); + + return -EAFNOSUPPORT; +} + bool in4_addr_is_localhost(const struct in_addr *a) { assert(a); diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h index 64a812c322..51a5aa67e4 100644 --- a/src/basic/in-addr-util.h +++ b/src/basic/in-addr-util.h @@ -39,6 +39,8 @@ struct in_addr_data { bool in4_addr_is_null(const struct in_addr *a); int in_addr_is_null(int family, const union in_addr_union *u); +int in_addr_is_multicast(int family, const union in_addr_union *u); + bool in4_addr_is_link_local(const struct in_addr *a); int in_addr_is_link_local(int family, const union in_addr_union *u); diff --git a/src/basic/journal-importer.c b/src/basic/journal-importer.c new file mode 100644 index 0000000000..4c13e46a49 --- /dev/null +++ b/src/basic/journal-importer.c @@ -0,0 +1,481 @@ +/*** + This file is part of systemd. + + Copyright 2014 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <unistd.h> + +#include "alloc-util.h" +#include "journal-importer.h" +#include "fd-util.h" +#include "parse-util.h" +#include "string-util.h" + +enum { + IMPORTER_STATE_LINE = 0, /* waiting to read, or reading line */ + IMPORTER_STATE_DATA_START, /* reading binary data header */ + IMPORTER_STATE_DATA, /* reading binary data */ + IMPORTER_STATE_DATA_FINISH, /* expecting newline */ + IMPORTER_STATE_EOF, /* done */ +}; + +static int iovw_put(struct iovec_wrapper *iovw, void* data, size_t len) { + if (!GREEDY_REALLOC(iovw->iovec, iovw->size_bytes, iovw->count + 1)) + return log_oom(); + + iovw->iovec[iovw->count++] = (struct iovec) {data, len}; + return 0; +} + +static void iovw_free_contents(struct iovec_wrapper *iovw) { + iovw->iovec = mfree(iovw->iovec); + iovw->size_bytes = iovw->count = 0; +} + +static void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) { + size_t i; + + for (i = 0; i < iovw->count; i++) + iovw->iovec[i].iov_base = (char*) iovw->iovec[i].iov_base - old + new; +} + +size_t iovw_size(struct iovec_wrapper *iovw) { + size_t n = 0, i; + + for (i = 0; i < iovw->count; i++) + n += iovw->iovec[i].iov_len; + + return n; +} + +void journal_importer_cleanup(JournalImporter *imp) { + if (imp->fd >= 0 && !imp->passive_fd) { + log_debug("Closing %s (fd=%d)", imp->name ?: "importer", imp->fd); + safe_close(imp->fd); + } + + free(imp->buf); + iovw_free_contents(&imp->iovw); +} + +static char* realloc_buffer(JournalImporter *imp, size_t size) { + char *b, *old = imp->buf; + + b = GREEDY_REALLOC(imp->buf, imp->size, size); + if (!b) + return NULL; + + iovw_rebase(&imp->iovw, old, imp->buf); + + return b; +} + +static int get_line(JournalImporter *imp, char **line, size_t *size) { + ssize_t n; + char *c = NULL; + + assert(imp); + assert(imp->state == IMPORTER_STATE_LINE); + assert(imp->offset <= imp->filled); + assert(imp->filled <= imp->size); + assert(imp->buf == NULL || imp->size > 0); + assert(imp->fd >= 0); + + for (;;) { + if (imp->buf) { + size_t start = MAX(imp->scanned, imp->offset); + + c = memchr(imp->buf + start, '\n', + imp->filled - start); + if (c != NULL) + break; + } + + imp->scanned = imp->filled; + if (imp->scanned >= DATA_SIZE_MAX) { + log_error("Entry is bigger than %u bytes.", DATA_SIZE_MAX); + return -E2BIG; + } + + if (imp->passive_fd) + /* we have to wait for some data to come to us */ + return -EAGAIN; + + /* We know that imp->filled is at most DATA_SIZE_MAX, so if + we reallocate it, we'll increase the size at least a bit. */ + assert_cc(DATA_SIZE_MAX < ENTRY_SIZE_MAX); + if (imp->size - imp->filled < LINE_CHUNK && + !realloc_buffer(imp, MIN(imp->filled + LINE_CHUNK, ENTRY_SIZE_MAX))) + return log_oom(); + + assert(imp->buf); + assert(imp->size - imp->filled >= LINE_CHUNK || + imp->size == ENTRY_SIZE_MAX); + + n = read(imp->fd, + imp->buf + imp->filled, + imp->size - imp->filled); + if (n < 0) { + if (errno != EAGAIN) + log_error_errno(errno, "read(%d, ..., %zu): %m", + imp->fd, + imp->size - imp->filled); + return -errno; + } else if (n == 0) + return 0; + + imp->filled += n; + } + + *line = imp->buf + imp->offset; + *size = c + 1 - imp->buf - imp->offset; + imp->offset += *size; + + return 1; +} + +static int fill_fixed_size(JournalImporter *imp, void **data, size_t size) { + + assert(imp); + assert(imp->state == IMPORTER_STATE_DATA_START || + imp->state == IMPORTER_STATE_DATA || + imp->state == IMPORTER_STATE_DATA_FINISH); + assert(size <= DATA_SIZE_MAX); + assert(imp->offset <= imp->filled); + assert(imp->filled <= imp->size); + assert(imp->buf != NULL || imp->size == 0); + assert(imp->buf == NULL || imp->size > 0); + assert(imp->fd >= 0); + assert(data); + + while (imp->filled - imp->offset < size) { + int n; + + if (imp->passive_fd) + /* we have to wait for some data to come to us */ + return -EAGAIN; + + if (!realloc_buffer(imp, imp->offset + size)) + return log_oom(); + + n = read(imp->fd, imp->buf + imp->filled, + imp->size - imp->filled); + if (n < 0) { + if (errno != EAGAIN) + log_error_errno(errno, "read(%d, ..., %zu): %m", imp->fd, + imp->size - imp->filled); + return -errno; + } else if (n == 0) + return 0; + + imp->filled += n; + } + + *data = imp->buf + imp->offset; + imp->offset += size; + + return 1; +} + +static int get_data_size(JournalImporter *imp) { + int r; + void *data; + + assert(imp); + assert(imp->state == IMPORTER_STATE_DATA_START); + assert(imp->data_size == 0); + + r = fill_fixed_size(imp, &data, sizeof(uint64_t)); + if (r <= 0) + return r; + + imp->data_size = le64toh( *(uint64_t *) data ); + if (imp->data_size > DATA_SIZE_MAX) { + log_error("Stream declares field with size %zu > DATA_SIZE_MAX = %u", + imp->data_size, DATA_SIZE_MAX); + return -EINVAL; + } + if (imp->data_size == 0) + log_warning("Binary field with zero length"); + + return 1; +} + +static int get_data_data(JournalImporter *imp, void **data) { + int r; + + assert(imp); + assert(data); + assert(imp->state == IMPORTER_STATE_DATA); + + r = fill_fixed_size(imp, data, imp->data_size); + if (r <= 0) + return r; + + return 1; +} + +static int get_data_newline(JournalImporter *imp) { + int r; + char *data; + + assert(imp); + assert(imp->state == IMPORTER_STATE_DATA_FINISH); + + r = fill_fixed_size(imp, (void**) &data, 1); + if (r <= 0) + return r; + + assert(data); + if (*data != '\n') { + log_error("expected newline, got '%c'", *data); + return -EINVAL; + } + + return 1; +} + +static int process_dunder(JournalImporter *imp, char *line, size_t n) { + const char *timestamp; + int r; + + assert(line); + assert(n > 0); + assert(line[n-1] == '\n'); + + /* XXX: is it worth to support timestamps in extended format? + * We don't produce them, but who knows... */ + + timestamp = startswith(line, "__CURSOR="); + if (timestamp) + /* ignore __CURSOR */ + return 1; + + timestamp = startswith(line, "__REALTIME_TIMESTAMP="); + if (timestamp) { + long long unsigned x; + line[n-1] = '\0'; + r = safe_atollu(timestamp, &x); + if (r < 0) + log_warning("Failed to parse __REALTIME_TIMESTAMP: '%s'", timestamp); + else + imp->ts.realtime = x; + return r < 0 ? r : 1; + } + + timestamp = startswith(line, "__MONOTONIC_TIMESTAMP="); + if (timestamp) { + long long unsigned x; + line[n-1] = '\0'; + r = safe_atollu(timestamp, &x); + if (r < 0) + log_warning("Failed to parse __MONOTONIC_TIMESTAMP: '%s'", timestamp); + else + imp->ts.monotonic = x; + return r < 0 ? r : 1; + } + + timestamp = startswith(line, "__"); + if (timestamp) { + log_notice("Unknown dunder line %s", line); + return 1; + } + + /* no dunder */ + return 0; +} + +int journal_importer_process_data(JournalImporter *imp) { + int r; + + switch(imp->state) { + case IMPORTER_STATE_LINE: { + char *line, *sep; + size_t n = 0; + + assert(imp->data_size == 0); + + r = get_line(imp, &line, &n); + if (r < 0) + return r; + if (r == 0) { + imp->state = IMPORTER_STATE_EOF; + return r; + } + assert(n > 0); + assert(line[n-1] == '\n'); + + if (n == 1) { + log_trace("Received empty line, event is ready"); + return 1; + } + + r = process_dunder(imp, line, n); + if (r != 0) + return r < 0 ? r : 0; + + /* MESSAGE=xxx\n + or + COREDUMP\n + LLLLLLLL0011223344...\n + */ + sep = memchr(line, '=', n); + if (sep) { + /* chomp newline */ + n--; + + r = iovw_put(&imp->iovw, line, n); + if (r < 0) + return r; + } else { + /* replace \n with = */ + line[n-1] = '='; + + imp->field_len = n; + imp->state = IMPORTER_STATE_DATA_START; + + /* we cannot put the field in iovec until we have all data */ + } + + log_trace("Received: %.*s (%s)", (int) n, line, sep ? "text" : "binary"); + + return 0; /* continue */ + } + + case IMPORTER_STATE_DATA_START: + assert(imp->data_size == 0); + + r = get_data_size(imp); + // log_debug("get_data_size() -> %d", r); + if (r < 0) + return r; + if (r == 0) { + imp->state = IMPORTER_STATE_EOF; + return 0; + } + + imp->state = imp->data_size > 0 ? + IMPORTER_STATE_DATA : IMPORTER_STATE_DATA_FINISH; + + return 0; /* continue */ + + case IMPORTER_STATE_DATA: { + void *data; + char *field; + + assert(imp->data_size > 0); + + r = get_data_data(imp, &data); + // log_debug("get_data_data() -> %d", r); + if (r < 0) + return r; + if (r == 0) { + imp->state = IMPORTER_STATE_EOF; + return 0; + } + + assert(data); + + field = (char*) data - sizeof(uint64_t) - imp->field_len; + memmove(field + sizeof(uint64_t), field, imp->field_len); + + r = iovw_put(&imp->iovw, field + sizeof(uint64_t), imp->field_len + imp->data_size); + if (r < 0) + return r; + + imp->state = IMPORTER_STATE_DATA_FINISH; + + return 0; /* continue */ + } + + case IMPORTER_STATE_DATA_FINISH: + r = get_data_newline(imp); + // log_debug("get_data_newline() -> %d", r); + if (r < 0) + return r; + if (r == 0) { + imp->state = IMPORTER_STATE_EOF; + return 0; + } + + imp->data_size = 0; + imp->state = IMPORTER_STATE_LINE; + + return 0; /* continue */ + default: + assert_not_reached("wtf?"); + } +} + +int journal_importer_push_data(JournalImporter *imp, const char *data, size_t size) { + assert(imp); + assert(imp->state != IMPORTER_STATE_EOF); + + if (!realloc_buffer(imp, imp->filled + size)) { + log_error("Failed to store received data of size %zu " + "(in addition to existing %zu bytes with %zu filled): %s", + size, imp->size, imp->filled, strerror(ENOMEM)); + return -ENOMEM; + } + + memcpy(imp->buf + imp->filled, data, size); + imp->filled += size; + + return 0; +} + +void journal_importer_drop_iovw(JournalImporter *imp) { + size_t remain, target; + + /* This function drops processed data that along with the iovw that points at it */ + + iovw_free_contents(&imp->iovw); + + /* possibly reset buffer position */ + remain = imp->filled - imp->offset; + + if (remain == 0) /* no brainer */ + imp->offset = imp->scanned = imp->filled = 0; + else if (imp->offset > imp->size - imp->filled && + imp->offset > remain) { + memcpy(imp->buf, imp->buf + imp->offset, remain); + imp->offset = imp->scanned = 0; + imp->filled = remain; + } + + target = imp->size; + while (target > 16 * LINE_CHUNK && imp->filled < target / 2) + target /= 2; + if (target < imp->size) { + char *tmp; + + tmp = realloc(imp->buf, target); + if (!tmp) + log_warning("Failed to reallocate buffer to (smaller) size %zu", + target); + else { + log_debug("Reallocated buffer from %zu to %zu bytes", + imp->size, target); + imp->buf = tmp; + imp->size = target; + } + } +} + +bool journal_importer_eof(const JournalImporter *imp) { + return imp->state == IMPORTER_STATE_EOF; +} diff --git a/src/basic/journal-importer.h b/src/basic/journal-importer.h new file mode 100644 index 0000000000..b3e308dd6d --- /dev/null +++ b/src/basic/journal-importer.h @@ -0,0 +1,70 @@ +/*** + This file is part of systemd. + + Copyright 2016 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#pragma once + +#include <stddef.h> +#include <stdbool.h> +#include <sys/uio.h> + +#include "time-util.h" + +/* Make sure not to make this smaller than the maximum coredump size. + * See COREDUMP_MAX in coredump.c */ +#define ENTRY_SIZE_MAX (1024*1024*770u) +#define DATA_SIZE_MAX (1024*1024*768u) +#define LINE_CHUNK 8*1024u + +struct iovec_wrapper { + struct iovec *iovec; + size_t size_bytes; + size_t count; +}; + +size_t iovw_size(struct iovec_wrapper *iovw); + +typedef struct JournalImporter { + int fd; + bool passive_fd; + char *name; + + char *buf; + size_t size; /* total size of the buffer */ + size_t offset; /* offset to the beginning of live data in the buffer */ + size_t scanned; /* number of bytes since the beginning of data without a newline */ + size_t filled; /* total number of bytes in the buffer */ + + size_t field_len; /* used for binary fields: the field name length */ + size_t data_size; /* and the size of the binary data chunk being processed */ + + struct iovec_wrapper iovw; + + int state; + dual_timestamp ts; +} JournalImporter; + +void journal_importer_cleanup(JournalImporter *); +int journal_importer_process_data(JournalImporter *); +int journal_importer_push_data(JournalImporter *, const char *data, size_t size); +void journal_importer_drop_iovw(JournalImporter *); +bool journal_importer_eof(const JournalImporter *); + +static inline size_t journal_importer_bytes_remaining(const JournalImporter *imp) { + return imp->filled; +} diff --git a/src/basic/khash.h b/src/basic/khash.h index f404a68236..410f3020e0 100644 --- a/src/basic/khash.h +++ b/src/basic/khash.h @@ -28,7 +28,7 @@ typedef struct khash khash; /* For plain hash functions. Hash functions commonly supported on today's kernels are: crc32c, crct10dif, crc32, - * sha224, sha256, sha512, sha384, sha1, md5, md4, sha3-224, sha3-256, sha3-384, sha3-512, and more.*/ + * sha224, sha256, sha512, sha384, sha1, md5, md4, sha3-224, sha3-256, sha3-384, sha3-512, and more. */ int khash_new(khash **ret, const char *algorithm); /* For keyed hash functions. Hash functions commonly supported on today's kernels are: hmac(sha256), cmac(aes), diff --git a/src/basic/log.c b/src/basic/log.c index 1362b1c086..36efc9ac7d 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -72,6 +72,7 @@ static bool show_color = false; static bool show_location = false; static bool upgrade_syslog_to_journal = false; +static bool always_reopen_console = false; /* Akin to glibc's __abort_msg; which is private and we hence cannot * use here. */ @@ -95,7 +96,7 @@ static int log_open_console(void) { if (console_fd >= 0) return 0; - if (getpid() == 1) { + if (always_reopen_console) { console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); if (console_fd < 0) return console_fd; @@ -1164,10 +1165,14 @@ int log_syntax_internal( return log_struct_internal( level, error, file, line, func, - LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION), + "MESSAGE_ID=" SD_MESSAGE_INVALID_CONFIGURATION_STR, "CONFIG_FILE=%s", config_file, "CONFIG_LINE=%u", config_line, LOG_MESSAGE("%s:%u: %s", config_file, config_line, buffer), unit_fmt, unit, NULL); } + +void log_set_always_reopen_console(bool b) { + always_reopen_console = b; +} diff --git a/src/basic/log.h b/src/basic/log.h index 2afee20bb5..72714e02e5 100644 --- a/src/basic/log.h +++ b/src/basic/log.h @@ -214,13 +214,13 @@ bool log_on_console(void) _pure_; const char *log_target_to_string(LogTarget target) _const_; LogTarget log_target_from_string(const char *s) _pure_; -/* Helpers to prepare various fields for structured logging */ +/* Helper to prepare various field for structured logging */ #define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__ -#define LOG_MESSAGE_ID(x) "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(x) void log_received_signal(int level, const struct signalfd_siginfo *si); void log_set_upgrade_syslog_to_journal(bool b); +void log_set_always_reopen_console(bool b); int log_syntax_internal( const char *unit, diff --git a/src/basic/missing_syscall.h b/src/basic/missing_syscall.h index e6fd67cb9d..9fc4156564 100644 --- a/src/basic/missing_syscall.h +++ b/src/basic/missing_syscall.h @@ -194,6 +194,8 @@ static inline pid_t raw_getpid(void) { # define __NR_renameat2 316 # elif defined __arm__ # define __NR_renameat2 382 +# elif defined __aarch64__ +# define __NR_renameat2 276 # elif defined _MIPS_SIM # if _MIPS_SIM == _MIPS_SIM_ABI32 # define __NR_renameat2 4351 diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c index f0bc9cac18..a8fd63fb45 100644 --- a/src/basic/mount-util.c +++ b/src/basic/mount-util.c @@ -112,9 +112,10 @@ int fd_is_mount_point(int fd, const char *filename, int flags) { r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags); if (r < 0) { - if (errno == ENOSYS) - /* This kernel does not support name_to_handle_at() - * fall back to simpler logic. */ + if (IN_SET(errno, ENOSYS, EACCES, EPERM)) + /* This kernel does not support name_to_handle_at() at all, or the syscall was blocked (maybe + * through seccomp, because we are running inside of a container?): fall back to simpler + * logic. */ goto fallback_fdinfo; else if (errno == EOPNOTSUPP) /* This kernel or file system does not support @@ -163,7 +164,7 @@ int fd_is_mount_point(int fd, const char *filename, int flags) { fallback_fdinfo: r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id); - if (IN_SET(r, -EOPNOTSUPP, -EACCES)) + if (IN_SET(r, -EOPNOTSUPP, -EACCES, -EPERM)) goto fallback_fstat; if (r < 0) return r; diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c index f703e7f145..8592a428d5 100644 --- a/src/basic/proc-cmdline.c +++ b/src/basic/proc-cmdline.c @@ -154,7 +154,7 @@ int proc_cmdline_get_key(const char *key, unsigned flags, char **value) { * * c) The "value" parameter is NULL. In this case a search for the exact "key" parameter is performed. * - * In all three cases, > 0 is returned if the key is found, 0 if not.*/ + * In all three cases, > 0 is returned if the key is found, 0 if not. */ if (isempty(key)) return -EINVAL; diff --git a/src/basic/process-util.c b/src/basic/process-util.c index eead8b00da..0df3fed640 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -311,7 +311,7 @@ int rename_process(const char name[]) { /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at - * the end. This is the best option for changing /proc/self/cmdline.*/ + * the end. This is the best option for changing /proc/self/cmdline. */ if (mm_size < l+1) { size_t nn_size; char *nn; @@ -703,7 +703,7 @@ int kill_and_sigcont(pid_t pid, int sig) { /* If this worked, also send SIGCONT, unless we already just sent a SIGCONT, or SIGKILL was sent which isn't * affected by a process being suspended anyway. */ - if (r >= 0 && !IN_SET(SIGCONT, SIGKILL)) + if (r >= 0 && !IN_SET(sig, SIGCONT, SIGKILL)) (void) kill(pid, SIGCONT); return r; diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index 17e90a8994..e5847dce00 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -900,6 +900,26 @@ bool ifname_valid(const char *p) { return true; } +bool address_label_valid(const char *p) { + + if (isempty(p)) + return false; + + if (strlen(p) >= IFNAMSIZ) + return false; + + while (*p) { + if ((uint8_t) *p >= 127U) + return false; + + if ((uint8_t) *p <= 31U) + return false; + p++; + } + + return true; +} + int getpeercred(int fd, struct ucred *ucred) { socklen_t n = sizeof(struct ucred); struct ucred u; diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index 3c42e220e5..73c3a339fc 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -126,6 +126,7 @@ int ip_tos_to_string_alloc(int i, char **s); int ip_tos_from_string(const char *s); bool ifname_valid(const char *p); +bool address_label_valid(const char *p); int getpeercred(int fd, struct ucred *ucred); int getpeersec(int fd, char **ret); diff --git a/src/basic/util.c b/src/basic/util.c index 6204906f37..3dce0ea92e 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -59,9 +59,6 @@ #include "user-util.h" #include "util.h" -/* Put this test here for a lack of better place */ -assert_cc(EAGAIN == EWOULDBLOCK); - int saved_argc = 0; char **saved_argv = NULL; static int saved_in_initrd = -1; @@ -80,146 +77,6 @@ size_t page_size(void) { return pgsz; } -static int do_execute(char **directories, usec_t timeout, char *argv[]) { - _cleanup_hashmap_free_free_ Hashmap *pids = NULL; - _cleanup_set_free_free_ Set *seen = NULL; - char **directory; - - /* We fork this all off from a child process so that we can - * somewhat cleanly make use of SIGALRM to set a time limit */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - - pids = hashmap_new(NULL); - if (!pids) - return log_oom(); - - seen = set_new(&string_hash_ops); - if (!seen) - return log_oom(); - - STRV_FOREACH(directory, directories) { - _cleanup_closedir_ DIR *d; - struct dirent *de; - - d = opendir(*directory); - if (!d) { - if (errno == ENOENT) - continue; - - return log_error_errno(errno, "Failed to open directory %s: %m", *directory); - } - - FOREACH_DIRENT(de, d, break) { - _cleanup_free_ char *path = NULL; - pid_t pid; - int r; - - if (!dirent_is_file(de)) - continue; - - if (set_contains(seen, de->d_name)) { - log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name); - continue; - } - - r = set_put_strdup(seen, de->d_name); - if (r < 0) - return log_oom(); - - path = strjoin(*directory, "/", de->d_name); - if (!path) - return log_oom(); - - if (null_or_empty_path(path)) { - log_debug("%s is empty (a mask).", path); - continue; - } - - pid = fork(); - if (pid < 0) { - log_error_errno(errno, "Failed to fork: %m"); - continue; - } else if (pid == 0) { - char *_argv[2]; - - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - - if (!argv) { - _argv[0] = path; - _argv[1] = NULL; - argv = _argv; - } else - argv[0] = path; - - execv(path, argv); - return log_error_errno(errno, "Failed to execute %s: %m", path); - } - - log_debug("Spawned %s as " PID_FMT ".", path, pid); - - r = hashmap_put(pids, PID_TO_PTR(pid), path); - if (r < 0) - return log_oom(); - path = NULL; - } - } - - /* Abort execution of this process after the timout. We simply - * rely on SIGALRM as default action terminating the process, - * and turn on alarm(). */ - - if (timeout != USEC_INFINITY) - alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC); - - while (!hashmap_isempty(pids)) { - _cleanup_free_ char *path = NULL; - pid_t pid; - - pid = PTR_TO_PID(hashmap_first_key(pids)); - assert(pid > 0); - - path = hashmap_remove(pids, PID_TO_PTR(pid)); - assert(path); - - wait_for_terminate_and_warn(path, pid, true); - } - - return 0; -} - -void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) { - pid_t executor_pid; - int r; - char *name; - char **dirs = (char**) directories; - - assert(!strv_isempty(dirs)); - - name = basename(dirs[0]); - assert(!isempty(name)); - - /* Executes all binaries in the directories in parallel and waits - * for them to finish. Optionally a timeout is applied. If a file - * with the same name exists in more than one directory, the - * earliest one wins. */ - - executor_pid = fork(); - if (executor_pid < 0) { - log_error_errno(errno, "Failed to fork: %m"); - return; - - } else if (executor_pid == 0) { - r = do_execute(dirs, timeout, argv); - _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); - } - - wait_for_terminate_and_warn(name, executor_pid, true); -} - bool plymouth_running(void) { return access("/run/plymouth/pid", F_OK) >= 0; } diff --git a/src/basic/util.h b/src/basic/util.h index c3802a811c..c7da6c39bf 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -65,8 +65,6 @@ static inline const char* enable_disable(bool b) { return b ? "enable" : "disable"; } -void execute_directories(const char* const* directories, usec_t timeout, char *argv[]); - bool plymouth_running(void); bool display_is_local(const char *display) _pure_; diff --git a/src/basic/virt.c b/src/basic/virt.c index 03ce71a728..ff4491d6d6 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -28,7 +28,6 @@ #include "env-util.h" #include "fd-util.h" #include "fileio.h" -#include "fs-util.h" #include "macro.h" #include "process-util.h" #include "stat-util.h" @@ -316,25 +315,31 @@ static int detect_vm_zvm(void) { /* Returns a short identifier for the various VM implementations */ int detect_vm(void) { static thread_local int cached_found = _VIRTUALIZATION_INVALID; - int r; + int r, dmi; if (cached_found >= 0) return cached_found; /* We have to use the correct order here: - * Some virtualization technologies do use KVM hypervisor but are - * expected to be detected as something else. So detect DMI first. * - * An example is Virtualbox since version 5.0, which uses KVM backend. - * Detection via DMI works corretly, the CPU ID would find KVM - * only. */ - r = detect_vm_dmi(); + * -> First try to detect Oracle Virtualbox, even if it uses KVM. + * -> Second try to detect from cpuid, this will report KVM for + * whatever software is used even if info in dmi is overwritten. + * -> Third try to detect from dmi. */ + + dmi = detect_vm_dmi(); + if (dmi == VIRTUALIZATION_ORACLE) { + r = dmi; + goto finish; + } + + r = detect_vm_cpuid(); if (r < 0) return r; if (r != VIRTUALIZATION_NONE) goto finish; - r = detect_vm_cpuid(); + r = dmi; if (r < 0) return r; if (r != VIRTUALIZATION_NONE) @@ -564,30 +569,16 @@ int running_in_userns(void) { } int running_in_chroot(void) { - _cleanup_free_ char *self_mnt = NULL, *pid1_mnt = NULL; - int r; - - /* Try to detect whether we are running in a chroot() environment. Specifically, check whether we have a - * different root directory than PID 1, even though we live in the same mount namespace as it. */ + int ret; if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0) return 0; - r = files_same("/proc/1/root", "/"); - if (r < 0) - return r; - if (r > 0) - return 0; - - r = readlink_malloc("/proc/self/ns/mnt", &self_mnt); - if (r < 0) - return r; - - r = readlink_malloc("/proc/1/ns/mnt", &pid1_mnt); - if (r < 0) - return r; + ret = files_same("/proc/1/root", "/"); + if (ret < 0) + return ret; - return streq(self_mnt, pid1_mnt); /* Only if we live in the same namespace! */ + return ret == 0; } static const char *const virtualization_table[_VIRTUALIZATION_MAX] = { diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index b747a95133..155bf278b2 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -38,20 +38,22 @@ #include "alloc-util.h" #include "blkid-util.h" +#include "copy.h" #include "dirent-util.h" #include "efivars.h" #include "fd-util.h" #include "fileio.h" +#include "fs-util.h" #include "locale-util.h" #include "parse-util.h" #include "rm-rf.h" +#include "stat-util.h" #include "string-util.h" #include "strv.h" #include "umask-util.h" #include "util.h" #include "verbs.h" #include "virt.h" -#include "stat-util.h" static char *arg_path = NULL; static bool arg_touch_variables = true; @@ -123,12 +125,8 @@ static int verify_esp( errno = 0; b = blkid_new_probe_from_filename(t); - if (!b) { - if (errno == 0) - return log_oom(); - - return log_error_errno(errno, "Failed to open file system \"%s\": %m", p); - } + if (!b) + return log_error_errno(errno ?: ENOMEM, "Failed to open file system \"%s\": %m", p); blkid_probe_enable_superblocks(b, 1); blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); @@ -143,17 +141,13 @@ static int verify_esp( } else if (r == 1) { log_error("File system \"%s\" does not contain a label.", p); return -ENODEV; - } else if (r != 0) { - r = errno ? -errno : -EIO; - return log_error_errno(r, "Failed to probe file system \"%s\": %m", p); - } + } else if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe file system \"%s\": %m", p); errno = 0; r = blkid_probe_lookup_value(b, "TYPE", &v, NULL); - if (r != 0) { - r = errno ? -errno : -EIO; - return log_error_errno(r, "Failed to probe file system type \"%s\": %m", p); - } + if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe file system type \"%s\": %m", p); if (!streq(v, "vfat")) { log_error("File system \"%s\" is not FAT.", p); return -ENODEV; @@ -161,10 +155,8 @@ static int verify_esp( errno = 0; r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL); - if (r != 0) { - r = errno ? -errno : -EIO; - return log_error_errno(r, "Failed to probe partition scheme \"%s\": %m", p); - } + if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe partition scheme \"%s\": %m", p); if (!streq(v, "gpt")) { log_error("File system \"%s\" is not on a GPT partition table.", p); return -ENODEV; @@ -172,10 +164,8 @@ static int verify_esp( errno = 0; r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); - if (r != 0) { - r = errno ? -errno : -EIO; - return log_error_errno(r, "Failed to probe partition type UUID \"%s\": %m", p); - } + if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID \"%s\": %m", p); if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) { log_error("File system \"%s\" has wrong type for an EFI System Partition (ESP).", p); return -ENODEV; @@ -183,10 +173,8 @@ static int verify_esp( errno = 0; r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL); - if (r != 0) { - r = errno ? -errno : -EIO; - return log_error_errno(r, "Failed to probe partition entry UUID \"%s\": %m", p); - } + if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe partition entry UUID \"%s\": %m", p); r = sd_id128_from_string(v, &uuid); if (r < 0) { log_error("Partition \"%s\" has invalid UUID \"%s\".", p, v); @@ -195,30 +183,24 @@ static int verify_esp( errno = 0; r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL); - if (r != 0) { - r = errno ? -errno : -EIO; - return log_error_errno(r, "Failed to probe partition number \"%s\": m", p); - } + if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe partition number \"%s\": m", p); r = safe_atou32(v, &part); if (r < 0) return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field."); errno = 0; r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL); - if (r != 0) { - r = errno ? -errno : -EIO; - return log_error_errno(r, "Failed to probe partition offset \"%s\": %m", p); - } + if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe partition offset \"%s\": %m", p); r = safe_atou64(v, &pstart); if (r < 0) return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field."); errno = 0; r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL); - if (r != 0) { - r = errno ? -errno : -EIO; - return log_error_errno(r, "Failed to probe partition size \"%s\": %m", p); - } + if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe partition size \"%s\": %m", p); r = safe_atou64(v, &psize); if (r < 0) return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field."); @@ -476,16 +458,16 @@ static int compare_version(const char *a, const char *b) { return strverscmp(a, b); } -static int version_check(int fd, const char *from, const char *to) { +static int version_check(int fd_from, const char *from, int fd_to, const char *to) { _cleanup_free_ char *a = NULL, *b = NULL; - _cleanup_close_ int fd2 = -1; int r; - assert(fd >= 0); + assert(fd_from >= 0); assert(from); + assert(fd_to >= 0); assert(to); - r = get_file_version(fd, &a); + r = get_file_version(fd_from, &a); if (r < 0) return r; if (r == 0) { @@ -493,15 +475,7 @@ static int version_check(int fd, const char *from, const char *to) { return -EINVAL; } - fd2 = open(to, O_RDONLY|O_CLOEXEC); - if (fd2 < 0) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", to); - } - - r = get_file_version(fd2, &b); + r = get_file_version(fd_to, &b); if (r < 0) return r; if (r == 0 || compare_product(a, b) != 0) { @@ -517,90 +491,59 @@ static int version_check(int fd, const char *from, const char *to) { return 0; } -static int copy_file(const char *from, const char *to, bool force) { - _cleanup_fclose_ FILE *f = NULL, *g = NULL; - char *p; +static int copy_file_with_version_check(const char *from, const char *to, bool force) { + _cleanup_close_ int fd_from = -1, fd_to = -1; + _cleanup_free_ char *t = NULL; int r; - struct timespec t[2]; - struct stat st; - assert(from); - assert(to); - - f = fopen(from, "re"); - if (!f) + fd_from = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd_from < 0) return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", from); if (!force) { - /* If this is an update, then let's compare versions first */ - r = version_check(fileno(f), from, to); - if (r < 0) - return r; - } - - p = strjoina(to, "~"); - g = fopen(p, "wxe"); - if (!g) { - /* Directory doesn't exist yet? Then let's skip this... */ - if (!force && errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", to); - } + fd_to = open(to, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd_to < 0) { + if (errno != -ENOENT) + return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", to); + } else { + r = version_check(fd_from, from, fd_to, to); + if (r < 0) + return r; - rewind(f); - do { - size_t k; - uint8_t buf[32*1024]; + if (lseek(fd_from, 0, SEEK_SET) == (off_t) -1) + return log_error_errno(errno, "Failed to seek in \%s\": %m", from); - k = fread(buf, 1, sizeof(buf), f); - if (ferror(f)) { - r = log_error_errno(EIO, "Failed to read \"%s\": %m", from); - goto error; + fd_to = safe_close(fd_to); } + } - if (k == 0) - break; - - fwrite(buf, 1, k, g); - if (ferror(g)) { - r = log_error_errno(EIO, "Failed to write \"%s\": %m", to); - goto error; - } - } while (!feof(f)); + r = tempfn_random(to, NULL, &t); + if (r < 0) + return log_oom(); - r = fflush_and_check(g); - if (r < 0) { - log_error_errno(r, "Failed to write \"%s\": %m", to); - goto error; + RUN_WITH_UMASK(0000) { + fd_to = open(t, O_WRONLY|O_CREAT|O_CLOEXEC|O_EXCL|O_NOFOLLOW, 0644); + if (fd_to < 0) + return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", t); } - r = fstat(fileno(f), &st); + r = copy_bytes(fd_from, fd_to, (uint64_t) -1, COPY_REFLINK); if (r < 0) { - r = log_error_errno(errno, "Failed to get file timestamps of \"%s\": %m", from); - goto error; + unlink(t); + return log_error_errno(errno, "Failed to copy data from \"%s\" to \"%s\": %m", from, t); } - t[0] = st.st_atim; - t[1] = st.st_mtim; + (void) copy_times(fd_from, fd_to); - r = futimens(fileno(g), t); + r = renameat(AT_FDCWD, t, AT_FDCWD, to); if (r < 0) { - r = log_error_errno(errno, "Failed to set file timestamps on \"%s\": %m", p); - goto error; - } - - if (rename(p, to) < 0) { - r = log_error_errno(errno, "Failed to rename \"%s\" to \"%s\": %m", p, to); - goto error; + (void) unlink_noerrno(t); + return log_error_errno(errno, "Failed to rename \"%s\" to \"%s\": %m", t, to); } log_info("Copied \"%s\" to \"%s\".", from, to); - return 0; -error: - (void) unlink(p); - return r; + return 0; } static int mkdir_one(const char *prefix, const char *suffix) { @@ -644,7 +587,7 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) { p = strjoina(BOOTLIBDIR "/", name); q = strjoina(esp_path, "/EFI/systemd/", name); - r = copy_file(p, q, force); + r = copy_file_with_version_check(p, q, force); if (startswith(name, "systemd-boot")) { int k; @@ -654,7 +597,7 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) { v = strjoina(esp_path, "/EFI/BOOT/BOOT", name + strlen("systemd-boot")); ascii_strupper(strrchr(v, '/') + 1); - k = copy_file(p, v, force); + k = copy_file_with_version_check(p, v, force); if (k < 0 && r == 0) r = k; } @@ -950,20 +893,31 @@ static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) { static int install_loader_config(const char *esp_path) { - _cleanup_fclose_ FILE *f = NULL; char machine_string[SD_ID128_STRING_MAX]; + _cleanup_(unlink_and_freep) char *t = NULL; + _cleanup_fclose_ FILE *f = NULL; sd_id128_t machine_id; const char *p; - int r; + int r, fd; r = sd_id128_get_machine(&machine_id); if (r < 0) return log_error_errno(r, "Failed to get machine did: %m"); p = strjoina(esp_path, "/loader/loader.conf"); - f = fopen(p, "wxe"); - if (!f) - return log_error_errno(errno, "Failed to open loader.conf for writing: %m"); + + if (access(p, F_OK) >= 0) /* Silently skip creation if the file already exists (early check) */ + return 0; + + fd = open_tmpfile_linkable(p, O_WRONLY|O_CLOEXEC, &t); + if (fd < 0) + return log_error_errno(fd, "Failed to open \"%s\" for writing: %m", p); + + f = fdopen(fd, "we"); + if (!f) { + safe_close(fd); + return log_oom(); + } fprintf(f, "#timeout 3\n"); fprintf(f, "default %s-*\n", sd_id128_to_string(machine_id, machine_string)); @@ -972,7 +926,15 @@ static int install_loader_config(const char *esp_path) { if (r < 0) return log_error_errno(r, "Failed to write \"%s\": %m", p); - return 0; + r = link_tmpfile(fd, t, p); + if (r == -EEXIST) + return 0; /* Silently skip creation if the file exists now (recheck) */ + if (r < 0) + return log_error_errno(r, "Failed to move \"%s\" into place: %m", p); + + t = mfree(t); + + return 1; } static int help(int argc, char *argv[], void *userdata) { diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 7cc54a8cdd..681e783f2e 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -1787,7 +1787,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { config_title_generate(&config); - /* select entry by configured pattern or EFI LoaderDefaultEntry= variable*/ + /* select entry by configured pattern or EFI LoaderDefaultEntry= variable */ config_default_entry_select(&config); /* if no configured entry to select from was found, enable the menu */ diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 7c1ffb1bca..b7d5d3cdae 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -87,7 +87,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { cmdline_len = szs[0]; /* if we are not in secure boot mode, accept a custom command line and replace the built-in one */ - if (!secure && loaded_image->LoadOptionsSize > 0) { + if (!secure && loaded_image->LoadOptionsSize > 0 && *(CHAR16 *)loaded_image->LoadOptions != 0) { CHAR16 *options; CHAR8 *line; UINTN i; diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c index 5574c14555..1b746a0e13 100644 --- a/src/cgls/cgls.c +++ b/src/cgls/cgls.c @@ -158,7 +158,7 @@ static int parse_argv(int argc, char *argv[]) { static void show_cg_info(const char *controller, const char *path) { - if (cg_all_unified() <= 0 && controller && !streq(controller, SYSTEMD_CGROUP_CONTROLLER)) + if (cg_all_unified() == 0 && controller && !streq(controller, SYSTEMD_CGROUP_CONTROLLER)) printf("Controller %s; ", controller); printf("Control group %s:\n", isempty(path) ? "/" : path); diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index 50ac6a58b0..a1c0f48c89 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -133,12 +133,16 @@ static int process( Group **ret) { Group *g; - int r; + int r, all_unified; assert(controller); assert(path); assert(a); + all_unified = cg_all_unified(); + if (all_unified < 0) + return all_unified; + g = hashmap_get(a, path); if (!g) { g = hashmap_get(b, path); @@ -214,7 +218,7 @@ static int process( uint64_t new_usage; nsec_t timestamp; - if (cg_all_unified() > 0) { + if (all_unified) { const char *keys[] = { "usage_usec", NULL }; _cleanup_free_ char *val = NULL; @@ -274,10 +278,10 @@ static int process( } else if (streq(controller, "memory")) { _cleanup_free_ char *p = NULL, *v = NULL; - if (cg_all_unified() <= 0) - r = cg_get_path(controller, path, "memory.usage_in_bytes", &p); - else + if (all_unified) r = cg_get_path(controller, path, "memory.current", &p); + else + r = cg_get_path(controller, path, "memory.usage_in_bytes", &p); if (r < 0) return r; @@ -294,15 +298,14 @@ static int process( if (g->memory > 0) g->memory_valid = true; - } else if ((streq(controller, "io") && cg_all_unified() > 0) || - (streq(controller, "blkio") && cg_all_unified() <= 0)) { + } else if ((streq(controller, "io") && all_unified) || + (streq(controller, "blkio") && !all_unified)) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *p = NULL; - bool unified = cg_all_unified() > 0; uint64_t wr = 0, rd = 0; nsec_t timestamp; - r = cg_get_path(controller, path, unified ? "io.stat" : "blkio.io_service_bytes", &p); + r = cg_get_path(controller, path, all_unified ? "io.stat" : "blkio.io_service_bytes", &p); if (r < 0) return r; @@ -325,7 +328,7 @@ static int process( l += strcspn(l, WHITESPACE); l += strspn(l, WHITESPACE); - if (unified) { + if (all_unified) { while (!isempty(l)) { if (sscanf(l, "rbytes=%" SCNu64, &k)) rd += k; diff --git a/src/core/automount.c b/src/core/automount.c index 8ff1ca90f7..99e8047620 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -101,17 +101,17 @@ static void unmount_autofs(Automount *a) { a->pipe_event_source = sd_event_source_unref(a->pipe_event_source); a->pipe_fd = safe_close(a->pipe_fd); - /* If we reload/reexecute things we keep the mount point - * around */ - if (a->where && - (UNIT(a)->manager->exit_code != MANAGER_RELOAD && - UNIT(a)->manager->exit_code != MANAGER_REEXECUTE)) { + /* If we reload/reexecute things we keep the mount point around */ + if (!IN_SET(UNIT(a)->manager->exit_code, MANAGER_RELOAD, MANAGER_REEXECUTE)) { + automount_send_ready(a, a->tokens, -EHOSTDOWN); automount_send_ready(a, a->expire_tokens, -EHOSTDOWN); - r = repeat_unmount(a->where, MNT_DETACH); - if (r < 0) - log_error_errno(r, "Failed to unmount: %m"); + if (a->where) { + r = repeat_unmount(a->where, MNT_DETACH); + if (r < 0) + log_error_errno(r, "Failed to unmount: %m"); + } } } @@ -186,6 +186,22 @@ static int automount_verify(Automount *a) { return 0; } +static int automount_set_where(Automount *a) { + int r; + + assert(a); + + if (a->where) + return 0; + + r = unit_name_to_path(UNIT(a)->id, &a->where); + if (r < 0) + return r; + + path_kill_slashes(a->where); + return 1; +} + static int automount_load(Unit *u) { Automount *a = AUTOMOUNT(u); int r; @@ -201,13 +217,9 @@ static int automount_load(Unit *u) { if (u->load_state == UNIT_LOADED) { Unit *x; - if (!a->where) { - r = unit_name_to_path(u->id, &a->where); - if (r < 0) - return r; - } - - path_kill_slashes(a->where); + r = automount_set_where(a); + if (r < 0) + return r; r = unit_load_related_unit(u, ".mount", &x); if (r < 0) @@ -256,26 +268,30 @@ static int automount_coldplug(Unit *u) { assert(a); assert(a->state == AUTOMOUNT_DEAD); - if (a->deserialized_state != a->state) { + if (a->deserialized_state == a->state) + return 0; + + if (IN_SET(a->deserialized_state, AUTOMOUNT_WAITING, AUTOMOUNT_RUNNING)) { + + r = automount_set_where(a); + if (r < 0) + return r; r = open_dev_autofs(u->manager); if (r < 0) return r; - if (a->deserialized_state == AUTOMOUNT_WAITING || - a->deserialized_state == AUTOMOUNT_RUNNING) { - assert(a->pipe_fd >= 0); + assert(a->pipe_fd >= 0); - r = sd_event_add_io(u->manager->event, &a->pipe_event_source, a->pipe_fd, EPOLLIN, automount_dispatch_io, u); - if (r < 0) - return r; + r = sd_event_add_io(u->manager->event, &a->pipe_event_source, a->pipe_fd, EPOLLIN, automount_dispatch_io, u); + if (r < 0) + return r; - (void) sd_event_source_set_description(a->pipe_event_source, "automount-io"); - if (a->deserialized_state == AUTOMOUNT_RUNNING) { - r = automount_start_expire(a); - if (r < 0) - log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m"); - } + (void) sd_event_source_set_description(a->pipe_event_source, "automount-io"); + if (a->deserialized_state == AUTOMOUNT_RUNNING) { + r = automount_start_expire(a); + if (r < 0) + log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m"); } automount_set_state(a, a->deserialized_state); @@ -733,6 +749,12 @@ static void automount_enter_runnning(Automount *a) { assert(a); + /* If the user masked our unit in the meantime, fail */ + if (UNIT(a)->load_state != UNIT_LOADED) { + log_unit_error(UNIT(a), "Suppressing automount event since unit is no longer loaded."); + goto fail; + } + /* We don't take mount requests anymore if we are supposed to * shut down anyway */ if (unit_stop_pending(UNIT(a))) { diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 5789e2aa82..774b832a63 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -859,8 +859,7 @@ static void cgroup_context_apply(Unit *u, CGroupMask mask, ManagerState state) { if ((mask & CGROUP_MASK_MEMORY) && !is_root) { if (cg_all_unified() > 0) { - uint64_t max; - uint64_t swap_max = CGROUP_LIMIT_MAX; + uint64_t max, swap_max = CGROUP_LIMIT_MAX; if (cgroup_context_has_unified_memory_config(c)) { max = c->memory_max; @@ -1260,9 +1259,9 @@ int unit_watch_cgroup(Unit *u) { return 0; /* Only applies to the unified hierarchy */ - r = cg_unified(SYSTEMD_CGROUP_CONTROLLER); + r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER); if (r < 0) - return log_unit_error_errno(u, r, "Failed detect whether the unified hierarchy is used: %m"); + return log_error_errno(r, "Failed to determine whether the name=systemd hierarchy is unified: %m"); if (r == 0) return 0; @@ -1673,6 +1672,8 @@ static int unit_watch_pids_in_path(Unit *u, const char *path) { } int unit_watch_all_pids(Unit *u) { + int r; + assert(u); /* Adds all PIDs from our cgroup to the set of PIDs we @@ -1683,7 +1684,10 @@ int unit_watch_all_pids(Unit *u) { if (!u->cgroup_path) return -ENOENT; - if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) > 0) /* On unified we can use proper notifications */ + r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER); + if (r < 0) + return r; + if (r > 0) /* On unified we can use proper notifications */ return 0; return unit_watch_pids_in_path(u, u->cgroup_path); @@ -1756,7 +1760,7 @@ static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents, int manager_setup_cgroup(Manager *m) { _cleanup_free_ char *path = NULL; CGroupController c; - int r, all_unified, systemd_unified; + int r, all_unified; char *e; assert(m); @@ -1793,25 +1797,30 @@ int manager_setup_cgroup(Manager *m) { if (r < 0) return log_error_errno(r, "Cannot find cgroup mount point: %m"); - all_unified = cg_all_unified(); - systemd_unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER); - - if (all_unified < 0 || systemd_unified < 0) - return log_error_errno(all_unified < 0 ? all_unified : systemd_unified, - "Couldn't determine if we are running in the unified hierarchy: %m"); + r = cg_unified_flush(); + if (r < 0) + return log_error_errno(r, "Couldn't determine if we are running in the unified hierarchy: %m"); - if (all_unified > 0) + all_unified = cg_all_unified(); + if (r < 0) + return log_error_errno(r, "Couldn't determine whether we are in all unified mode: %m"); + if (r > 0) log_debug("Unified cgroup hierarchy is located at %s.", path); - else if (systemd_unified > 0) - log_debug("Unified cgroup hierarchy is located at %s. Controllers are on legacy hierarchies.", path); - else - log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path); + else { + r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER); + if (r < 0) + return log_error_errno(r, "Failed to determine whether systemd's own controller is in unified mode: %m"); + if (r > 0) + log_debug("Unified cgroup hierarchy is located at %s. Controllers are on legacy hierarchies.", path); + else + log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER_LEGACY ". File system hierarchy is at %s.", path); + } if (!m->test_run) { const char *scope_path; /* 3. Install agent */ - if (systemd_unified) { + if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0) { /* In the unified hierarchy we can get * cgroup empty notifications via inotify. */ @@ -1997,10 +2006,13 @@ int unit_get_memory_current(Unit *u, uint64_t *ret) { if ((u->cgroup_realized_mask & CGROUP_MASK_MEMORY) == 0) return -ENODATA; - if (cg_all_unified() <= 0) - r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v); - else + r = cg_all_unified(); + if (r < 0) + return r; + if (r > 0) r = cg_get_attribute("memory", u->cgroup_path, "memory.current", &v); + else + r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v); if (r == -ENOENT) return -ENODATA; if (r < 0) @@ -2042,7 +2054,10 @@ static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) { if (!u->cgroup_path) return -ENODATA; - if (cg_all_unified() > 0) { + r = cg_all_unified(); + if (r < 0) + return r; + if (r > 0) { const char *keys[] = { "usage_usec", NULL }; _cleanup_free_ char *val = NULL; uint64_t us; diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 0136d38833..f87b52a266 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -848,13 +848,9 @@ static int method_get_unit_processes(sd_bus_message *message, void *userdata, sd if (r < 0) return r; - r = manager_load_unit(m, name, NULL, error, &u); - if (r < 0) - return r; - - r = bus_unit_check_load_state(u, error); - if (r < 0) - return r; + u = manager_get_unit(m, name); + if (!u) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", name); return bus_unit_method_get_processes(message, u, error); } @@ -1342,7 +1338,7 @@ static int verify_run_space(const char *message, sd_bus_error *error) { } int verify_run_space_and_log(const char *message) { - sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; r = verify_run_space(message, &error); @@ -1916,63 +1912,6 @@ static int send_unit_files_changed(sd_bus *bus, void *userdata) { return sd_bus_send(bus, message, NULL); } -static int reply_unit_file_changes_and_free( - Manager *m, - sd_bus_message *message, - int carries_install_info, - UnitFileChange *changes, - unsigned n_changes) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - unsigned i; - int r; - - if (unit_file_changes_have_modification(changes, n_changes)) { - r = bus_foreach_bus(m, NULL, send_unit_files_changed, NULL); - if (r < 0) - log_debug_errno(r, "Failed to send UnitFilesChanged signal: %m"); - } - - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - goto fail; - - if (carries_install_info >= 0) { - r = sd_bus_message_append(reply, "b", carries_install_info); - if (r < 0) - goto fail; - } - - r = sd_bus_message_open_container(reply, 'a', "(sss)"); - if (r < 0) - goto fail; - - for (i = 0; i < n_changes; i++) - if (changes[i].type >= 0) { - const char *change = unit_file_change_type_to_string(changes[i].type); - assert(change != NULL); - - r = sd_bus_message_append( - reply, "(sss)", - change, - changes[i].path, - changes[i].source); - if (r < 0) - goto fail; - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - goto fail; - - unit_file_changes_free(changes, n_changes); - return sd_bus_send(NULL, reply, NULL); - -fail: - unit_file_changes_free(changes, n_changes); - return r; -} - /* Create an error reply, using the error information from changes[] * if possible, and fall back to generating an error from error code c. * The error message only describes the first error. @@ -1986,12 +1925,14 @@ static int install_error( unsigned n_changes) { int r; unsigned i; - assert(c < 0); for (i = 0; i < n_changes; i++) + switch(changes[i].type) { + case 0 ... INT_MAX: continue; + case -EEXIST: if (changes[i].source) r = sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, @@ -2002,29 +1943,106 @@ static int install_error( "File %s already exists.", changes[i].path); goto found; + case -ERFKILL: r = sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file %s is masked.", changes[i].path); goto found; + case -EADDRNOTAVAIL: r = sd_bus_error_setf(error, BUS_ERROR_UNIT_GENERATED, "Unit %s is transient or generated.", changes[i].path); goto found; + case -ELOOP: r = sd_bus_error_setf(error, BUS_ERROR_UNIT_LINKED, "Refusing to operate on linked unit file %s", changes[i].path); goto found; + + case -ENOENT: + r = sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit file %s does not exist.", changes[i].path); + goto found; + default: r = sd_bus_error_set_errnof(error, changes[i].type, "File %s: %m", changes[i].path); goto found; } - r = c; + r = c < 0 ? c : -EINVAL; + found: unit_file_changes_free(changes, n_changes); return r; } +static int reply_unit_file_changes_and_free( + Manager *m, + sd_bus_message *message, + int carries_install_info, + UnitFileChange *changes, + unsigned n_changes, + sd_bus_error *error) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + bool bad = false, good = false; + unsigned i; + int r; + + if (unit_file_changes_have_modification(changes, n_changes)) { + r = bus_foreach_bus(m, NULL, send_unit_files_changed, NULL); + if (r < 0) + log_debug_errno(r, "Failed to send UnitFilesChanged signal: %m"); + } + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + goto fail; + + if (carries_install_info >= 0) { + r = sd_bus_message_append(reply, "b", carries_install_info); + if (r < 0) + goto fail; + } + + r = sd_bus_message_open_container(reply, 'a', "(sss)"); + if (r < 0) + goto fail; + + for (i = 0; i < n_changes; i++) { + + if (changes[i].type < 0) { + bad = true; + continue; + } + + r = sd_bus_message_append( + reply, "(sss)", + unit_file_change_type_to_string(changes[i].type), + changes[i].path, + changes[i].source); + if (r < 0) + goto fail; + + good = true; + } + + /* If there was a failed change, and no successful change, then return the first failure as proper method call + * error. */ + if (bad && !good) + return install_error(error, 0, changes, n_changes); + + r = sd_bus_message_close_container(reply); + if (r < 0) + goto fail; + + unit_file_changes_free(changes, n_changes); + return sd_bus_send(NULL, reply, NULL); + +fail: + unit_file_changes_free(changes, n_changes); + return r; +} + static int method_enable_unit_files_generic( sd_bus_message *message, Manager *m, @@ -2061,7 +2079,7 @@ static int method_enable_unit_files_generic( if (r < 0) return install_error(error, r, changes, n_changes); - return reply_unit_file_changes_and_free(m, message, carries_install_info ? r : -1, changes, n_changes); + return reply_unit_file_changes_and_free(m, message, carries_install_info ? r : -1, changes, n_changes, error); } static int method_enable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -2130,7 +2148,7 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use if (r < 0) return install_error(error, r, changes, n_changes); - return reply_unit_file_changes_and_free(m, message, r, changes, n_changes); + return reply_unit_file_changes_and_free(m, message, r, changes, n_changes, error); } static int method_disable_unit_files_generic( @@ -2165,7 +2183,7 @@ static int method_disable_unit_files_generic( if (r < 0) return install_error(error, r, changes, n_changes); - return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); + return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes, error); } static int method_disable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -2200,7 +2218,7 @@ static int method_revert_unit_files(sd_bus_message *message, void *userdata, sd_ if (r < 0) return install_error(error, r, changes, n_changes); - return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); + return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes, error); } static int method_set_default_target(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -2231,7 +2249,7 @@ static int method_set_default_target(sd_bus_message *message, void *userdata, sd if (r < 0) return install_error(error, r, changes, n_changes); - return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); + return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes, error); } static int method_preset_all_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -2274,7 +2292,7 @@ static int method_preset_all_unit_files(sd_bus_message *message, void *userdata, if (r < 0) return install_error(error, r, changes, n_changes); - return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); + return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes, error); } static int method_add_dependency_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -2314,7 +2332,7 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd if (r < 0) return install_error(error, r, changes, n_changes); - return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); + return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes, error); } static int method_get_unit_file_links(sd_bus_message *message, void *userdata, sd_bus_error *error) { diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 60e889e1ef..f15bb2196c 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -1006,6 +1006,10 @@ int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bu assert(message); + r = mac_selinux_unit_access_check(u, message, "status", error); + if (r < 0) + return r; + pids = set_new(NULL); if (!pids) return -ENOMEM; @@ -1127,7 +1131,7 @@ void bus_unit_send_change_signal(Unit *u) { if (!u->id) return; - r = bus_foreach_bus(u->manager, NULL, u->sent_dbus_new_signal ? send_changed_signal : send_new_signal, u); + r = bus_foreach_bus(u->manager, u->bus_track, u->sent_dbus_new_signal ? send_changed_signal : send_new_signal, u); if (r < 0) log_unit_debug_errno(u, r, "Failed to send unit change signal for %s: %m", u->id); @@ -1173,7 +1177,7 @@ void bus_unit_send_removed_signal(Unit *u) { if (!u->id) return; - r = bus_foreach_bus(u->manager, NULL, send_removed_signal, u); + r = bus_foreach_bus(u->manager, u->bus_track, send_removed_signal, u); if (r < 0) log_unit_debug_errno(u, r, "Failed to send unit remove signal for %s: %m", u->id); } diff --git a/src/core/dbus.c b/src/core/dbus.c index a3f701c064..065f2d81d6 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -298,7 +298,7 @@ static int bus_job_find(sd_bus *bus, const char *path, const char *interface, vo } static int find_unit(Manager *m, sd_bus *bus, const char *path, Unit **unit, sd_bus_error *error) { - Unit *u; + Unit *u = NULL; /* just to appease gcc, initialization is not really necessary */ int r; assert(m); @@ -323,15 +323,15 @@ static int find_unit(Manager *m, sd_bus *bus, const char *path, Unit **unit, sd_ return r; u = manager_get_unit_by_pid(m, pid); + if (!u) + return 0; } else { r = manager_load_unit_from_dbus_path(m, path, error, &u); if (r < 0) return 0; + assert(u); } - if (!u) - return 0; - *unit = u; return 1; } @@ -1041,6 +1041,7 @@ int bus_init(Manager *m, bool try_bus_connect) { static void destroy_bus(Manager *m, sd_bus **bus) { Iterator i; + Unit *u; Job *j; assert(m); @@ -1049,6 +1050,17 @@ static void destroy_bus(Manager *m, sd_bus **bus) { if (!*bus) return; + /* Make sure all bus slots watching names are released. */ + HASHMAP_FOREACH(u, m->watch_bus, i) { + if (!u->match_bus_slot) + continue; + + if (sd_bus_slot_get_bus(u->match_bus_slot) != *bus) + continue; + + u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot); + } + /* Get rid of tracked clients on this bus */ if (m->subscribed && sd_bus_track_get_bus(m->subscribed) == *bus) m->subscribed = sd_bus_track_unref(m->subscribed); diff --git a/src/core/execute.c b/src/core/execute.c index 6041da46d6..d7798387c5 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -1144,11 +1144,13 @@ static int setup_pam( /* Tell the parent that our setup is done. This is especially * important regarding dropping privileges. Otherwise, unit - * setup might race against our setresuid(2) call. */ - barrier_place(&barrier); + * setup might race against our setresuid(2) call. + * + * If the parent aborted, we'll detect this below, hence ignore + * return failure here. */ + (void) barrier_place(&barrier); - /* Check if our parent process might already have - * died? */ + /* Check if our parent process might already have died? */ if (getppid() == parent_pid) { sigset_t ss; @@ -1938,10 +1940,13 @@ static int compile_read_write_paths( return 0; } -static int apply_mount_namespace(Unit *u, const ExecContext *context, - const ExecParameters *params, - ExecRuntime *runtime) { - int r; +static int apply_mount_namespace( + Unit *u, + ExecCommand *command, + const ExecContext *context, + const ExecParameters *params, + ExecRuntime *runtime) { + _cleanup_strv_free_ char **rw = NULL; char *tmp = NULL, *var = NULL; const char *root_dir = NULL, *root_image = NULL; @@ -1953,6 +1958,8 @@ static int apply_mount_namespace(Unit *u, const ExecContext *context, .protect_kernel_modules = context->protect_kernel_modules, .mount_apivfs = context->mount_apivfs, }; + bool apply_restrictions; + int r; assert(context); @@ -1986,16 +1993,18 @@ static int apply_mount_namespace(Unit *u, const ExecContext *context, if (!context->dynamic_user && root_dir) ns_info.ignore_protect_paths = true; + apply_restrictions = (params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged; + r = setup_namespace(root_dir, root_image, &ns_info, rw, - context->read_only_paths, - context->inaccessible_paths, + apply_restrictions ? context->read_only_paths : NULL, + apply_restrictions ? context->inaccessible_paths : NULL, context->bind_mounts, context->n_bind_mounts, tmp, var, - context->protect_home, - context->protect_system, + apply_restrictions ? context->protect_home : PROTECT_HOME_NO, + apply_restrictions ? context->protect_system : PROTECT_SYSTEM_NO, context->mount_flags, DISSECT_IMAGE_DISCARD_ON_LOOP); @@ -2606,7 +2615,7 @@ static int exec_child( needs_mount_namespace = exec_needs_mount_namespace(context, params, runtime); if (needs_mount_namespace) { - r = apply_mount_namespace(unit, context, params, runtime); + r = apply_mount_namespace(unit, command, context, params, runtime); if (r < 0) { *exit_status = EXIT_NAMESPACE; return r; @@ -2974,7 +2983,7 @@ int exec_spawn(Unit *unit, log_open(); if (error_message) log_struct_errno(LOG_ERR, r, - LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED), + "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR, LOG_UNIT_ID(unit), LOG_UNIT_MESSAGE(unit, "%s: %m", error_message), @@ -2982,7 +2991,7 @@ int exec_spawn(Unit *unit, NULL); else log_struct_errno(LOG_ERR, r, - LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED), + "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR, LOG_UNIT_ID(unit), LOG_UNIT_MESSAGE(unit, "Failed at step %s spawning %s: %m", exit_status_to_string(exit_status, EXIT_STATUS_SYSTEMD), diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c index 68be52856b..845e31e1c5 100644 --- a/src/core/hostname-setup.c +++ b/src/core/hostname-setup.c @@ -31,10 +31,10 @@ #include "util.h" int hostname_setup(void) { - int r; _cleanup_free_ char *b = NULL; - const char *hn; bool enoent = false; + const char *hn; + int r; r = read_hostname_config("/etc/hostname", &b); if (r < 0) { @@ -56,7 +56,7 @@ int hostname_setup(void) { if (enoent) log_info("No hostname configured."); - hn = "localhost"; + hn = FALLBACK_HOSTNAME; } r = sethostname_idempotent(hn); diff --git a/src/core/job.c b/src/core/job.c index 00f7d7998f..e2349830a8 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -627,6 +627,8 @@ int job_run_and_invalidate(Job *j) { r = job_finish_and_invalidate(j, JOB_ASSERT, true, false); else if (r == -EOPNOTSUPP) r = job_finish_and_invalidate(j, JOB_UNSUPPORTED, true, false); + else if (r == -ENOLINK) + r = job_finish_and_invalidate(j, JOB_DEPENDENCY, true, false); else if (r == -EAGAIN) job_set_state(j, JOB_WAITING); else if (r < 0) @@ -744,9 +746,8 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { } static void job_log_status_message(Unit *u, JobType t, JobResult result) { - const char *format; + const char *format, *mid; char buf[LINE_MAX]; - sd_id128_t mid; static const int job_result_log_level[_JOB_RESULT_MAX] = { [JOB_DONE] = LOG_INFO, [JOB_CANCELED] = LOG_INFO, @@ -782,16 +783,19 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) { switch (t) { case JOB_START: - mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED; + if (result == JOB_DONE) + mid = "MESSAGE_ID=" SD_MESSAGE_UNIT_STARTED_STR; + else + mid = "MESSAGE_ID=" SD_MESSAGE_UNIT_FAILED_STR; break; case JOB_RELOAD: - mid = SD_MESSAGE_UNIT_RELOADED; + mid = "MESSAGE_ID=" SD_MESSAGE_UNIT_RELOADED_STR; break; case JOB_STOP: case JOB_RESTART: - mid = SD_MESSAGE_UNIT_STOPPED; + mid = "MESSAGE_ID=" SD_MESSAGE_UNIT_STOPPED_STR; break; default: @@ -804,7 +808,7 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) { } log_struct(job_result_log_level[result], - LOG_MESSAGE_ID(mid), + mid, LOG_UNIT_ID(u), LOG_MESSAGE("%s", buf), "RESULT=%s", job_result_to_string(result), diff --git a/src/core/killall.c b/src/core/killall.c index 7a9df546ee..3fe9fa2ed0 100644 --- a/src/core/killall.c +++ b/src/core/killall.c @@ -213,7 +213,8 @@ static int killall(int sig, Set *pids, bool send_sighup) { if (get_ctty_devnr(pid, NULL) >= 0) - kill(pid, SIGHUP); + /* it's OK if the process is gone, just ignore the result */ + (void) kill(pid, SIGHUP); } } diff --git a/src/core/macros.systemd.in b/src/core/macros.systemd.in index 8d7ce1c238..a2a7edd1ee 100644 --- a/src/core/macros.systemd.in +++ b/src/core/macros.systemd.in @@ -31,6 +31,8 @@ %_binfmtdir @binfmtdir@ %_systemdgeneratordir @systemgeneratordir@ %_systemdusergeneratordir @usergeneratordir@ +%_systemd_system_env_generator_dir @systemenvgeneratordir@ +%_systemd_user_env_generator_dir @userenvgeneratordir@ %systemd_requires \ Requires(post): systemd \ diff --git a/src/core/main.c b/src/core/main.c index ad2ce1330e..bcf9ea5f25 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1414,10 +1414,17 @@ int main(int argc, char *argv[]) { log_set_upgrade_syslog_to_journal(true); - /* Disable the umask logic */ - if (getpid() == 1) + if (getpid() == 1) { + /* Disable the umask logic */ umask(0); + /* Always reopen /dev/console when running as PID 1 or one of its pre-execve() children. This is + * important so that we never end up logging to any foreign stderr, for example if we have to log in a + * child process right before execve()'ing the actual binary, at a point in time where socket + * activation stderr/stdout area already set up. */ + log_set_always_reopen_console(true); + } + if (getpid() == 1 && detect_container() <= 0) { /* Running outside of a container as PID 1 */ @@ -1830,8 +1837,10 @@ int main(int argc, char *argv[]) { before_startup = now(CLOCK_MONOTONIC); r = manager_startup(m, arg_serialization, fds); - if (r < 0) + if (r < 0) { log_error_errno(r, "Failed to fully start up daemon: %m"); + goto finish; + } /* This will close all file descriptors that were opened, but * not claimed by any unit. */ diff --git a/src/core/manager.c b/src/core/manager.c index e4da945777..cff38e28de 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -52,6 +52,7 @@ #include "dirent-util.h" #include "env-util.h" #include "escape.h" +#include "exec-util.h" #include "exit-status.h" #include "fd-util.h" #include "fileio.h" @@ -102,6 +103,7 @@ static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32 static int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata); static int manager_dispatch_run_queue(sd_event_source *source, void *userdata); +static int manager_run_environment_generators(Manager *m); static int manager_run_generators(Manager *m); static void manager_watch_jobs_in_progress(Manager *m) { @@ -530,9 +532,9 @@ static int manager_default_environment(Manager *m) { if (MANAGER_IS_SYSTEM(m)) { /* The system manager always starts with a clean * environment for its children. It does not import - * the kernel or the parents exported variables. + * the kernel's or the parents' exported variables. * - * The initial passed environ is untouched to keep + * The initial passed environment is untouched to keep * /proc/self/environ valid; it is used for tagging * the init process inside containers. */ m->environment = strv_new("PATH=" DEFAULT_PATH, @@ -540,11 +542,10 @@ static int manager_default_environment(Manager *m) { /* Import locale variables LC_*= from configuration */ locale_setup(&m->environment); - } else { + } else /* The user manager passes its own environment * along to its children. */ m->environment = strv_copy(environ); - } if (!m->environment) return -ENOMEM; @@ -775,7 +776,10 @@ static int manager_setup_cgroups_agent(Manager *m) { if (!MANAGER_IS_SYSTEM(m)) return 0; - if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) > 0) /* We don't need this anymore on the unified hierarchy */ + r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER); + if (r < 0) + return log_error_errno(r, "Failed to determine whether unified cgroups hierarchy is used: %m"); + if (r > 0) /* We don't need this anymore on the unified hierarchy */ return 0; if (m->cgroups_agent_fd < 0) { @@ -1262,6 +1266,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { if (r < 0) return r; + r = manager_run_environment_generators(m); + if (r < 0) + return r; + /* Make sure the transient directory always exists, so that it remains in the search path */ if (!m->test_run) { r = mkdir_p_label(m->lookup_paths.transient, 0755); @@ -1398,7 +1406,7 @@ tr_abort: } int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, sd_bus_error *e, Job **ret) { - Unit *unit; + Unit *unit = NULL; /* just to appease gcc, initialization is not really necessary */ int r; assert(m); @@ -1409,6 +1417,7 @@ int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode r = manager_load_unit(m, name, NULL, NULL, &unit); if (r < 0) return r; + assert(unit); return manager_add_job(m, type, unit, mode, e, ret); } @@ -1481,6 +1490,7 @@ int manager_load_unit_prepare( assert(m); assert(name || path); + assert(_ret); /* This will prepare the unit for loading, but not actually * load anything from disk. */ @@ -1528,8 +1538,7 @@ int manager_load_unit_prepare( unit_add_to_dbus_queue(ret); unit_add_to_gc_queue(ret); - if (_ret) - *_ret = ret; + *_ret = ret; return 0; } @@ -1544,6 +1553,7 @@ int manager_load_unit( int r; assert(m); + assert(_ret); /* This will load the service information files, but not actually * start any services or anything. */ @@ -1554,8 +1564,7 @@ int manager_load_unit( manager_dispatch_load_queue(m); - if (_ret) - *_ret = unit_follow_merge(*_ret); + *_ret = unit_follow_merge(*_ret); return 0; } @@ -2170,7 +2179,7 @@ static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint assert(m->time_change_fd == fd); log_struct(LOG_DEBUG, - LOG_MESSAGE_ID(SD_MESSAGE_TIME_CHANGE), + "MESSAGE_ID=" SD_MESSAGE_TIME_CHANGE_STR, LOG_MESSAGE("Time has been changed"), NULL); @@ -2436,22 +2445,14 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) { } int manager_open_serialization(Manager *m, FILE **_f) { - int fd = -1; + int fd; FILE *f; assert(_f); - fd = memfd_create("systemd-serialization", MFD_CLOEXEC); - if (fd < 0) { - const char *path; - - path = MANAGER_IS_SYSTEM(m) ? "/run/systemd" : "/tmp"; - fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC); - if (fd < 0) - return -errno; - log_debug("Serializing state to %s.", path); - } else - log_debug("Serializing state to memfd."); + fd = open_serialization_fd("systemd-state"); + if (fd < 0) + return fd; f = fdopen(fd, "w+"); if (!f) { @@ -2460,7 +2461,6 @@ int manager_open_serialization(Manager *m, FILE **_f) { } *_f = f; - return 0; } @@ -2468,7 +2468,6 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { Iterator i; Unit *u; const char *t; - char **e; int r; assert(m); @@ -2498,17 +2497,8 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { dual_timestamp_serialize(f, "units-load-finish-timestamp", &m->units_load_finish_timestamp); } - if (!switching_root) { - STRV_FOREACH(e, m->environment) { - _cleanup_free_ char *ce; - - ce = cescape(*e); - if (!ce) - return -ENOMEM; - - fprintf(f, "env=%s\n", *e); - } - } + if (!switching_root) + (void) serialize_environment(f, m->environment); if (m->notify_fd >= 0) { int copy; @@ -2671,21 +2661,9 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { else if ((val = startswith(l, "units-load-finish-timestamp="))) dual_timestamp_deserialize(val, &m->units_load_finish_timestamp); else if (startswith(l, "env=")) { - _cleanup_free_ char *uce = NULL; - char **e; - - r = cunescape(l + 4, UNESCAPE_RELAX, &uce); + r = deserialize_environment(&m->environment, l); if (r < 0) - goto finish; - - e = strv_env_set(m->environment, uce); - if (!e) { - r = -ENOMEM; - goto finish; - } - - strv_free(m->environment); - m->environment = e; + return r; } else if ((val = startswith(l, "notify-fd="))) { int fd; @@ -2826,6 +2804,10 @@ int manager_reload(Manager *m) { if (q < 0 && r >= 0) r = q; + q = manager_run_environment_generators(m); + if (q < 0 && r >= 0) + r = q; + /* Find new unit paths */ q = manager_run_generators(m); if (q < 0 && r >= 0) @@ -2929,7 +2911,7 @@ static void manager_notify_finished(Manager *m) { initrd_usec = m->userspace_timestamp.monotonic - m->initrd_timestamp.monotonic; log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), + "MESSAGE_ID=" SD_MESSAGE_STARTUP_FINISHED_STR, "KERNEL_USEC="USEC_FMT, kernel_usec, "INITRD_USEC="USEC_FMT, initrd_usec, "USERSPACE_USEC="USEC_FMT, userspace_usec, @@ -2944,7 +2926,7 @@ static void manager_notify_finished(Manager *m) { initrd_usec = 0; log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), + "MESSAGE_ID=" SD_MESSAGE_STARTUP_FINISHED_STR, "KERNEL_USEC="USEC_FMT, kernel_usec, "USERSPACE_USEC="USEC_FMT, userspace_usec, LOG_MESSAGE("Startup finished in %s (kernel) + %s (userspace) = %s.", @@ -2958,7 +2940,7 @@ static void manager_notify_finished(Manager *m) { total_usec = userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic; log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_USER_STARTUP_FINISHED), + "MESSAGE_ID=" SD_MESSAGE_USER_STARTUP_FINISHED_STR, "USERSPACE_USEC="USEC_FMT, userspace_usec, LOG_MESSAGE("Startup finished in %s.", format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)), @@ -3017,10 +2999,56 @@ void manager_check_finished(Manager *m) { manager_invalidate_startup_units(m); } +static bool generator_path_any(const char* const* paths) { + char **path; + bool found = false; + + /* Optimize by skipping the whole process by not creating output directories + * if no generators are found. */ + STRV_FOREACH(path, (char**) paths) + if (access(*path, F_OK) == 0) + found = true; + else if (errno != ENOENT) + log_warning_errno(errno, "Failed to open generator directory %s: %m", *path); + + return found; +} + +static const char* system_env_generator_binary_paths[] = { + "/run/systemd/system-environment-generators", + "/etc/systemd/system-environment-generators", + "/usr/local/lib/systemd/system-environment-generators", + SYSTEM_ENV_GENERATOR_PATH, + NULL +}; + +static const char* user_env_generator_binary_paths[] = { + "/run/systemd/user-environment-generators", + "/etc/systemd/user-environment-generators", + "/usr/local/lib/systemd/user-environment-generators", + USER_ENV_GENERATOR_PATH, + NULL +}; + +static int manager_run_environment_generators(Manager *m) { + char **tmp = NULL; /* this is only used in the forked process, no cleanup here */ + const char **paths; + void* args[] = {&tmp, &tmp, &m->environment}; + + if (m->test_run) + return 0; + + paths = MANAGER_IS_SYSTEM(m) ? system_env_generator_binary_paths : user_env_generator_binary_paths; + + if (!generator_path_any(paths)) + return 0; + + return execute_directories(paths, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL); +} + static int manager_run_generators(Manager *m) { _cleanup_strv_free_ char **paths = NULL; const char *argv[5]; - char **path; int r; assert(m); @@ -3032,18 +3060,9 @@ static int manager_run_generators(Manager *m) { if (!paths) return log_oom(); - /* Optimize by skipping the whole process by not creating output directories - * if no generators are found. */ - STRV_FOREACH(path, paths) { - if (access(*path, F_OK) >= 0) - goto found; - if (errno != ENOENT) - log_warning_errno(errno, "Failed to open generator directory %s: %m", *path); - } - - return 0; + if (!generator_path_any((const char* const*) paths)) + return 0; - found: r = lookup_paths_mkdir_generator(&m->lookup_paths); if (r < 0) goto finish; @@ -3055,7 +3074,8 @@ static int manager_run_generators(Manager *m) { argv[4] = NULL; RUN_WITH_UMASK(0022) - execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, (char**) argv); + execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, + NULL, NULL, (char**) argv); finish: lookup_paths_trim_generator(&m->lookup_paths); diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c index 9c2bf3a0ef..7295efbf31 100644 --- a/src/core/mount-setup.c +++ b/src/core/mount-setup.c @@ -96,15 +96,15 @@ static const MountPoint mount_table[] = { { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, NULL, MNT_FATAL|MNT_IN_CONTAINER }, { "cgroup", "/sys/fs/cgroup", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, - cg_is_unified_wanted, MNT_FATAL|MNT_IN_CONTAINER }, + cg_is_unified_wanted, MNT_IN_CONTAINER }, { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER }, - { "cgroup", "/sys/fs/cgroup/systemd", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, - cg_is_unified_systemd_controller_wanted, MNT_IN_CONTAINER }, + { "cgroup", "/sys/fs/cgroup/unified", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + cg_is_hybrid_wanted, MNT_IN_CONTAINER }, { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV, - cg_is_legacy_systemd_controller_wanted, MNT_IN_CONTAINER }, + cg_is_legacy_wanted, MNT_IN_CONTAINER }, { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, - cg_is_legacy_systemd_controller_wanted, MNT_FATAL|MNT_IN_CONTAINER }, + cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER }, { "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL, MNT_NONE }, #ifdef ENABLE_EFI diff --git a/src/core/namespace.c b/src/core/namespace.c index 75dca5b791..4f29217bc4 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -799,7 +799,7 @@ static int make_read_only(MountEntry *m, char **blacklist) { if (mount_entry_read_only(m)) r = bind_remount_recursive(mount_entry_path(m), true, blacklist); - else if (m->mode == PRIVATE_DEV) { /* Superblock can be readonly but the submounts can't*/ + else if (m->mode == PRIVATE_DEV) { /* Superblock can be readonly but the submounts can't */ if (mount(NULL, mount_entry_path(m), NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL) < 0) r = -errno; } else diff --git a/src/core/scope.c b/src/core/scope.c index 9540fb67d9..a1d5c1cfd5 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -475,7 +475,7 @@ static void scope_sigchld_event(Unit *u, pid_t pid, int code, int status) { /* If the PID set is empty now, then let's finish this off (On unified we use proper notifications) */ - if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) <= 0 && set_isempty(u->pids)) + if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) == 0 && set_isempty(u->pids)) scope_notify_cgroup_empty_event(u); } diff --git a/src/core/service.c b/src/core/service.c index 54074ff7bc..74054887b9 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -849,11 +849,8 @@ static int service_load_pid_file(Service *s, bool may_warn) { return r; r = unit_watch_pid(UNIT(s), pid); - if (r < 0) { - /* FIXME: we need to do something here */ - log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" for service: %m", pid); - return r; - } + if (r < 0) /* FIXME: we need to do something here */ + return log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" for service: %m", pid); return 0; } @@ -1374,8 +1371,7 @@ static int service_spawn( return r; r = unit_watch_pid(UNIT(s), pid); - if (r < 0) - /* FIXME: we need to do something here */ + if (r < 0) /* FIXME: we need to do something here */ return r; *_pid = pid; @@ -2938,7 +2934,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { /* If the PID set is empty now, then let's finish this off (On unified we use proper notifications) */ - if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) <= 0 && set_isempty(u->pids)) + if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) == 0 && set_isempty(u->pids)) service_notify_cgroup_empty_event(u); } @@ -3097,6 +3093,8 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds) log_unit_warning(u, "Failed to parse MAINPID= field in notification message: %s", e); else if (pid == s->control_pid) log_unit_warning(u, "A control process cannot also be the main process"); + else if (pid == getpid() || pid == 1) + log_unit_warning(u, "Service manager can't be main process, ignoring sd_notify() MAINPID= field"); else { service_set_main_pid(s, pid); unit_watch_pid(UNIT(s), pid); @@ -3286,7 +3284,7 @@ static void service_bus_name_owner_change( if (r >= 0) r = sd_bus_creds_get_pid(creds, &pid); if (r >= 0) { - log_unit_debug(u, "D-Bus name %s is now owned by process %u", name, (unsigned) pid); + log_unit_debug(u, "D-Bus name %s is now owned by process " PID_FMT, name, pid); service_set_main_pid(s, pid); unit_watch_pid(UNIT(s), pid); diff --git a/src/core/shutdown.c b/src/core/shutdown.c index a795d875bb..a2309b7726 100644 --- a/src/core/shutdown.c +++ b/src/core/shutdown.c @@ -32,6 +32,7 @@ #include "alloc-util.h" #include "cgroup-util.h" #include "def.h" +#include "exec-util.h" #include "fileio.h" #include "killall.h" #include "log.h" @@ -321,7 +322,7 @@ int main(int argc, char *argv[]) { arguments[0] = NULL; arguments[1] = arg_verb; arguments[2] = NULL; - execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments); if (!in_container && !in_initrd() && access("/run/initramfs/shutdown", X_OK) == 0) { diff --git a/src/core/socket.c b/src/core/socket.c index a7b9ada65c..c4da227e09 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1260,7 +1260,7 @@ static int usbffs_address_create(const char *path) { if (fstat(fd, &st) < 0) return -errno; - /* Check whether this is a regular file (ffs endpoint)*/ + /* Check whether this is a regular file (ffs endpoint) */ if (!S_ISREG(st.st_mode)) return -EEXIST; @@ -1340,11 +1340,11 @@ static int usbffs_write_descs(int fd, Service *s) { if (!s->usb_function_descriptors || !s->usb_function_strings) return -EINVAL; - r = copy_file_fd(s->usb_function_descriptors, fd, false); + r = copy_file_fd(s->usb_function_descriptors, fd, 0); if (r < 0) return r; - return copy_file_fd(s->usb_function_strings, fd, false); + return copy_file_fd(s->usb_function_strings, fd, 0); } static int usbffs_select_ep(const struct dirent *d) { diff --git a/src/core/unit.c b/src/core/unit.c index 90d7eea956..f76b6c30a8 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -402,6 +402,7 @@ void unit_add_to_dbus_queue(Unit *u) { /* Shortcut things if nobody cares */ if (sd_bus_track_count(u->manager->subscribed) <= 0 && + sd_bus_track_count(u->bus_track) <= 0 && set_isempty(u->manager->private_buses)) { u->sent_dbus_new_signal = true; return; @@ -1466,9 +1467,8 @@ static void unit_status_print_starting_stopping(Unit *u, JobType t) { } static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { - const char *format; + const char *format, *mid; char buf[LINE_MAX]; - sd_id128_t mid; assert(u); @@ -1486,9 +1486,9 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { snprintf(buf, sizeof buf, format, unit_description(u)); REENABLE_WARNING; - mid = t == JOB_START ? SD_MESSAGE_UNIT_STARTING : - t == JOB_STOP ? SD_MESSAGE_UNIT_STOPPING : - SD_MESSAGE_UNIT_RELOADING; + mid = t == JOB_START ? "MESSAGE_ID=" SD_MESSAGE_UNIT_STARTING_STR : + t == JOB_STOP ? "MESSAGE_ID=" SD_MESSAGE_UNIT_STOPPING_STR : + "MESSAGE_ID=" SD_MESSAGE_UNIT_RELOADING_STR; /* Note that we deliberately use LOG_MESSAGE() instead of * LOG_UNIT_MESSAGE() here, since this is supposed to mimic @@ -1497,7 +1497,7 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { * possible, which means we should avoid the low-level unit * name. */ log_struct(LOG_INFO, - LOG_MESSAGE_ID(mid), + mid, LOG_UNIT_ID(u), LOG_MESSAGE("%s", buf), NULL); @@ -1527,6 +1527,7 @@ int unit_start_limit_test(Unit *u) { } bool unit_shall_confirm_spawn(Unit *u) { + assert(u); if (manager_is_confirm_spawn_disabled(u->manager)) return false; @@ -1537,6 +1538,31 @@ bool unit_shall_confirm_spawn(Unit *u) { return !unit_get_exec_context(u)->same_pgrp; } +static bool unit_verify_deps(Unit *u) { + Unit *other; + Iterator j; + + assert(u); + + /* Checks whether all BindsTo= dependencies of this unit are fulfilled — if they are also combined with + * After=. We do not check Requires= or Requisite= here as they only should have an effect on the job + * processing, but do not have any effect afterwards. We don't check BindsTo= dependencies that are not used in + * conjunction with After= as for them any such check would make things entirely racy. */ + + SET_FOREACH(other, u->dependencies[UNIT_BINDS_TO], j) { + + if (!set_contains(u->dependencies[UNIT_AFTER], other)) + continue; + + if (!UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(other))) { + log_unit_notice(u, "Bound to unit %s, but unit isn't active.", other->id); + return false; + } + } + + return true; +} + /* Errors: * -EBADR: This unit type does not support starting. * -EALREADY: Unit is already started. @@ -1545,6 +1571,7 @@ bool unit_shall_confirm_spawn(Unit *u) { * -EPROTO: Assert failed * -EINVAL: Unit not loaded * -EOPNOTSUPP: Unit type not supported + * -ENOLINK: The necessary dependencies are not fulfilled. */ int unit_start(Unit *u) { UnitActiveState state; @@ -1590,6 +1617,12 @@ int unit_start(Unit *u) { if (!unit_supported(u)) return -EOPNOTSUPP; + /* Let's make sure that the deps really are in order before we start this. Normally the job engine should have + * taken care of this already, but let's check this here again. After all, our dependencies might not be in + * effect anymore, due to a reload or due to a failed condition. */ + if (!unit_verify_deps(u)) + return -ENOLINK; + /* Forward to the main object, if we aren't it. */ following = unit_following(u); if (following) { @@ -2650,7 +2683,7 @@ void unit_unwatch_bus_name(Unit *u, const char *name) { assert(u); assert(name); - hashmap_remove_value(u->manager->watch_bus, name, u); + (void) hashmap_remove_value(u->manager->watch_bus, name, u); u->match_bus_slot = sd_bus_slot_unref(u->match_bus_slot); } @@ -3121,6 +3154,11 @@ static bool fragment_mtime_newer(const char *path, usec_t mtime, bool path_maske if (!path) return false; + /* If the source is some virtual kernel file system, then we assume we watch it anyway, and hence pretend we + * are never out-of-date. */ + if (PATH_STARTSWITH_SET(path, "/proc", "/sys")) + return false; + if (stat(path, &st) < 0) /* What, cannot access this anymore? */ return true; @@ -3863,10 +3901,10 @@ int unit_kill_context( * should not exist in non-delegated units. On * the unified hierarchy that's different, * there we get proper events. Hence rely on - * them.*/ + * them. */ - if (cg_unified(SYSTEMD_CGROUP_CONTROLLER) > 0 || - (detect_container() == 0 && !unit_cgroup_delegate(u))) + if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0 || + (detect_container() == 0 && !unit_cgroup_delegate(u))) wait_for_exit = true; if (send_sighup) { @@ -4036,7 +4074,7 @@ void unit_warn_if_dir_nonempty(Unit *u, const char* where) { } log_struct(LOG_NOTICE, - LOG_MESSAGE_ID(SD_MESSAGE_OVERMOUNTING), + "MESSAGE_ID=" SD_MESSAGE_OVERMOUNTING_STR, LOG_UNIT_ID(u), LOG_UNIT_MESSAGE(u, "Directory %s to mount over is not empty, mounting anyway.", where), "WHERE=%s", where, @@ -4058,7 +4096,7 @@ int unit_fail_if_symlink(Unit *u, const char* where) { return 0; log_struct(LOG_ERR, - LOG_MESSAGE_ID(SD_MESSAGE_OVERMOUNTING), + "MESSAGE_ID=" SD_MESSAGE_OVERMOUNTING_STR, LOG_UNIT_ID(u), LOG_UNIT_MESSAGE(u, "Mount on symlink %s not allowed.", where), "WHERE=%s", where, diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 1f6fb5de1e..4c4f36aea0 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -47,13 +47,14 @@ #include "fileio.h" #include "fs-util.h" #include "io-util.h" -#include "journald-native.h" +#include "journal-importer.h" #include "log.h" #include "macro.h" #include "missing.h" #include "mkdir.h" #include "parse-util.h" #include "process-util.h" +#include "signal-util.h" #include "socket-util.h" #include "special.h" #include "stacktrace.h" @@ -77,8 +78,22 @@ assert_cc(JOURNAL_SIZE_MAX <= DATA_SIZE_MAX); enum { - /* We use this as array indexes for a couple of special fields we use for naming coredumping files, and - * attaching xattrs */ + /* We use this as array indexes for a couple of special fields we use for + * naming coredump files, and attaching xattrs, and for indexing argv[]. + + * Our pattern for man:systectl(1) kernel.core_pattern is such that the + * kernel passes fields until CONTEXT_RLIMIT as arguments in argv[]. After + * that it gets complicated: the kernel passes "comm" as one or more fields + * starting at index CONTEXT_COMM (in other words, full "comm" is under index + * CONTEXT_COMM when it does not contain spaces, which is the common + * case). This mapping is not reversible, so we prefer to retrieve "comm" + * from /proc. We only fall back to argv[CONTEXT_COMM...] when that fails. + * + * In the internal context[] array, fields before CONTEXT_COMM are the + * strings from argv[], so they should not be freed. The strings at indices + * CONTEXT_COMM and higher are allocated by us and should be freed at the + * end. + */ CONTEXT_PID, CONTEXT_UID, CONTEXT_GID, @@ -87,6 +102,7 @@ enum { CONTEXT_RLIMIT, CONTEXT_COMM, CONTEXT_EXE, + CONTEXT_UNIT, _CONTEXT_MAX }; @@ -186,6 +202,7 @@ static int fix_xattr(int fd, const char *context[_CONTEXT_MAX]) { [CONTEXT_GID] = "user.coredump.gid", [CONTEXT_SIGNAL] = "user.coredump.signal", [CONTEXT_TIMESTAMP] = "user.coredump.timestamp", + [CONTEXT_RLIMIT] = "user.coredump.rlimit", [CONTEXT_COMM] = "user.coredump.comm", [CONTEXT_EXE] = "user.coredump.exe", }; @@ -308,7 +325,8 @@ static int save_external_coredump( char **ret_filename, int *ret_node_fd, int *ret_data_fd, - uint64_t *ret_size) { + uint64_t *ret_size, + bool *ret_truncated) { _cleanup_free_ char *fn = NULL, *tmp = NULL; _cleanup_close_ int fd = -1; @@ -352,15 +370,17 @@ static int save_external_coredump( if (fd < 0) return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn); - r = copy_bytes(input_fd, fd, max_size, false); + r = copy_bytes(input_fd, fd, max_size, 0); if (r < 0) { log_error_errno(r, "Cannot store coredump of %s (%s): %m", context[CONTEXT_PID], context[CONTEXT_COMM]); goto fail; - } else if (r == 1) + } + *ret_truncated = r == 1; + if (*ret_truncated) log_struct(LOG_INFO, LOG_MESSAGE("Core file was truncated to %zu bytes.", max_size), "SIZE_LIMIT=%zu", max_size, - LOG_MESSAGE_ID(SD_MESSAGE_TRUNCATED_CORE), + "MESSAGE_ID=" SD_MESSAGE_TRUNCATED_CORE_STR, NULL); if (fstat(fd, &st) < 0) { @@ -675,6 +695,21 @@ static int change_uid_gid(const char *context[]) { return drop_privileges(uid, gid, 0); } +static bool is_journald_crash(const char *context[_CONTEXT_MAX]) { + assert(context); + + return streq_ptr(context[CONTEXT_UNIT], SPECIAL_JOURNALD_SERVICE); +} + +static bool is_pid1_crash(const char *context[_CONTEXT_MAX]) { + assert(context); + + return streq_ptr(context[CONTEXT_UNIT], SPECIAL_INIT_SCOPE) || + streq_ptr(context[CONTEXT_PID], "1"); +} + +#define SUBMIT_COREDUMP_FIELDS 4 + static int submit_coredump( const char *context[_CONTEXT_MAX], struct iovec *iovec, @@ -685,18 +720,22 @@ static int submit_coredump( _cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1; _cleanup_free_ char *core_message = NULL, *filename = NULL, *coredump_data = NULL; uint64_t coredump_size = UINT64_MAX; + bool truncated = false, journald_crash; int r; assert(context); assert(iovec); - assert(n_iovec_allocated >= n_iovec + 3); + assert(n_iovec_allocated >= n_iovec + SUBMIT_COREDUMP_FIELDS); assert(input_fd >= 0); + journald_crash = is_journald_crash(context); + /* Vacuum before we write anything again */ (void) coredump_vacuum(-1, arg_keep_free, arg_max_use); /* Always stream the coredump to disk, if that's possible */ - r = save_external_coredump(context, input_fd, &filename, &coredump_node_fd, &coredump_fd, &coredump_size); + r = save_external_coredump(context, input_fd, + &filename, &coredump_node_fd, &coredump_fd, &coredump_size, &truncated); if (r < 0) /* Skip whole core dumping part */ goto log; @@ -735,8 +774,10 @@ static int submit_coredump( if (r >= 0) core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], " (", context[CONTEXT_COMM], ") of user ", - context[CONTEXT_UID], " dumped core.\n\n", - stacktrace); + context[CONTEXT_UID], " dumped core.", + journald_crash ? "\nCoredump diverted to " : "", + journald_crash ? filename : "", + "\n\n", stacktrace); else if (r == -EINVAL) log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno())); else @@ -748,12 +789,27 @@ static int submit_coredump( if (!core_message) #endif log: - core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], " (", - context[CONTEXT_COMM], ") of user ", - context[CONTEXT_UID], " dumped core."); + core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], + " (", context[CONTEXT_COMM], ") of user ", + context[CONTEXT_UID], " dumped core.", + journald_crash ? "\nCoredump diverted to " : NULL, + journald_crash ? filename : NULL); + if (!core_message) + return log_oom(); + + if (journald_crash) { + /* We cannot log to the journal, so just print the MESSAGE. + * The target was set previously to something safe. */ + log_struct(LOG_ERR, core_message, NULL); + return 0; + } + if (core_message) IOVEC_SET_STRING(iovec[n_iovec++], core_message); + if (truncated) + IOVEC_SET_STRING(iovec[n_iovec++], "COREDUMP_TRUNCATED=1"); + /* Optionally store the entire coredump in the journal */ if (arg_storage == COREDUMP_STORAGE_JOURNAL) { if (coredump_size <= arg_journal_size_max) { @@ -782,17 +838,17 @@ log: return 0; } -static void map_context_fields(const struct iovec *iovec, const char *context[]) { +static void map_context_fields(const struct iovec *iovec, const char* context[]) { - static const char * const context_field_names[_CONTEXT_MAX] = { + static const char * const context_field_names[] = { [CONTEXT_PID] = "COREDUMP_PID=", [CONTEXT_UID] = "COREDUMP_UID=", [CONTEXT_GID] = "COREDUMP_GID=", [CONTEXT_SIGNAL] = "COREDUMP_SIGNAL=", [CONTEXT_TIMESTAMP] = "COREDUMP_TIMESTAMP=", + [CONTEXT_RLIMIT] = "COREDUMP_RLIMIT=", [CONTEXT_COMM] = "COREDUMP_COMM=", [CONTEXT_EXE] = "COREDUMP_EXE=", - [CONTEXT_RLIMIT] = "COREDUMP_RLIMIT=", }; unsigned i; @@ -800,9 +856,12 @@ static void map_context_fields(const struct iovec *iovec, const char *context[]) assert(iovec); assert(context); - for (i = 0; i < _CONTEXT_MAX; i++) { + for (i = 0; i < ELEMENTSOF(context_field_names); i++) { size_t l; + if (!context_field_names[i]) + continue; + l = strlen(context_field_names[i]); if (iovec->iov_len < l) continue; @@ -820,7 +879,7 @@ static void map_context_fields(const struct iovec *iovec, const char *context[]) static int process_socket(int fd) { _cleanup_close_ int coredump_fd = -1; struct iovec *iovec = NULL; - size_t n_iovec = 0, n_iovec_allocated = 0, i; + size_t n_iovec = 0, n_allocated = 0, i, k; const char *context[_CONTEXT_MAX] = {}; int r; @@ -830,6 +889,8 @@ static int process_socket(int fd) { log_parse_environment(); log_open(); + log_debug("Processing coredump received on stdin..."); + for (;;) { union { struct cmsghdr cmsghdr; @@ -843,7 +904,7 @@ static int process_socket(int fd) { ssize_t n; ssize_t l; - if (!GREEDY_REALLOC(iovec, n_iovec_allocated, n_iovec + 3)) { + if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec + SUBMIT_COREDUMP_FIELDS)) { r = log_oom(); goto finish; } @@ -907,7 +968,7 @@ static int process_socket(int fd) { n_iovec++; } - if (!GREEDY_REALLOC(iovec, n_iovec_allocated, n_iovec + 3)) { + if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec + SUBMIT_COREDUMP_FIELDS)) { r = log_oom(); goto finish; } @@ -922,7 +983,14 @@ static int process_socket(int fd) { assert(context[CONTEXT_COMM]); assert(coredump_fd >= 0); - r = submit_coredump(context, iovec, n_iovec_allocated, n_iovec, coredump_fd); + /* Small quirk: the journal fields contain the timestamp padded with six zeroes, so that the kernel-supplied 1s + * granularity timestamps becomes 1µs granularity, i.e. the granularity systemd usually operates in. Since we + * are reconstructing the original kernel context, we chop this off again, here. */ + k = strlen(context[CONTEXT_TIMESTAMP]); + if (k > 6) + context[CONTEXT_TIMESTAMP] = strndupa(context[CONTEXT_TIMESTAMP], k - 6); + + r = submit_coredump(context, iovec, n_allocated, n_iovec, coredump_fd); finish: for (i = 0; i < n_iovec; i++) @@ -998,284 +1066,317 @@ static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd) return 0; } -static int process_special_crash(const char *context[], int input_fd) { - _cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1; - _cleanup_free_ char *filename = NULL; - uint64_t coredump_size; - int r; - - assert(context); - assert(input_fd >= 0); - - /* If we are pid1 or journald, we cut things short, don't write to the journal, but still create a coredump. */ - - if (arg_storage != COREDUMP_STORAGE_NONE) - arg_storage = COREDUMP_STORAGE_EXTERNAL; +static char* set_iovec_field(struct iovec iovec[27], size_t *n_iovec, const char *field, const char *value) { + char *x; - r = save_external_coredump(context, input_fd, &filename, &coredump_node_fd, &coredump_fd, &coredump_size); - if (r < 0) - return r; - - r = maybe_remove_external_coredump(filename, coredump_size); - if (r < 0) - return r; + x = strappend(field, value); + if (x) + IOVEC_SET_STRING(iovec[(*n_iovec)++], x); + return x; +} - log_notice("Detected coredump of the journal daemon or PID 1, diverted to %s.", filename); +static char* set_iovec_field_free(struct iovec iovec[27], size_t *n_iovec, const char *field, char *value) { + char *x; - return 0; + x = set_iovec_field(iovec, n_iovec, field, value); + free(value); + return x; } -static int process_kernel(int argc, char* argv[]) { +static int gather_pid_metadata( + char* context[_CONTEXT_MAX], + char **comm_fallback, + struct iovec *iovec, size_t *n_iovec) { + + /* We need 26 empty slots in iovec! + * + * Note that if we fail on oom later on, we do not roll-back changes to the iovec structure. (It remains valid, + * with the first n_iovec fields initialized.) */ - /* The small core field we allocate on the stack, to keep things simple */ - char - *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL, - *core_session = NULL, *core_exe = NULL, *core_comm = NULL, *core_cmdline = NULL, - *core_cgroup = NULL, *core_cwd = NULL, *core_root = NULL, *core_unit = NULL, - *core_user_unit = NULL, *core_slice = NULL, *core_timestamp = NULL, *core_rlimit = NULL; - - /* The larger ones we allocate on the heap */ - _cleanup_free_ char - *core_owner_uid = NULL, *core_open_fds = NULL, *core_proc_status = NULL, - *core_proc_maps = NULL, *core_proc_limits = NULL, *core_proc_cgroup = NULL, *core_environ = NULL, - *core_proc_mountinfo = NULL, *core_container_cmdline = NULL; - - _cleanup_free_ char *exe = NULL, *comm = NULL; - const char *context[_CONTEXT_MAX]; - bool proc_self_root_is_slash; - struct iovec iovec[27]; - size_t n_iovec = 0; uid_t owner_uid; - const char *p; pid_t pid; char *t; - int r; - - if (argc < CONTEXT_COMM + 1) { - log_error("Not enough arguments passed from kernel (%i, expected %i).", argc - 1, CONTEXT_COMM + 1 - 1); - return -EINVAL; - } + const char *p; + int r, signo; - r = parse_pid(argv[CONTEXT_PID + 1], &pid); + r = parse_pid(context[CONTEXT_PID], &pid); if (r < 0) - return log_error_errno(r, "Failed to parse PID."); + return log_error_errno(r, "Failed to parse PID \"%s\": %m", context[CONTEXT_PID]); - r = get_process_comm(pid, &comm); + r = get_process_comm(pid, &context[CONTEXT_COMM]); if (r < 0) { log_warning_errno(r, "Failed to get COMM, falling back to the command line: %m"); - comm = strv_join(argv + CONTEXT_COMM + 1, " "); - if (!comm) + context[CONTEXT_COMM] = strv_join(comm_fallback, " "); + if (!context[CONTEXT_COMM]) return log_oom(); } - r = get_process_exe(pid, &exe); + r = get_process_exe(pid, &context[CONTEXT_EXE]); if (r < 0) log_warning_errno(r, "Failed to get EXE, ignoring: %m"); - context[CONTEXT_PID] = argv[CONTEXT_PID + 1]; - context[CONTEXT_UID] = argv[CONTEXT_UID + 1]; - context[CONTEXT_GID] = argv[CONTEXT_GID + 1]; - context[CONTEXT_SIGNAL] = argv[CONTEXT_SIGNAL + 1]; - context[CONTEXT_TIMESTAMP] = argv[CONTEXT_TIMESTAMP + 1]; - context[CONTEXT_RLIMIT] = argv[CONTEXT_RLIMIT + 1]; - context[CONTEXT_COMM] = comm; - context[CONTEXT_EXE] = exe; - - if (cg_pid_get_unit(pid, &t) >= 0) { + if (cg_pid_get_unit(pid, &context[CONTEXT_UNIT]) >= 0) { + if (!is_journald_crash((const char**) context)) { + /* OK, now we know it's not the journal, hence we can make use of it now. */ + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); + log_open(); + } /* If this is PID 1 disable coredump collection, we'll unlikely be able to process it later on. */ - if (streq(t, SPECIAL_INIT_SCOPE)) { + if (is_pid1_crash((const char**) context)) { log_notice("Due to PID 1 having crashed coredump collection will now be turned off."); (void) write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0); } - /* Let's avoid dead-locks when processing journald and init crashes, as socket activation and logging - * are unlikely to work then. */ - if (STR_IN_SET(t, SPECIAL_JOURNALD_SERVICE, SPECIAL_INIT_SCOPE)) { - free(t); - return process_special_crash(context, STDIN_FILENO); - } - - core_unit = strjoina("COREDUMP_UNIT=", t); - free(t); - - IOVEC_SET_STRING(iovec[n_iovec++], core_unit); + set_iovec_field(iovec, n_iovec, "COREDUMP_UNIT=", context[CONTEXT_UNIT]); } - /* OK, now we know it's not the journal, hence we can make use of it now. */ - log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); - log_open(); + if (cg_pid_get_user_unit(pid, &t) >= 0) + set_iovec_field_free(iovec, n_iovec, "COREDUMP_USER_UNIT=", t); - if (cg_pid_get_user_unit(pid, &t) >= 0) { - core_user_unit = strjoina("COREDUMP_USER_UNIT=", t); - free(t); + /* The next few are mandatory */ + if (!set_iovec_field(iovec, n_iovec, "COREDUMP_PID=", context[CONTEXT_PID])) + return log_oom(); - IOVEC_SET_STRING(iovec[n_iovec++], core_user_unit); - } + if (!set_iovec_field(iovec, n_iovec, "COREDUMP_UID=", context[CONTEXT_UID])) + return log_oom(); - core_pid = strjoina("COREDUMP_PID=", context[CONTEXT_PID]); - IOVEC_SET_STRING(iovec[n_iovec++], core_pid); + if (!set_iovec_field(iovec, n_iovec, "COREDUMP_GID=", context[CONTEXT_GID])) + return log_oom(); - core_uid = strjoina("COREDUMP_UID=", context[CONTEXT_UID]); - IOVEC_SET_STRING(iovec[n_iovec++], core_uid); + if (!set_iovec_field(iovec, n_iovec, "COREDUMP_SIGNAL=", context[CONTEXT_SIGNAL])) + return log_oom(); - core_gid = strjoina("COREDUMP_GID=", context[CONTEXT_GID]); - IOVEC_SET_STRING(iovec[n_iovec++], core_gid); + if (!set_iovec_field(iovec, n_iovec, "COREDUMP_RLIMIT=", context[CONTEXT_RLIMIT])) + return log_oom(); - core_signal = strjoina("COREDUMP_SIGNAL=", context[CONTEXT_SIGNAL]); - IOVEC_SET_STRING(iovec[n_iovec++], core_signal); + if (!set_iovec_field(iovec, n_iovec, "COREDUMP_COMM=", context[CONTEXT_COMM])) + return log_oom(); - core_rlimit = strjoina("COREDUMP_RLIMIT=", context[CONTEXT_RLIMIT]); - IOVEC_SET_STRING(iovec[n_iovec++], core_rlimit); + if (context[CONTEXT_EXE] && + !set_iovec_field(iovec, n_iovec, "COREDUMP_EXE=", context[CONTEXT_EXE])) + return log_oom(); - if (sd_pid_get_session(pid, &t) >= 0) { - core_session = strjoina("COREDUMP_SESSION=", t); - free(t); - - IOVEC_SET_STRING(iovec[n_iovec++], core_session); - } + if (sd_pid_get_session(pid, &t) >= 0) + set_iovec_field_free(iovec, n_iovec, "COREDUMP_SESSION=", t); if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) { - r = asprintf(&core_owner_uid, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid); + r = asprintf(&t, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid); if (r > 0) - IOVEC_SET_STRING(iovec[n_iovec++], core_owner_uid); + IOVEC_SET_STRING(iovec[(*n_iovec)++], t); } - if (sd_pid_get_slice(pid, &t) >= 0) { - core_slice = strjoina("COREDUMP_SLICE=", t); - free(t); + if (sd_pid_get_slice(pid, &t) >= 0) + set_iovec_field_free(iovec, n_iovec, "COREDUMP_SLICE=", t); - IOVEC_SET_STRING(iovec[n_iovec++], core_slice); - } + if (get_process_cmdline(pid, 0, false, &t) >= 0) + set_iovec_field_free(iovec, n_iovec, "COREDUMP_CMDLINE=", t); - if (comm) { - core_comm = strjoina("COREDUMP_COMM=", comm); - IOVEC_SET_STRING(iovec[n_iovec++], core_comm); - } + if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) + set_iovec_field_free(iovec, n_iovec, "COREDUMP_CGROUP=", t); - if (exe) { - core_exe = strjoina("COREDUMP_EXE=", exe); - IOVEC_SET_STRING(iovec[n_iovec++], core_exe); - } + if (compose_open_fds(pid, &t) >= 0) + set_iovec_field_free(iovec, n_iovec, "COREDUMP_OPEN_FDS=", t); - if (get_process_cmdline(pid, 0, false, &t) >= 0) { - core_cmdline = strjoina("COREDUMP_CMDLINE=", t); - free(t); + p = procfs_file_alloca(pid, "status"); + if (read_full_file(p, &t, NULL) >= 0) + set_iovec_field_free(iovec, n_iovec, "COREDUMP_PROC_STATUS=", t); - IOVEC_SET_STRING(iovec[n_iovec++], core_cmdline); - } + p = procfs_file_alloca(pid, "maps"); + if (read_full_file(p, &t, NULL) >= 0) + set_iovec_field_free(iovec, n_iovec, "COREDUMP_PROC_MAPS=", t); - if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) { - core_cgroup = strjoina("COREDUMP_CGROUP=", t); - free(t); + p = procfs_file_alloca(pid, "limits"); + if (read_full_file(p, &t, NULL) >= 0) + set_iovec_field_free(iovec, n_iovec, "COREDUMP_PROC_LIMITS=", t); - IOVEC_SET_STRING(iovec[n_iovec++], core_cgroup); - } + p = procfs_file_alloca(pid, "cgroup"); + if (read_full_file(p, &t, NULL) >=0) + set_iovec_field_free(iovec, n_iovec, "COREDUMP_PROC_CGROUP=", t); - if (compose_open_fds(pid, &t) >= 0) { - core_open_fds = strappend("COREDUMP_OPEN_FDS=", t); - free(t); + p = procfs_file_alloca(pid, "mountinfo"); + if (read_full_file(p, &t, NULL) >=0) + set_iovec_field_free(iovec, n_iovec, "COREDUMP_PROC_MOUNTINFO=", t); - if (core_open_fds) - IOVEC_SET_STRING(iovec[n_iovec++], core_open_fds); - } + if (get_process_cwd(pid, &t) >= 0) + set_iovec_field_free(iovec, n_iovec, "COREDUMP_CWD=", t); - p = procfs_file_alloca(pid, "status"); - if (read_full_file(p, &t, NULL) >= 0) { - core_proc_status = strappend("COREDUMP_PROC_STATUS=", t); - free(t); + if (get_process_root(pid, &t) >= 0) { + bool proc_self_root_is_slash; - if (core_proc_status) - IOVEC_SET_STRING(iovec[n_iovec++], core_proc_status); - } + proc_self_root_is_slash = strcmp(t, "/") == 0; - p = procfs_file_alloca(pid, "maps"); - if (read_full_file(p, &t, NULL) >= 0) { - core_proc_maps = strappend("COREDUMP_PROC_MAPS=", t); - free(t); + set_iovec_field_free(iovec, n_iovec, "COREDUMP_ROOT=", t); - if (core_proc_maps) - IOVEC_SET_STRING(iovec[n_iovec++], core_proc_maps); + /* If the process' root is "/", then there is a chance it has + * mounted own root and hence being containerized. */ + if (proc_self_root_is_slash && get_process_container_parent_cmdline(pid, &t) > 0) + set_iovec_field_free(iovec, n_iovec, "COREDUMP_CONTAINER_CMDLINE=", t); } - p = procfs_file_alloca(pid, "limits"); - if (read_full_file(p, &t, NULL) >= 0) { - core_proc_limits = strappend("COREDUMP_PROC_LIMITS=", t); - free(t); + if (get_process_environ(pid, &t) >= 0) + set_iovec_field_free(iovec, n_iovec, "COREDUMP_ENVIRON=", t); - if (core_proc_limits) - IOVEC_SET_STRING(iovec[n_iovec++], core_proc_limits); - } + t = strjoin("COREDUMP_TIMESTAMP=", context[CONTEXT_TIMESTAMP], "000000", NULL); + if (t) + IOVEC_SET_STRING(iovec[(*n_iovec)++], t); - p = procfs_file_alloca(pid, "cgroup"); - if (read_full_file(p, &t, NULL) >=0) { - core_proc_cgroup = strappend("COREDUMP_PROC_CGROUP=", t); - free(t); + if (safe_atoi(context[CONTEXT_SIGNAL], &signo) >= 0 && SIGNAL_VALID(signo)) + set_iovec_field(iovec, n_iovec, "COREDUMP_SIGNAL_NAME=SIG", signal_to_string(signo)); - if (core_proc_cgroup) - IOVEC_SET_STRING(iovec[n_iovec++], core_proc_cgroup); - } + return 0; /* we successfully acquired all metadata */ +} - p = procfs_file_alloca(pid, "mountinfo"); - if (read_full_file(p, &t, NULL) >=0) { - core_proc_mountinfo = strappend("COREDUMP_PROC_MOUNTINFO=", t); - free(t); +static int process_kernel(int argc, char* argv[]) { - if (core_proc_mountinfo) - IOVEC_SET_STRING(iovec[n_iovec++], core_proc_mountinfo); - } + char* context[_CONTEXT_MAX] = {}; + struct iovec iovec[28 + SUBMIT_COREDUMP_FIELDS]; + size_t i, n_iovec, n_to_free = 0; + int r; - if (get_process_cwd(pid, &t) >= 0) { - core_cwd = strjoina("COREDUMP_CWD=", t); - free(t); + log_debug("Processing coredump received from the kernel..."); - IOVEC_SET_STRING(iovec[n_iovec++], core_cwd); + if (argc < CONTEXT_COMM + 1) { + log_error("Not enough arguments passed by the kernel (%i, expected %i).", argc - 1, CONTEXT_COMM + 1 - 1); + return -EINVAL; } - if (get_process_root(pid, &t) >= 0) { - core_root = strjoina("COREDUMP_ROOT=", t); + context[CONTEXT_PID] = argv[1 + CONTEXT_PID]; + context[CONTEXT_UID] = argv[1 + CONTEXT_UID]; + context[CONTEXT_GID] = argv[1 + CONTEXT_GID]; + context[CONTEXT_SIGNAL] = argv[1 + CONTEXT_SIGNAL]; + context[CONTEXT_TIMESTAMP] = argv[1 + CONTEXT_TIMESTAMP]; + context[CONTEXT_RLIMIT] = argv[1 + CONTEXT_RLIMIT]; - IOVEC_SET_STRING(iovec[n_iovec++], core_root); + r = gather_pid_metadata(context, argv + 1 + CONTEXT_COMM, iovec, &n_to_free); + if (r < 0) + goto finish; - /* If the process' root is "/", then there is a chance it has - * mounted own root and hence being containerized. */ - proc_self_root_is_slash = strcmp(t, "/") == 0; - free(t); - if (proc_self_root_is_slash && get_process_container_parent_cmdline(pid, &t) > 0) { - core_container_cmdline = strappend("COREDUMP_CONTAINER_CMDLINE=", t); - free(t); + n_iovec = n_to_free; - if (core_container_cmdline) - IOVEC_SET_STRING(iovec[n_iovec++], core_container_cmdline); - } + IOVEC_SET_STRING(iovec[n_iovec++], "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR); + + assert_cc(2 == LOG_CRIT); + IOVEC_SET_STRING(iovec[n_iovec++], "PRIORITY=2"); + + assert(n_iovec <= ELEMENTSOF(iovec)); + + if (is_journald_crash((const char**) context) || is_pid1_crash((const char**) context)) + r = submit_coredump((const char**) context, + iovec, ELEMENTSOF(iovec), n_iovec, + STDIN_FILENO); + else + r = send_iovec(iovec, n_iovec, STDIN_FILENO); + + finish: + for (i = 0; i < n_to_free; i++) + free(iovec[i].iov_base); + + /* Those fields are allocated by gather_pid_metadata */ + free(context[CONTEXT_COMM]); + free(context[CONTEXT_EXE]); + free(context[CONTEXT_UNIT]); + + return r; +} + +static int process_backtrace(int argc, char *argv[]) { + char *context[_CONTEXT_MAX] = {}; + _cleanup_free_ char *message = NULL; + _cleanup_free_ struct iovec *iovec = NULL; + size_t n_iovec, n_allocated, n_to_free = 0, i; + int r; + JournalImporter importer = { + .fd = STDIN_FILENO, + }; + + log_debug("Processing backtrace on stdin..."); + + if (argc < CONTEXT_COMM + 1) { + log_error("Not enough arguments passed (%i, expected %i).", argc - 1, CONTEXT_COMM + 1 - 1); + return -EINVAL; } - if (get_process_environ(pid, &t) >= 0) { - core_environ = strappend("COREDUMP_ENVIRON=", t); - free(t); + context[CONTEXT_PID] = argv[2 + CONTEXT_PID]; + context[CONTEXT_UID] = argv[2 + CONTEXT_UID]; + context[CONTEXT_GID] = argv[2 + CONTEXT_GID]; + context[CONTEXT_SIGNAL] = argv[2 + CONTEXT_SIGNAL]; + context[CONTEXT_TIMESTAMP] = argv[2 + CONTEXT_TIMESTAMP]; + context[CONTEXT_RLIMIT] = argv[2 + CONTEXT_RLIMIT]; + + n_allocated = 33 + COREDUMP_STORAGE_EXTERNAL; + /* 25 metadata, 2 static, +unknown input, 4 storage, rounded up */ + iovec = new(struct iovec, n_allocated); + if (!iovec) + return log_oom(); + + r = gather_pid_metadata(context, argv + 2 + CONTEXT_COMM, iovec, &n_to_free); + if (r < 0) + goto finish; + if (r > 0) { + /* This was a special crash, and has already been processed. */ + r = 0; + goto finish; + } + n_iovec = n_to_free; - if (core_environ) - IOVEC_SET_STRING(iovec[n_iovec++], core_environ); + for (;;) { + r = journal_importer_process_data(&importer); + if (r < 0) { + log_error_errno(r, "Failed to parse journal entry on stdin: %m"); + goto finish; + } + if (r == 1) + break; } - core_timestamp = strjoina("COREDUMP_TIMESTAMP=", context[CONTEXT_TIMESTAMP], "000000"); - IOVEC_SET_STRING(iovec[n_iovec++], core_timestamp); + if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec + importer.iovw.count + 2)) + return log_oom(); - IOVEC_SET_STRING(iovec[n_iovec++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1"); + if (journal_importer_eof(&importer)) { + log_warning("Did not receive a full journal entry on stdin, ignoring message sent by reporter"); + message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], + " (", context[CONTEXT_COMM], ")" + " of user ", context[CONTEXT_UID], + " failed with ", context[CONTEXT_SIGNAL]); + if (!message) { + r = log_oom(); + goto finish; + } + IOVEC_SET_STRING(iovec[n_iovec++], message); + } else { + for (i = 0; i < importer.iovw.count; i++) + iovec[n_iovec++] = importer.iovw.iovec[i]; + } + + IOVEC_SET_STRING(iovec[n_iovec++], "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR); assert_cc(2 == LOG_CRIT); IOVEC_SET_STRING(iovec[n_iovec++], "PRIORITY=2"); - assert(n_iovec <= ELEMENTSOF(iovec)); + assert(n_iovec <= n_allocated); - return send_iovec(iovec, n_iovec, STDIN_FILENO); + r = sd_journal_sendv(iovec, n_iovec); + if (r < 0) + log_error_errno(r, "Failed to log backtrace: %m"); + + finish: + for (i = 0; i < n_to_free; i++) + free(iovec[i].iov_base); + + /* Those fields are allocated by gather_pid_metadata */ + free(context[CONTEXT_COMM]); + free(context[CONTEXT_EXE]); + free(context[CONTEXT_UNIT]); + + return r; } int main(int argc, char *argv[]) { int r; - /* First, log to a safe place, since we don't know what crashed and it might be journald which we'd rather not - * log to then. */ + /* First, log to a safe place, since we don't know what crashed and it might + * be journald which we'd rather not log to then. */ log_set_target(LOG_TARGET_KMSG); log_open(); @@ -1295,11 +1396,14 @@ int main(int argc, char *argv[]) { goto finish; } - /* If we got an fd passed, we are running in coredumpd mode. Otherwise we are invoked from the kernel as - * coredump handler */ - if (r == 0) - r = process_kernel(argc, argv); - else if (r == 1) + /* If we got an fd passed, we are running in coredumpd mode. Otherwise we + * are invoked from the kernel as coredump handler. */ + if (r == 0) { + if (streq_ptr(argv[1], "--backtrace")) + r = process_backtrace(argc, argv); + else + r = process_kernel(argc, argv); + } else if (r == 1) r = process_socket(SD_LISTEN_FDS_START); else { log_error("Received unexpected number of file descriptors."); diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index 646757f9d9..114a13fc78 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -24,28 +24,37 @@ #include <string.h> #include <unistd.h> +#include "sd-bus.h" #include "sd-journal.h" +#include "sd-messages.h" #include "alloc-util.h" +#include "bus-error.h" +#include "bus-util.h" #include "compress.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" #include "journal-internal.h" +#include "journal-util.h" #include "log.h" #include "macro.h" #include "pager.h" #include "parse-util.h" #include "path-util.h" #include "process-util.h" -#include "set.h" #include "sigbus.h" #include "signal-util.h" #include "string-util.h" +#include "strv.h" #include "terminal-util.h" #include "user-util.h" #include "util.h" +#define SHORT_BUS_CALL_TIMEOUT_USEC (3 * USEC_PER_SEC) + +static usec_t arg_since = USEC_INFINITY, arg_until = USEC_INFINITY; + static enum { ACTION_NONE, ACTION_INFO, @@ -59,36 +68,11 @@ static bool arg_no_pager = false; static int arg_no_legend = false; static int arg_one = false; static FILE* arg_output = NULL; +static bool arg_reverse = false; +static char** arg_matches = NULL; +static bool arg_quiet = false; -static Set *new_matches(void) { - Set *set; - char *tmp; - int r; - - set = set_new(NULL); - if (!set) { - log_oom(); - return NULL; - } - - tmp = strdup("MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1"); - if (!tmp) { - log_oom(); - set_free(set); - return NULL; - } - - r = set_consume(set, tmp); - if (r < 0) { - log_error_errno(r, "failed to add to set: %m"); - set_free(set); - return NULL; - } - - return set; -} - -static int add_match(Set *set, const char *match) { +static int add_match(sd_journal *j, const char *match) { _cleanup_free_ char *p = NULL; char *pattern = NULL; const char* prefix; @@ -100,7 +84,8 @@ static int add_match(Set *set, const char *match) { else if (strchr(match, '/')) { r = path_make_absolute_cwd(match, &p); if (r < 0) - goto fail; + return log_error_errno(r, "path_make_absolute_cwd(\"%s\"): %m", match); + match = p; prefix = "COREDUMP_EXE="; } else if (parse_pid(match, &pid) >= 0) @@ -109,19 +94,35 @@ static int add_match(Set *set, const char *match) { prefix = "COREDUMP_COMM="; pattern = strjoin(prefix, match); - if (!pattern) { - r = -ENOMEM; - goto fail; - } + if (!pattern) + return log_oom(); + + log_debug("Adding match: %s", pattern); + r = sd_journal_add_match(j, pattern, 0); + if (r < 0) + return log_error_errno(r, "Failed to add match \"%s\": %m", match); + return 0; +} + +static int add_matches(sd_journal *j) { + char **match; + int r; - log_debug("Adding pattern: %s", pattern); - r = set_consume(set, pattern); + r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR, 0); if (r < 0) - goto fail; + return log_error_errno(r, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR); + + r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR, 0); + if (r < 0) + return log_error_errno(r, "Failed to add match \"%s\": %m", "MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR); + + STRV_FOREACH(match, arg_matches) { + r = add_match(j, *match); + if (r < 0) + return r; + } return 0; -fail: - return log_error_errno(r, "Failed to add match: %m"); } static void help(void) { @@ -133,10 +134,13 @@ static void help(void) { " --no-pager Do not pipe output into a pager\n" " --no-legend Do not print the column headers.\n" " -1 Show information about most recent entry only\n" + " -S --since=DATE Only print coredumps since the date\n" + " -U --until=DATE Only print coredumps until the date\n" + " -r --reverse Show the newest entries first\n" " -F --field=FIELD List all values a certain field takes\n" - " -o --output=FILE Write output to FILE\n\n" + " -o --output=FILE Write output to FILE\n" " -D --directory=DIR Use journal files from directory\n\n" - + " -q --quiet Do not show info messages and privilege warning\n" "Commands:\n" " list [MATCHES...] List available coredumps (default)\n" " info [MATCHES...] Show detailed information about one or more coredumps\n" @@ -145,14 +149,14 @@ static void help(void) { , program_invocation_short_name); } -static int parse_argv(int argc, char *argv[], Set *matches) { +static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, ARG_NO_PAGER, ARG_NO_LEGEND, }; - int r, c; + int c, r; static const struct option options[] = { { "help", no_argument, NULL, 'h' }, @@ -162,15 +166,18 @@ static int parse_argv(int argc, char *argv[], Set *matches) { { "output", required_argument, NULL, 'o' }, { "field", required_argument, NULL, 'F' }, { "directory", required_argument, NULL, 'D' }, + { "reverse", no_argument, NULL, 'r' }, + { "since", required_argument, NULL, 'S' }, + { "until", required_argument, NULL, 'U' }, + { "quiet", no_argument, NULL, 'q' }, {} }; assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "ho:F:1D:", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "ho:F:1D:rS:U:q", options, NULL)) >= 0) switch(c) { - case 'h': arg_action = ACTION_NONE; help(); @@ -200,6 +207,18 @@ static int parse_argv(int argc, char *argv[], Set *matches) { break; + case 'S': + r = parse_timestamp(optarg, &arg_since); + if (r < 0) + return log_error_errno(r, "Failed to parse timestamp: %s", optarg); + break; + + case 'U': + r = parse_timestamp(optarg, &arg_until); + if (r < 0) + return log_error_errno(r, "Failed to parse timestamp: %s", optarg); + break; + case 'F': if (arg_field) { log_error("cannot use --field/-F more than once"); @@ -216,6 +235,14 @@ static int parse_argv(int argc, char *argv[], Set *matches) { arg_directory = optarg; break; + case 'r': + arg_reverse = true; + break; + + case 'q': + arg_quiet = true; + break; + case '?': return -EINVAL; @@ -223,6 +250,12 @@ static int parse_argv(int argc, char *argv[], Set *matches) { assert_not_reached("Unhandled option"); } + if (arg_since != USEC_INFINITY && arg_until != USEC_INFINITY && + arg_since > arg_until) { + log_error("--since= must be before --until=."); + return -EINVAL; + } + if (optind < argc) { const char *cmd = argv[optind++]; if (streq(cmd, "list")) @@ -244,12 +277,8 @@ static int parse_argv(int argc, char *argv[], Set *matches) { return -EINVAL; } - while (optind < argc) { - r = add_match(matches, argv[optind]); - if (r != 0) - return r; - optind++; - } + if (optind < argc) + arg_matches = argv + optind; return 0; } @@ -322,20 +351,22 @@ static int print_field(FILE* file, sd_journal *j) { static int print_list(FILE* file, sd_journal *j, int had_legend) { _cleanup_free_ char - *pid = NULL, *uid = NULL, *gid = NULL, + *mid = NULL, *pid = NULL, *uid = NULL, *gid = NULL, *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL, - *filename = NULL, *coredump = NULL; + *filename = NULL, *truncated = NULL, *coredump = NULL; const void *d; size_t l; usec_t t; char buf[FORMAT_TIMESTAMP_MAX]; int r; const char *present; + bool normal_coredump; assert(file); assert(j); SD_JOURNAL_FOREACH_DATA(j, d, l) { + RETRIEVE(d, l, "MESSAGE_ID", mid); RETRIEVE(d, l, "COREDUMP_PID", pid); RETRIEVE(d, l, "COREDUMP_UID", uid); RETRIEVE(d, l, "COREDUMP_GID", gid); @@ -344,6 +375,7 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) { RETRIEVE(d, l, "COREDUMP_COMM", comm); RETRIEVE(d, l, "COREDUMP_CMDLINE", cmdline); RETRIEVE(d, l, "COREDUMP_FILENAME", filename); + RETRIEVE(d, l, "COREDUMP_TRUNCATED", truncated); RETRIEVE(d, l, "COREDUMP", coredump); } @@ -359,15 +391,17 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) { format_timestamp(buf, sizeof(buf), t); if (!had_legend && !arg_no_legend) - fprintf(file, "%-*s %*s %*s %*s %*s %*s %s\n", + fprintf(file, "%-*s %*s %*s %*s %*s %-*s %s\n", FORMAT_TIMESTAMP_WIDTH, "TIME", 6, "PID", 5, "UID", 5, "GID", 3, "SIG", - 8, "COREFILE", + 9, "COREFILE", "EXE"); + normal_coredump = streq_ptr(mid, SD_MESSAGE_COREDUMP_STR); + if (filename) if (access(filename, R_OK) == 0) present = "present"; @@ -377,16 +411,21 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) { present = "error"; else if (coredump) present = "journal"; - else + else if (normal_coredump) present = "none"; + else + present = "-"; + + if (STR_IN_SET(present, "present", "journal") && truncated && parse_boolean(truncated) > 0) + present = "truncated"; fprintf(file, "%-*s %*s %*s %*s %*s %-*s %s\n", FORMAT_TIMESTAMP_WIDTH, buf, 6, strna(pid), 5, strna(uid), 5, strna(gid), - 3, strna(sgnl), - 8, present, + 3, normal_coredump ? strna(sgnl) : "-", + 9, present, strna(exe ?: (comm ?: cmdline))); return 0; @@ -394,21 +433,23 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) { static int print_info(FILE *file, sd_journal *j, bool need_space) { _cleanup_free_ char - *pid = NULL, *uid = NULL, *gid = NULL, + *mid = NULL, *pid = NULL, *uid = NULL, *gid = NULL, *sgnl = NULL, *exe = NULL, *comm = NULL, *cmdline = NULL, *unit = NULL, *user_unit = NULL, *session = NULL, *boot_id = NULL, *machine_id = NULL, *hostname = NULL, *slice = NULL, *cgroup = NULL, *owner_uid = NULL, *message = NULL, *timestamp = NULL, *filename = NULL, - *coredump = NULL; + *truncated = NULL, *coredump = NULL; const void *d; size_t l; + bool normal_coredump; int r; assert(file); assert(j); SD_JOURNAL_FOREACH_DATA(j, d, l) { + RETRIEVE(d, l, "MESSAGE_ID", mid); RETRIEVE(d, l, "COREDUMP_PID", pid); RETRIEVE(d, l, "COREDUMP_UID", uid); RETRIEVE(d, l, "COREDUMP_GID", gid); @@ -424,6 +465,7 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) { RETRIEVE(d, l, "COREDUMP_CGROUP", cgroup); RETRIEVE(d, l, "COREDUMP_TIMESTAMP", timestamp); RETRIEVE(d, l, "COREDUMP_FILENAME", filename); + RETRIEVE(d, l, "COREDUMP_TRUNCATED", truncated); RETRIEVE(d, l, "COREDUMP", coredump); RETRIEVE(d, l, "_BOOT_ID", boot_id); RETRIEVE(d, l, "_MACHINE_ID", machine_id); @@ -434,6 +476,8 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) { if (need_space) fputs("\n", file); + normal_coredump = streq_ptr(mid, SD_MESSAGE_COREDUMP_STR); + if (comm) fprintf(file, " PID: %s%s%s (%s)\n", @@ -479,11 +523,12 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) { if (sgnl) { int sig; + const char *name = normal_coredump ? "Signal" : "Reason"; - if (safe_atoi(sgnl, &sig) >= 0) - fprintf(file, " Signal: %s (%s)\n", sgnl, signal_to_string(sig)); + if (normal_coredump && safe_atoi(sgnl, &sig) >= 0) + fprintf(file, " %s: %s (%s)\n", name, sgnl, signal_to_string(sig)); else - fprintf(file, " Signal: %s\n", sgnl); + fprintf(file, " %s: %s\n", name, sgnl); } if (timestamp) { @@ -539,9 +584,24 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) { if (hostname) fprintf(file, " Hostname: %s\n", hostname); - if (filename) - fprintf(file, " Storage: %s%s\n", filename, - access(filename, R_OK) < 0 ? " (inaccessible)" : ""); + if (filename) { + bool inacc, trunc; + + inacc = access(filename, R_OK) < 0; + trunc = truncated && parse_boolean(truncated) > 0; + + if (inacc || trunc) + fprintf(file, " Storage: %s%s (%s%s%s)%s\n", + ansi_highlight_red(), + filename, + inacc ? "inaccessible" : "", + inacc && trunc ? ", " : "", + trunc ? "truncated" : "", + ansi_normal()); + else + fprintf(file, " Storage: %s\n", filename); + } + else if (coredump) fprintf(file, " Storage: journal\n"); else @@ -602,14 +662,57 @@ static int dump_list(sd_journal *j) { return print_entry(j, 0); } else { - SD_JOURNAL_FOREACH(j) { + if (arg_since != USEC_INFINITY && !arg_reverse) + r = sd_journal_seek_realtime_usec(j, arg_since); + else if (arg_until != USEC_INFINITY && arg_reverse) + r = sd_journal_seek_realtime_usec(j, arg_until); + else if (arg_reverse) + r = sd_journal_seek_tail(j); + else + r = sd_journal_seek_head(j); + if (r < 0) + return log_error_errno(r, "Failed to seek to date: %m"); + + for (;;) { + if (!arg_reverse) + r = sd_journal_next(j); + else + r = sd_journal_previous(j); + + if (r < 0) + return log_error_errno(r, "Failed to iterate through journal: %m"); + + if (r == 0) + break; + + if (arg_until != USEC_INFINITY && !arg_reverse) { + usec_t usec; + + r = sd_journal_get_realtime_usec(j, &usec); + if (r < 0) + return log_error_errno(r, "Failed to determine timestamp: %m"); + if (usec > arg_until) + continue; + } + + if (arg_since != USEC_INFINITY && arg_reverse) { + usec_t usec; + + r = sd_journal_get_realtime_usec(j, &usec); + if (r < 0) + return log_error_errno(r, "Failed to determine timestamp: %m"); + if (usec < arg_since) + continue; + } + r = print_entry(j, n_found++); if (r < 0) return r; } if (!arg_field && n_found <= 0) { - log_notice("No coredumps found."); + if (!arg_quiet) + log_notice("No coredumps found."); return -ESRCH; } } @@ -771,8 +874,8 @@ static int dump_core(sd_journal* j) { return r; r = sd_journal_previous(j); - if (r > 0) - log_warning("More than one entry matches, ignoring rest."); + if (r > 0 && !arg_quiet) + log_notice("More than one entry matches, ignoring rest."); return 0; } @@ -858,24 +961,75 @@ finish: return r; } +static int check_units_active(void) { + _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int c = 0, r; + const char *id, *state, *substate; + + if (arg_quiet) + return false; + + r = sd_bus_default_system(&bus); + if (r < 0) + return log_error_errno(r, "Failed to acquire bus: %m"); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListUnitsByPatterns"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, NULL); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, STRV_MAKE("systemd-coredump@*.service")); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, SHORT_BUS_CALL_TIMEOUT_USEC, &error, &reply); + if (r < 0) + return log_error_errno(r, "Failed to check if any systemd-coredump@.service units are running: %s", + bus_error_message(&error, r)); + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read( + reply, "(ssssssouso)", + &id, NULL, NULL, &state, &substate, + NULL, NULL, NULL, NULL, NULL)) > 0) { + bool found = !STR_IN_SET(state, "inactive", "dead", "failed"); + log_debug("Unit %s is %s/%s, %scounting it.", id, state, substate, found ? "" : "not "); + c += found; + } + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + return c; +} + int main(int argc, char *argv[]) { _cleanup_(sd_journal_closep) sd_journal*j = NULL; - const char* match; - Iterator it; - int r = 0; - _cleanup_set_free_free_ Set *matches = NULL; + int r = 0, units_active; setlocale(LC_ALL, ""); log_parse_environment(); log_open(); - matches = new_matches(); - if (!matches) { - r = -ENOMEM; - goto end; - } - - r = parse_argv(argc, argv, matches); + r = parse_argv(argc, argv); if (r < 0) goto end; @@ -898,14 +1052,13 @@ int main(int argc, char *argv[]) { } } - SET_FOREACH(match, matches, it) { - r = sd_journal_add_match(j, match, strlen(match)); - if (r != 0) { - log_error_errno(r, "Failed to add match '%s': %m", - match); - goto end; - } - } + r = journal_access_check_and_warn(j, arg_quiet); + if (r < 0) + goto end; + + r = add_matches(j); + if (r < 0) + goto end; if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) { _cleanup_free_ char *filter; @@ -914,6 +1067,8 @@ int main(int argc, char *argv[]) { log_debug("Journal filter: %s", filter); } + units_active = check_units_active(); /* error is treated the same as 0 */ + switch(arg_action) { case ACTION_LIST: @@ -934,6 +1089,11 @@ int main(int argc, char *argv[]) { assert_not_reached("Shouldn't be here"); } + if (units_active > 0) + printf("%s-- Notice: %d systemd-coredump@.service %s, output may be incomplete.%s\n", + ansi_highlight_red(), + units_active, units_active == 1 ? "unit is running" : "units are running", + ansi_normal()); end: pager_close(); diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c index 23bf014929..b58b6db7c9 100644 --- a/src/cryptsetup/cryptsetup-generator.c +++ b/src/cryptsetup/cryptsetup-generator.c @@ -144,13 +144,17 @@ static int create_disk( } } - if (is_device_path(u)) + if (is_device_path(u)) { fprintf(f, "BindsTo=%s\n" "After=%s\n" "Before=umount.target\n", d, d); - else + + if (swap) + fputs("Before=dev-mapper-%i.swap\n", + f); + } else fprintf(f, "RequiresMountsFor=%s\n", u); diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index 59bd7d9e84..06564e94b1 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -208,6 +208,14 @@ int main(int argc, char *argv[]) { log_error_errno(r, "No root partition for specified root hash found in %s.", arg_image); goto finish; } + if (r == -ENOTUNIQ) { + log_error_errno(r, "Multiple suitable root partitions found in image %s.", arg_image); + goto finish; + } + if (r == -ENXIO) { + log_error_errno(r, "No suitable root partition found in image %s.", arg_image); + goto finish; + } if (r < 0) { log_error_errno(r, "Failed to dissect image: %m"); goto finish; diff --git a/src/environment-d-generator/Makefile b/src/environment-d-generator/Makefile new file mode 120000 index 0000000000..d0b0e8e008 --- /dev/null +++ b/src/environment-d-generator/Makefile @@ -0,0 +1 @@ +../Makefile
\ No newline at end of file diff --git a/src/environment-d-generator/environment-d-generator.c b/src/environment-d-generator/environment-d-generator.c new file mode 100644 index 0000000000..2d4c4235e4 --- /dev/null +++ b/src/environment-d-generator/environment-d-generator.c @@ -0,0 +1,107 @@ +/*** + This file is part of systemd. + + Copyright 2017 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "sd-path.h" + +#include "conf-files.h" +#include "def.h" +#include "escape.h" +#include "fileio.h" +#include "log.h" +#include "path-lookup.h" + +static int environment_dirs(char ***ret) { + _cleanup_strv_free_ char **dirs = NULL; + _cleanup_free_ char *c = NULL; + int r; + + dirs = strv_split_nulstr(CONF_PATHS_NULSTR("environment.d")); + if (!dirs) + return -ENOMEM; + + /* ~/.config/systemd/environment.d */ + r = sd_path_home(SD_PATH_USER_CONFIGURATION, "environment.d", &c); + if (r < 0) + return r; + + r = strv_extend_front(&dirs, c); + if (r < 0) + return r; + + *ret = dirs; + dirs = NULL; + return 0; +} + +static int load_and_print(void) { + _cleanup_strv_free_ char **dirs = NULL, **files = NULL, **env = NULL; + char **i; + int r; + + r = environment_dirs(&dirs); + if (r < 0) + return r; + + r = conf_files_list_strv(&files, ".conf", NULL, (const char **) dirs); + if (r < 0) + return r; + + /* This will mutate the existing environment, based on the presumption + * that in case of failure, a partial update is better than none. */ + + STRV_FOREACH(i, files) { + r = merge_env_file(&env, NULL, *i); + if (r == -ENOMEM) + return r; + } + + STRV_FOREACH(i, env) { + char *t; + _cleanup_free_ char *q = NULL; + + t = strchr(*i, '='); + assert(t); + + q = shell_maybe_quote(t + 1); + if (!q) + return log_oom(); + + printf("%.*s=%s\n", (int) (t - *i), *i, q); + } + + return 0; +} + +int main(int argc, char *argv[]) { + int r; + + log_parse_environment(); + log_open(); + + if (argc > 1) { + log_error("This program takes no arguments."); + return EXIT_FAILURE; + } + + r = load_and_print(); + if (r < 0) + log_error_errno(r, "Failed to load environment.d: %m"); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index fd7051f21e..bc16290c72 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -252,7 +252,7 @@ static int process_locale(void) { if (arg_copy_locale && arg_root) { mkdir_parents(etc_localeconf, 0755); - r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644, 0); + r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644, 0, COPY_REFLINK); if (r != -ENOENT) { if (r < 0) return log_error_errno(r, "Failed to copy %s: %m", etc_localeconf); diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index d97bafd1fb..2677a3fb32 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -209,7 +209,8 @@ static int write_mount_timeout(FILE *f, const char *where, const char *opts) { "x-systemd.mount-timeout\0", "TimeoutSec"); } -static int write_requires_after(FILE *f, const char *opts) { +static int write_dependency(FILE *f, const char *opts, + const char *filter, const char *format) { _cleanup_strv_free_ char **names = NULL, **units = NULL; _cleanup_free_ char *res = NULL; char **s; @@ -218,7 +219,7 @@ static int write_requires_after(FILE *f, const char *opts) { assert(f); assert(opts); - r = fstab_extract_values(opts, "x-systemd.requires", &names); + r = fstab_extract_values(opts, filter, &names); if (r < 0) return log_warning_errno(r, "Failed to parse options: %m"); if (r == 0) @@ -239,12 +240,29 @@ static int write_requires_after(FILE *f, const char *opts) { res = strv_join(units, " "); if (!res) return log_oom(); - fprintf(f, "After=%1$s\nRequires=%1$s\n", res); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + fprintf(f, format, res); +#pragma GCC diagnostic pop } return 0; } +static int write_after(FILE *f, const char *opts) { + return write_dependency(f, opts, "x-systemd.after", "After=%1$s\n"); +} + +static int write_requires_after(FILE *f, const char *opts) { + return write_dependency(f, opts, + "x-systemd.requires", "After=%1$s\nRequires=%1$s\n"); +} + +static int write_before(FILE *f, const char *opts) { + return write_dependency(f, opts, + "x-systemd.before", "Before=%1$s\n"); +} + static int write_requires_mounts_for(FILE *f, const char *opts) { _cleanup_strv_free_ char **paths = NULL; _cleanup_free_ char *res = NULL; @@ -344,9 +362,15 @@ static int add_mount( fprintf(f, "Before=%s\n", post); if (!automount && opts) { + r = write_after(f, opts); + if (r < 0) + return r; r = write_requires_after(f, opts); if (r < 0) return r; + r = write_before(f, opts); + if (r < 0) + return r; r = write_requires_mounts_for(f, opts); if (r < 0) return r; @@ -421,9 +445,15 @@ static int add_mount( fprintf(f, "Before=%s\n", post); if (opts) { + r = write_after(f, opts); + if (r < 0) + return r; r = write_requires_after(f, opts); if (r < 0) return r; + r = write_before(f, opts); + if (r < 0) + return r; r = write_requires_mounts_for(f, opts); if (r < 0) return r; diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index b958070c90..80f676e477 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -475,7 +475,7 @@ static int add_esp(DissectedPartition *p) { 120 * USEC_PER_SEC); } #else -static int add_esp(const char *what) { +static int add_esp(DissectedPartition *p) { return 0; } #endif diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c index 07c57fb567..f5a9de94a6 100644 --- a/src/hostname/hostnamectl.c +++ b/src/hostname/hostnamectl.c @@ -137,10 +137,8 @@ static int show_one_name(sd_bus *bus, const char* attr) { "org.freedesktop.hostname1", attr, &error, &reply, "s"); - if (r < 0) { - log_error("Could not get property: %s", bus_error_message(&error, -r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not get property: %s", bus_error_message(&error, r)); r = sd_bus_message_read(reply, "s", &s); if (r < 0) @@ -151,7 +149,7 @@ static int show_one_name(sd_bus *bus, const char* attr) { return 0; } -static int show_all_names(sd_bus *bus) { +static int show_all_names(sd_bus *bus, sd_bus_error *error) { StatusInfo info = {}; static const struct bus_properties_map hostname_map[] = { @@ -181,6 +179,7 @@ static int show_all_names(sd_bus *bus) { "org.freedesktop.hostname1", "/org/freedesktop/hostname1", hostname_map, + error, &info); if (r < 0) goto fail; @@ -189,6 +188,7 @@ static int show_all_names(sd_bus *bus) { "org.freedesktop.systemd1", "/org/freedesktop/systemd1", manager_map, + error, &info); print_status_info(&info); @@ -212,6 +212,8 @@ fail: } static int show_status(sd_bus *bus, char **args, unsigned n) { + int r; + assert(args); if (arg_pretty || arg_static || arg_transient) { @@ -226,8 +228,15 @@ static int show_status(sd_bus *bus, char **args, unsigned n) { arg_static ? "StaticHostname" : "Hostname"; return show_one_name(bus, attr); - } else - return show_all_names(bus); + } else { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + + r = show_all_names(bus, &error); + if (r < 0) + return log_error_errno(r, "Failed to query system properties: %s", bus_error_message(&error, r)); + + return 0; + } } static int set_simple_string(sd_bus *bus, const char *method, const char *value) { diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index 4657cf8c77..fe0aa00efb 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -133,6 +133,7 @@ static bool valid_chassis(const char *chassis) { "container\0" "desktop\0" "laptop\0" + "convertible\0" "server\0" "tablet\0" "handset\0" @@ -199,6 +200,10 @@ static const char* fallback_chassis(void) { case 0x1E: /* Tablet */ return "tablet"; + + case 0x1F: /* Convertible */ + case 0x20: /* Detachable */ + return "convertible"; } try_acpi: diff --git a/src/import/curl-util.c b/src/import/curl-util.c index 734e1560e6..0e8f3fb918 100644 --- a/src/import/curl-util.c +++ b/src/import/curl-util.c @@ -400,7 +400,7 @@ int curl_header_strdup(const void *contents, size_t sz, const char *field, char sz--; } - /* Truncate trailing whitespace*/ + /* Truncate trailing whitespace */ while (sz > 0 && strchr(WHITESPACE, p[sz-1])) sz--; diff --git a/src/import/importd.c b/src/import/importd.c index 9d31a956a5..3d379d6de9 100644 --- a/src/import/importd.c +++ b/src/import/importd.c @@ -449,8 +449,11 @@ static int transfer_start(Transfer *t) { stdio_unset_cloexec(); - setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1); - setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1); + if (setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1) < 0 || + setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1) < 0) { + log_error_errno(errno, "setenv() failed: %m"); + _exit(EXIT_FAILURE); + } if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_IMPORT_RAW)) cmd[k++] = SYSTEMD_IMPORT_PATH; diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c index ef7fb6ac42..60a769e944 100644 --- a/src/import/pull-raw.c +++ b/src/import/pull-raw.c @@ -315,7 +315,7 @@ static int raw_pull_copy_auxiliary_file( local = strjoina(i->image_root, "/", i->local, suffix); - r = copy_file_atomic(*path, local, 0644, i->force_local, 0); + r = copy_file_atomic(*path, local, 0644, 0, COPY_REFLINK | (i->force_local ? COPY_REPLACE : 0)); if (r == -EEXIST) log_warning_errno(r, "File %s already exists, not replacing.", local); else if (r == -ENOENT) @@ -378,7 +378,7 @@ static int raw_pull_make_local_copy(RawPull *i) { if (r < 0) log_warning_errno(r, "Failed to set file attributes on %s: %m", tp); - r = copy_bytes(i->raw_job->disk_fd, dfd, (uint64_t) -1, true); + r = copy_bytes(i->raw_job->disk_fd, dfd, (uint64_t) -1, COPY_REFLINK); if (r < 0) { unlink(tp); return log_error_errno(r, "Failed to make writable copy of image: %m"); diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index 375ee778e2..91833d6174 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -256,7 +256,7 @@ static int tar_pull_make_local_copy(TarPull *i) { local_settings = strjoina(i->image_root, "/", i->local, ".nspawn"); - r = copy_file_atomic(i->settings_path, local_settings, 0664, i->force_local, 0); + r = copy_file_atomic(i->settings_path, local_settings, 0664, 0, COPY_REFLINK | (i->force_local ? COPY_REPLACE : 0)); if (r == -EEXIST) log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings); else if (r == -ENOENT) diff --git a/src/journal-remote/journal-remote-parse.c b/src/journal-remote/journal-remote-parse.c index 9ba9ee3fc0..79afe6604c 100644 --- a/src/journal-remote/journal-remote-parse.c +++ b/src/journal-remote/journal-remote-parse.c @@ -24,20 +24,11 @@ #include "parse-util.h" #include "string-util.h" -#define LINE_CHUNK 8*1024u - void source_free(RemoteSource *source) { if (!source) return; - if (source->fd >= 0 && !source->passive_fd) { - log_debug("Closing fd:%d (%s)", source->fd, source->name); - safe_close(source->fd); - } - - free(source->name); - free(source->buf); - iovw_free_contents(&source->iovw); + journal_importer_cleanup(&source->importer); log_debug("Writer ref count %i", source->writer->n_ref); writer_unref(source->writer); @@ -65,442 +56,44 @@ RemoteSource* source_new(int fd, bool passive_fd, char *name, Writer *writer) { if (!source) return NULL; - source->fd = fd; - source->passive_fd = passive_fd; - source->name = name; + source->importer.fd = fd; + source->importer.passive_fd = passive_fd; + source->importer.name = name; + source->writer = writer; return source; } -static char* realloc_buffer(RemoteSource *source, size_t size) { - char *b, *old = source->buf; - - b = GREEDY_REALLOC(source->buf, source->size, size); - if (!b) - return NULL; - - iovw_rebase(&source->iovw, old, source->buf); - - return b; -} - -static int get_line(RemoteSource *source, char **line, size_t *size) { - ssize_t n; - char *c = NULL; - - assert(source); - assert(source->state == STATE_LINE); - assert(source->offset <= source->filled); - assert(source->filled <= source->size); - assert(source->buf == NULL || source->size > 0); - assert(source->fd >= 0); - - for (;;) { - if (source->buf) { - size_t start = MAX(source->scanned, source->offset); - - c = memchr(source->buf + start, '\n', - source->filled - start); - if (c != NULL) - break; - } - - source->scanned = source->filled; - if (source->scanned >= DATA_SIZE_MAX) { - log_error("Entry is bigger than %u bytes.", DATA_SIZE_MAX); - return -E2BIG; - } - - if (source->passive_fd) - /* we have to wait for some data to come to us */ - return -EAGAIN; - - /* We know that source->filled is at most DATA_SIZE_MAX, so if - we reallocate it, we'll increase the size at least a bit. */ - assert_cc(DATA_SIZE_MAX < ENTRY_SIZE_MAX); - if (source->size - source->filled < LINE_CHUNK && - !realloc_buffer(source, MIN(source->filled + LINE_CHUNK, ENTRY_SIZE_MAX))) - return log_oom(); - - assert(source->buf); - assert(source->size - source->filled >= LINE_CHUNK || - source->size == ENTRY_SIZE_MAX); - - n = read(source->fd, - source->buf + source->filled, - source->size - source->filled); - if (n < 0) { - if (errno != EAGAIN) - log_error_errno(errno, "read(%d, ..., %zu): %m", - source->fd, - source->size - source->filled); - return -errno; - } else if (n == 0) - return 0; - - source->filled += n; - } - - *line = source->buf + source->offset; - *size = c + 1 - source->buf - source->offset; - source->offset += *size; - - return 1; -} - -int push_data(RemoteSource *source, const char *data, size_t size) { - assert(source); - assert(source->state != STATE_EOF); - - if (!realloc_buffer(source, source->filled + size)) { - log_error("Failed to store received data of size %zu " - "(in addition to existing %zu bytes with %zu filled): %s", - size, source->size, source->filled, strerror(ENOMEM)); - return -ENOMEM; - } - - memcpy(source->buf + source->filled, data, size); - source->filled += size; - - return 0; -} - -static int fill_fixed_size(RemoteSource *source, void **data, size_t size) { - - assert(source); - assert(source->state == STATE_DATA_START || - source->state == STATE_DATA || - source->state == STATE_DATA_FINISH); - assert(size <= DATA_SIZE_MAX); - assert(source->offset <= source->filled); - assert(source->filled <= source->size); - assert(source->buf != NULL || source->size == 0); - assert(source->buf == NULL || source->size > 0); - assert(source->fd >= 0); - assert(data); - - while (source->filled - source->offset < size) { - int n; - - if (source->passive_fd) - /* we have to wait for some data to come to us */ - return -EAGAIN; - - if (!realloc_buffer(source, source->offset + size)) - return log_oom(); - - n = read(source->fd, source->buf + source->filled, - source->size - source->filled); - if (n < 0) { - if (errno != EAGAIN) - log_error_errno(errno, "read(%d, ..., %zu): %m", source->fd, - source->size - source->filled); - return -errno; - } else if (n == 0) - return 0; - - source->filled += n; - } - - *data = source->buf + source->offset; - source->offset += size; - - return 1; -} - -static int get_data_size(RemoteSource *source) { - int r; - void *data; - - assert(source); - assert(source->state == STATE_DATA_START); - assert(source->data_size == 0); - - r = fill_fixed_size(source, &data, sizeof(uint64_t)); - if (r <= 0) - return r; - - source->data_size = le64toh( *(uint64_t *) data ); - if (source->data_size > DATA_SIZE_MAX) { - log_error("Stream declares field with size %zu > DATA_SIZE_MAX = %u", - source->data_size, DATA_SIZE_MAX); - return -EINVAL; - } - if (source->data_size == 0) - log_warning("Binary field with zero length"); - - return 1; -} - -static int get_data_data(RemoteSource *source, void **data) { - int r; - - assert(source); - assert(data); - assert(source->state == STATE_DATA); - - r = fill_fixed_size(source, data, source->data_size); - if (r <= 0) - return r; - - return 1; -} - -static int get_data_newline(RemoteSource *source) { - int r; - char *data; - - assert(source); - assert(source->state == STATE_DATA_FINISH); - - r = fill_fixed_size(source, (void**) &data, 1); - if (r <= 0) - return r; - - assert(data); - if (*data != '\n') { - log_error("expected newline, got '%c'", *data); - return -EINVAL; - } - - return 1; -} - -static int process_dunder(RemoteSource *source, char *line, size_t n) { - const char *timestamp; - int r; - - assert(line); - assert(n > 0); - assert(line[n-1] == '\n'); - - /* XXX: is it worth to support timestamps in extended format? - * We don't produce them, but who knows... */ - - timestamp = startswith(line, "__CURSOR="); - if (timestamp) - /* ignore __CURSOR */ - return 1; - - timestamp = startswith(line, "__REALTIME_TIMESTAMP="); - if (timestamp) { - long long unsigned x; - line[n-1] = '\0'; - r = safe_atollu(timestamp, &x); - if (r < 0) - log_warning("Failed to parse __REALTIME_TIMESTAMP: '%s'", timestamp); - else - source->ts.realtime = x; - return r < 0 ? r : 1; - } - - timestamp = startswith(line, "__MONOTONIC_TIMESTAMP="); - if (timestamp) { - long long unsigned x; - line[n-1] = '\0'; - r = safe_atollu(timestamp, &x); - if (r < 0) - log_warning("Failed to parse __MONOTONIC_TIMESTAMP: '%s'", timestamp); - else - source->ts.monotonic = x; - return r < 0 ? r : 1; - } - - timestamp = startswith(line, "__"); - if (timestamp) { - log_notice("Unknown dunder line %s", line); - return 1; - } - - /* no dunder */ - return 0; -} - -static int process_data(RemoteSource *source) { - int r; - - switch(source->state) { - case STATE_LINE: { - char *line, *sep; - size_t n = 0; - - assert(source->data_size == 0); - - r = get_line(source, &line, &n); - if (r < 0) - return r; - if (r == 0) { - source->state = STATE_EOF; - return r; - } - assert(n > 0); - assert(line[n-1] == '\n'); - - if (n == 1) { - log_trace("Received empty line, event is ready"); - return 1; - } - - r = process_dunder(source, line, n); - if (r != 0) - return r < 0 ? r : 0; - - /* MESSAGE=xxx\n - or - COREDUMP\n - LLLLLLLL0011223344...\n - */ - sep = memchr(line, '=', n); - if (sep) { - /* chomp newline */ - n--; - - r = iovw_put(&source->iovw, line, n); - if (r < 0) - return r; - } else { - /* replace \n with = */ - line[n-1] = '='; - - source->field_len = n; - source->state = STATE_DATA_START; - - /* we cannot put the field in iovec until we have all data */ - } - - log_trace("Received: %.*s (%s)", (int) n, line, sep ? "text" : "binary"); - - return 0; /* continue */ - } - - case STATE_DATA_START: - assert(source->data_size == 0); - - r = get_data_size(source); - // log_debug("get_data_size() -> %d", r); - if (r < 0) - return r; - if (r == 0) { - source->state = STATE_EOF; - return 0; - } - - source->state = source->data_size > 0 ? - STATE_DATA : STATE_DATA_FINISH; - - return 0; /* continue */ - - case STATE_DATA: { - void *data; - char *field; - - assert(source->data_size > 0); - - r = get_data_data(source, &data); - // log_debug("get_data_data() -> %d", r); - if (r < 0) - return r; - if (r == 0) { - source->state = STATE_EOF; - return 0; - } - - assert(data); - - field = (char*) data - sizeof(uint64_t) - source->field_len; - memmove(field + sizeof(uint64_t), field, source->field_len); - - r = iovw_put(&source->iovw, field + sizeof(uint64_t), source->field_len + source->data_size); - if (r < 0) - return r; - - source->state = STATE_DATA_FINISH; - - return 0; /* continue */ - } - - case STATE_DATA_FINISH: - r = get_data_newline(source); - // log_debug("get_data_newline() -> %d", r); - if (r < 0) - return r; - if (r == 0) { - source->state = STATE_EOF; - return 0; - } - - source->data_size = 0; - source->state = STATE_LINE; - - return 0; /* continue */ - default: - assert_not_reached("wtf?"); - } -} - int process_source(RemoteSource *source, bool compress, bool seal) { - size_t remain, target; int r; assert(source); assert(source->writer); - r = process_data(source); + r = journal_importer_process_data(&source->importer); if (r <= 0) return r; /* We have a full event */ log_trace("Received full event from source@%p fd:%d (%s)", - source, source->fd, source->name); + source, source->importer.fd, source->importer.name); - if (!source->iovw.count) { + if (source->importer.iovw.count == 0) { log_warning("Entry with no payload, skipping"); goto freeing; } - assert(source->iovw.iovec); - assert(source->iovw.count); + assert(source->importer.iovw.iovec); - r = writer_write(source->writer, &source->iovw, &source->ts, compress, seal); + r = writer_write(source->writer, &source->importer.iovw, &source->importer.ts, compress, seal); if (r < 0) log_error_errno(r, "Failed to write entry of %zu bytes: %m", - iovw_size(&source->iovw)); + iovw_size(&source->importer.iovw)); else r = 1; freeing: - iovw_free_contents(&source->iovw); - - /* possibly reset buffer position */ - remain = source->filled - source->offset; - - if (remain == 0) /* no brainer */ - source->offset = source->scanned = source->filled = 0; - else if (source->offset > source->size - source->filled && - source->offset > remain) { - memcpy(source->buf, source->buf + source->offset, remain); - source->offset = source->scanned = 0; - source->filled = remain; - } - - target = source->size; - while (target > 16 * LINE_CHUNK && source->filled < target / 2) - target /= 2; - if (target < source->size) { - char *tmp; - - tmp = realloc(source->buf, target); - if (!tmp) - log_warning("Failed to reallocate buffer to (smaller) size %zu", - target); - else { - log_debug("Reallocated buffer from %zu to %zu bytes", - source->size, target); - source->buf = tmp; - source->size = target; - } - } - + journal_importer_drop_iovw(&source->importer); return r; } diff --git a/src/journal-remote/journal-remote-parse.h b/src/journal-remote/journal-remote-parse.h index 1740a21f92..e3632528cf 100644 --- a/src/journal-remote/journal-remote-parse.h +++ b/src/journal-remote/journal-remote-parse.h @@ -21,34 +21,11 @@ #include "sd-event.h" +#include "journal-importer.h" #include "journal-remote-write.h" -typedef enum { - STATE_LINE = 0, /* waiting to read, or reading line */ - STATE_DATA_START, /* reading binary data header */ - STATE_DATA, /* reading binary data */ - STATE_DATA_FINISH, /* expecting newline */ - STATE_EOF, /* done */ -} source_state; - typedef struct RemoteSource { - char *name; - int fd; - bool passive_fd; - - char *buf; - size_t size; /* total size of the buffer */ - size_t offset; /* offset to the beginning of live data in the buffer */ - size_t scanned; /* number of bytes since the beginning of data without a newline */ - size_t filled; /* total number of bytes in the buffer */ - - size_t field_len; /* used for binary fields: the field name length */ - size_t data_size; /* and the size of the binary data chunk being processed */ - - struct iovec_wrapper iovw; - - source_state state; - dual_timestamp ts; + JournalImporter importer; Writer *writer; @@ -57,13 +34,5 @@ typedef struct RemoteSource { } RemoteSource; RemoteSource* source_new(int fd, bool passive_fd, char *name, Writer *writer); - -static inline size_t source_non_empty(RemoteSource *source) { - assert(source); - - return source->filled; -} - void source_free(RemoteSource *source); -int push_data(RemoteSource *source, const char *data, size_t size); int process_source(RemoteSource *source, bool compress, bool seal); diff --git a/src/journal-remote/journal-remote-write.c b/src/journal-remote/journal-remote-write.c index 8729372aa3..734cad333f 100644 --- a/src/journal-remote/journal-remote-write.c +++ b/src/journal-remote/journal-remote-write.c @@ -20,39 +20,6 @@ #include "alloc-util.h" #include "journal-remote.h" -int iovw_put(struct iovec_wrapper *iovw, void* data, size_t len) { - if (!GREEDY_REALLOC(iovw->iovec, iovw->size_bytes, iovw->count + 1)) - return log_oom(); - - iovw->iovec[iovw->count++] = (struct iovec) {data, len}; - return 0; -} - -void iovw_free_contents(struct iovec_wrapper *iovw) { - iovw->iovec = mfree(iovw->iovec); - iovw->size_bytes = iovw->count = 0; -} - -size_t iovw_size(struct iovec_wrapper *iovw) { - size_t n = 0, i; - - for (i = 0; i < iovw->count; i++) - n += iovw->iovec[i].iov_len; - - return n; -} - -void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) { - size_t i; - - for (i = 0; i < iovw->count; i++) - iovw->iovec[i].iov_base = (char*) iovw->iovec[i].iov_base - old + new; -} - -/********************************************************************** - ********************************************************************** - **********************************************************************/ - static int do_rotate(JournalFile **f, bool compress, bool seal) { int r = journal_file_rotate(f, compress, seal, NULL); if (r < 0) { diff --git a/src/journal-remote/journal-remote-write.h b/src/journal-remote/journal-remote-write.h index 53ba45fc04..e04af54e55 100644 --- a/src/journal-remote/journal-remote-write.h +++ b/src/journal-remote/journal-remote-write.h @@ -20,20 +20,10 @@ ***/ #include "journal-file.h" +#include "journal-importer.h" typedef struct RemoteServer RemoteServer; -struct iovec_wrapper { - struct iovec *iovec; - size_t size_bytes; - size_t count; -}; - -int iovw_put(struct iovec_wrapper *iovw, void* data, size_t len); -void iovw_free_contents(struct iovec_wrapper *iovw); -size_t iovw_size(struct iovec_wrapper *iovw); -void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new); - typedef struct Writer { JournalFile *journal; JournalMetrics metrics; diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c index d0d8d936e3..202a5a3f97 100644 --- a/src/journal-remote/journal-remote.c +++ b/src/journal-remote/journal-remote.c @@ -512,7 +512,8 @@ static int process_http_upload( if (*upload_data_size) { log_trace("Received %zu bytes", *upload_data_size); - r = push_data(source, upload_data, *upload_data_size); + r = journal_importer_push_data(&source->importer, + upload_data, *upload_data_size); if (r < 0) return mhd_respond_oom(connection); @@ -542,7 +543,7 @@ static int process_http_upload( /* The upload is finished */ - remaining = source_non_empty(source); + remaining = journal_importer_bytes_remaining(&source->importer); if (remaining > 0) { log_warning("Premature EOF byte. %zu bytes lost.", remaining); return mhd_respondf(connection, @@ -1036,19 +1037,19 @@ static int handle_raw_source(sd_event_source *event, assert(fd >= 0 && fd < (ssize_t) s->sources_size); source = s->sources[fd]; - assert(source->fd == fd); + assert(source->importer.fd == fd); r = process_source(source, arg_compress, arg_seal); - if (source->state == STATE_EOF) { + if (journal_importer_eof(&source->importer)) { size_t remaining; - log_debug("EOF reached with source fd:%d (%s)", - source->fd, source->name); + log_debug("EOF reached with source %s (fd=%d)", + source->importer.name, source->importer.fd); - remaining = source_non_empty(source); + remaining = journal_importer_bytes_remaining(&source->importer); if (remaining > 0) log_notice("Premature EOF. %zu bytes lost.", remaining); - remove_source(s, source->fd); + remove_source(s, source->importer.fd); log_debug("%zu active sources remaining", s->active); return 0; } else if (r == -E2BIG) { @@ -1072,7 +1073,7 @@ static int dispatch_raw_source_until_block(sd_event_source *event, /* Make sure event stays around even if source is destroyed */ sd_event_source_ref(event); - r = handle_raw_source(event, source->fd, EPOLLIN, server); + r = handle_raw_source(event, source->importer.fd, EPOLLIN, server); if (r != 1) /* No more data for now */ sd_event_source_set_enabled(event, SD_EVENT_OFF); @@ -1105,7 +1106,7 @@ static int dispatch_blocking_source_event(sd_event_source *event, void *userdata) { RemoteSource *source = userdata; - return handle_raw_source(event, source->fd, EPOLLIN, server); + return handle_raw_source(event, source->importer.fd, EPOLLIN, server); } static int accept_connection(const char* type, int fd, diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index ef87b176fa..a6ccb679a8 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -3283,7 +3283,7 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal, Set *deferred return -EINVAL; /* Is this a journal file that was passed to us as fd? If so, we synthesized a path name for it, and we refuse - * rotation, since we don't know the actual path, and couldn't rename the file hence.*/ + * rotation, since we don't know the actual path, and couldn't rename the file hence. */ if (path_startswith(old_file->path, "/proc/self/fd")) return -EINVAL; diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 9ad6f115a1..ad11fb314d 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -52,6 +52,7 @@ #include "journal-def.h" #include "journal-internal.h" #include "journal-qrcode.h" +#include "journal-util.h" #include "journal-vacuum.h" #include "journal-verify.h" #include "locale-util.h" @@ -912,7 +913,7 @@ static int generate_new_id128(void) { SD_ID128_FORMAT_STR "\n\n" "As UUID:\n" "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n\n" - "As macro:\n" + "As man:sd-id128(3) macro:\n" "#define MESSAGE_XYZ SD_ID128_MAKE(", SD_ID128_FORMAT_VAL(id), SD_ID128_FORMAT_VAL(id)); @@ -1805,131 +1806,6 @@ static int verify(sd_journal *j) { return r; } -static int access_check_var_log_journal(sd_journal *j) { -#ifdef HAVE_ACL - _cleanup_strv_free_ char **g = NULL; - const char* dir; -#endif - int r; - - assert(j); - - if (arg_quiet) - return 0; - - /* If we are root, we should have access, don't warn. */ - if (getuid() == 0) - return 0; - - /* If we are in the 'systemd-journal' group, we should have - * access too. */ - r = in_group("systemd-journal"); - if (r < 0) - return log_error_errno(r, "Failed to check if we are in the 'systemd-journal' group: %m"); - if (r > 0) - return 0; - -#ifdef HAVE_ACL - if (laccess("/run/log/journal", F_OK) >= 0) - dir = "/run/log/journal"; - else - dir = "/var/log/journal"; - - /* If we are in any of the groups listed in the journal ACLs, - * then all is good, too. Let's enumerate all groups from the - * default ACL of the directory, which generally should allow - * access to most journal files too. */ - r = acl_search_groups(dir, &g); - if (r < 0) - return log_error_errno(r, "Failed to search journal ACL: %m"); - if (r > 0) - return 0; - - /* Print a pretty list, if there were ACLs set. */ - if (!strv_isempty(g)) { - _cleanup_free_ char *s = NULL; - - /* Thre are groups in the ACL, let's list them */ - r = strv_extend(&g, "systemd-journal"); - if (r < 0) - return log_oom(); - - strv_sort(g); - strv_uniq(g); - - s = strv_join(g, "', '"); - if (!s) - return log_oom(); - - log_notice("Hint: You are currently not seeing messages from other users and the system.\n" - " Users in groups '%s' can see all messages.\n" - " Pass -q to turn off this notice.", s); - return 1; - } -#endif - - /* If no ACLs were found, print a short version of the message. */ - log_notice("Hint: You are currently not seeing messages from other users and the system.\n" - " Users in the 'systemd-journal' group can see all messages. Pass -q to\n" - " turn off this notice."); - - return 1; -} - -static int access_check(sd_journal *j) { - Iterator it; - void *code; - char *path; - int r = 0; - - assert(j); - - if (hashmap_isempty(j->errors)) { - if (ordered_hashmap_isempty(j->files)) - log_notice("No journal files were found."); - - return 0; - } - - if (hashmap_contains(j->errors, INT_TO_PTR(-EACCES))) { - (void) access_check_var_log_journal(j); - - if (ordered_hashmap_isempty(j->files)) - r = log_error_errno(EACCES, "No journal files were opened due to insufficient permissions."); - } - - HASHMAP_FOREACH_KEY(path, code, j->errors, it) { - int err; - - err = abs(PTR_TO_INT(code)); - - switch (err) { - case EACCES: - continue; - - case ENODATA: - log_warning_errno(err, "Journal file %s is truncated, ignoring file.", path); - break; - - case EPROTONOSUPPORT: - log_warning_errno(err, "Journal file %1$s uses an unsupported feature, ignoring file.\n" - "Use SYSTEMD_LOG_LEVEL=debug journalctl --file=%1$s to see the details.", - path); - break; - - case EBADMSG: - log_warning_errno(err, "Journal file %s corrupted, ignoring file.", path); - break; - - default: - log_warning_errno(err, "An error was encountered while opening journal file or directory %s, ignoring file: %m", path); - break; - } - } - - return r; -} - static int flush_to_var(void) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -2241,7 +2117,7 @@ int main(int argc, char *argv[]) { goto finish; } - r = access_check(j); + r = journal_access_check_and_warn(j, arg_quiet); if (r < 0) goto finish; diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c index 18c8644507..8afaec0ced 100644 --- a/src/journal/journald-kmsg.c +++ b/src/journal/journald-kmsg.c @@ -156,7 +156,8 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) { /* Did we lose any? */ if (serial > *s->kernel_seqnum) - server_driver_message(s, SD_MESSAGE_JOURNAL_MISSED, + server_driver_message(s, + "MESSAGE_ID=" SD_MESSAGE_JOURNAL_MISSED_STR, LOG_MESSAGE("Missed %"PRIu64" kernel messages", serial - *s->kernel_seqnum), NULL); diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c index 0a1ce205c2..3c03b83754 100644 --- a/src/journal/journald-native.c +++ b/src/journal/journald-native.c @@ -27,6 +27,7 @@ #include "fd-util.h" #include "fs-util.h" #include "io-util.h" +#include "journal-importer.h" #include "journald-console.h" #include "journald-kmsg.h" #include "journald-native.h" diff --git a/src/journal/journald-native.h b/src/journal/journald-native.h index c13b80aa4f..1ab415ac85 100644 --- a/src/journal/journald-native.h +++ b/src/journal/journald-native.h @@ -21,11 +21,6 @@ #include "journald-server.h" -/* Make sure not to make this smaller than the maximum coredump - * size. See COREDUMP_MAX in coredump.c */ -#define ENTRY_SIZE_MAX (1024*1024*770u) -#define DATA_SIZE_MAX (1024*1024*768u) - bool valid_user_field(const char *p, size_t l, bool allow_protected); void server_process_native_message(Server *s, const void *buffer, size_t buffer_size, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len); diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 8b92ea3def..6466e46ccc 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -214,7 +214,7 @@ void server_space_usage_message(Server *s, JournalStorage *storage) { format_bytes(fb5, sizeof(fb5), storage->space.limit); format_bytes(fb6, sizeof(fb6), storage->space.available); - server_driver_message(s, SD_MESSAGE_JOURNAL_USAGE, + server_driver_message(s, "MESSAGE_ID=" SD_MESSAGE_JOURNAL_USAGE_STR, LOG_MESSAGE("%s (%s) is %s, max %s, %s free.", storage->name, storage->path, fb1, fb5, fb6), "JOURNAL_NAME=%s", storage->name, @@ -760,7 +760,8 @@ static void dispatch_message_real( const char *label, size_t label_len, const char *unit_id, int priority, - pid_t object_pid) { + pid_t object_pid, + char *cgroup) { char pid[sizeof("_PID=") + DECIMAL_STR_MAX(pid_t)], uid[sizeof("_UID=") + DECIMAL_STR_MAX(uid_t)], @@ -846,7 +847,12 @@ static void dispatch_message_real( } #endif - r = cg_pid_get_path_shifted(ucred->pid, s->cgroup_root, &c); + r = 0; + if (cgroup) + c = cgroup; + else + r = cg_pid_get_path_shifted(ucred->pid, s->cgroup_root, &c); + if (r >= 0) { _cleanup_free_ char *raw_unit = NULL, *raw_slice = NULL; char *session = NULL; @@ -904,7 +910,8 @@ static void dispatch_message_real( } } - free(c); + if (!cgroup) + free(c); } else if (unit_id) { x = strjoina("_SYSTEMD_UNIT=", unit_id); IOVEC_SET_STRING(iovec[n++], x); @@ -1061,8 +1068,7 @@ static void dispatch_message_real( write_to_journal(s, journal_uid, iovec, n, priority); } -void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) { - char mid[11 + 32 + 1]; +void server_driver_message(Server *s, const char *message_id, const char *format, ...) { struct iovec iovec[N_IOVEC_META_FIELDS + 5 + N_IOVEC_PAYLOAD_FIELDS]; unsigned n = 0, m; int r; @@ -1080,11 +1086,8 @@ void server_driver_message(Server *s, sd_id128_t message_id, const char *format, assert_cc(6 == LOG_INFO); IOVEC_SET_STRING(iovec[n++], "PRIORITY=6"); - if (!sd_id128_is_null(message_id)) { - snprintf(mid, sizeof(mid), LOG_MESSAGE_ID(message_id)); - IOVEC_SET_STRING(iovec[n++], mid); - } - + if (message_id) + IOVEC_SET_STRING(iovec[n++], message_id); m = n; va_start(ap, format); @@ -1097,7 +1100,7 @@ void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ucred.gid = getgid(); if (r >= 0) - dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL, NULL, 0, NULL, LOG_INFO, 0); + dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL, NULL, 0, NULL, LOG_INFO, 0, NULL); while (m < n) free(iovec[m++].iov_base); @@ -1111,7 +1114,7 @@ void server_driver_message(Server *s, sd_id128_t message_id, const char *format, n = 3; IOVEC_SET_STRING(iovec[n++], "PRIORITY=4"); IOVEC_SET_STRING(iovec[n++], buf); - dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL, NULL, 0, NULL, LOG_INFO, 0); + dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL, NULL, 0, NULL, LOG_INFO, 0, NULL); } } @@ -1128,7 +1131,7 @@ void server_dispatch_message( int rl, r; _cleanup_free_ char *path = NULL; uint64_t available = 0; - char *c; + char *c = NULL; assert(s); assert(iovec || n == 0); @@ -1174,12 +1177,15 @@ void server_dispatch_message( /* Write a suppression message if we suppressed something */ if (rl > 1) - server_driver_message(s, SD_MESSAGE_JOURNAL_DROPPED, + server_driver_message(s, "MESSAGE_ID=" SD_MESSAGE_JOURNAL_DROPPED_STR, LOG_MESSAGE("Suppressed %u messages from %s", rl - 1, path), NULL); finish: - dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len, unit_id, priority, object_pid); + /* restore cgroup path for logging */ + if (c) + *c = '/'; + dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len, unit_id, priority, object_pid, path); } int server_flush_to_var(Server *s, bool require_flag_file) { @@ -1273,7 +1279,7 @@ finish: sd_journal_close(j); - server_driver_message(s, SD_ID128_NULL, + server_driver_message(s, NULL, LOG_MESSAGE("Time spent on flushing to /var is %s for %u entries.", format_timespan(ts, sizeof(ts), now(CLOCK_MONOTONIC) - start, 0), n), diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h index 716e758b7c..75ac114d24 100644 --- a/src/journal/journald-server.h +++ b/src/journal/journald-server.h @@ -176,7 +176,7 @@ struct Server { #define N_IOVEC_PAYLOAD_FIELDS 15 void server_dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigned m, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len, const char *unit_id, int priority, pid_t object_pid); -void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) _printf_(3,0) _sentinel_; +void server_driver_message(Server *s, const char *message_id, const char *format, ...) _printf_(3,0) _sentinel_; /* gperf lookup function */ const struct ConfigPerfItem* journald_gperf_lookup(const char *key, GPERF_LEN_TYPE length); diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c index 896303fb85..474369039a 100644 --- a/src/journal/journald-syslog.c +++ b/src/journal/journald-syslog.c @@ -444,7 +444,8 @@ void server_maybe_warn_forward_syslog_missed(Server *s) { if (s->last_warn_forward_syslog_missed + WARN_FORWARD_SYSLOG_MISSED_USEC > n) return; - server_driver_message(s, SD_MESSAGE_FORWARD_SYSLOG_MISSED, + server_driver_message(s, + "MESSAGE_ID=" SD_MESSAGE_FORWARD_SYSLOG_MISSED_STR, LOG_MESSAGE("Forwarding to syslog missed %u messages.", s->n_forward_syslog_missed), NULL); diff --git a/src/journal/journald.c b/src/journal/journald.c index 54fd1f999d..1aaef387b4 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -56,7 +56,8 @@ int main(int argc, char *argv[]) { server_flush_dev_kmsg(&server); log_debug("systemd-journald running as pid "PID_FMT, getpid()); - server_driver_message(&server, SD_MESSAGE_JOURNAL_START, + server_driver_message(&server, + "MESSAGE_ID=" SD_MESSAGE_JOURNAL_START_STR, LOG_MESSAGE("Journal started"), NULL); @@ -114,7 +115,8 @@ int main(int argc, char *argv[]) { } log_debug("systemd-journald stopped as pid "PID_FMT, getpid()); - server_driver_message(&server, SD_MESSAGE_JOURNAL_STOP, + server_driver_message(&server, + "MESSAGE_ID=" SD_MESSAGE_JOURNAL_STOP_STR, LOG_MESSAGE("Journal stopped"), NULL); diff --git a/src/journal/test-compress.c b/src/journal/test-compress.c index 0469393f3b..44a2cf5217 100644 --- a/src/journal/test-compress.c +++ b/src/journal/test-compress.c @@ -109,7 +109,7 @@ static void test_decompress_startswith(int compression, size_t csize, usize = 0, len; int r; - log_info("/* testing decompress_startswith with %s on %.20s text*/", + log_info("/* testing decompress_startswith with %s on %.20s text */", object_compressed_to_string(compression), data); #define BUFSIZE_1 512 diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c index 02028bf28a..2e02b3fa66 100644 --- a/src/libsystemd-network/arp-util.c +++ b/src/libsystemd-network/arp-util.c @@ -58,7 +58,7 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1), /* A == 0 ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about*/ + /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about */ BPF_STMT(BPF_LD + BPF_IMM, htobe32(address)), /* A <- clients IP */ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)), /* A <- SPA */ diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index b4bf75a3dc..7c0317640f 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -825,6 +825,15 @@ static int client_send_request(sd_dhcp_client *client) { return r; } + if (client->vendor_class_identifier) { + r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, + SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER, + strlen(client->vendor_class_identifier), + client->vendor_class_identifier); + if (r < 0) + return r; + } + r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, SD_DHCP_OPTION_END, 0, NULL); if (r < 0) diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c index 4dd343c101..2ebc00f247 100644 --- a/src/libsystemd-network/sd-ipv4acd.c +++ b/src/libsystemd-network/sd-ipv4acd.c @@ -242,8 +242,6 @@ static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC); if (r < 0) goto fail; - - acd->n_conflict = 0; } else { r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC); if (r < 0) diff --git a/src/libsystemd-network/test-lldp.c b/src/libsystemd-network/test-lldp.c index 6bcd65de0a..430c58ae60 100644 --- a/src/libsystemd-network/test-lldp.c +++ b/src/libsystemd-network/test-lldp.c @@ -98,14 +98,14 @@ static void test_receive_basic_packet(sd_event *e) { static const uint8_t frame[] = { /* Ethernet header */ - 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC*/ + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ 0x88, 0xcc, /* Ethertype */ /* LLDP mandatory TLVs */ 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */ 0x03, 0x04, 0x05, 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port: interface name, "1/3" */ - 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds*/ + 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */ /* LLDP optional TLVs */ 0x08, 0x04, 0x50, 0x6f, 0x72, 0x74, /* Port Description: "Port" */ 0x0a, 0x03, 0x53, 0x59, 0x53, /* System Name: "SYS" */ @@ -162,7 +162,7 @@ static void test_receive_incomplete_packet(sd_event *e) { sd_lldp_neighbor **neighbors; uint8_t frame[] = { /* Ethernet header */ - 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC*/ + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ 0x88, 0xcc, /* Ethertype */ /* LLDP mandatory TLVs */ @@ -189,14 +189,14 @@ static void test_receive_oui_packet(sd_event *e) { sd_lldp_neighbor **neighbors; uint8_t frame[] = { /* Ethernet header */ - 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC*/ + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC */ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ 0x88, 0xcc, /* Ethertype */ /* LLDP mandatory TLVs */ 0x02, 0x07, 0x04, 0x00, 0x01, 0x02, /* Chassis: MAC, 00:01:02:03:04:05 */ 0x03, 0x04, 0x05, 0x04, 0x04, 0x05, 0x31, 0x2f, 0x33, /* Port TLV: interface name, "1/3" */ - 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds*/ + 0x06, 0x02, 0x00, 0x78, /* TTL: 120 seconds */ /* LLDP optional TLVs */ 0xfe, 0x06, 0x00, 0x80, 0xc2, 0x01, /* Port VLAN ID: 0x1234 */ 0x12, 0x34, diff --git a/src/libsystemd/sd-bus/test-bus-creds.c b/src/libsystemd/sd-bus/test-bus-creds.c index 6fdcfa4128..64bd76a576 100644 --- a/src/libsystemd/sd-bus/test-bus-creds.c +++ b/src/libsystemd/sd-bus/test-bus-creds.c @@ -31,7 +31,7 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); - if (cg_all_unified() == -ENOMEDIUM) { + if (cg_unified_flush() == -ENOMEDIUM) { log_info("Skipping test: /sys/fs/cgroup/ not available"); return EXIT_TEST_SKIP; } diff --git a/src/libsystemd/sd-device/device-internal.h b/src/libsystemd/sd-device/device-internal.h index 9fad388953..f4783deef8 100644 --- a/src/libsystemd/sd-device/device-internal.h +++ b/src/libsystemd/sd-device/device-internal.h @@ -34,7 +34,7 @@ struct sd_device { uint64_t properties_generation; /* changes whenever the properties are changed */ uint64_t properties_iterator_generation; /* generation when iteration was started */ - /* the subset of the properties that should be written to the db*/ + /* the subset of the properties that should be written to the db */ OrderedHashmap *properties_db; Hashmap *sysattr_values; /* cached sysattr values */ diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index efeadf0cd4..04ead29338 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -1859,8 +1859,7 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, _cleanup_free_ char *value = NULL; const char *syspath; char *path; - struct stat statbuf; - size_t value_len = 0; + size_t len = 0; ssize_t size; int r; @@ -1878,8 +1877,14 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, return r; path = strjoina(syspath, "/", sysattr); - r = lstat(path, &statbuf); - if (r < 0) { + + fd = open(path, O_WRONLY | O_CLOEXEC | O_NOFOLLOW); + if (fd < 0) { + if (errno == ELOOP) + return -EINVAL; + if (errno == EISDIR) + return -EISDIR; + value = strdup(""); if (!value) return -ENOMEM; @@ -1891,46 +1896,30 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, return -ENXIO; } - if (S_ISLNK(statbuf.st_mode)) - return -EINVAL; - - /* skip directories */ - if (S_ISDIR(statbuf.st_mode)) - return -EISDIR; - - /* skip non-readable files */ - if ((statbuf.st_mode & S_IRUSR) == 0) - return -EACCES; - - value_len = strlen(_value); + len = strlen(_value); /* drop trailing newlines */ - while (value_len > 0 && _value[value_len - 1] == '\n') - _value[--value_len] = '\0'; + while (len > 0 && _value[len - 1] == '\n') + len --; /* value length is limited to 4k */ - if (value_len > 4096) + if (len > 4096) return -EINVAL; - fd = open(path, O_WRONLY | O_CLOEXEC); - if (fd < 0) - return -errno; - - value = strdup(_value); + value = strndup(_value, len); if (!value) return -ENOMEM; - size = write(fd, value, value_len); + size = write(fd, value, len); if (size < 0) return -errno; - if ((size_t)size != value_len) + if ((size_t)size != len) return -EIO; r = device_add_sysattr_value(device, sysattr, value); if (r < 0) return r; - value = NULL; return 0; diff --git a/src/libsystemd/sd-netlink/netlink-socket.c b/src/libsystemd/sd-netlink/netlink-socket.c index a0fd8a3ac9..129bfd2d80 100644 --- a/src/libsystemd/sd-netlink/netlink-socket.c +++ b/src/libsystemd/sd-netlink/netlink-socket.c @@ -281,7 +281,7 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool else if (errno == EAGAIN) log_debug("rtnl: no data in socket"); - return (errno == EAGAIN || errno == EINTR) ? 0 : -errno; + return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno; } if (sender.nl.nl_pid != 0) { @@ -292,7 +292,7 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool /* drop the message */ r = recvmsg(fd, &msg, 0); if (r < 0) - return (errno == EAGAIN || errno == EINTR) ? 0 : -errno; + return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno; } return 0; diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c index 0f8b0cc70b..ff0e99558e 100644 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ b/src/libsystemd/sd-netlink/netlink-types.c @@ -149,7 +149,7 @@ static const NLType rtnl_link_info_data_vxlan_types[] = { [IFLA_VXLAN_ID] = { .type = NETLINK_TYPE_U32 }, [IFLA_VXLAN_GROUP] = { .type = NETLINK_TYPE_IN_ADDR }, [IFLA_VXLAN_LINK] = { .type = NETLINK_TYPE_U32 }, - [IFLA_VXLAN_LOCAL] = { .type = NETLINK_TYPE_U32}, + [IFLA_VXLAN_LOCAL] = { .type = NETLINK_TYPE_IN_ADDR }, [IFLA_VXLAN_TTL] = { .type = NETLINK_TYPE_U8 }, [IFLA_VXLAN_TOS] = { .type = NETLINK_TYPE_U8 }, [IFLA_VXLAN_LEARNING] = { .type = NETLINK_TYPE_U8 }, diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c index 43114eb825..68435564de 100644 --- a/src/libsystemd/sd-netlink/sd-netlink.c +++ b/src/libsystemd/sd-netlink/sd-netlink.c @@ -276,6 +276,10 @@ static int dispatch_rqueue(sd_netlink *rtnl, sd_netlink_message **message) { if (rtnl->rqueue_size <= 0) { /* Try to read a new message */ r = socket_read_message(rtnl); + if (r == -ENOBUFS) { /* FIXME: ignore buffer overruns for now */ + log_debug_errno(r, "Got ENOBUFS from netlink socket, ignoring."); + return 1; + } if (r <= 0) return r; } diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c index 0d8d99c56d..8b4af5a2c3 100644 --- a/src/libsystemd/sd-network/sd-network.c +++ b/src/libsystemd/sd-network/sd-network.c @@ -245,7 +245,7 @@ static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) { } if (ifis) - ifis[c] = 0; /* Let's add a 0 ifindex to the end, to be nice*/ + ifis[c] = 0; /* Let's add a 0 ifindex to the end, to be nice */ *ret = ifis; ifis = NULL; diff --git a/src/locale/localectl.c b/src/locale/localectl.c index 81afb4909f..0bd18a5c0b 100644 --- a/src/locale/localectl.c +++ b/src/locale/localectl.c @@ -166,6 +166,8 @@ static int show_status(sd_bus *bus, char **args, unsigned n) { { "Locale", "as", NULL, offsetof(StatusInfo, locale) }, {} }; + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; assert(bus); @@ -174,9 +176,10 @@ static int show_status(sd_bus *bus, char **args, unsigned n) { "org.freedesktop.locale1", "/org/freedesktop/locale1", map, + &error, &info); if (r < 0) - return log_error_errno(r, "Could not get properties: %m"); + return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r)); print_overridden_variables(); print_status_info(&info); diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 1aac7ae979..7dea5c0859 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -482,14 +482,15 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li {} }; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1; char since2[FORMAT_TIMESTAMP_MAX], *s2; _cleanup_(session_status_info_clear) SessionStatusInfo i = {}; int r; - r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i); + r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &error, &i); if (r < 0) - return log_error_errno(r, "Could not get properties: %m"); + return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r)); if (*new_line) printf("\n"); @@ -611,14 +612,15 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) {} }; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1; char since2[FORMAT_TIMESTAMP_MAX], *s2; _cleanup_(user_status_info_clear) UserStatusInfo i = {}; int r; - r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i); + r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &error, &i); if (r < 0) - return log_error_errno(r, "Could not get properties: %m"); + return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r)); if (*new_line) printf("\n"); @@ -685,12 +687,13 @@ static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) {} }; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(seat_status_info_clear) SeatStatusInfo i = {}; int r; - r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i); + r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &error, &i); if (r < 0) - return log_error_errno(r, "Could not get properties: %m"); + return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r)); if (*new_line) printf("\n"); diff --git a/src/login/logind-button.c b/src/login/logind-button.c index 90fb93bbaf..d739af8ea2 100644 --- a/src/login/logind-button.c +++ b/src/login/logind-button.c @@ -155,7 +155,7 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u case KEY_POWER2: log_struct(LOG_INFO, LOG_MESSAGE("Power key pressed."), - LOG_MESSAGE_ID(SD_MESSAGE_POWER_KEY), + "MESSAGE_ID=" SD_MESSAGE_POWER_KEY_STR, NULL); manager_handle_action(b->manager, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true); @@ -170,7 +170,7 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u case KEY_SLEEP: log_struct(LOG_INFO, LOG_MESSAGE("Suspend key pressed."), - LOG_MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY), + "MESSAGE_ID=" SD_MESSAGE_SUSPEND_KEY_STR, NULL); manager_handle_action(b->manager, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true); @@ -179,7 +179,7 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u case KEY_SUSPEND: log_struct(LOG_INFO, LOG_MESSAGE("Hibernate key pressed."), - LOG_MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY), + "MESSAGE_ID=" SD_MESSAGE_HIBERNATE_KEY_STR, NULL); manager_handle_action(b->manager, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true); @@ -191,7 +191,7 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u if (ev.code == SW_LID) { log_struct(LOG_INFO, LOG_MESSAGE("Lid closed."), - LOG_MESSAGE_ID(SD_MESSAGE_LID_CLOSED), + "MESSAGE_ID=" SD_MESSAGE_LID_CLOSED_STR, NULL); b->lid_closed = true; @@ -201,7 +201,7 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u } else if (ev.code == SW_DOCK) { log_struct(LOG_INFO, LOG_MESSAGE("System docked."), - LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_DOCKED), + "MESSAGE_ID=" SD_MESSAGE_SYSTEM_DOCKED_STR, NULL); b->docked = true; @@ -212,7 +212,7 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u if (ev.code == SW_LID) { log_struct(LOG_INFO, LOG_MESSAGE("Lid opened."), - LOG_MESSAGE_ID(SD_MESSAGE_LID_OPENED), + "MESSAGE_ID=" SD_MESSAGE_LID_OPENED_STR, NULL); b->lid_closed = false; @@ -221,7 +221,7 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u } else if (ev.code == SW_DOCK) { log_struct(LOG_INFO, LOG_MESSAGE("System undocked."), - LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_UNDOCKED), + "MESSAGE_ID=" SD_MESSAGE_SYSTEM_UNDOCKED_STR, NULL); b->docked = false; diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index ad44ca290e..c6be596af3 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -1430,7 +1430,7 @@ static int bus_manager_log_shutdown( p = strjoina(p, " (", m->wall_message, ")."); return log_struct(LOG_NOTICE, - LOG_MESSAGE_ID(SD_MESSAGE_SHUTDOWN), + "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR, p, q, NULL); diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c index bfbd07309d..30dac7997b 100644 --- a/src/login/logind-seat.c +++ b/src/login/logind-seat.c @@ -379,7 +379,8 @@ int seat_read_active_vt(Seat *s) { if (!seat_has_vts(s)) return 0; - lseek(s->manager->console_active_fd, SEEK_SET, 0); + if (lseek(s->manager->console_active_fd, SEEK_SET, 0) < 0) + return log_error_errno(errno, "lseek on console_active_fd failed: %m"); k = read(s->manager->console_active_fd, t, sizeof(t)-1); if (k <= 0) { @@ -396,10 +397,8 @@ int seat_read_active_vt(Seat *s) { } r = safe_atou(t+3, &vtnr); - if (r < 0) { - log_error("Failed to parse VT number %s", t+3); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to parse VT number \"%s\": %m", t+3); if (!vtnr) { log_error("VT number invalid: %s", t+3); @@ -416,7 +415,7 @@ int seat_start(Seat *s) { return 0; log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SEAT_START), + "MESSAGE_ID=" SD_MESSAGE_SEAT_START_STR, "SEAT_ID=%s", s->id, LOG_MESSAGE("New seat %s.", s->id), NULL); @@ -444,7 +443,7 @@ int seat_stop(Seat *s, bool force) { if (s->started) log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SEAT_STOP), + "MESSAGE_ID=" SD_MESSAGE_SEAT_STOP_STR, "SEAT_ID=%s", s->id, LOG_MESSAGE("Removed seat %s.", s->id), NULL); diff --git a/src/login/logind-session.c b/src/login/logind-session.c index fd7fcf7f2c..4a168906d6 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -561,7 +561,7 @@ int session_start(Session *s) { return r; log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SESSION_START), + "MESSAGE_ID=" SD_MESSAGE_SESSION_START_STR, "SESSION_ID=%s", s->id, "USER_ID=%s", s->user->name, "LEADER="PID_FMT, s->leader, @@ -666,7 +666,7 @@ int session_finalize(Session *s) { if (s->started) log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SESSION_STOP), + "MESSAGE_ID=" SD_MESSAGE_SESSION_STOP_STR, "SESSION_ID=%s", s->id, "USER_ID=%s", s->user->name, "LEADER="PID_FMT, s->leader, diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c index d5051007fc..2f69e2c7b7 100644 --- a/src/machine/image-dbus.c +++ b/src/machine/image-dbus.c @@ -374,7 +374,7 @@ static int raw_image_get_os_release(Image *image, char ***ret, sd_bus_error *err if (fd < 0) _exit(EXIT_FAILURE); - r = copy_bytes(fd, pair[1], (uint64_t) -1, false); + r = copy_bytes(fd, pair[1], (uint64_t) -1, 0); if (r < 0) _exit(EXIT_FAILURE); diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index af745b6567..36568b65ef 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -411,7 +411,7 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s if (fd < 0) _exit(EXIT_FAILURE); - r = copy_bytes(fd, pair[1], (uint64_t) -1, false); + r = copy_bytes(fd, pair[1], (uint64_t) -1, 0); if (r < 0) _exit(EXIT_FAILURE); @@ -841,6 +841,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu int read_only, make_directory; pid_t child; siginfo_t si; + uid_t uid; int r; assert(message); @@ -875,6 +876,12 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu if (r == 0) return 1; /* Will call us back */ + r = machine_get_uid_shift(m, &uid); + if (r < 0) + return r; + if (uid != 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Can't bind mount on container with user namespacing applied."); + /* One day, when bind mounting /proc/self/fd/n works across * namespace boundaries we should rework this logic to make * use of it... */ @@ -1055,10 +1062,12 @@ finish: 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 }; + CopyFlags copy_flags = COPY_REFLINK|COPY_MERGE; _cleanup_close_ int hostfd = -1; Machine *m = userdata; bool copy_from; pid_t child; + uid_t uid_shift; char *t; int r; @@ -1097,6 +1106,10 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro if (r == 0) return 1; /* Will call us back */ + r = machine_get_uid_shift(m, &uid_shift); + if (r < 0) + return r; + copy_from = strstr(sd_bus_message_get_member(message), "CopyFrom"); if (copy_from) { @@ -1151,10 +1164,13 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro goto child_fail; } + /* Run the actual copy operation. Note that when an UID shift is set we'll either clamp the UID/GID to + * 0 or to the actual UID shift depending on the direction we copy. If no UID shift is set we'll copy + * the UID/GIDs as they are. */ if (copy_from) - r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true); + r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, uid_shift == 0 ? UID_INVALID : 0, uid_shift == 0 ? GID_INVALID : 0, copy_flags); else - r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true); + r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, uid_shift == 0 ? UID_INVALID : uid_shift, uid_shift == 0 ? GID_INVALID : uid_shift, copy_flags); hostfd = safe_close(hostfd); containerfd = safe_close(containerfd); @@ -1276,6 +1292,32 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda return sd_bus_reply_method_return(message, "h", fd); } +int bus_machine_method_get_uid_shift(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Machine *m = userdata; + uid_t shift = 0; + int r; + + assert(message); + assert(m); + + /* You wonder why this is a method and not a property? Well, properties are not supposed to return errors, but + * we kinda have to for this. */ + + if (m->class == MACHINE_HOST) + return sd_bus_reply_method_return(message, "u", UINT32_C(0)); + + if (m->class != MACHINE_CONTAINER) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "UID/GID shift may only be determined for container machines."); + + r = machine_get_uid_shift(m, &shift); + if (r == -ENXIO) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Machine %s uses a complex UID/GID mapping, cannot determine shift", m->name); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, "u", (uint32_t) shift); +} + const sd_bus_vtable machine_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST), @@ -1293,6 +1335,7 @@ const sd_bus_vtable machine_vtable[] = { SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetUIDShift", NULL, "u", bus_machine_method_get_uid_shift, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h index c513783480..2aa7b4ce06 100644 --- a/src/machine/machine-dbus.h +++ b/src/machine/machine-dbus.h @@ -39,6 +39,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_machine_method_get_uid_shift(sd_bus_message *message, void *userdata, sd_bus_error *error); int machine_send_signal(Machine *m, bool new_machine); int machine_send_create_reply(Machine *m, sd_bus_error *error); diff --git a/src/machine/machine.c b/src/machine/machine.c index eb4b35d52a..d3433d9b96 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -38,6 +38,7 @@ #include "parse-util.h" #include "process-util.h" #include "special.h" +#include "stdio-util.h" #include "string-table.h" #include "terminal-util.h" #include "unit-name.h" @@ -401,7 +402,7 @@ int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) { return r; log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_START), + "MESSAGE_ID=" SD_MESSAGE_MACHINE_START_STR, "NAME=%s", m->name, "LEADER="PID_FMT, m->leader, LOG_MESSAGE("New machine %s.", m->name), @@ -464,7 +465,7 @@ int machine_finalize(Machine *m) { if (m->started) log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_STOP), + "MESSAGE_ID=" SD_MESSAGE_MACHINE_STOP_STR, "NAME=%s", m->name, "LEADER="PID_FMT, m->leader, LOG_MESSAGE("Machine %s terminated.", m->name), @@ -604,6 +605,96 @@ void machine_release_unit(Machine *m) { m->unit = mfree(m->unit); } +int machine_get_uid_shift(Machine *m, uid_t *ret) { + char p[strlen("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1]; + uid_t uid_base, uid_shift, uid_range; + gid_t gid_base, gid_shift, gid_range; + _cleanup_fclose_ FILE *f = NULL; + int k; + + assert(m); + assert(ret); + + /* Return the base UID/GID of the specified machine. Note that this only works for containers with simple + * mappings. In most cases setups should be simple like this, and administrators should only care about the + * basic offset a container has relative to the host. This is what this function exposes. + * + * If we encounter any more complex mappings we politely refuse this with ENXIO. */ + + if (m->class == MACHINE_HOST) { + *ret = 0; + return 0; + } + + if (m->class != MACHINE_CONTAINER) + return -EOPNOTSUPP; + + xsprintf(p, "/proc/" PID_FMT "/uid_map", m->leader); + f = fopen(p, "re"); + if (!f) { + if (errno == ENOENT) { + /* If the file doesn't exist, user namespacing is off in the kernel, return a zero mapping hence. */ + *ret = 0; + return 0; + } + + return -errno; + } + + /* Read the first line. There's at least one. */ + errno = 0; + k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range); + if (k != 3) { + if (ferror(f)) + return -errno; + + return -EBADMSG; + } + + /* Not a mapping starting at 0? Then it's a complex mapping we can't expose here. */ + if (uid_base != 0) + return -ENXIO; + /* Insist that at least the nobody user is mapped, everything else is weird, and hence complex, and we don't support it */ + if (uid_range < (uid_t) 65534U) + return -ENXIO; + + /* If there's more than one line, then we don't support this mapping. */ + if (fgetc(f) != EOF) + return -ENXIO; + + fclose(f); + + xsprintf(p, "/proc/" PID_FMT "/gid_map", m->leader); + f = fopen(p, "re"); + if (!f) + return -errno; + + /* Read the first line. There's at least one. */ + errno = 0; + k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range); + if (k != 3) { + if (ferror(f)) + return -errno; + + return -EBADMSG; + } + + /* If there's more than one line, then we don't support this file. */ + if (fgetc(f) != EOF) + return -ENXIO; + + /* If the UID and GID mapping doesn't match, we don't support this mapping. */ + if (uid_base != (uid_t) gid_base) + return -ENXIO; + if (uid_shift != (uid_t) gid_shift) + return -ENXIO; + if (uid_range != (uid_t) gid_range) + return -ENXIO; + + *ret = uid_shift; + return 0; +} + static const char* const machine_class_table[_MACHINE_CLASS_MAX] = { [MACHINE_CONTAINER] = "container", [MACHINE_VM] = "vm", diff --git a/src/machine/machine.h b/src/machine/machine.h index e5d75361a9..6bdb204ed6 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -108,3 +108,5 @@ KillWho kill_who_from_string(const char *s) _pure_; int machine_openpt(Machine *m, int flags); int machine_open_terminal(Machine *m, const char *path, int mode); + +int machine_get_uid_shift(Machine *m, uid_t *ret); diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 4f5f659c7c..28384286fb 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -327,8 +327,10 @@ static int list_machines(int argc, char *argv[], void *userdata) { (int) max_version_id, strdash_if_empty(machines[j].version_id)); r = print_addresses(bus, machines[j].name, 0, "", prefix, arg_addrs); - if (r == -EOPNOTSUPP) - printf("-\n"); + if (r <= 0) /* error or no addresses defined? */ + fputs("-\n", stdout); + else + fputc('\n', stdout); } if (arg_legend) { @@ -520,6 +522,7 @@ static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *p _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_free_ char *addresses = NULL; bool truncate = false; + unsigned n = 0; int r; assert(bus); @@ -567,7 +570,7 @@ static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *p else strcpy(buf_ifi, ""); - if(!strextend(&addresses, prefix, inet_ntop(family, a, buffer, sizeof(buffer)), buf_ifi, NULL)) + if (!strextend(&addresses, prefix, inet_ntop(family, a, buffer, sizeof(buffer)), buf_ifi, NULL)) return log_oom(); } else truncate = true; @@ -581,6 +584,8 @@ static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *p if (n_addr > 0) n_addr -= 1; + + n++; } if (r < 0) return bus_log_parse_error(r); @@ -589,8 +594,10 @@ static int print_addresses(sd_bus *bus, const char *name, int ifi, const char *p if (r < 0) return bus_log_parse_error(r); - fprintf(stdout, "%s%s\n", addresses, truncate ? "..." : ""); - return 0; + if (n > 0) + fprintf(stdout, "%s%s", addresses, truncate ? "..." : ""); + + return (int) n; } static int print_os_release(sd_bus *bus, const char *method, const char *name, const char *prefix) { @@ -611,6 +618,37 @@ static int print_os_release(sd_bus *bus, const char *method, const char *name, c return 0; } +static int print_uid_shift(sd_bus *bus, const char *name) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + uint32_t shift; + int r; + + assert(bus); + assert(name); + + r = sd_bus_call_method(bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "GetMachineUIDShift", + &error, + &reply, + "s", name); + if (r < 0) + return log_debug_errno(r, "Failed to query UID/GID shift: %s", bus_error_message(&error, r)); + + r = sd_bus_message_read(reply, "u", &shift); + if (r < 0) + return r; + + if (shift == 0) /* Don't show trivial mappings */ + return 0; + + printf(" UID Shift: %" PRIu32 "\n", shift); + return 0; +} + typedef struct MachineStatusInfo { char *name; sd_id128_t id; @@ -707,13 +745,16 @@ static void print_machine_status_info(sd_bus *bus, MachineStatusInfo *i) { fputc('\n', stdout); } - print_addresses(bus, i->name, ifi, - "\t Address: ", - "\n\t ", - ALL_IP_ADDRESSES); + if (print_addresses(bus, i->name, ifi, + "\t Address: ", + "\n\t ", + ALL_IP_ADDRESSES) > 0) + fputc('\n', stdout); print_os_release(bus, "GetMachineOSRelease", i->name, "\t OS: "); + print_uid_shift(bus, i->name); + if (i->unit) { printf("\t Unit: %s\n", i->unit); show_unit_cgroup(bus, i->unit, i->leader); @@ -772,6 +813,7 @@ static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bo {} }; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(machine_status_info_clear) MachineStatusInfo info = {}; int r; @@ -784,9 +826,10 @@ static int show_machine_info(const char *verb, sd_bus *bus, const char *path, bo "org.freedesktop.machine1", path, map, + &error, &info); if (r < 0) - return log_error_errno(r, "Could not get properties: %m"); + return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r)); if (*new_line) printf("\n"); @@ -962,6 +1005,7 @@ static int show_image_info(sd_bus *bus, const char *path, bool *new_line) { {} }; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(image_status_info_clear) ImageStatusInfo info = {}; int r; @@ -973,9 +1017,10 @@ static int show_image_info(sd_bus *bus, const char *path, bool *new_line) { "org.freedesktop.machine1", path, map, + &error, &info); if (r < 0) - return log_error_errno(r, "Could not get properties: %m"); + return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r)); if (*new_line) printf("\n"); @@ -1029,6 +1074,8 @@ static int show_pool_info(sd_bus *bus) { .usage = (uint64_t) -1, .limit = (uint64_t) -1, }; + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; assert(bus); @@ -1037,9 +1084,10 @@ static int show_pool_info(sd_bus *bus) { "org.freedesktop.machine1", "/org/freedesktop/machine1", map, + &error, &info); if (r < 0) - return log_error_errno(r, "Could not get properties: %m"); + return log_error_errno(r, "Could not get properties: %s", bus_error_message(&error, r)); print_pool_status_info(bus, &info); diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index fd9e5b56fc..c9b92d2765 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -729,6 +729,26 @@ static int method_open_machine_root_directory(sd_bus_message *message, void *use return bus_machine_method_open_root_directory(message, machine, error); } +static int method_get_machine_uid_shift(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + return bus_machine_method_get_uid_shift(message, machine, error); +} + static int method_remove_image(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_(image_unrefp) Image* i = NULL; const char *name; @@ -1416,6 +1436,7 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("OpenMachineRootDirectory", "s", "h", method_open_machine_root_directory, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetMachineUIDShift", "s", "u", method_get_machine_uid_shift, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/machine/operation.c b/src/machine/operation.c index c966d0d21c..f7d5310f44 100644 --- a/src/machine/operation.c +++ b/src/machine/operation.c @@ -61,8 +61,10 @@ static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdat } else { /* The default operation when done is to simply return an error on failure or an empty success * message on success. */ - if (r < 0) + if (r < 0) { + sd_bus_error_set_errno(&error, r); goto fail; + } r = sd_bus_reply_method_return(o->message, NULL); if (r < 0) diff --git a/src/machine/org.freedesktop.machine1.conf b/src/machine/org.freedesktop.machine1.conf index 82ebfba50c..daa365a9dd 100644 --- a/src/machine/org.freedesktop.machine1.conf +++ b/src/machine/org.freedesktop.machine1.conf @@ -66,6 +66,10 @@ <allow send_destination="org.freedesktop.machine1" send_interface="org.freedesktop.machine1.Manager" + send_member="GetMachineUIDShift"/> + + <allow send_destination="org.freedesktop.machine1" + send_interface="org.freedesktop.machine1.Manager" send_member="OpenMachineLogin"/> <allow send_destination="org.freedesktop.machine1" @@ -150,6 +154,10 @@ <allow send_destination="org.freedesktop.machine1" send_interface="org.freedesktop.machine1.Machine" + send_member="GetUIDShift"/> + + <allow send_destination="org.freedesktop.machine1" + send_interface="org.freedesktop.machine1.Machine" send_member="OpenLogin"/> <allow send_destination="org.freedesktop.machine1" diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c index 4b3cac8a22..b709166aa9 100644 --- a/src/mount/mount-tool.c +++ b/src/mount/mount-tool.c @@ -1025,7 +1025,7 @@ static int list_devices(void) { j = items + n++; for (c = 0; c < _COLUMN_MAX; c++) { - const char *x; + const char *x = NULL; size_t k; switch (c) { diff --git a/src/network/netdev/Makefile b/src/network/netdev/Makefile new file mode 120000 index 0000000000..94aaae2c4d --- /dev/null +++ b/src/network/netdev/Makefile @@ -0,0 +1 @@ +../../Makefile
\ No newline at end of file diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index e74ae9eb9f..e19fa9817e 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -26,94 +26,96 @@ struct ConfigPerfItem; %struct-type %includes %% -Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, match_host) -Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, match_virt) -Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, match_kernel) -Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, match_arch) -NetDev.Description, config_parse_string, 0, offsetof(NetDev, description) -NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname) -NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind) -NetDev.MTUBytes, config_parse_iec_size, 0, offsetof(NetDev, mtu) -NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac) -VLAN.Id, config_parse_vlanid, 0, offsetof(VLan, id) -MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) -MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) -IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode) -Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local) -Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote) -Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos) -Tunnel.TTL, config_parse_unsigned, 0, offsetof(Tunnel, ttl) -Tunnel.Key, config_parse_tunnel_key, 0, offsetof(Tunnel, key) -Tunnel.InputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, ikey) -Tunnel.OutputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, okey) -Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(Tunnel, pmtudisc) -Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode) -Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, offsetof(Tunnel, ipv6_flowlabel) -Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp) -Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit) -Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer) -Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer) -VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id) -VXLAN.Group, config_parse_vxlan_group_address, 0, offsetof(VxLan, group) -VXLAN.TOS, config_parse_unsigned, 0, offsetof(VxLan, tos) -VXLAN.TTL, config_parse_unsigned, 0, offsetof(VxLan, ttl) -VXLAN.MacLearning, config_parse_bool, 0, offsetof(VxLan, learning) -VXLAN.ARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy) -VXLAN.ReduceARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy) -VXLAN.L2MissNotification, config_parse_bool, 0, offsetof(VxLan, l2miss) -VXLAN.L3MissNotification, config_parse_bool, 0, offsetof(VxLan, l3miss) -VXLAN.RouteShortCircuit, config_parse_bool, 0, offsetof(VxLan, route_short_circuit) -VXLAN.UDPCheckSum, config_parse_bool, 0, offsetof(VxLan, udpcsum) -VXLAN.UDPChecksum, config_parse_bool, 0, offsetof(VxLan, udpcsum) -VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx) -VXLAN.UDP6ZeroChecksumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx) -VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx) -VXLAN.UDP6ZeroChecksumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx) -VXLAN.RemoteChecksumTx, config_parse_bool, 0, offsetof(VxLan, remote_csum_tx) -VXLAN.RemoteChecksumRx, config_parse_bool, 0, offsetof(VxLan, remote_csum_rx) -VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing) -VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy) -VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb) -VXLAN.PortRange, config_parse_port_range, 0, 0 -VXLAN.DestinationPort, config_parse_destination_port, 0, offsetof(VxLan, dest_port) -Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) -Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) -Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) -Tun.User, config_parse_string, 0, offsetof(TunTap, user_name) -Tun.Group, config_parse_string, 0, offsetof(TunTap, group_name) -Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) -Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) -Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) -Tap.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr) -Tap.User, config_parse_string, 0, offsetof(TunTap, user_name) -Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name) -Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode) -Bond.TransmitHashPolicy, config_parse_bond_xmit_hash_policy, 0, offsetof(Bond, xmit_hash_policy) -Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0, offsetof(Bond, lacp_rate) -Bond.AdSelect, config_parse_bond_ad_select, 0, offsetof(Bond, ad_select) -Bond.FailOverMACPolicy, config_parse_bond_fail_over_mac, 0, offsetof(Bond, fail_over_mac) -Bond.ARPIPTargets, config_parse_arp_ip_target_address, 0, 0 -Bond.ARPValidate, config_parse_bond_arp_validate, 0, offsetof(Bond, arp_validate) -Bond.ARPAllTargets, config_parse_bond_arp_all_targets, 0, offsetof(Bond, arp_all_targets) -Bond.PrimaryReselectPolicy, config_parse_bond_primary_reselect, 0, offsetof(Bond, primary_reselect) -Bond.ResendIGMP, config_parse_unsigned, 0, offsetof(Bond, resend_igmp) -Bond.PacketsPerSlave, config_parse_unsigned, 0, offsetof(Bond, packets_per_slave) -Bond.GratuitousARP, config_parse_unsigned, 0, offsetof(Bond, num_grat_arp) -Bond.AllSlavesActive, config_parse_unsigned, 0, offsetof(Bond, all_slaves_active) -Bond.MinLinks, config_parse_unsigned, 0, offsetof(Bond, min_links) -Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon) -Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay) -Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay) -Bond.ARPIntervalSec, config_parse_sec, 0, offsetof(Bond, arp_interval) -Bond.LearnPacketIntervalSec, config_parse_sec, 0, offsetof(Bond, lp_interval) -Bridge.HelloTimeSec, config_parse_sec, 0, offsetof(Bridge, hello_time) -Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age) -Bridge.AgeingTimeSec, config_parse_sec, 0, offsetof(Bridge, ageing_time) -Bridge.ForwardDelaySec, config_parse_sec, 0, offsetof(Bridge, forward_delay) -Bridge.Priority, config_parse_uint16, 0, offsetof(Bridge, priority) -Bridge.DefaultPVID, config_parse_vlanid, 0, offsetof(Bridge, default_pvid) -Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier) -Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping) -Bridge.VLANFiltering, config_parse_tristate, 0, offsetof(Bridge, vlan_filtering) -Bridge.STP, config_parse_tristate, 0, offsetof(Bridge, stp) -VRF.TableId, config_parse_uint32, 0, offsetof(Vrf, table_id) +Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, match_host) +Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, match_virt) +Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, match_kernel) +Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, match_arch) +NetDev.Description, config_parse_string, 0, offsetof(NetDev, description) +NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname) +NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind) +NetDev.MTUBytes, config_parse_iec_size, 0, offsetof(NetDev, mtu) +NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac) +VLAN.Id, config_parse_vlanid, 0, offsetof(VLan, id) +MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) +MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) +IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode) +Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local) +Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote) +Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos) +Tunnel.TTL, config_parse_unsigned, 0, offsetof(Tunnel, ttl) +Tunnel.Key, config_parse_tunnel_key, 0, offsetof(Tunnel, key) +Tunnel.InputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, ikey) +Tunnel.OutputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, okey) +Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(Tunnel, pmtudisc) +Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode) +Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, offsetof(Tunnel, ipv6_flowlabel) +Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp) +Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit) +Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer) +Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer) +VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id) +VXLAN.Group, config_parse_vxlan_address, 0, offsetof(VxLan, remote) +VXLAN.Local, config_parse_vxlan_address, 0, offsetof(VxLan, local) +VXLAN.Remote, config_parse_vxlan_address, 0, offsetof(VxLan, remote) +VXLAN.TOS, config_parse_unsigned, 0, offsetof(VxLan, tos) +VXLAN.TTL, config_parse_unsigned, 0, offsetof(VxLan, ttl) +VXLAN.MacLearning, config_parse_bool, 0, offsetof(VxLan, learning) +VXLAN.ARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy) +VXLAN.ReduceARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy) +VXLAN.L2MissNotification, config_parse_bool, 0, offsetof(VxLan, l2miss) +VXLAN.L3MissNotification, config_parse_bool, 0, offsetof(VxLan, l3miss) +VXLAN.RouteShortCircuit, config_parse_bool, 0, offsetof(VxLan, route_short_circuit) +VXLAN.UDPCheckSum, config_parse_bool, 0, offsetof(VxLan, udpcsum) +VXLAN.UDPChecksum, config_parse_bool, 0, offsetof(VxLan, udpcsum) +VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx) +VXLAN.UDP6ZeroChecksumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx) +VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx) +VXLAN.UDP6ZeroChecksumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx) +VXLAN.RemoteChecksumTx, config_parse_bool, 0, offsetof(VxLan, remote_csum_tx) +VXLAN.RemoteChecksumRx, config_parse_bool, 0, offsetof(VxLan, remote_csum_rx) +VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing) +VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy) +VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb) +VXLAN.PortRange, config_parse_port_range, 0, 0 +VXLAN.DestinationPort, config_parse_destination_port, 0, offsetof(VxLan, dest_port) +Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) +Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) +Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) +Tun.User, config_parse_string, 0, offsetof(TunTap, user_name) +Tun.Group, config_parse_string, 0, offsetof(TunTap, group_name) +Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) +Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) +Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) +Tap.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr) +Tap.User, config_parse_string, 0, offsetof(TunTap, user_name) +Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name) +Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode) +Bond.TransmitHashPolicy, config_parse_bond_xmit_hash_policy, 0, offsetof(Bond, xmit_hash_policy) +Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0, offsetof(Bond, lacp_rate) +Bond.AdSelect, config_parse_bond_ad_select, 0, offsetof(Bond, ad_select) +Bond.FailOverMACPolicy, config_parse_bond_fail_over_mac, 0, offsetof(Bond, fail_over_mac) +Bond.ARPIPTargets, config_parse_arp_ip_target_address, 0, 0 +Bond.ARPValidate, config_parse_bond_arp_validate, 0, offsetof(Bond, arp_validate) +Bond.ARPAllTargets, config_parse_bond_arp_all_targets, 0, offsetof(Bond, arp_all_targets) +Bond.PrimaryReselectPolicy, config_parse_bond_primary_reselect, 0, offsetof(Bond, primary_reselect) +Bond.ResendIGMP, config_parse_unsigned, 0, offsetof(Bond, resend_igmp) +Bond.PacketsPerSlave, config_parse_unsigned, 0, offsetof(Bond, packets_per_slave) +Bond.GratuitousARP, config_parse_unsigned, 0, offsetof(Bond, num_grat_arp) +Bond.AllSlavesActive, config_parse_unsigned, 0, offsetof(Bond, all_slaves_active) +Bond.MinLinks, config_parse_unsigned, 0, offsetof(Bond, min_links) +Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon) +Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay) +Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay) +Bond.ARPIntervalSec, config_parse_sec, 0, offsetof(Bond, arp_interval) +Bond.LearnPacketIntervalSec, config_parse_sec, 0, offsetof(Bond, lp_interval) +Bridge.HelloTimeSec, config_parse_sec, 0, offsetof(Bridge, hello_time) +Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age) +Bridge.AgeingTimeSec, config_parse_sec, 0, offsetof(Bridge, ageing_time) +Bridge.ForwardDelaySec, config_parse_sec, 0, offsetof(Bridge, forward_delay) +Bridge.Priority, config_parse_uint16, 0, offsetof(Bridge, priority) +Bridge.DefaultPVID, config_parse_vlanid, 0, offsetof(Bridge, default_pvid) +Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier) +Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping) +Bridge.VLANFiltering, config_parse_tristate, 0, offsetof(Bridge, vlan_filtering) +Bridge.STP, config_parse_tristate, 0, offsetof(Bridge, stp) +VRF.TableId, config_parse_uint32, 0, offsetof(Vrf, table_id) diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c index c11ac0c539..67f4fab400 100644 --- a/src/network/netdev/tunnel.c +++ b/src/network/netdev/tunnel.c @@ -410,10 +410,10 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) { return -EINVAL; } - if (netdev->kind == NETDEV_KIND_VTI6 && + if (IN_SET(netdev->kind, NETDEV_KIND_VTI6, NETDEV_KIND_IP6TNL, NETDEV_KIND_IP6GRE) && (t->family != AF_INET6 || in_addr_is_null(t->family, &t->local))) { log_netdev_error(netdev, - "vti6 tunnel without a local IPv4 address configured in %s. Ignoring", filename); + "vti6/ip6tnl/ip6gre tunnel without a local IPv6 address configured in %s. Ignoring", filename); return -EINVAL; } diff --git a/src/network/netdev/vxlan.c b/src/network/netdev/vxlan.c index 231f5cb442..b677b000fd 100644 --- a/src/network/netdev/vxlan.c +++ b/src/network/netdev/vxlan.c @@ -24,6 +24,8 @@ #include "conf-parser.h" #include "alloc-util.h" #include "extract-word.h" +#include "string-util.h" +#include "strv.h" #include "parse-util.h" #include "missing.h" @@ -48,9 +50,29 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_ID attribute: %m"); } - r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_GROUP, &v->group.in); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_GROUP attribute: %m"); + if (!in_addr_is_null(v->remote_family, &v->remote)) { + + if (v->remote_family == AF_INET) + r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_GROUP, &v->remote.in); + else + r = sd_netlink_message_append_in6_addr(m, IFLA_VXLAN_GROUP6, &v->remote.in6); + + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_GROUP attribute: %m"); + + } + + if (!in_addr_is_null(v->local_family, &v->local)) { + + if (v->local_family == AF_INET) + r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_LOCAL, &v->local.in); + else + r = sd_netlink_message_append_in6_addr(m, IFLA_VXLAN_LOCAL6, &v->local.in6); + + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LOCAL attribute: %m"); + + } r = sd_netlink_message_append_u32(m, IFLA_VXLAN_LINK, link->ifindex); if (r < 0) @@ -144,16 +166,16 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli return r; } -int config_parse_vxlan_group_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_vxlan_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) { VxLan *v = userdata; union in_addr_union *addr = data, buffer; int r, f; @@ -165,16 +187,28 @@ int config_parse_vxlan_group_address(const char *unit, r = in_addr_from_string_auto(rvalue, &f, &buffer); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "vxlan multicast group address is invalid, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "vxlan '%s' address is invalid, ignoring assignment: %s", lvalue, rvalue); return 0; } - if (v->family != AF_UNSPEC && v->family != f) { - log_syntax(unit, LOG_ERR, filename, line, 0, "vxlan multicast group incompatible, ignoring assignment: %s", rvalue); - return 0; + r = in_addr_is_multicast(f, &buffer); + + if (STR_IN_SET(lvalue, "Group", "Remote")) { + if (r <= 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "vxlan invalid multicast '%s' address, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + v->remote_family = f; + } else { + if (r > 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "vxlan %s can not be multicast address, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + v->local_family = f; } - v->family = f; *addr = buffer; return 0; diff --git a/src/network/netdev/vxlan.h b/src/network/netdev/vxlan.h index 6c3081d5fc..dca58e7fe6 100644 --- a/src/network/netdev/vxlan.h +++ b/src/network/netdev/vxlan.h @@ -31,8 +31,11 @@ struct VxLan { uint64_t id; - int family; - union in_addr_union group; + int remote_family; + int local_family; + + union in_addr_union remote; + union in_addr_union local; unsigned tos; unsigned ttl; @@ -60,16 +63,16 @@ struct VxLan { DEFINE_NETDEV_CAST(VXLAN, VxLan); extern const NetDevVTable vxlan_vtable; -int config_parse_vxlan_group_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_vxlan_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_port_range(const char *unit, const char *filename, unsigned line, diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index ffd2e18a45..2e6c763aba 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -53,15 +53,21 @@ int address_new(Address **ret) { return 0; } -int address_new_static(Network *network, unsigned section, Address **ret) { +int address_new_static(Network *network, const char *filename, unsigned section_line, Address **ret) { + _cleanup_network_config_section_free_ NetworkConfigSection *n = NULL; _cleanup_address_free_ Address *address = NULL; int r; assert(network); assert(ret); + assert(!!filename == (section_line > 0)); - if (section) { - address = hashmap_get(network->addresses_by_section, UINT_TO_PTR(section)); + if (filename) { + r = network_config_section_new(filename, section_line, &n); + if (r < 0) + return r; + + address = hashmap_get(network->addresses_by_section, n); if (address) { *ret = address; address = NULL; @@ -77,9 +83,13 @@ int address_new_static(Network *network, unsigned section, Address **ret) { if (r < 0) return r; - if (section) { - address->section = section; - hashmap_put(network->addresses_by_section, UINT_TO_PTR(address->section), address); + if (filename) { + address->section = n; + n = NULL; + + r = hashmap_put(network->addresses_by_section, address->section, address); + if (r < 0) + return r; } address->network = network; @@ -101,8 +111,10 @@ void address_free(Address *address) { assert(address->network->n_static_addresses > 0); address->network->n_static_addresses--; - if (address->section) - hashmap_remove(address->network->addresses_by_section, UINT_TO_PTR(address->section)); + if (address->section) { + hashmap_remove(address->network->addresses_by_section, address->section); + network_config_section_free(address->section); + } } if (address->link) { @@ -676,7 +688,7 @@ int config_parse_broadcast( assert(rvalue); assert(data); - r = address_new_static(network, section_line, &n); + r = address_new_static(network, filename, section_line, &n); if (r < 0) return r; @@ -723,10 +735,10 @@ int config_parse_address(const char *unit, if (streq(section, "Network")) { /* we are not in an Address section, so treat * this as the special '0' section */ - section_line = 0; - } + r = address_new_static(network, NULL, 0, &n); + } else + r = address_new_static(network, filename, section_line, &n); - r = address_new_static(network, section_line, &n); if (r < 0) return r; @@ -805,12 +817,12 @@ int config_parse_label( assert(rvalue); assert(data); - r = address_new_static(network, section_line, &n); + r = address_new_static(network, filename, section_line, &n); if (r < 0) return r; - if (strlen(rvalue) >= IFNAMSIZ) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is too long, ignoring assignment: %s", rvalue); + if (!address_label_valid(rvalue)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is too long or invalid, ignoring assignment: %s", rvalue); return 0; } @@ -844,7 +856,7 @@ int config_parse_lifetime(const char *unit, assert(rvalue); assert(data); - r = address_new_static(network, section_line, &n); + r = address_new_static(network, filename, section_line, &n); if (r < 0) return r; @@ -891,7 +903,7 @@ int config_parse_address_flags(const char *unit, assert(rvalue); assert(data); - r = address_new_static(network, section_line, &n); + r = address_new_static(network, filename, section_line, &n); if (r < 0) return r; diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index bc3b4fc7f3..71a07ea7a3 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -33,10 +33,11 @@ typedef struct Address Address; typedef struct Network Network; typedef struct Link Link; +typedef struct NetworkConfigSection NetworkConfigSection; struct Address { Network *network; - unsigned section; + NetworkConfigSection *section; Link *link; @@ -62,7 +63,7 @@ struct Address { LIST_FIELDS(Address, addresses); }; -int address_new_static(Network *network, unsigned section, Address **ret); +int address_new_static(Network *network, const char *filename, unsigned section, Address **ret); int address_new(Address **ret); void address_free(Address *address); int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret); diff --git a/src/network/networkd-ipv6-proxy-ndp.c b/src/network/networkd-ipv6-proxy-ndp.c new file mode 100644 index 0000000000..11c1cd9268 --- /dev/null +++ b/src/network/networkd-ipv6-proxy-ndp.c @@ -0,0 +1,209 @@ +/*** + This file is part of systemd. + + Copyright 2017 Florian Klink <flokli@flokli.de> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <netinet/ether.h> +#include <linux/if.h> +#include <unistd.h> + +#include "fileio.h" +#include "netlink-util.h" +#include "networkd-ipv6-proxy-ndp.h" +#include "networkd-link.h" +#include "networkd-manager.h" +#include "networkd-network.h" +#include "string-util.h" + +static bool ipv6_proxy_ndp_is_needed(Link *link) { + assert(link); + + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + return false; + + if (link->network->n_ipv6_proxy_ndp_addresses == 0) + return false; + + return true; +} + +static int ipv6_proxy_ndp_set(Link *link) { + const char *p = NULL; + int r, v; + + assert(link); + + v = ipv6_proxy_ndp_is_needed(link); + p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/proxy_ndp"); + + r = write_string_file(p, one_zero(v), WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) + log_link_warning_errno(link, r, "Cannot configure proxy NDP for interface: %m"); + + return 0; +} + +int ipv6_proxy_ndp_address_new_static(Network *network, IPv6ProxyNDPAddress **ret) { + _cleanup_(ipv6_proxy_ndp_address_freep) IPv6ProxyNDPAddress *ipv6_proxy_ndp_address = NULL; + + assert(network); + assert(ret); + + /* allocate space for IPv6ProxyNDPAddress entry */ + ipv6_proxy_ndp_address = new0(IPv6ProxyNDPAddress, 1); + if (!ipv6_proxy_ndp_address) + return -ENOMEM; + + ipv6_proxy_ndp_address->network = network; + + LIST_PREPEND(ipv6_proxy_ndp_addresses, network->ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address); + network->n_ipv6_proxy_ndp_addresses++; + + *ret = ipv6_proxy_ndp_address; + ipv6_proxy_ndp_address = NULL; + + return 0; +} + +void ipv6_proxy_ndp_address_free(IPv6ProxyNDPAddress *ipv6_proxy_ndp_address) { + if (!ipv6_proxy_ndp_address) + return; + + if (ipv6_proxy_ndp_address->network) { + LIST_REMOVE(ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address->network->ipv6_proxy_ndp_addresses, + ipv6_proxy_ndp_address); + + assert(ipv6_proxy_ndp_address->network->n_ipv6_proxy_ndp_addresses > 0); + ipv6_proxy_ndp_address->network->n_ipv6_proxy_ndp_addresses--; + } + + free(ipv6_proxy_ndp_address); +} + +int config_parse_ipv6_proxy_ndp_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) { + + Network *network = userdata; + _cleanup_(ipv6_proxy_ndp_address_freep) IPv6ProxyNDPAddress *ipv6_proxy_ndp_address = NULL; + int r; + union in_addr_union buffer; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = ipv6_proxy_ndp_address_new_static(network, &ipv6_proxy_ndp_address); + if (r < 0) + return r; + + r = in_addr_from_string(AF_INET6, rvalue, &buffer); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IPv6 proxy NDP address, ignoring: %s", + rvalue); + return 0; + } + + r = in_addr_is_null(AF_INET6, &buffer); + if (r != 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "IPv6 proxy NDP address can not be the ANY address, ignoring: %s", rvalue); + return 0; + } + + ipv6_proxy_ndp_address->in_addr = buffer.in6; + ipv6_proxy_ndp_address = NULL; + + return 0; +} + +static int set_ipv6_proxy_ndp_address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + Link *link = userdata; + int r; + + assert(link); + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) + log_link_error_errno(link, r, "Could not add IPv6 proxy ndp address entry: %m"); + + return 1; +} + +/* send a request to the kernel to add a IPv6 Proxy entry to the neighbour table */ +int ipv6_proxy_ndp_address_configure(Link *link, IPv6ProxyNDPAddress *ipv6_proxy_ndp_address) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + sd_netlink *rtnl; + int r; + + assert(link); + assert(link->network); + assert(link->manager); + assert(ipv6_proxy_ndp_address); + + rtnl = link->manager->rtnl; + + /* create new netlink message */ + r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_NEWNEIGH, link->ifindex, AF_INET6); + if (r < 0) + return rtnl_log_create_error(r); + + r = sd_rtnl_message_neigh_set_flags(req, NLM_F_REQUEST | NTF_PROXY); + if (r < 0) + return rtnl_log_create_error(r); + + r = sd_netlink_message_append_in6_addr(req, NDA_DST, &ipv6_proxy_ndp_address->in_addr); + if (r < 0) + return rtnl_log_create_error(r); + + r = sd_netlink_call_async(rtnl, req, set_ipv6_proxy_ndp_address_handler, link, 0, NULL); + if (r < 0) + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + + return 0; +} + +/* configure all ipv6 proxy ndp addresses */ +int ipv6_proxy_ndp_addresses_configure(Link *link) { + IPv6ProxyNDPAddress *ipv6_proxy_ndp_address; + int r; + + /* enable or disable proxy_ndp itself depending on whether ipv6_proxy_ndp_addresses are set or not */ + r = ipv6_proxy_ndp_set(link); + if (r != 0) + return r; + + LIST_FOREACH(ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address, link->network->ipv6_proxy_ndp_addresses) { + r = ipv6_proxy_ndp_address_configure(link, ipv6_proxy_ndp_address); + if (r != 0) + return r; + } + return 0; +} diff --git a/src/network/networkd-ipv6-proxy-ndp.h b/src/network/networkd-ipv6-proxy-ndp.h new file mode 100644 index 0000000000..f09169f40f --- /dev/null +++ b/src/network/networkd-ipv6-proxy-ndp.h @@ -0,0 +1,44 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2017 Florian Klink <flokli@flokli.de> + + 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 "list.h" +#include "macro.h" + +typedef struct Network Network; +typedef struct IPv6ProxyNDPAddress IPv6ProxyNDPAddress; +typedef struct Link Link; + +struct IPv6ProxyNDPAddress { + Network *network; + struct in6_addr in_addr; + + LIST_FIELDS(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses); +}; + + +int ipv6_proxy_ndp_address_new_static(Network *network, IPv6ProxyNDPAddress ** ipv6_proxy_ndp_address); +void ipv6_proxy_ndp_address_free(IPv6ProxyNDPAddress *ipv6_proxy_ndp_address); +int ipv6_proxy_ndp_address_configure(Link *link, IPv6ProxyNDPAddress *ipv6_proxy_ndp_address); +int ipv6_proxy_ndp_addresses_configure(Link *link); + +DEFINE_TRIVIAL_CLEANUP_FUNC(IPv6ProxyNDPAddress*, ipv6_proxy_ndp_address_free); + +int config_parse_ipv6_proxy_ndp_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); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index b993d27c2f..0c1229336b 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -28,6 +28,7 @@ #include "fileio.h" #include "netlink-util.h" #include "network-internal.h" +#include "networkd-ipv6-proxy-ndp.h" #include "networkd-lldp-tx.h" #include "networkd-manager.h" #include "networkd-ndisc.h" @@ -2448,6 +2449,10 @@ static int link_configure(Link *link) { if (r < 0) return r; + r = ipv6_proxy_ndp_addresses_configure(link); + if (r < 0) + return r; + r = link_set_ipv4_forward(link); if (r < 0) return r; diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 7b54e81fb8..68052ba544 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -67,6 +67,7 @@ Network.ActiveSlave, config_parse_bool, Network.PrimarySlave, config_parse_bool, 0, offsetof(Network, primary_slave) Network.IPv4ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp) Network.ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp) +Network.IPv6ProxyNDPAddress, config_parse_ipv6_proxy_ndp_address, 0, 0 Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier) Address.Address, config_parse_address, 0, 0 Address.Peer, config_parse_address, 0, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index bc4dc95ff9..ab372568de 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -36,6 +36,49 @@ #include "string-util.h" #include "util.h" +static void network_config_hash_func(const void *p, struct siphash *state) { + const NetworkConfigSection *c = p; + + siphash24_compress(c->filename, strlen(c->filename), state); + siphash24_compress(&c->line, sizeof(c->line), state); +} + +static int network_config_compare_func(const void *a, const void *b) { + const NetworkConfigSection *x = a, *y = b; + int r; + + r = strcmp(x->filename, y->filename); + if (r != 0) + return r; + + return y->line - x->line; +} + +const struct hash_ops network_config_hash_ops = { + .hash = network_config_hash_func, + .compare = network_config_compare_func, +}; + +int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s) { + NetworkConfigSection *cs; + + cs = malloc0(offsetof(NetworkConfigSection, filename) + strlen(filename) + 1); + if (!cs) + return -ENOMEM; + + strcpy(cs->filename, filename); + cs->line = line; + + *s = cs; + cs = NULL; + + return 0; +} + +void network_config_section_free(NetworkConfigSection *cs) { + free(cs); +} + static int network_load_one(Manager *manager, const char *filename) { _cleanup_network_free_ Network *network = NULL; _cleanup_fclose_ FILE *file = NULL; @@ -70,16 +113,17 @@ static int network_load_one(Manager *manager, const char *filename) { LIST_HEAD_INIT(network->static_addresses); LIST_HEAD_INIT(network->static_routes); LIST_HEAD_INIT(network->static_fdb_entries); + LIST_HEAD_INIT(network->ipv6_proxy_ndp_addresses); network->stacked_netdevs = hashmap_new(&string_hash_ops); if (!network->stacked_netdevs) return log_oom(); - network->addresses_by_section = hashmap_new(NULL); + network->addresses_by_section = hashmap_new(&network_config_hash_ops); if (!network->addresses_by_section) return log_oom(); - network->routes_by_section = hashmap_new(NULL); + network->routes_by_section = hashmap_new(&network_config_hash_ops); if (!network->routes_by_section) return log_oom(); @@ -152,6 +196,7 @@ static int network_load_one(Manager *manager, const char *filename) { "DHCPv4\0" /* compat */ "DHCPServer\0" "IPv6AcceptRA\0" + "IPv6NDPProxyAddress\0" "Bridge\0" "BridgeFDB\0" "BridgeVLAN\0", @@ -224,6 +269,7 @@ void network_free(Network *network) { Route *route; Address *address; FdbEntry *fdb_entry; + IPv6ProxyNDPAddress *ipv6_proxy_ndp_address; Iterator i; if (!network) @@ -268,6 +314,9 @@ void network_free(Network *network) { while ((fdb_entry = network->static_fdb_entries)) fdb_entry_free(fdb_entry); + while ((ipv6_proxy_ndp_address = network->ipv6_proxy_ndp_addresses)) + ipv6_proxy_ndp_address_free(ipv6_proxy_ndp_address); + hashmap_free(network->addresses_by_section); hashmap_free(network->routes_by_section); hashmap_free(network->fdb_entries_by_section); @@ -379,7 +428,7 @@ int network_apply(Network *network, Link *link) { if (network->ipv4ll_route) { Route *route; - r = route_new_static(network, 0, &route); + r = route_new_static(network, "Network", 0, &route); if (r < 0) return r; diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index b7da9d22d4..4ce066a764 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -31,6 +31,7 @@ #include "networkd-brvlan.h" #include "networkd-fdb.h" #include "networkd-lldp-tx.h" +#include "networkd-ipv6-proxy-ndp.h" #include "networkd-route.h" #include "networkd-util.h" #include "netdev/netdev.h" @@ -81,6 +82,17 @@ typedef struct DUID { uint8_t raw_data[MAX_DUID_LEN]; } DUID; +typedef struct NetworkConfigSection { + unsigned line; + char filename[]; +} NetworkConfigSection; + +int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s); +void network_config_section_free(NetworkConfigSection *network); + +DEFINE_TRIVIAL_CLEANUP_FUNC(NetworkConfigSection*, network_config_section_free); +#define _cleanup_network_config_section_free_ _cleanup_(network_config_section_freep) + typedef struct Manager Manager; struct Network { @@ -188,10 +200,12 @@ struct Network { LIST_HEAD(Address, static_addresses); LIST_HEAD(Route, static_routes); LIST_HEAD(FdbEntry, static_fdb_entries); + LIST_HEAD(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses); unsigned n_static_addresses; unsigned n_static_routes; unsigned n_static_fdb_entries; + unsigned n_ipv6_proxy_ndp_addresses; Hashmap *addresses_by_section; Hashmap *routes_by_section; diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index bde26a42d4..570083f180 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -77,15 +77,21 @@ int route_new(Route **ret) { return 0; } -int route_new_static(Network *network, unsigned section, Route **ret) { +int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) { + _cleanup_network_config_section_free_ NetworkConfigSection *n = NULL; _cleanup_route_free_ Route *route = NULL; int r; assert(network); assert(ret); + assert(!!filename == (section_line > 0)); - if (section) { - route = hashmap_get(network->routes_by_section, UINT_TO_PTR(section)); + if (filename) { + r = network_config_section_new(filename, section_line, &n); + if (r < 0) + return r; + + route = hashmap_get(network->routes_by_section, n); if (route) { *ret = route; route = NULL; @@ -103,10 +109,11 @@ int route_new_static(Network *network, unsigned section, Route **ret) { route->protocol = RTPROT_STATIC; - if (section) { - route->section = section; + if (filename) { + route->section = n; + n = NULL; - r = hashmap_put(network->routes_by_section, UINT_TO_PTR(route->section), route); + r = hashmap_put(network->routes_by_section, route->section, route); if (r < 0) return r; } @@ -132,9 +139,11 @@ void route_free(Route *route) { route->network->n_static_routes--; if (route->section) - hashmap_remove(route->network->routes_by_section, UINT_TO_PTR(route->section)); + hashmap_remove(route->network->routes_by_section, route->section); } + network_config_section_free(route->section); + if (route->link) { set_remove(route->link->routes, route); set_remove(route->link->routes_foreign, route); @@ -673,10 +682,10 @@ int config_parse_gateway(const char *unit, if (streq(section, "Network")) { /* we are not in an Route section, so treat * this as the special '0' section */ - section_line = 0; - } + r = route_new_static(network, NULL, 0, &n); + } else + r = route_new_static(network, filename, section_line, &n); - r = route_new_static(network, section_line, &n); if (r < 0) return r; @@ -715,7 +724,7 @@ int config_parse_preferred_src(const char *unit, assert(rvalue); assert(data); - r = route_new_static(network, section_line, &n); + r = route_new_static(network, filename, section_line, &n); if (r < 0) return r; @@ -757,7 +766,7 @@ int config_parse_destination(const char *unit, assert(rvalue); assert(data); - r = route_new_static(network, section_line, &n); + r = route_new_static(network, filename, section_line, &n); if (r < 0) return r; @@ -835,7 +844,7 @@ int config_parse_route_priority(const char *unit, assert(rvalue); assert(data); - r = route_new_static(network, section_line, &n); + r = route_new_static(network, filename, section_line, &n); if (r < 0) return r; @@ -872,7 +881,7 @@ int config_parse_route_scope(const char *unit, assert(rvalue); assert(data); - r = route_new_static(network, section_line, &n); + r = route_new_static(network, filename, section_line, &n); if (r < 0) return r; @@ -913,7 +922,7 @@ int config_parse_route_table(const char *unit, assert(rvalue); assert(data); - r = route_new_static(network, section_line, &n); + r = route_new_static(network, filename, section_line, &n); if (r < 0) return r; diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 02f0b27675..4ebfa0f0bd 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -20,12 +20,13 @@ ***/ typedef struct Route Route; +typedef struct NetworkConfigSection NetworkConfigSection; #include "networkd-network.h" struct Route { Network *network; - unsigned section; + NetworkConfigSection *section; Link *link; @@ -52,7 +53,7 @@ struct Route { LIST_FIELDS(Route, routes); }; -int route_new_static(Network *network, unsigned section, Route **ret); +int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret); int route_new(Route **ret); void route_free(Route *route); int route_configure(Route *route, Link *link, sd_netlink_message_handler_t callback); diff --git a/src/network/wait-online/Makefile b/src/network/wait-online/Makefile new file mode 120000 index 0000000000..94aaae2c4d --- /dev/null +++ b/src/network/wait-online/Makefile @@ -0,0 +1 @@ +../../Makefile
\ No newline at end of file diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c index 5274767b96..d749756437 100644 --- a/src/nspawn/nspawn-cgroup.c +++ b/src/nspawn/nspawn-cgroup.c @@ -78,13 +78,12 @@ int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t arg_uid_shift) char tree[] = "/tmp/unifiedXXXXXX", pid_string[DECIMAL_STR_MAX(pid) + 1]; bool undo_mount = false; const char *fn; - int unified, r; + int r, unified_controller; - unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER); - if (unified < 0) - return log_error_errno(unified, "Failed to determine whether the unified hierarchy is used: %m"); - - if ((unified > 0) == (unified_requested >= CGROUP_UNIFIED_SYSTEMD)) + unified_controller = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER); + if (unified_controller < 0) + return log_error_errno(unified_controller, "Failed to determine whether the systemd hierarchy is unified: %m"); + if ((unified_controller > 0) == (unified_requested >= CGROUP_UNIFIED_SYSTEMD)) return 0; /* When the host uses the legacy cgroup setup, but the @@ -100,7 +99,7 @@ int sync_cgroup(pid_t pid, CGroupUnified unified_requested, uid_t arg_uid_shift) if (!mkdtemp(tree)) return log_error_errno(errno, "Failed to generate temporary mount point for unified hierarchy: %m"); - if (unified) + if (unified_controller > 0) r = mount_verbose(LOG_ERR, "cgroup", tree, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, "none,name=systemd,xattr"); else @@ -142,7 +141,7 @@ finish: int create_subcgroup(pid_t pid, CGroupUnified unified_requested) { _cleanup_free_ char *cgroup = NULL; const char *child; - int unified, r; + int r; CGroupMask supported; /* In the unified hierarchy inner nodes may only contain @@ -154,10 +153,10 @@ int create_subcgroup(pid_t pid, CGroupUnified unified_requested) { if (unified_requested == CGROUP_UNIFIED_NONE) return 0; - unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER); - if (unified < 0) - return log_error_errno(unified, "Failed to determine whether the unified hierarchy is used: %m"); - if (unified == 0) + r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER); + if (r < 0) + return log_error_errno(r, "Failed to determine whether the systemd controller is unified: %m"); + if (r == 0) return 0; r = cg_mask_supported(&supported); diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 4b2838b752..d276994120 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -545,10 +545,10 @@ int mount_all(const char *dest, static const MountPoint mount_table[] = { /* inner child mounts */ { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, MOUNT_FATAL|MOUNT_IN_USERNS }, - { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, MOUNT_FATAL|MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, /* Bind mount first ...*/ + { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, MOUNT_FATAL|MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, /* Bind mount first ... */ { "/proc/sys/net", "/proc/sys/net", NULL, NULL, MS_BIND, MOUNT_FATAL|MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO|MOUNT_APPLY_APIVFS_NETNS }, /* (except for this) */ { NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, MOUNT_FATAL|MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, /* ... then, make it r/o */ - { "/proc/sysrq-trigger", "/proc/sysrq-trigger", NULL, NULL, MS_BIND, MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, /* Bind mount first ...*/ + { "/proc/sysrq-trigger", "/proc/sysrq-trigger", NULL, NULL, MS_BIND, MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, /* Bind mount first ... */ { NULL, "/proc/sysrq-trigger", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, /* ... then, make it r/o */ /* outer child mounts */ @@ -890,7 +890,7 @@ static int get_controllers(Set *subsystems) { *e = 0; - if (STR_IN_SET(l, "", "name=systemd")) + if (STR_IN_SET(l, "", "name=systemd", "name=unified")) continue; p = strdup(l); @@ -909,7 +909,6 @@ static int mount_legacy_cgroup_hierarchy( const char *dest, const char *controller, const char *hierarchy, - CGroupUnified unified_requested, bool read_only) { const char *to, *fstype, *opts; @@ -927,14 +926,12 @@ static int mount_legacy_cgroup_hierarchy( /* The superblock mount options of the mount point need to be * identical to the hosts', and hence writable... */ - if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { - if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) { - fstype = "cgroup2"; - opts = NULL; - } else { - fstype = "cgroup"; - opts = "none,name=systemd,xattr"; - } + if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_HYBRID)) { + fstype = "cgroup2"; + opts = NULL; + } else if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_LEGACY)) { + fstype = "cgroup"; + opts = "none,name=systemd,xattr"; } else { fstype = "cgroup"; opts = controller; @@ -994,7 +991,10 @@ static int mount_legacy_cgns_supported( return r; } - if (cg_all_unified() > 0) + r = cg_all_unified(); + if (r < 0) + return r; + if (r > 0) goto skip_controllers; controllers = set_new(&string_hash_ops); @@ -1012,7 +1012,7 @@ static int mount_legacy_cgns_supported( if (!controller) break; - r = mount_legacy_cgroup_hierarchy("", controller, controller, unified_requested, !userns); + r = mount_legacy_cgroup_hierarchy("", controller, controller, !userns); if (r < 0) return r; @@ -1046,7 +1046,13 @@ static int mount_legacy_cgns_supported( } skip_controllers: - r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER, "systemd", unified_requested, false); + if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) { + r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false); + if (r < 0) + return r; + } + + r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false); if (r < 0) return r; @@ -1091,7 +1097,10 @@ static int mount_legacy_cgns_unsupported( return r; } - if (cg_all_unified() > 0) + r = cg_all_unified(); + if (r < 0) + return r; + if (r > 0) goto skip_controllers; controllers = set_new(&string_hash_ops); @@ -1117,7 +1126,7 @@ static int mount_legacy_cgns_unsupported( if (r == -EINVAL) { /* Not a symbolic link, but directly a single cgroup hierarchy */ - r = mount_legacy_cgroup_hierarchy(dest, controller, controller, unified_requested, true); + r = mount_legacy_cgroup_hierarchy(dest, controller, controller, true); if (r < 0) return r; @@ -1137,7 +1146,7 @@ static int mount_legacy_cgns_unsupported( continue; } - r = mount_legacy_cgroup_hierarchy(dest, combined, combined, unified_requested, true); + r = mount_legacy_cgroup_hierarchy(dest, combined, combined, true); if (r < 0) return r; @@ -1150,7 +1159,13 @@ static int mount_legacy_cgns_unsupported( } skip_controllers: - r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER, "systemd", unified_requested, false); + if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) { + r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false); + if (r < 0) + return r; + } + + r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false); if (r < 0) return r; @@ -1202,12 +1217,25 @@ int mount_cgroups( return mount_legacy_cgns_unsupported(dest, unified_requested, userns, uid_shift, uid_range, selinux_apifs_context); } +static int mount_systemd_cgroup_writable_one(const char *systemd_own, const char *systemd_root) +{ + int r; + + /* Make our own cgroup a (writable) bind mount */ + r = mount_verbose(LOG_ERR, systemd_own, systemd_own, NULL, MS_BIND, NULL); + if (r < 0) + return r; + + /* And then remount the systemd cgroup root read-only */ + return mount_verbose(LOG_ERR, NULL, systemd_root, NULL, + MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL); +} + int mount_systemd_cgroup_writable( const char *dest, CGroupUnified unified_requested) { _cleanup_free_ char *own_cgroup_path = NULL; - const char *systemd_root, *systemd_own; int r; assert(dest); @@ -1220,22 +1248,19 @@ int mount_systemd_cgroup_writable( if (path_equal(own_cgroup_path, "/")) return 0; - if (unified_requested >= CGROUP_UNIFIED_ALL) { - systemd_own = strjoina(dest, "/sys/fs/cgroup", own_cgroup_path); - systemd_root = prefix_roota(dest, "/sys/fs/cgroup"); - } else { - systemd_own = strjoina(dest, "/sys/fs/cgroup/systemd", own_cgroup_path); - systemd_root = prefix_roota(dest, "/sys/fs/cgroup/systemd"); - } + if (unified_requested >= CGROUP_UNIFIED_ALL) + return mount_systemd_cgroup_writable_one(strjoina(dest, "/sys/fs/cgroup", own_cgroup_path), + prefix_roota(dest, "/sys/fs/cgroup")); - /* Make our own cgroup a (writable) bind mount */ - r = mount_verbose(LOG_ERR, systemd_own, systemd_own, NULL, MS_BIND, NULL); - if (r < 0) - return r; + if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) { + r = mount_systemd_cgroup_writable_one(strjoina(dest, "/sys/fs/cgroup/unified", own_cgroup_path), + prefix_roota(dest, "/sys/fs/cgroup/unified")); + if (r < 0) + return r; + } - /* And then remount the systemd cgroup root read-only */ - return mount_verbose(LOG_ERR, NULL, systemd_root, NULL, - MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL); + return mount_systemd_cgroup_writable_one(strjoina(dest, "/sys/fs/cgroup/systemd", own_cgroup_path), + prefix_roota(dest, "/sys/fs/cgroup/systemd")); } int setup_volatile_state( diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index b172b44933..1fc0501c2e 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -41,6 +41,7 @@ #include <sys/wait.h> #include <unistd.h> +#include "sd-bus.h" #include "sd-daemon.h" #include "sd-id128.h" @@ -49,6 +50,7 @@ #include "base-filesystem.h" #include "blkid-util.h" #include "btrfs-util.h" +#include "bus-util.h" #include "cap-list.h" #include "capability-util.h" #include "cgroup-util.h" @@ -314,7 +316,7 @@ static int custom_mount_check_all(void) { static int detect_unified_cgroup_hierarchy(const char *directory) { const char *e; - int r, all_unified, systemd_unified; + int r; /* Allow the user to control whether the unified hierarchy is used */ e = getenv("UNIFIED_CGROUP_HIERARCHY"); @@ -330,15 +332,11 @@ static int detect_unified_cgroup_hierarchy(const char *directory) { return 0; } - all_unified = cg_all_unified(); - systemd_unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER); - - if (all_unified < 0 || systemd_unified < 0) - return log_error_errno(all_unified < 0 ? all_unified : systemd_unified, - "Failed to determine whether the unified cgroups hierarchy is used: %m"); - /* Otherwise inherit the default from the host system */ - if (all_unified > 0) { + r = cg_all_unified(); + if (r < 0) + return log_error_errno(r, "Failed to determine whether we are in all unified mode."); + if (r > 0) { /* Unified cgroup hierarchy support was added in 230. Unfortunately the detection * routine only detects 231, so we'll have a false negative here for 230. */ r = systemd_installation_has_version(directory, 230); @@ -348,9 +346,9 @@ static int detect_unified_cgroup_hierarchy(const char *directory) { arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_ALL; else arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_NONE; - } else if (systemd_unified > 0) { - /* Mixed cgroup hierarchy support was added in 232 */ - r = systemd_installation_has_version(directory, 232); + } else if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0) { + /* Mixed cgroup hierarchy support was added in 233 */ + r = systemd_installation_has_version(directory, 233); if (r < 0) return log_error_errno(r, "Failed to determine systemd version in container: %m"); if (r > 0) @@ -1323,6 +1321,19 @@ static int setup_timezone(const char *dest) { return 0; } +static int resolved_running(void) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int r; + + /* Check if resolved is running */ + + r = sd_bus_open_system(&bus); + if (r < 0) + return r; + + return bus_name_has_owner(bus, "org.freedesktop.resolve1", NULL); +} + static int setup_resolv_conf(const char *dest) { _cleanup_free_ char *resolved = NULL, *etc = NULL; const char *where; @@ -1346,8 +1357,8 @@ static int setup_resolv_conf(const char *dest) { return 0; } - if (access("/run/systemd/resolve/resolv.conf", F_OK) >= 0 && - access("/usr/lib/systemd/resolv.conf", F_OK) >= 0) { + if (access("/usr/lib/systemd/resolv.conf", F_OK) >= 0 && + resolved_running() > 0) { /* resolved is enabled on the host. In this, case bind mount its static resolv.conf file into the * container, so that the container can use the host's resolver. Given that network namespacing is @@ -1364,7 +1375,7 @@ static int setup_resolv_conf(const char *dest) { } /* If that didn't work, let's copy the file */ - r = copy_file("/etc/resolv.conf", where, O_TRUNC|O_NOFOLLOW, 0644, 0); + r = copy_file("/etc/resolv.conf", where, O_TRUNC|O_NOFOLLOW, 0644, 0, COPY_REFLINK); if (r < 0) { /* If the file already exists as symlink, let's suppress the warning, under the assumption that * resolved or something similar runs inside and the symlink points there. @@ -2153,8 +2164,6 @@ static int inner_child( assert(directory); assert(kmsg_socket >= 0); - cg_unified_flush(); - if (arg_userns_mode != USER_NAMESPACE_NO) { /* Tell the parent, that it now can write the UID map. */ (void) barrier_place(barrier); /* #1 */ @@ -2425,8 +2434,6 @@ static int outer_child( assert(notify_socket >= 0); assert(kmsg_socket >= 0); - cg_unified_flush(); - if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0) return log_error_errno(errno, "PR_SET_PDEATHSIG failed: %m"); @@ -2471,10 +2478,6 @@ static int outer_child( if (r < 0) return r; - r = detect_unified_cgroup_hierarchy(directory); - if (r < 0) - return r; - if (arg_userns_mode != USER_NAMESPACE_NO) { /* Let the parent know which UID shift we read from the image */ l = send(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL); @@ -2539,7 +2542,7 @@ static int outer_child( * inside the containter that create a new mount namespace. * See https://github.com/systemd/systemd/issues/3860 * Further submounts (such as /dev) done after this will inherit the - * shared propagation mode.*/ + * shared propagation mode. */ r = mount_verbose(LOG_ERR, NULL, directory, NULL, MS_SHARED|MS_REC, NULL); if (r < 0) return r; @@ -3527,6 +3530,10 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); + r = cg_unified_flush(); + if (r < 0) + return log_error_errno(r, "Failed to determine whether the unified cgroups hierarchy is used: %m"); + /* Make sure rename_process() in the stub init process can work */ saved_argv = argv; saved_argc = argc; @@ -3700,7 +3707,7 @@ int main(int argc, char *argv[]) { goto finish; } - r = copy_file(arg_image, np, O_EXCL, arg_read_only ? 0400 : 0600, FS_NOCOW_FL); + r = copy_file(arg_image, np, O_EXCL, arg_read_only ? 0400 : 0600, FS_NOCOW_FL, COPY_REFLINK); if (r < 0) { r = log_error_errno(r, "Failed to copy image file: %m"); goto finish; @@ -3795,6 +3802,10 @@ int main(int argc, char *argv[]) { if (r < 0) goto finish; + r = detect_unified_cgroup_hierarchy(arg_directory); + if (r < 0) + goto finish; + interactive = isatty(STDIN_FILENO) > 0 && isatty(STDOUT_FILENO) > 0; @@ -3856,7 +3867,7 @@ finish: /* Try to flush whatever is still queued in the pty */ if (master >= 0) { - (void) copy_bytes(master, STDOUT_FILENO, (uint64_t) -1, false); + (void) copy_bytes(master, STDOUT_FILENO, (uint64_t) -1, 0); master = safe_close(master); } diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c index 11c27575c0..0570fde592 100644 --- a/src/nss-myhostname/nss-myhostname.c +++ b/src/nss-myhostname/nss-myhostname.c @@ -55,7 +55,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r( _cleanup_free_ struct local_address *addresses = NULL; _cleanup_free_ char *hn = NULL; const char *canonical = NULL; - int n_addresses = 0, lo_ifi; + int n_addresses = 0; uint32_t local_address_ipv4; struct local_address *a; size_t l, idx, ms; @@ -111,14 +111,11 @@ enum nss_status _nss_myhostname_gethostbyname4_r( local_address_ipv4 = LOCALADDRESS_IPV4; } - /* If this call fails we fill in 0 as scope. Which is fine */ - lo_ifi = n_addresses <= 0 ? LOOPBACK_IFINDEX : 0; - l = strlen(canonical); ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * (n_addresses > 0 ? n_addresses : 2); if (buflen < ms) { - *errnop = ENOMEM; - *h_errnop = NO_RECOVERY; + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; return NSS_STATUS_TRYAGAIN; } @@ -135,7 +132,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r( r_tuple->name = r_name; r_tuple->family = AF_INET6; memcpy(r_tuple->addr, LOCALADDRESS_IPV6, 16); - r_tuple->scopeid = (uint32_t) lo_ifi; + r_tuple->scopeid = 0; idx += ALIGN(sizeof(struct gaih_addrtuple)); r_tuple_prev = r_tuple; @@ -146,7 +143,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r( r_tuple->name = r_name; r_tuple->family = AF_INET; *(uint32_t*) r_tuple->addr = local_address_ipv4; - r_tuple->scopeid = (uint32_t) lo_ifi; + r_tuple->scopeid = 0; idx += ALIGN(sizeof(struct gaih_addrtuple)); r_tuple_prev = r_tuple; @@ -158,7 +155,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r( r_tuple->next = r_tuple_prev; r_tuple->name = r_name; r_tuple->family = a->family; - r_tuple->scopeid = a->ifindex; + r_tuple->scopeid = a->family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&a->address.in6) ? a->ifindex : 0; memcpy(r_tuple->addr, &a->address, 16); idx += ALIGN(sizeof(struct gaih_addrtuple)); @@ -223,8 +220,8 @@ static enum nss_status fill_in_hostent( (c > 0 ? c+1 : 2) * sizeof(char*); if (buflen < ms) { - *errnop = ENOMEM; - *h_errnop = NO_RECOVERY; + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; return NSS_STATUS_TRYAGAIN; } diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c index fac37faea5..ea90953abb 100644 --- a/src/nss-mymachines/nss-mymachines.c +++ b/src/nss-mymachines/nss-mymachines.c @@ -151,8 +151,8 @@ enum nss_status _nss_mymachines_gethostbyname4_r( l = strlen(name); ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c; if (buflen < ms) { - *errnop = ENOMEM; - *h_errnop = TRY_AGAIN; + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; return NSS_STATUS_TRYAGAIN; } @@ -306,8 +306,8 @@ enum nss_status _nss_mymachines_gethostbyname3_r( ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*); if (buflen < ms) { - *errnop = ENOMEM; - *h_errnop = NO_RECOVERY; + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; return NSS_STATUS_TRYAGAIN; } @@ -471,7 +471,7 @@ enum nss_status _nss_mymachines_getpwnam_r( l = strlen(name); if (buflen < l+1) { - *errnop = ENOMEM; + *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } @@ -550,7 +550,7 @@ enum nss_status _nss_mymachines_getpwuid_r( goto not_found; if (snprintf(buffer, buflen, "vu-%s-" UID_FMT, machine, (uid_t) mapped) >= (int) buflen) { - *errnop = ENOMEM; + *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } @@ -645,7 +645,7 @@ enum nss_status _nss_mymachines_getgrnam_r( l = sizeof(char*) + strlen(name) + 1; if (buflen < l) { - *errnop = ENOMEM; + *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } @@ -722,13 +722,13 @@ enum nss_status _nss_mymachines_getgrgid_r( goto not_found; if (buflen < sizeof(char*) + 1) { - *errnop = ENOMEM; + *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } memzero(buffer, sizeof(char*)); if (snprintf(buffer + sizeof(char*), buflen - sizeof(char*), "vg-%s-" GID_FMT, machine, (gid_t) mapped) >= (int) buflen) { - *errnop = ENOMEM; + *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c index d46a3afe91..d155625e11 100644 --- a/src/nss-resolve/nss-resolve.c +++ b/src/nss-resolve/nss-resolve.c @@ -110,6 +110,20 @@ static int count_addresses(sd_bus_message *m, int af, const char **canonical) { return c; } +static uint32_t ifindex_to_scopeid(int family, const void *a, int ifindex) { + struct in6_addr in6; + + if (family != AF_INET6) + return 0; + + /* Some apps can't deal with the scope ID attached to non-link-local addresses. Hence, let's suppress that. */ + + assert(sizeof(in6) == FAMILY_ADDRESS_SIZE(AF_INET6)); + memcpy(&in6, a, sizeof(struct in6_addr)); + + return IN6_IS_ADDR_LINKLOCAL(&in6) ? ifindex : 0; +} + enum nss_status _nss_resolve_gethostbyname4_r( const char *name, struct gaih_addrtuple **pat, @@ -192,8 +206,8 @@ enum nss_status _nss_resolve_gethostbyname4_r( l = strlen(canonical); ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c; if (buflen < ms) { - *errnop = ENOMEM; - *h_errnop = TRY_AGAIN; + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; return NSS_STATUS_TRYAGAIN; } @@ -245,7 +259,7 @@ enum nss_status _nss_resolve_gethostbyname4_r( r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple))); r_tuple->name = r_name; r_tuple->family = family; - r_tuple->scopeid = ifindex; + r_tuple->scopeid = ifindex_to_scopeid(family, a, ifindex); memcpy(r_tuple->addr, a, sz); idx += ALIGN(sizeof(struct gaih_addrtuple)); @@ -380,8 +394,8 @@ enum nss_status _nss_resolve_gethostbyname3_r( ms = ALIGN(l+1) + c * ALIGN(alen) + (c+2) * sizeof(char*); if (buflen < ms) { - *errnop = ENOMEM; - *h_errnop = TRY_AGAIN; + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; return NSS_STATUS_TRYAGAIN; } @@ -601,8 +615,8 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( c * sizeof(char*); /* pointers to aliases, plus trailing NULL */ if (buflen < ms) { - *errnop = ENOMEM; - *h_errnop = TRY_AGAIN; + *errnop = ERANGE; + *h_errnop = NETDB_INTERNAL; return NSS_STATUS_TRYAGAIN; } diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c index fd5064c937..f404755dac 100644 --- a/src/nss-systemd/nss-systemd.c +++ b/src/nss-systemd/nss-systemd.c @@ -185,7 +185,7 @@ enum nss_status _nss_systemd_getpwnam_r( l = strlen(name); if (buflen < l+1) { - *errnop = ENOMEM; + *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } @@ -286,7 +286,7 @@ enum nss_status _nss_systemd_getpwuid_r( l = strlen(translated) + 1; if (buflen < l) { - *errnop = ENOMEM; + *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } @@ -386,7 +386,7 @@ enum nss_status _nss_systemd_getgrnam_r( l = sizeof(char*) + strlen(name) + 1; if (buflen < l) { - *errnop = ENOMEM; + *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } @@ -484,7 +484,7 @@ enum nss_status _nss_systemd_getgrgid_r( l = sizeof(char*) + strlen(translated) + 1; if (buflen < l) { - *errnop = ENOMEM; + *errnop = ERANGE; return NSS_STATUS_TRYAGAIN; } diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c index 07d9582ccb..32537ce6e8 100644 --- a/src/resolve/resolve-tool.c +++ b/src/resolve/resolve-tool.c @@ -38,7 +38,7 @@ #include "strv.h" #include "terminal-util.h" -#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC) +#define DNS_CALL_TIMEOUT_USEC (90*USEC_PER_SEC) static int arg_family = AF_UNSPEC; static int arg_ifindex = 0; @@ -1186,6 +1186,7 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, bool *empt {} }; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_free_ char *ifi = NULL, *p = NULL; char ifname[IF_NAMESIZE] = ""; char **i; @@ -1213,9 +1214,10 @@ static int status_ifindex(sd_bus *bus, int ifindex, const char *name, bool *empt "org.freedesktop.resolve1", p, property_map, + &error, &link_info); if (r < 0) { - log_error_errno(r, "Failed to get link data for %i: %m", ifindex); + log_error_errno(r, "Failed to get link data for %i: %s", ifindex, bus_error_message(&error, r)); goto finish; } @@ -1405,6 +1407,7 @@ static int status_global(sd_bus *bus, bool *empty_line) { {} }; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; char **i; int r; @@ -1415,9 +1418,10 @@ static int status_global(sd_bus *bus, bool *empty_line) { "org.freedesktop.resolve1", "/org/freedesktop/resolve1", property_map, + &error, &global_info); if (r < 0) { - log_error_errno(r, "Failed to get global data: %m"); + log_error_errno(r, "Failed to get global data: %s", bus_error_message(&error, r)); goto finish; } @@ -1524,7 +1528,7 @@ static int status_all(sd_bus *bus) { static void help_protocol_types(void) { if (arg_legend) puts("Known protocol types:"); - puts("dns\nllmnr\nllmnr-ipv4\nllmnr-ipv6"); + puts("dns\nllmnr\nllmnr-ipv4\nllmnr-ipv6\nmdns\nmnds-ipv4\nmdns-ipv6"); } static void help_dns_types(void) { @@ -1722,6 +1726,12 @@ static int parse_argv(int argc, char *argv[]) { arg_flags |= SD_RESOLVED_LLMNR_IPV4; else if (streq(optarg, "llmnr-ipv6")) arg_flags |= SD_RESOLVED_LLMNR_IPV6; + else if (streq(optarg, "mdns")) + arg_flags |= SD_RESOLVED_MDNS; + else if (streq(optarg, "mdns-ipv4")) + arg_flags |= SD_RESOLVED_MDNS_IPV4; + else if (streq(optarg, "mdns-ipv6")) + arg_flags |= SD_RESOLVED_MDNS_IPV6; else { log_error("Unknown protocol specifier: %s", optarg); return -EINVAL; diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 2ca65e6953..2c50109388 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -211,7 +211,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { r = sd_bus_message_append( reply, "st", normalized, - SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated)); + SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, dns_query_fully_authenticated(q))); if (r < 0) goto finish; @@ -439,7 +439,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { if (r < 0) goto finish; - r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated)); + r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, dns_query_fully_authenticated(q))); if (r < 0) goto finish; @@ -605,7 +605,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { if (r < 0) goto finish; - r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated)); + r = sd_bus_message_append(reply, "t", SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, dns_query_fully_authenticated(q))); if (r < 0) goto finish; @@ -979,7 +979,7 @@ static void resolve_service_all_complete(DnsQuery *q) { reply, "ssst", name, type, domain, - SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated)); + SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, dns_query_fully_authenticated(q))); if (r < 0) goto finish; diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c index ab85754bf7..db86b4dcf6 100644 --- a/src/resolve/resolved-dns-answer.c +++ b/src/resolve/resolved-dns-answer.c @@ -148,7 +148,7 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFl * match. We don't really care if they match * precisely, but we do care whether one is 0 * and the other is not. See RFC 2181, Section - * 5.2.*/ + * 5.2. */ if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0)) return -EINVAL; diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h index 4a92bd1150..11d2e25eeb 100644 --- a/src/resolve/resolved-dns-answer.h +++ b/src/resolve/resolved-dns-answer.h @@ -33,9 +33,11 @@ typedef struct DnsAnswerItem DnsAnswerItem; * Note that we usually encode the empty DnsAnswer object as a simple NULL. */ typedef enum DnsAnswerFlags { - DNS_ANSWER_AUTHENTICATED = 1, /* Item has been authenticated */ - DNS_ANSWER_CACHEABLE = 2, /* Item is subject to caching */ - DNS_ANSWER_SHARED_OWNER = 4, /* For mDNS: RRset may be owner by multiple peers */ + DNS_ANSWER_AUTHENTICATED = 1, /* Item has been authenticated */ + DNS_ANSWER_CACHEABLE = 2, /* Item is subject to caching */ + DNS_ANSWER_SHARED_OWNER = 4, /* For mDNS: RRset may be owner by multiple peers */ + DNS_ANSWER_CACHE_FLUSH = 8, /* For mDNS: sets cache-flush bit in the rrclass of response records */ + DNS_ANSWER_GOODBYE = 16, /* For mDNS: item is subject to disappear */ } DnsAnswerFlags; struct DnsAnswerItem { diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 9233fb0ac1..f8dab01308 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -34,6 +34,10 @@ /* We never keep any item longer than 2h in our cache */ #define CACHE_TTL_MAX_USEC (2 * USEC_PER_HOUR) +/* How long to cache strange rcodes, i.e. rcodes != SUCCESS and != NXDOMAIN (specifically: that's only SERVFAIL for + * now) */ +#define CACHE_TTL_STRANGE_RCODE_USEC (30 * USEC_PER_SEC) + typedef enum DnsCacheItemType DnsCacheItemType; typedef struct DnsCacheItem DnsCacheItem; @@ -41,12 +45,14 @@ enum DnsCacheItemType { DNS_CACHE_POSITIVE, DNS_CACHE_NODATA, DNS_CACHE_NXDOMAIN, + DNS_CACHE_RCODE, /* "strange" RCODE (effective only SERVFAIL for now) */ }; struct DnsCacheItem { DnsCacheItemType type; DnsResourceKey *key; DnsResourceRecord *rr; + int rcode; usec_t until; bool authenticated:1; @@ -60,6 +66,27 @@ struct DnsCacheItem { LIST_FIELDS(DnsCacheItem, by_key); }; +static const char *dns_cache_item_type_to_string(DnsCacheItem *item) { + assert(item); + + switch (item->type) { + + case DNS_CACHE_POSITIVE: + return "POSITIVE"; + + case DNS_CACHE_NODATA: + return "NODATA"; + + case DNS_CACHE_NXDOMAIN: + return "NXDOMAIN"; + + case DNS_CACHE_RCODE: + return dns_rcode_to_string(item->rcode); + } + + return NULL; +} + static void dns_cache_item_free(DnsCacheItem *i) { if (!i) return; @@ -406,7 +433,7 @@ static int dns_cache_put_positive( return 0; } - /* Entry exists already? Update TTL, timestamp and owner*/ + /* Entry exists already? Update TTL, timestamp and owner */ existing = dns_cache_get(c, rr); if (existing) { dns_cache_item_update_positive( @@ -484,7 +511,6 @@ static int dns_cache_put_negative( assert(c); assert(key); - assert(soa); assert(owner_address); /* Never cache pseudo RR keys. DNS_TYPE_ANY is particularly @@ -495,13 +521,17 @@ static int dns_cache_put_negative( if (dns_type_is_pseudo(key->type)) return 0; - if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) { - log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s", - dns_resource_key_to_string(key, key_str, sizeof key_str)); - return 0; - } + if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) { + if (!soa) + return 0; - if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) + /* For negative replies, check if we have a TTL of a SOA */ + if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) { + log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s", + dns_resource_key_to_string(key, key_str, sizeof key_str)); + return 0; + } + } else if (rcode != DNS_RCODE_SERVFAIL) return 0; r = dns_cache_init(c); @@ -514,12 +544,17 @@ static int dns_cache_put_negative( if (!i) return -ENOMEM; - i->type = rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA : DNS_CACHE_NXDOMAIN; - i->until = calculate_until(soa, nsec_ttl, timestamp, true); + i->type = + rcode == DNS_RCODE_SUCCESS ? DNS_CACHE_NODATA : + rcode == DNS_RCODE_NXDOMAIN ? DNS_CACHE_NXDOMAIN : DNS_CACHE_RCODE; + i->until = + i->type == DNS_CACHE_RCODE ? timestamp + CACHE_TTL_STRANGE_RCODE_USEC : + calculate_until(soa, nsec_ttl, timestamp, true); i->authenticated = authenticated; i->owner_family = owner_family; i->owner_address = *owner_address; i->prioq_idx = PRIOQ_IDX_NULL; + i->rcode = rcode; if (i->type == DNS_CACHE_NXDOMAIN) { /* NXDOMAIN entries should apply equally to all types, so we use ANY as @@ -543,7 +578,7 @@ static int dns_cache_put_negative( return r; log_debug("Added %s cache entry for %s "USEC_FMT"s", - i->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", + dns_cache_item_type_to_string(i), dns_resource_key_to_string(i->key, key_str, sizeof key_str), (i->until - timestamp) / USEC_PER_SEC); @@ -615,6 +650,7 @@ int dns_cache_put( const union in_addr_union *owner_address) { DnsResourceRecord *soa = NULL, *rr; + bool weird_rcode = false; DnsAnswerFlags flags; unsigned cache_keys; int r, ifindex; @@ -624,18 +660,28 @@ int dns_cache_put( dns_cache_remove_previous(c, key, answer); - /* We only care for positive replies and NXDOMAINs, on all - * other replies we will simply flush the respective entries, - * and that's it */ - if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) - return 0; + /* We only care for positive replies and NXDOMAINs, on all other replies we will simply flush the respective + * entries, and that's it. (Well, with one further exception: since some DNS zones (akamai!) return SERVFAIL + * consistently for some lookups, and forwarders tend to propagate that we'll cache that too, but only for a + * short time.) */ - if (dns_answer_size(answer) <= 0) { - char key_str[DNS_RESOURCE_KEY_STRING_MAX]; + if (IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) { - log_debug("Not caching negative entry without a SOA record: %s", - dns_resource_key_to_string(key, key_str, sizeof key_str)); - return 0; + if (dns_answer_size(answer) <= 0) { + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; + + log_debug("Not caching negative entry without a SOA record: %s", + dns_resource_key_to_string(key, key_str, sizeof key_str)); + return 0; + } + + } else { + /* Only cache SERVFAIL as "weird" rcode for now. We can add more later, should that turn out to be + * beneficial. */ + if (rcode != DNS_RCODE_SERVFAIL) + return 0; + + weird_rcode = true; } cache_keys = dns_answer_size(answer); @@ -690,19 +736,20 @@ int dns_cache_put( if (r > 0) return 0; - /* See https://tools.ietf.org/html/rfc2308, which say that a - * matching SOA record in the packet is used to enable - * negative caching. */ + /* See https://tools.ietf.org/html/rfc2308, which say that a matching SOA record in the packet is used to + * enable negative caching. We apply one exception though: if we are about to cache a weird rcode we do so + * regardless of a SOA. */ r = dns_answer_find_soa(answer, key, &soa, &flags); if (r < 0) goto fail; - if (r == 0) - return 0; - - /* Refuse using the SOA data if it is unsigned, but the key is - * signed */ - if (authenticated && (flags & DNS_ANSWER_AUTHENTICATED) == 0) + if (r == 0 && !weird_rcode) return 0; + if (r > 0) { + /* Refuse using the SOA data if it is unsigned, but the key is + * signed */ + if (authenticated && (flags & DNS_ANSWER_AUTHENTICATED) == 0) + return 0; + } r = dns_cache_put_negative( c, @@ -799,6 +846,7 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod DnsCacheItem *j, *first, *nsec = NULL; bool have_authenticated = false, have_non_authenticated = false; usec_t current; + int found_rcode = -1; assert(c); assert(key); @@ -817,6 +865,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod *ret = NULL; *rcode = DNS_RCODE_SUCCESS; + *authenticated = false; + return 0; } @@ -831,6 +881,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod *ret = NULL; *rcode = DNS_RCODE_SUCCESS; + *authenticated = false; + return 0; } @@ -842,6 +894,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod n++; } else if (j->type == DNS_CACHE_NXDOMAIN) nxdomain = true; + else if (j->type == DNS_CACHE_RCODE) + found_rcode = j->rcode; if (j->authenticated) have_authenticated = true; @@ -849,6 +903,19 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, bool clamp_ttl, int *rcod have_non_authenticated = true; } + if (found_rcode >= 0) { + log_debug("RCODE %s cache hit for %s", + dns_rcode_to_string(found_rcode), + dns_resource_key_to_string(key, key_str, sizeof(key_str))); + + *ret = NULL; + *rcode = found_rcode; + *authenticated = false; + + c->n_hit++; + return 1; + } + if (nsec && !IN_SET(key->type, DNS_TYPE_NSEC, DNS_TYPE_DS)) { /* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC RRs from * the lower-zone of a zone cut, but the DS RRs are on the upper zone. */ @@ -980,7 +1047,7 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) { if (!j->shared_owner) continue; - r = dns_packet_append_rr(p, j->rr, NULL, NULL); + r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL); if (r == -EMSGSIZE && p->protocol == DNS_PROTOCOL_MDNS) { /* For mDNS, if we're unable to stuff all known answers into the given packet, * allocate a new one, push the RR into that one and link it to the current one. @@ -995,7 +1062,7 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) { /* continue with new packet */ p = p->more; - r = dns_packet_append_rr(p, j->rr, NULL, NULL); + r = dns_packet_append_rr(p, j->rr, 0, NULL, NULL); } if (r < 0) @@ -1042,7 +1109,7 @@ void dns_cache_dump(DnsCache *cache, FILE *f) { fputs(dns_resource_key_to_string(j->key, key_str, sizeof key_str), f); fputs(" -- ", f); - fputs(j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", f); + fputs(dns_cache_item_type_to_string(j), f); fputc('\n', f); } } diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 51327105d0..eddab58a81 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -1710,7 +1710,8 @@ static int dnssec_nsec_covers(DnsResourceRecord *rr, const char *name) { } static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) { - const char *common_suffix, *wc; + _cleanup_free_ char *wc = NULL; + const char *common_suffix; int r; assert(rr); @@ -1734,7 +1735,10 @@ static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) if (r <= 0) return r; - wc = strjoina("*.", common_suffix); + r = dns_name_concat("*", common_suffix, &wc); + if (r < 0) + return r; + return dns_name_between(dns_resource_key_name(rr->key), wc, rr->nsec.next_domain_name); } diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 337a8c473f..652970284e 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -569,8 +569,9 @@ fail: return r; } -int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start) { +int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, const DnsAnswerFlags flags, size_t *start) { size_t saved_size; + uint16_t class; int r; assert(p); @@ -586,7 +587,8 @@ int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start) if (r < 0) goto fail; - r = dns_packet_append_uint16(p, k->class, NULL); + class = flags & DNS_ANSWER_CACHE_FLUSH ? k->class | MDNS_RR_CACHE_FLUSH : k->class; + r = dns_packet_append_uint16(p, class, NULL); if (r < 0) goto fail; @@ -791,9 +793,10 @@ int dns_packet_truncate_opt(DnsPacket *p) { return 1; } -int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start, size_t *rdata_start) { +int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAnswerFlags flags, size_t *start, size_t *rdata_start) { size_t saved_size, rdlength_offset, end, rdlength, rds; + uint32_t ttl; int r; assert(p); @@ -801,11 +804,12 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star saved_size = p->size; - r = dns_packet_append_key(p, rr->key, NULL); + r = dns_packet_append_key(p, rr->key, flags, NULL); if (r < 0) goto fail; - r = dns_packet_append_uint32(p, rr->ttl, NULL); + ttl = flags & DNS_ANSWER_GOODBYE ? 0 : rr->ttl; + r = dns_packet_append_uint32(p, ttl, NULL); if (r < 0) goto fail; @@ -1143,7 +1147,7 @@ int dns_packet_append_question(DnsPacket *p, DnsQuestion *q) { assert(p); DNS_QUESTION_FOREACH(key, q) { - r = dns_packet_append_key(p, key, NULL); + r = dns_packet_append_key(p, key, 0, NULL); if (r < 0) return r; } @@ -1153,12 +1157,13 @@ int dns_packet_append_question(DnsPacket *p, DnsQuestion *q) { int dns_packet_append_answer(DnsPacket *p, DnsAnswer *a) { DnsResourceRecord *rr; + DnsAnswerFlags flags; int r; assert(p); - DNS_ANSWER_FOREACH(rr, a) { - r = dns_packet_append_rr(p, rr, NULL, NULL); + DNS_ANSWER_FOREACH_FLAGS(rr, flags, a) { + r = dns_packet_append_rr(p, rr, flags, NULL, NULL); if (r < 0) return r; } diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 054dc88a85..2c92392e4d 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -209,8 +209,8 @@ int dns_packet_append_string(DnsPacket *p, const char *s, size_t *start); int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_t *start); int dns_packet_append_label(DnsPacket *p, const char *s, size_t l, bool canonical_candidate, size_t *start); int dns_packet_append_name(DnsPacket *p, const char *name, bool allow_compression, bool canonical_candidate, size_t *start); -int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *key, size_t *start); -int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *start, size_t *rdata_start); +int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *key, const DnsAnswerFlags flags, size_t *start); +int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAnswerFlags flags, size_t *start, size_t *rdata_start); int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, int rcode, size_t *start); int dns_packet_append_question(DnsPacket *p, DnsQuestion *q); int dns_packet_append_answer(DnsPacket *p, DnsAnswer *a); diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index e03db4d003..2b091e6c45 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -28,7 +28,7 @@ #include "string-util.h" /* How long to wait for the query in total */ -#define QUERY_TIMEOUT_USEC (30 * USEC_PER_SEC) +#define QUERY_TIMEOUT_USEC (60 * USEC_PER_SEC) #define CNAME_MAX 8 #define QUERIES_MAX 2048 @@ -403,6 +403,7 @@ DnsQuery *dns_query_free(DnsQuery *q) { sd_bus_track_unref(q->bus_track); dns_packet_unref(q->request_dns_packet); + dns_packet_unref(q->reply_dns_packet); if (q->request_dns_stream) { /* Detach the stream from our query, in case something else keeps a reference to it. */ @@ -810,6 +811,7 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) { q->answer = dns_answer_unref(q->answer); q->answer_rcode = 0; q->answer_dnssec_result = _DNSSEC_RESULT_INVALID; + q->answer_authenticated = false; q->answer_errno = c->error_code; } @@ -846,15 +848,18 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) { continue; default: - /* Any kind of failure? Store the data away, - * if there's nothing stored yet. */ - + /* Any kind of failure? Store the data away, if there's nothing stored yet. */ if (state == DNS_TRANSACTION_SUCCESS) continue; + /* If there's already an authenticated negative reply stored, then prefer that over any unauthenticated one */ + if (q->answer_authenticated && !t->answer_authenticated) + continue; + q->answer = dns_answer_unref(q->answer); q->answer_rcode = t->answer_rcode; q->answer_dnssec_result = t->answer_dnssec_result; + q->answer_authenticated = t->answer_authenticated; q->answer_errno = t->answer_errno; state = t->state; @@ -1028,6 +1033,9 @@ int dns_query_process_cname(DnsQuery *q) { if (q->flags & SD_RESOLVED_NO_CNAME) return -ELOOP; + if (!q->answer_authenticated) + q->previous_redirect_unauthenticated = true; + /* OK, let's actually follow the CNAME */ r = dns_query_cname_redirect(q, cname); if (r < 0) @@ -1115,3 +1123,9 @@ const char *dns_query_string(DnsQuery *q) { return dns_question_first_name(q->question_idna); } + +bool dns_query_fully_authenticated(DnsQuery *q) { + assert(q); + + return q->answer_authenticated && !q->previous_redirect_unauthenticated; +} diff --git a/src/resolve/resolved-dns-query.h b/src/resolve/resolved-dns-query.h index 49a35b846b..b8ea48f6af 100644 --- a/src/resolve/resolved-dns-query.h +++ b/src/resolve/resolved-dns-query.h @@ -71,7 +71,6 @@ struct DnsQuery { * family */ bool suppress_unroutable_family; - /* If true, the RR TTLs of the answer will be clamped by their current left validity in the cache */ bool clamp_ttl; @@ -90,6 +89,7 @@ struct DnsQuery { int answer_family; DnsSearchDomain *answer_search_domain; int answer_errno; /* if state is DNS_TRANSACTION_ERRNO */ + bool previous_redirect_unauthenticated; /* Bus client information */ sd_bus_message *request; @@ -102,6 +102,7 @@ struct DnsQuery { /* DNS stub information */ DnsPacket *request_dns_packet; DnsStream *request_dns_stream; + DnsPacket *reply_dns_packet; /* Completion callback */ void (*complete)(DnsQuery* q); @@ -139,3 +140,5 @@ DnsQuestion* dns_query_question_for_protocol(DnsQuery *q, DnsProtocol protocol); const char *dns_query_string(DnsQuery *q); DEFINE_TRIVIAL_CLEANUP_FUNC(DnsQuery*, dns_query_free); + +bool dns_query_fully_authenticated(DnsQuery *q); diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index 209d565033..e8c05ed0da 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -1262,7 +1262,7 @@ int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical) { if (rr->wire_format && rr->wire_format_canonical == canonical) return 0; - r = dns_packet_append_rr(&packet, rr, &start, &rds); + r = dns_packet_append_rr(&packet, rr, 0, &start, &rds); if (r < 0) return r; diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 8dbc7f623b..ffaefbe3f2 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -124,6 +124,8 @@ DnsScope* dns_scope_free(DnsScope *s) { ordered_hashmap_free(s->conflict_queue); sd_event_source_unref(s->conflict_event_source); + sd_event_source_unref(s->announce_event_source); + dns_cache_flush(&s->cache); dns_zone_flush(&s->zone); @@ -549,7 +551,11 @@ static int dns_scope_multicast_membership(DnsScope *s, bool b, struct in_addr in .imr_ifindex = s->link->ifindex, }; - fd = manager_llmnr_ipv4_udp_fd(s->manager); + if (s->protocol == DNS_PROTOCOL_LLMNR) + fd = manager_llmnr_ipv4_udp_fd(s->manager); + else + fd = manager_mdns_ipv4_fd(s->manager); + if (fd < 0) return fd; @@ -568,7 +574,11 @@ static int dns_scope_multicast_membership(DnsScope *s, bool b, struct in_addr in .ipv6mr_interface = s->link->ifindex, }; - fd = manager_llmnr_ipv6_udp_fd(s->manager); + if (s->protocol == DNS_PROTOCOL_LLMNR) + fd = manager_llmnr_ipv6_udp_fd(s->manager); + else + fd = manager_mdns_ipv6_fd(s->manager); + if (fd < 0) return fd; @@ -601,7 +611,7 @@ int dns_scope_mdns_membership(DnsScope *s, bool b) { return dns_scope_multicast_membership(s, b, MDNS_MULTICAST_IPV4_ADDRESS, MDNS_MULTICAST_IPV6_ADDRESS); } -static int dns_scope_make_reply_packet( +int dns_scope_make_reply_packet( DnsScope *s, uint16_t id, int rcode, @@ -830,11 +840,11 @@ static int dns_scope_make_conflict_packet( DNS_PACKET_HEADER(p)->qdcount = htobe16(1); DNS_PACKET_HEADER(p)->arcount = htobe16(1); - r = dns_packet_append_key(p, rr->key, NULL); + r = dns_packet_append_key(p, rr->key, 0, NULL); if (r < 0) return r; - r = dns_packet_append_rr(p, rr, NULL, NULL); + r = dns_packet_append_rr(p, rr, 0, NULL, NULL); if (r < 0) return r; @@ -928,17 +938,19 @@ void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) { assert(scope); assert(p); - if (p->protocol != DNS_PROTOCOL_LLMNR) + if (!IN_SET(p->protocol, DNS_PROTOCOL_LLMNR, DNS_PROTOCOL_MDNS)) return; if (DNS_PACKET_RRCOUNT(p) <= 0) return; - if (DNS_PACKET_LLMNR_C(p) != 0) - return; + if (p->protocol == DNS_PROTOCOL_LLMNR) { + if (DNS_PACKET_LLMNR_C(p) != 0) + return; - if (DNS_PACKET_LLMNR_T(p) != 0) - return; + if (DNS_PACKET_LLMNR_T(p) != 0) + return; + } if (manager_our_packet(scope->manager, p)) return; @@ -1041,3 +1053,77 @@ int dns_scope_ifindex(DnsScope *s) { return 0; } + +static int on_announcement_timeout(sd_event_source *s, usec_t usec, void *userdata) { + DnsScope *scope = userdata; + + assert(s); + + scope->announce_event_source = sd_event_source_unref(scope->announce_event_source); + + (void) dns_scope_announce(scope, false); + return 0; +} + +int dns_scope_announce(DnsScope *scope, bool goodbye) { + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + LinkAddress *a; + int r; + + if (!scope) + return 0; + + if (scope->protocol != DNS_PROTOCOL_MDNS) + return 0; + + answer = dns_answer_new(scope->link->n_addresses * 2); + if (!answer) + return log_oom(); + + LIST_FOREACH(addresses, a, scope->link->addresses) { + r = dns_answer_add(answer, a->mdns_address_rr, 0, goodbye ? DNS_ANSWER_GOODBYE : DNS_ANSWER_CACHE_FLUSH); + if (r < 0) + return log_debug_errno(r, "Failed to add address RR to answer: %m"); + + r = dns_answer_add(answer, a->mdns_ptr_rr, 0, goodbye ? DNS_ANSWER_GOODBYE : DNS_ANSWER_CACHE_FLUSH); + if (r < 0) + return log_debug_errno(r, "Failed to add PTR RR to answer: %m"); + } + + if (dns_answer_isempty(answer)) + return 0; + + r = dns_scope_make_reply_packet(scope, 0, DNS_RCODE_SUCCESS, NULL, answer, NULL, false, &p); + if (r < 0) + return log_debug_errno(r, "Failed to build reply packet: %m"); + + r = dns_scope_emit_udp(scope, -1, p); + if (r < 0) + return log_debug_errno(r, "Failed to send reply packet: %m"); + + /* In section 8.3 of RFC6762: "The Multicast DNS responder MUST send at least two unsolicited + * responses, one second apart." */ + if (!scope->announced) { + usec_t ts; + + scope->announced = true; + + assert_se(sd_event_now(scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0); + ts += MDNS_ANNOUNCE_DELAY; + + r = sd_event_add_time( + scope->manager->event, + &scope->announce_event_source, + clock_boottime_or_monotonic(), + ts, + MDNS_JITTER_RANGE_USEC, + on_announcement_timeout, scope); + if (r < 0) + return log_debug_errno(r, "Failed to schedule second announcement: %m"); + + (void) sd_event_source_set_description(scope->announce_event_source, "mdns-announce"); + } + + return 0; +} diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index 01a83a76b2..6f94b1fdcd 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -56,6 +56,9 @@ struct DnsScope { OrderedHashmap *conflict_queue; sd_event_source *conflict_event_source; + bool announced:1; + sd_event_source *announce_event_source; + RateLimit ratelimit; usec_t resend_timeout; @@ -96,6 +99,7 @@ void dns_scope_next_dns_server(DnsScope *s); int dns_scope_llmnr_membership(DnsScope *s, bool b); int dns_scope_mdns_membership(DnsScope *s, bool b); +int dns_scope_make_reply_packet(DnsScope *s, uint16_t id, int rcode, DnsQuestion *q, DnsAnswer *answer, DnsAnswer *soa, bool tentative, DnsPacket **ret); void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p); DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key, bool cache_ok); @@ -112,3 +116,5 @@ bool dns_scope_name_needs_search_domain(DnsScope *s, const char *name); bool dns_scope_network_good(DnsScope *s); int dns_scope_ifindex(DnsScope *s); + +int dns_scope_announce(DnsScope *scope, bool goodbye); diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index 22c64e8491..5498f7b9cb 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -28,7 +28,7 @@ #include "string-util.h" /* After how much time to repeat classic DNS requests */ -#define DNS_TIMEOUT_MIN_USEC (500 * USEC_PER_MSEC) +#define DNS_TIMEOUT_MIN_USEC (750 * USEC_PER_MSEC) #define DNS_TIMEOUT_MAX_USEC (5 * USEC_PER_SEC) /* The amount of time to wait before retrying with a full feature set */ @@ -399,12 +399,24 @@ static bool dns_server_grace_period_expired(DnsServer *s) { } DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) { + DnsServerFeatureLevel best; + assert(s); - if (s->possible_feature_level != DNS_SERVER_FEATURE_LEVEL_BEST && - dns_server_grace_period_expired(s)) { + /* Determine the best feature level we care about. If DNSSEC mode is off there's no point in using anything + * better than EDNS0, hence don't even try. */ + best = dns_server_get_dnssec_mode(s) == DNSSEC_NO ? + DNS_SERVER_FEATURE_LEVEL_EDNS0 : + DNS_SERVER_FEATURE_LEVEL_BEST; + + /* Clamp the feature level the highest level we care about. The DNSSEC mode might have changed since the last + * time, hence let's downgrade if we are still at a higher level. */ + if (s->possible_feature_level > best) + s->possible_feature_level = best; + + if (s->possible_feature_level < best && dns_server_grace_period_expired(s)) { - s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_BEST; + s->possible_feature_level = best; dns_server_reset_counters(s); @@ -415,6 +427,8 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) { dns_server_feature_level_to_string(s->possible_feature_level), dns_server_string(s)); + dns_server_flush_cache(s); + } else if (s->possible_feature_level <= s->verified_feature_level) s->possible_feature_level = s->verified_feature_level; else { @@ -451,18 +465,22 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) { s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_EDNS0; } else if (s->n_failed_udp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS && - s->possible_feature_level >= DNS_SERVER_FEATURE_LEVEL_UDP) { + s->possible_feature_level >= (dns_server_get_dnssec_mode(s) == DNSSEC_YES ? DNS_SERVER_FEATURE_LEVEL_LARGE : DNS_SERVER_FEATURE_LEVEL_UDP)) { /* We lost too many UDP packets in a row, and are on a feature level of UDP or higher. If the * packets are lost, maybe the server cannot parse them, hence downgrading sounds like a good - * idea. We might downgrade all the way down to TCP this way. */ + * idea. We might downgrade all the way down to TCP this way. + * + * If strict DNSSEC mode is used we won't downgrade below DO level however, as packet loss + * might have many reasons, a broken DNSSEC implementation being only one reason. And if the + * user is strict on DNSSEC, then let's assume that DNSSEC is not the fault here. */ log_debug("Lost too many UDP packets, downgrading feature level..."); s->possible_feature_level--; } else if (s->n_failed_tcp >= DNS_SERVER_FEATURE_RETRY_ATTEMPTS && s->packet_truncated && - s->possible_feature_level > DNS_SERVER_FEATURE_LEVEL_UDP) { + s->possible_feature_level > (dns_server_get_dnssec_mode(s) == DNSSEC_YES ? DNS_SERVER_FEATURE_LEVEL_LARGE : DNS_SERVER_FEATURE_LEVEL_UDP)) { /* We got too many TCP connection failures in a row, we had at least one truncated packet, and * are on a feature level above UDP. By downgrading things and getting rid of DNSSEC or EDNS0 @@ -566,7 +584,7 @@ void dns_server_warn_downgrade(DnsServer *server) { return; log_struct(LOG_NOTICE, - LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_DOWNGRADE), + "MESSAGE_ID=" SD_MESSAGE_DNSSEC_DOWNGRADE_STR, LOG_MESSAGE("Server %s does not support DNSSEC, downgrading to non-DNSSEC mode.", dns_server_string(server)), "DNS_SERVER=%s", dns_server_string(server), "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(server->possible_feature_level), @@ -779,6 +797,34 @@ bool dns_server_address_valid(int family, const union in_addr_union *sa) { return true; } +DnssecMode dns_server_get_dnssec_mode(DnsServer *s) { + assert(s); + + if (s->link) + return link_get_dnssec_mode(s->link); + + return manager_get_dnssec_mode(s->manager); +} + +void dns_server_flush_cache(DnsServer *s) { + DnsServer *current; + DnsScope *scope; + + assert(s); + + /* Flush the cache of the scope this server belongs to */ + + current = s->link ? s->link->current_dns_server : s->manager->current_dns_server; + if (current != s) + return; + + scope = s->link ? s->link->unicast_scope : s->manager->unicast_scope; + if (!scope) + return; + + dns_cache_flush(&scope->cache); +} + static const char* const dns_server_type_table[_DNS_SERVER_TYPE_MAX] = { [DNS_SERVER_SYSTEM] = "system", [DNS_SERVER_FALLBACK] = "fallback", diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h index 83e288a202..bc95d53c6a 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -144,6 +144,10 @@ void manager_next_dns_server(Manager *m); bool dns_server_address_valid(int family, const union in_addr_union *sa); +DnssecMode dns_server_get_dnssec_mode(DnsServer *s); + DEFINE_TRIVIAL_CLEANUP_FUNC(DnsServer*, dns_server_unref); extern const struct hash_ops dns_server_hash_ops; + +void dns_server_flush_cache(DnsServer *s); diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c index 4a3c5f612f..7afbfedfb0 100644 --- a/src/resolve/resolved-dns-stub.c +++ b/src/resolve/resolved-dns-stub.c @@ -29,49 +29,33 @@ static int manager_dns_stub_udp_fd(Manager *m); static int manager_dns_stub_tcp_fd(Manager *m); static int dns_stub_make_reply_packet( - uint16_t id, - int rcode, + DnsPacket **p, DnsQuestion *q, - DnsAnswer *answer, - bool add_opt, /* add an OPT RR to this packet */ - bool edns0_do, /* set the EDNS0 DNSSEC OK bit */ - bool ad, /* set the DNSSEC authenticated data bit */ - DnsPacket **ret) { + DnsAnswer *answer) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; DnsResourceRecord *rr; unsigned c = 0; int r; + assert(p); + /* Note that we don't bother with any additional RRs, as this is stub is for local lookups only, and hence * roundtrips aren't expensive. */ - r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0); - if (r < 0) - return r; - - /* If the client didn't do EDNS, clamp the rcode to 4 bit */ - if (!add_opt && rcode > 0xF) - rcode = DNS_RCODE_SERVFAIL; + if (!*p) { + r = dns_packet_new(p, DNS_PROTOCOL_DNS, 0); + if (r < 0) + return r; - DNS_PACKET_HEADER(p)->id = id; - DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS( - 1 /* qr */, - 0 /* opcode */, - 0 /* aa */, - 0 /* tc */, - 1 /* rd */, - 1 /* ra */, - ad /* ad */, - 0 /* cd */, - rcode)); + r = dns_packet_append_question(*p, q); + if (r < 0) + return r; - r = dns_packet_append_question(p, q); - if (r < 0) - return r; - DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(q)); + DNS_PACKET_HEADER(*p)->qdcount = htobe16(dns_question_size(q)); + } DNS_ANSWER_FOREACH(rr, answer) { + r = dns_question_matches_rr(q, rr, NULL); if (r < 0) return r; @@ -86,13 +70,55 @@ static int dns_stub_make_reply_packet( continue; add: - r = dns_packet_append_rr(p, rr, NULL, NULL); + r = dns_packet_append_rr(*p, rr, 0, NULL, NULL); if (r < 0) return r; c++; } - DNS_PACKET_HEADER(p)->ancount = htobe16(c); + + DNS_PACKET_HEADER(*p)->ancount = htobe16(be16toh(DNS_PACKET_HEADER(*p)->ancount) + c); + + return 0; +} + +static int dns_stub_finish_reply_packet( + DnsPacket *p, + uint16_t id, + int rcode, + bool add_opt, /* add an OPT RR to this packet? */ + bool edns0_do, /* set the EDNS0 DNSSEC OK bit? */ + bool ad) { /* set the DNSSEC authenticated data bit? */ + + int r; + + assert(p); + + if (!add_opt) { + /* If the client can't to EDNS0, don't do DO either */ + edns0_do = false; + + /* If the client didn't do EDNS, clamp the rcode to 4 bit */ + if (rcode > 0xF) + rcode = DNS_RCODE_SERVFAIL; + } + + /* Don't set the AD bit unless DO is on, too */ + if (!edns0_do) + ad = false; + + DNS_PACKET_HEADER(p)->id = id; + + DNS_PACKET_HEADER(p)->flags = htobe16(DNS_PACKET_MAKE_FLAGS( + 1 /* qr */, + 0 /* opcode */, + 0 /* aa */, + 0 /* tc */, + 1 /* rd */, + 1 /* ra */, + ad /* ad */, + 0 /* cd */, + rcode)); if (add_opt) { r = dns_packet_append_opt(p, ADVERTISE_DATAGRAM_SIZE_MAX, edns0_do, rcode, NULL); @@ -100,9 +126,6 @@ static int dns_stub_make_reply_packet( return r; } - *ret = p; - p = NULL; - return 0; } @@ -148,14 +171,18 @@ static int dns_stub_send(Manager *m, DnsStream *s, DnsPacket *p, DnsPacket *repl return 0; } -static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rcode) { +static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rcode, bool authenticated) { _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL; int r; assert(m); assert(p); - r = dns_stub_make_reply_packet(DNS_PACKET_ID(p), rcode, p->question, NULL, !!p->opt, DNS_PACKET_DO(p), false, &reply); + r = dns_stub_make_reply_packet(&reply, p->question, NULL); + if (r < 0) + return log_debug_errno(r, "Failed to make failure packet: %m"); + + r = dns_stub_finish_reply_packet(reply, DNS_PACKET_ID(p), rcode, !!p->opt, DNS_PACKET_DO(p), authenticated); if (r < 0) return log_debug_errno(r, "Failed to build failure packet: %m"); @@ -170,33 +197,47 @@ static void dns_stub_query_complete(DnsQuery *q) { switch (q->state) { - case DNS_TRANSACTION_SUCCESS: { - _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL; + case DNS_TRANSACTION_SUCCESS: + + r = dns_stub_make_reply_packet(&q->reply_dns_packet, q->question_idna, q->answer); + if (r < 0) { + log_debug_errno(r, "Failed to build reply packet: %m"); + break; + } - r = dns_stub_make_reply_packet( + r = dns_query_process_cname(q); + if (r == -ELOOP) { + (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false); + break; + } + if (r < 0) { + log_debug_errno(r, "Failed to process CNAME: %m"); + break; + } + if (r == DNS_QUERY_RESTARTED) + return; + + r = dns_stub_finish_reply_packet( + q->reply_dns_packet, DNS_PACKET_ID(q->request_dns_packet), q->answer_rcode, - q->question_idna, - q->answer, !!q->request_dns_packet->opt, DNS_PACKET_DO(q->request_dns_packet), - DNS_PACKET_DO(q->request_dns_packet) && q->answer_authenticated, - &reply); + dns_query_fully_authenticated(q)); if (r < 0) { - log_debug_errno(r, "Failed to build reply packet: %m"); + log_debug_errno(r, "Failed to finish reply packet: %m"); break; } - (void) dns_stub_send(q->manager, q->request_dns_stream, q->request_dns_packet, reply); + (void) dns_stub_send(q->manager, q->request_dns_stream, q->request_dns_packet, q->reply_dns_packet); break; - } case DNS_TRANSACTION_RCODE_FAILURE: - (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode); + (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, q->answer_rcode, dns_query_fully_authenticated(q)); break; case DNS_TRANSACTION_NOT_FOUND: - (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_NXDOMAIN); + (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_NXDOMAIN, dns_query_fully_authenticated(q)); break; case DNS_TRANSACTION_TIMEOUT: @@ -212,7 +253,7 @@ static void dns_stub_query_complete(DnsQuery *q) { case DNS_TRANSACTION_NO_TRUST_ANCHOR: case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED: case DNS_TRANSACTION_NETWORK_DOWN: - (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL); + (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false); break; case DNS_TRANSACTION_NULL: @@ -259,52 +300,52 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) { if (in_addr_is_localhost(p->family, &p->sender) <= 0 || in_addr_is_localhost(p->family, &p->destination) <= 0) { log_error("Got packet on unexpected IP range, refusing."); - dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL); + dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false); goto fail; } r = dns_packet_extract(p); if (r < 0) { log_debug_errno(r, "Failed to extract resources from incoming packet, ignoring packet: %m"); - dns_stub_send_failure(m, s, p, DNS_RCODE_FORMERR); + dns_stub_send_failure(m, s, p, DNS_RCODE_FORMERR, false); goto fail; } if (!DNS_PACKET_VERSION_SUPPORTED(p)) { log_debug("Got EDNS OPT field with unsupported version number."); - dns_stub_send_failure(m, s, p, DNS_RCODE_BADVERS); + dns_stub_send_failure(m, s, p, DNS_RCODE_BADVERS, false); goto fail; } if (dns_type_is_obsolete(p->question->keys[0]->type)) { log_debug("Got message with obsolete key type, refusing."); - dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP); + dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false); goto fail; } if (dns_type_is_zone_transer(p->question->keys[0]->type)) { log_debug("Got request for zone transfer, refusing."); - dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP); + dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false); goto fail; } if (!DNS_PACKET_RD(p)) { /* If the "rd" bit is off (i.e. recursion was not requested), then refuse operation */ log_debug("Got request with recursion disabled, refusing."); - dns_stub_send_failure(m, s, p, DNS_RCODE_REFUSED); + dns_stub_send_failure(m, s, p, DNS_RCODE_REFUSED, false); goto fail; } if (DNS_PACKET_DO(p) && DNS_PACKET_CD(p)) { log_debug("Got request with DNSSEC CD bit set, refusing."); - dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP); + dns_stub_send_failure(m, s, p, DNS_RCODE_NOTIMP, false); goto fail; } - r = dns_query_new(m, &q, p->question, p->question, 0, SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_SEARCH|SD_RESOLVED_NO_CNAME); + r = dns_query_new(m, &q, p->question, p->question, 0, SD_RESOLVED_PROTOCOLS_ALL|SD_RESOLVED_NO_SEARCH); if (r < 0) { log_error_errno(r, "Failed to generate query object: %m"); - dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL); + dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false); goto fail; } @@ -324,7 +365,7 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) { r = dns_query_go(q); if (r < 0) { log_error_errno(r, "Failed to start query: %m"); - dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL); + dns_stub_send_failure(m, s, p, DNS_RCODE_SERVFAIL, false); goto fail; } diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 2fce44ec8b..ff2ad9c1de 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -31,6 +31,7 @@ #include "string-table.h" #define TRANSACTIONS_MAX 4096 +#define TRANSACTION_TCP_TIMEOUT_USEC (10U*USEC_PER_SEC) static void dns_transaction_reset_answer(DnsTransaction *t) { assert(t); @@ -318,7 +319,7 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { dns_resource_key_to_string(t->key, key_str, sizeof key_str); log_struct(LOG_NOTICE, - LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_FAILURE), + "MESSAGE_ID=" SD_MESSAGE_DNSSEC_FAILURE_STR, LOG_MESSAGE("DNSSEC validation failed for question %s: %s", key_str, dnssec_result_to_string(t->answer_dnssec_result)), "DNS_TRANSACTION=%" PRIu16, t->id, "DNS_QUESTION=%s", key_str, @@ -363,6 +364,8 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { SET_FOREACH_MOVE(z, t->notify_zone_items_done, t->notify_zone_items) dns_zone_item_notify(z); SWAP_TWO(t->notify_zone_items, t->notify_zone_items_done); + if (t->probing) + (void) dns_scope_announce(t->scope, false); SET_FOREACH_MOVE(d, t->notify_transactions_done, t->notify_transactions) dns_transaction_notify(d, t); @@ -830,7 +833,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { * should hence not attempt to access the query or transaction * after calling this function. */ - log_debug("Processing incoming packet on transaction %" PRIu16".", t->id); + log_debug("Processing incoming packet on transaction %" PRIu16". (rcode=%s)", t->id, dns_rcode_to_string(DNS_PACKET_RCODE(p))); switch (t->scope->protocol) { @@ -908,9 +911,13 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { /* Request failed, immediately try again with reduced features */ - if (t->current_feature_level <= DNS_SERVER_FEATURE_LEVEL_WORST) { - /* This was already at the lowest possible feature level? If so, we can't downgrade - * this transaction anymore, hence let's process the response, and accept the rcode. */ + if (t->current_feature_level <= DNS_SERVER_FEATURE_LEVEL_UDP) { + /* This was already at UDP feature level? If so, it doesn't make sense to downgrade + * this transaction anymore, hence let's process the response, and accept the + * rcode. Note that we don't retry on TCP, since that's a suitable way to mitigate + * packet loss, but is not going to give us better rcodes should we actually have + * managed to get them already at UDP level. */ + log_debug("Server returned error: %s", dns_rcode_to_string(DNS_PACKET_RCODE(p))); break; } @@ -924,7 +931,16 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { dns_transaction_retry(t, false /* use the same server */); return; - } else if (DNS_PACKET_TC(p)) + } + + if (DNS_PACKET_RCODE(p) == DNS_RCODE_REFUSED) { + /* This server refused our request? If so, try again, use a different server */ + log_debug("Server returned REFUSED, switching servers, and retrying."); + dns_transaction_retry(t, true /* pick a new server */); + return; + } + + if (DNS_PACKET_TC(p)) dns_server_packet_truncated(t->server, t->current_feature_level); break; @@ -1003,15 +1019,20 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { if (r > 0) /* Transaction got restarted... */ return; - if (IN_SET(t->scope->protocol, DNS_PROTOCOL_DNS, DNS_PROTOCOL_LLMNR)) { + if (IN_SET(t->scope->protocol, DNS_PROTOCOL_DNS, DNS_PROTOCOL_LLMNR, DNS_PROTOCOL_MDNS)) { - /* Only consider responses with equivalent query section to the request */ - r = dns_packet_is_reply_for(p, t->key); - if (r < 0) - goto fail; - if (r == 0) { - dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); - return; + /* When dealing with protocols other than mDNS only consider responses with + * equivalent query section to the request. For mDNS this check doesn't make + * sense, because the section 6 of RFC6762 states that "Multicast DNS responses MUST NOT + * contain any questions in the Question Section". */ + if (t->scope->protocol != DNS_PROTOCOL_MDNS) { + r = dns_packet_is_reply_for(p, t->key); + if (r < 0) + goto fail; + if (r == 0) { + dns_transaction_complete(t, DNS_TRANSACTION_INVALID_REPLY); + return; + } } /* Install the answer as answer to the transaction */ @@ -1119,7 +1140,7 @@ static int dns_transaction_emit_udp(DnsTransaction *t) { return r; if (t->current_feature_level < DNS_SERVER_FEATURE_LEVEL_UDP) - return -EAGAIN; + return -EAGAIN; /* Sorry, can't do UDP, try TCP! */ if (!dns_server_dnssec_supported(t->server) && dns_type_is_dnssec(t->key->type)) return -EOPNOTSUPP; @@ -1196,15 +1217,26 @@ static usec_t transaction_get_resend_timeout(DnsTransaction *t) { assert(t); assert(t->scope); + switch (t->scope->protocol) { case DNS_PROTOCOL_DNS: + + /* When we do TCP, grant a much longer timeout, as in this case there's no need for us to quickly + * resend, as the kernel does that anyway for us, and we really don't want to interrupt it in that + * needlessly. */ + if (t->stream) + return TRANSACTION_TCP_TIMEOUT_USEC; + assert(t->server); return t->server->resend_timeout; case DNS_PROTOCOL_MDNS: assert(t->n_attempts > 0); - return (1 << (t->n_attempts - 1)) * USEC_PER_SEC; + if (t->probing) + return MDNS_PROBING_INTERVAL_USEC; + else + return (1 << (t->n_attempts - 1)) * USEC_PER_SEC; case DNS_PROTOCOL_LLMNR: return t->scope->resend_timeout; @@ -1358,7 +1390,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) { if (r < 0) return r; - r = dns_packet_append_key(p, t->key, NULL); + r = dns_packet_append_key(p, t->key, 0, NULL); if (r < 0) return r; @@ -1390,7 +1422,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) { if (qdcount >= UINT16_MAX) break; - r = dns_packet_append_key(p, other->key, NULL); + r = dns_packet_append_key(p, other->key, 0, NULL); /* * If we can't stuff more questions into the packet, just give up. @@ -1417,7 +1449,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) { if (r < 0) return r; - (void) sd_event_source_set_description(t->timeout_event_source, "dns-transaction-timeout"); + (void) sd_event_source_set_description(other->timeout_event_source, "dns-transaction-timeout"); other->state = DNS_TRANSACTION_PENDING; other->next_attempt_after = ts; @@ -1459,7 +1491,7 @@ static int dns_transaction_make_packet(DnsTransaction *t) { if (r < 0) return r; - r = dns_packet_append_key(p, t->key, NULL); + r = dns_packet_append_key(p, t->key, 0, NULL); if (r < 0) return r; @@ -1560,7 +1592,7 @@ int dns_transaction_go(DnsTransaction *t) { r = dns_transaction_emit_udp(t); if (r == -EMSGSIZE) log_debug("Sending query via TCP since it is too large."); - if (r == -EAGAIN) + else if (r == -EAGAIN) log_debug("Sending query via TCP since server doesn't support UDP."); if (r == -EMSGSIZE || r == -EAGAIN) r = dns_transaction_open_tcp(t); @@ -1977,8 +2009,18 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { r = dns_resource_key_match_rr(t->key, rr, NULL); if (r < 0) return r; - if (r == 0) - continue; + if (r == 0) { + /* Hmm, so this SOA RR doesn't match our original question. In this case, maybe this is + * a negative reply, and we need the a SOA RR's TTL in order to cache a negative entry? + * If so, we need to validate it, too. */ + + r = dns_answer_match_key(t->answer, t->key, NULL); + if (r < 0) + return r; + if (r > 0) /* positive reply, we won't need the SOA and hence don't need to validate + * it. */ + continue; + } r = dnssec_has_rrsig(t->answer, rr->key); if (r < 0) @@ -2416,7 +2458,7 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) { if (r > 0) { /* The lookup is from a TLD that is proven not to * exist, and we are in downgrade mode, hence ignore - * that fact that we didn't get any NSEC RRs.*/ + * that fact that we didn't get any NSEC RRs. */ log_info("Detected a negative query %s in a private DNS zone, permitting unsigned response.", dns_resource_key_to_string(t->key, key_str, sizeof key_str)); @@ -2721,7 +2763,7 @@ static int dnssec_validate_records( const char *source; /* This RRset validated, but as a wildcard. This means we need - * to prove via NSEC/NSEC3 that no matching non-wildcard RR exists.*/ + * to prove via NSEC/NSEC3 that no matching non-wildcard RR exists. */ /* First step, determine the source of synthesis */ r = dns_resource_record_source(rrsig, &source); @@ -2756,7 +2798,7 @@ static int dnssec_validate_records( return r; if (r == 0) { /* Data does not require signing. In that case, just copy it over, - * but remember that this is by no means authenticated.*/ + * but remember that this is by no means authenticated. */ r = dns_answer_move_by_key(validated, &t->answer, rr->key, 0); if (r < 0) return r; diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index 5a1df70422..a8d97738ef 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -78,6 +78,8 @@ struct DnsTransaction { bool clamp_ttl:1; + bool probing:1; + DnsPacket *sent, *received; DnsAnswer *answer; @@ -172,10 +174,20 @@ DnsTransactionSource dns_transaction_source_from_string(const char *s) _pure_; #define MDNS_JITTER_MIN_USEC (20 * USEC_PER_MSEC) #define MDNS_JITTER_RANGE_USEC (100 * USEC_PER_MSEC) +/* mDNS probing interval, see RFC 6762 Section 8.1 */ +#define MDNS_PROBING_INTERVAL_USEC (250 * USEC_PER_MSEC) + /* Maximum attempts to send DNS requests, across all DNS servers */ -#define DNS_TRANSACTION_ATTEMPTS_MAX 16 +#define DNS_TRANSACTION_ATTEMPTS_MAX 24 /* Maximum attempts to send LLMNR requests, see RFC 4795 Section 2.7 */ #define LLMNR_TRANSACTION_ATTEMPTS_MAX 3 -#define TRANSACTION_ATTEMPTS_MAX(p) ((p) == DNS_PROTOCOL_LLMNR ? LLMNR_TRANSACTION_ATTEMPTS_MAX : DNS_TRANSACTION_ATTEMPTS_MAX) +/* Maximum attempts to send MDNS requests, see RFC 6762 Section 8.1 */ +#define MDNS_TRANSACTION_ATTEMPTS_MAX 3 + +#define TRANSACTION_ATTEMPTS_MAX(p) (((p) == DNS_PROTOCOL_LLMNR) ? \ + LLMNR_TRANSACTION_ATTEMPTS_MAX : \ + (((p) == DNS_PROTOCOL_MDNS) ? \ + MDNS_TRANSACTION_ATTEMPTS_MAX : \ + DNS_TRANSACTION_ATTEMPTS_MAX)) diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index 9917b9e984..7e9f9e5a20 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -35,11 +35,16 @@ static const char trust_anchor_dirs[] = CONF_PATHS_NULSTR("dnssec-trust-anchors.d"); -/* The DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved December 2015 */ -static const uint8_t root_digest[] = +/* The first DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved December 2015 */ +static const uint8_t root_digest1[] = { 0x49, 0xAA, 0xC1, 0x1D, 0x7B, 0x6F, 0x64, 0x46, 0x70, 0x2E, 0x54, 0xA1, 0x60, 0x73, 0x71, 0x60, 0x7A, 0x1A, 0x41, 0x85, 0x52, 0x00, 0xFD, 0x2C, 0xE1, 0xCD, 0xDE, 0x32, 0xF2, 0x4E, 0x8F, 0xB5 }; +/* The second DS RR from https://data.iana.org/root-anchors/root-anchors.xml, retrieved February 2017 */ +static const uint8_t root_digest2[] = + { 0xE0, 0x6D, 0x44, 0xB8, 0x0B, 0x8F, 0x1D, 0x39, 0xA9, 0x5C, 0x0B, 0x0D, 0x7C, 0x65, 0xD0, 0x84, + 0x58, 0xE8, 0x80, 0x40, 0x9B, 0xBC, 0x68, 0x34, 0x57, 0x10, 0x42, 0x37, 0xC7, 0xF8, 0xEC, 0x8D }; + static bool dns_trust_anchor_knows_domain_positive(DnsTrustAnchor *d, const char *name) { assert(d); @@ -51,9 +56,40 @@ static bool dns_trust_anchor_knows_domain_positive(DnsTrustAnchor *d, const char hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, name)); } -static int dns_trust_anchor_add_builtin_positive(DnsTrustAnchor *d) { +static int add_root_ksk( + DnsAnswer *answer, + DnsResourceKey *key, + uint16_t key_tag, + uint8_t algorithm, + uint8_t digest_type, + const void *digest, + size_t digest_size) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + int r; + + rr = dns_resource_record_new(key); + if (!rr) + return -ENOMEM; + + rr->ds.key_tag = key_tag; + rr->ds.algorithm = algorithm; + rr->ds.digest_type = digest_type; + rr->ds.digest_size = digest_size; + rr->ds.digest = memdup(digest, rr->ds.digest_size); + if (!rr->ds.digest) + return -ENOMEM; + + r = dns_answer_add(answer, rr, 0, DNS_ANSWER_AUTHENTICATED); + if (r < 0) + return r; + + return 0; +} + +static int dns_trust_anchor_add_builtin_positive(DnsTrustAnchor *d) { _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; int r; assert(d); @@ -62,35 +98,29 @@ static int dns_trust_anchor_add_builtin_positive(DnsTrustAnchor *d) { if (r < 0) return r; - /* Only add the built-in trust anchor if there's neither a DS - * nor a DNSKEY defined for the root domain. That way users - * have an easy way to override the root domain DS/DNSKEY - * data. */ + /* Only add the built-in trust anchor if there's neither a DS nor a DNSKEY defined for the root domain. That + * way users have an easy way to override the root domain DS/DNSKEY data. */ if (dns_trust_anchor_knows_domain_positive(d, ".")) return 0; - /* Add the RR from https://data.iana.org/root-anchors/root-anchors.xml */ - rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, ""); - if (!rr) + key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_DS, ""); + if (!key) return -ENOMEM; - rr->ds.key_tag = 19036; - rr->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256; - rr->ds.digest_type = DNSSEC_DIGEST_SHA256; - rr->ds.digest_size = sizeof(root_digest); - rr->ds.digest = memdup(root_digest, rr->ds.digest_size); - if (!rr->ds.digest) - return -ENOMEM; - - answer = dns_answer_new(1); + answer = dns_answer_new(2); if (!answer) return -ENOMEM; - r = dns_answer_add(answer, rr, 0, DNS_ANSWER_AUTHENTICATED); + /* Add the two RRs from https://data.iana.org/root-anchors/root-anchors.xml */ + r = add_root_ksk(answer, key, 19036, DNSSEC_ALGORITHM_RSASHA256, DNSSEC_DIGEST_SHA256, root_digest1, sizeof(root_digest1)); if (r < 0) return r; - r = hashmap_put(d->positive_by_key, rr->key, answer); + r = add_root_ksk(answer, key, 20326, DNSSEC_ALGORITHM_RSASHA256, DNSSEC_DIGEST_SHA256, root_digest2, sizeof(root_digest2)); + if (r < 0) + return r; + + r = hashmap_put(d->positive_by_key, key, answer); if (r < 0) return r; @@ -547,10 +577,33 @@ int dns_trust_anchor_lookup_positive(DnsTrustAnchor *d, const DnsResourceKey *ke } int dns_trust_anchor_lookup_negative(DnsTrustAnchor *d, const char *name) { + int r; + assert(d); assert(name); - return set_contains(d->negative_by_name, name); + for (;;) { + /* If the domain is listed as-is in the NTA database, then that counts */ + if (set_contains(d->negative_by_name, name)) + return true; + + /* If the domain isn't listed as NTA, but is listed as positive trust anchor, then that counts. See RFC + * 7646, section 1.1 */ + if (hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_DS, name))) + return false; + + if (hashmap_contains(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_IN, DNS_TYPE_KEY, name))) + return false; + + /* And now, let's look at the parent, and check that too */ + r = dns_name_parent(&name); + if (r < 0) + return r; + if (r == 0) + break; + } + + return false; } static int dns_trust_anchor_revoked_put(DnsTrustAnchor *d, DnsResourceRecord *rr) { @@ -594,7 +647,7 @@ static int dns_trust_anchor_remove_revoked(DnsTrustAnchor *d, DnsResourceRecord /* We found the key! Warn the user */ log_struct(LOG_WARNING, - LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED), + "MESSAGE_ID=" SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED_STR, LOG_MESSAGE("DNSSEC Trust anchor %s has been revoked. Please update the trust anchor, or upgrade your operating system."), strna(dns_resource_record_to_string(rr)), "TRUST_ANCHOR=%s", dns_resource_record_to_string(rr), NULL); diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index 746a979f47..ad024b54f5 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -196,6 +196,7 @@ static int dns_zone_item_probe_start(DnsZoneItem *i) { goto gc; i->probe_transaction = t; + t->probing = true; if (t->state == DNS_TRANSACTION_NULL) { diff --git a/src/resolve/resolved-dns-zone.h b/src/resolve/resolved-dns-zone.h index a41df37e6b..545ec958fb 100644 --- a/src/resolve/resolved-dns-zone.h +++ b/src/resolve/resolved-dns-zone.h @@ -37,6 +37,9 @@ typedef enum DnsZoneItemState DnsZoneItemState; /* RFC 4795 Section 2.8. suggests a TTL of 30s by default */ #define LLMNR_DEFAULT_TTL (30) +/* RFC 6762 Section 10. suggests a TTL of 120s by default */ +#define MDNS_DEFAULT_TTL (120) + enum DnsZoneItemState { DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index e7e5c5f5a7..3f7f9035cf 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -85,6 +85,10 @@ Link *link_free(Link *l) { if (!l) return NULL; + /* Send goodbye messages. */ + dns_scope_announce(l->mdns_ipv4_scope, true); + dns_scope_announce(l->mdns_ipv6_scope, true); + link_flush_settings(l); while (l->addresses) @@ -539,7 +543,7 @@ bool link_relevant(Link *l, int family, bool local_multicast) { * beat, can do multicast and has at least one link-local (or better) IP address. * * A link is relevant for non-multicast traffic if it isn't a loopback device, has a link beat, and has at - * least one routable address.*/ + * least one routable address. */ if (l->flags & (IFF_LOOPBACK|IFF_DORMANT)) return false; @@ -665,6 +669,7 @@ int link_address_new(Link *l, LinkAddress **ret, int family, const union in_addr a->link = l; LIST_PREPEND(addresses, l->addresses, a); + l->n_addresses++; if (ret) *ret = a; @@ -679,6 +684,9 @@ LinkAddress *link_address_free(LinkAddress *a) { if (a->link) { LIST_REMOVE(addresses, a->link->addresses, a); + assert(a->link->n_addresses > 0); + a->link->n_addresses--; + if (a->llmnr_address_rr) { if (a->family == AF_INET && a->link->llmnr_ipv4_scope) dns_zone_remove_rr(&a->link->llmnr_ipv4_scope->zone, a->llmnr_address_rr); @@ -692,10 +700,26 @@ LinkAddress *link_address_free(LinkAddress *a) { else if (a->family == AF_INET6 && a->link->llmnr_ipv6_scope) dns_zone_remove_rr(&a->link->llmnr_ipv6_scope->zone, a->llmnr_ptr_rr); } + + if (a->mdns_address_rr) { + if (a->family == AF_INET && a->link->mdns_ipv4_scope) + dns_zone_remove_rr(&a->link->mdns_ipv4_scope->zone, a->mdns_address_rr); + else if (a->family == AF_INET6 && a->link->mdns_ipv6_scope) + dns_zone_remove_rr(&a->link->mdns_ipv6_scope->zone, a->mdns_address_rr); + } + + if (a->mdns_ptr_rr) { + if (a->family == AF_INET && a->link->mdns_ipv4_scope) + dns_zone_remove_rr(&a->link->mdns_ipv4_scope->zone, a->mdns_ptr_rr); + else if (a->family == AF_INET6 && a->link->mdns_ipv6_scope) + dns_zone_remove_rr(&a->link->mdns_ipv6_scope->zone, a->mdns_ptr_rr); + } } dns_resource_record_unref(a->llmnr_address_rr); dns_resource_record_unref(a->llmnr_ptr_rr); + dns_resource_record_unref(a->mdns_address_rr); + dns_resource_record_unref(a->mdns_ptr_rr); return mfree(a); } @@ -746,7 +770,7 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false); if (r < 0) - log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m"); + log_warning_errno(r, "Failed to add IPv4 PTR record to LLMNR zone: %m"); } else { if (a->llmnr_address_rr) { if (a->link->llmnr_ipv4_scope) @@ -760,6 +784,59 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr); } } + + if (!force_remove && + link_address_relevant(a, true) && + a->link->mdns_ipv4_scope && + a->link->mdns_support == RESOLVE_SUPPORT_YES && + a->link->manager->mdns_support == RESOLVE_SUPPORT_YES) { + if (!a->link->manager->mdns_host_ipv4_key) { + a->link->manager->mdns_host_ipv4_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_A, a->link->manager->mdns_hostname); + if (!a->link->manager->mdns_host_ipv4_key) { + r = -ENOMEM; + goto fail; + } + } + + if (!a->mdns_address_rr) { + a->mdns_address_rr = dns_resource_record_new(a->link->manager->mdns_host_ipv4_key); + if (!a->mdns_address_rr) { + r = -ENOMEM; + goto fail; + } + + a->mdns_address_rr->a.in_addr = a->in_addr.in; + a->mdns_address_rr->ttl = MDNS_DEFAULT_TTL; + } + + if (!a->mdns_ptr_rr) { + r = dns_resource_record_new_reverse(&a->mdns_ptr_rr, a->family, &a->in_addr, a->link->manager->mdns_hostname); + if (r < 0) + goto fail; + + a->mdns_ptr_rr->ttl = MDNS_DEFAULT_TTL; + } + + r = dns_zone_put(&a->link->mdns_ipv4_scope->zone, a->link->mdns_ipv4_scope, a->mdns_address_rr, true); + if (r < 0) + log_warning_errno(r, "Failed to add A record to MDNS zone: %m"); + + r = dns_zone_put(&a->link->mdns_ipv4_scope->zone, a->link->mdns_ipv4_scope, a->mdns_ptr_rr, false); + if (r < 0) + log_warning_errno(r, "Failed to add IPv4 PTR record to MDNS zone: %m"); + } else { + if (a->mdns_address_rr) { + if (a->link->mdns_ipv4_scope) + dns_zone_remove_rr(&a->link->mdns_ipv4_scope->zone, a->mdns_address_rr); + a->mdns_address_rr = dns_resource_record_unref(a->mdns_address_rr); + } + + if (a->mdns_ptr_rr) { + if (a->link->mdns_ipv4_scope) + dns_zone_remove_rr(&a->link->mdns_ipv4_scope->zone, a->mdns_ptr_rr); + a->mdns_ptr_rr = dns_resource_record_unref(a->mdns_ptr_rr); + } + } } if (a->family == AF_INET6) { @@ -817,6 +894,60 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { a->llmnr_ptr_rr = dns_resource_record_unref(a->llmnr_ptr_rr); } } + + if (!force_remove && + link_address_relevant(a, true) && + a->link->mdns_ipv6_scope && + a->link->mdns_support == RESOLVE_SUPPORT_YES && + a->link->manager->mdns_support == RESOLVE_SUPPORT_YES) { + + if (!a->link->manager->mdns_host_ipv6_key) { + a->link->manager->mdns_host_ipv6_key = dns_resource_key_new(DNS_CLASS_IN, DNS_TYPE_AAAA, a->link->manager->mdns_hostname); + if (!a->link->manager->mdns_host_ipv6_key) { + r = -ENOMEM; + goto fail; + } + } + + if (!a->mdns_address_rr) { + a->mdns_address_rr = dns_resource_record_new(a->link->manager->mdns_host_ipv6_key); + if (!a->mdns_address_rr) { + r = -ENOMEM; + goto fail; + } + + a->mdns_address_rr->aaaa.in6_addr = a->in_addr.in6; + a->mdns_address_rr->ttl = MDNS_DEFAULT_TTL; + } + + if (!a->mdns_ptr_rr) { + r = dns_resource_record_new_reverse(&a->mdns_ptr_rr, a->family, &a->in_addr, a->link->manager->mdns_hostname); + if (r < 0) + goto fail; + + a->mdns_ptr_rr->ttl = MDNS_DEFAULT_TTL; + } + + r = dns_zone_put(&a->link->mdns_ipv6_scope->zone, a->link->mdns_ipv6_scope, a->mdns_address_rr, true); + if (r < 0) + log_warning_errno(r, "Failed to add AAAA record to MDNS zone: %m"); + + r = dns_zone_put(&a->link->mdns_ipv6_scope->zone, a->link->mdns_ipv6_scope, a->mdns_ptr_rr, false); + if (r < 0) + log_warning_errno(r, "Failed to add IPv6 PTR record to MDNS zone: %m"); + } else { + if (a->mdns_address_rr) { + if (a->link->mdns_ipv6_scope) + dns_zone_remove_rr(&a->link->mdns_ipv6_scope->zone, a->mdns_address_rr); + a->mdns_address_rr = dns_resource_record_unref(a->mdns_address_rr); + } + + if (a->mdns_ptr_rr) { + if (a->link->mdns_ipv6_scope) + dns_zone_remove_rr(&a->link->mdns_ipv6_scope->zone, a->mdns_ptr_rr); + a->mdns_ptr_rr = dns_resource_record_unref(a->mdns_ptr_rr); + } + } } return; diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h index c9b2a58c34..55a56b7906 100644 --- a/src/resolve/resolved-link.h +++ b/src/resolve/resolved-link.h @@ -47,6 +47,8 @@ struct LinkAddress { DnsResourceRecord *llmnr_address_rr; DnsResourceRecord *llmnr_ptr_rr; + DnsResourceRecord *mdns_address_rr; + DnsResourceRecord *mdns_ptr_rr; LIST_FIELDS(LinkAddress, addresses); }; @@ -58,6 +60,7 @@ struct Link { unsigned flags; LIST_HEAD(LinkAddress, addresses); + unsigned n_addresses; LIST_HEAD(DnsServer, dns_servers); DnsServer *current_dns_server; diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 667774b906..c4e4409fe3 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -322,28 +322,28 @@ static int manager_network_monitor_listen(Manager *m) { return 0; } -static int determine_hostname(char **llmnr_hostname, char **mdns_hostname) { +static int determine_hostname(char **full_hostname, char **llmnr_hostname, char **mdns_hostname) { _cleanup_free_ char *h = NULL, *n = NULL; char label[DNS_LABEL_MAX]; const char *p; int r, k; + assert(full_hostname); assert(llmnr_hostname); assert(mdns_hostname); - /* Extract and normalize the first label of the locally - * configured hostname, and check it's not "localhost". */ + /* Extract and normalize the first label of the locally configured hostname, and check it's not "localhost". */ - h = gethostname_malloc(); - if (!h) - return log_oom(); + r = gethostname_strict(&h); + if (r < 0) + return log_debug_errno(r, "Can't determine system hostname: %m"); p = h; r = dns_label_unescape(&p, label, sizeof(label)); if (r < 0) return log_error_errno(r, "Failed to unescape host name: %m"); if (r == 0) { - log_error("Couldn't find a single label in hosntame."); + log_error("Couldn't find a single label in hostname."); return -EINVAL; } @@ -374,32 +374,84 @@ static int determine_hostname(char **llmnr_hostname, char **mdns_hostname) { *llmnr_hostname = n; n = NULL; + *full_hostname = h; + h = NULL; + + return 0; +} + +static const char *fallback_hostname(void) { + + /* Determine the fall back hostname. For exposing this system to the outside world, we cannot have it to be + * "localhost" even if that's the compiled in hostname. In this case, let's revert to "linux" instead. */ + + if (is_localhost(FALLBACK_HOSTNAME)) + return "linux"; + + return FALLBACK_HOSTNAME; +} + +static int make_fallback_hostnames(char **full_hostname, char **llmnr_hostname, char **mdns_hostname) { + _cleanup_free_ char *n = NULL, *m = NULL; + char label[DNS_LABEL_MAX], *h; + const char *p; + int r; + + assert(full_hostname); + assert(llmnr_hostname); + assert(mdns_hostname); + + p = fallback_hostname(); + r = dns_label_unescape(&p, label, sizeof(label)); + if (r < 0) + return log_error_errno(r, "Failed to unescape fallback host name: %m"); + + assert(r > 0); /* The fallback hostname must have at least one label */ + + r = dns_label_escape_new(label, r, &n); + if (r < 0) + return log_error_errno(r, "Failed to escape fallback hostname: %m"); + + r = dns_name_concat(n, "local", &m); + if (r < 0) + return log_error_errno(r, "Failed to concatenate mDNS hostname: %m"); + + h = strdup(fallback_hostname()); + if (!h) + return log_oom(); + + *llmnr_hostname = n; + n = NULL; + + *mdns_hostname = m; + m = NULL; + + *full_hostname = h; + return 0; } static int on_hostname_change(sd_event_source *es, int fd, uint32_t revents, void *userdata) { - _cleanup_free_ char *llmnr_hostname = NULL, *mdns_hostname = NULL; + _cleanup_free_ char *full_hostname = NULL, *llmnr_hostname = NULL, *mdns_hostname = NULL; Manager *m = userdata; int r; assert(m); - r = determine_hostname(&llmnr_hostname, &mdns_hostname); + r = determine_hostname(&full_hostname, &llmnr_hostname, &mdns_hostname); if (r < 0) return 0; /* ignore invalid hostnames */ - if (streq(llmnr_hostname, m->llmnr_hostname) && streq(mdns_hostname, m->mdns_hostname)) + if (streq(full_hostname, m->full_hostname) && + streq(llmnr_hostname, m->llmnr_hostname) && + streq(mdns_hostname, m->mdns_hostname)) return 0; - log_info("System hostname changed to '%s'.", llmnr_hostname); + log_info("System hostname changed to '%s'.", full_hostname); - free(m->llmnr_hostname); - free(m->mdns_hostname); - - m->llmnr_hostname = llmnr_hostname; - m->mdns_hostname = mdns_hostname; - - llmnr_hostname = mdns_hostname = NULL; + free_and_replace(m->full_hostname, full_hostname); + free_and_replace(m->llmnr_hostname, llmnr_hostname); + free_and_replace(m->mdns_hostname, mdns_hostname); manager_refresh_rrs(m); @@ -428,18 +480,15 @@ static int manager_watch_hostname(Manager *m) { (void) sd_event_source_set_description(m->hostname_event_source, "hostname"); - r = determine_hostname(&m->llmnr_hostname, &m->mdns_hostname); + r = determine_hostname(&m->full_hostname, &m->llmnr_hostname, &m->mdns_hostname); if (r < 0) { - log_info("Defaulting to hostname 'linux'."); - m->llmnr_hostname = strdup("linux"); - if (!m->llmnr_hostname) - return log_oom(); - - m->mdns_hostname = strdup("linux.local"); - if (!m->mdns_hostname) - return log_oom(); + log_info("Defaulting to hostname '%s'.", fallback_hostname()); + + r = make_fallback_hostnames(&m->full_hostname, &m->llmnr_hostname, &m->mdns_hostname); + if (r < 0) + return r; } else - log_info("Using system hostname '%s'.", m->llmnr_hostname); + log_info("Using system hostname '%s'.", m->full_hostname); return 0; } @@ -498,7 +547,7 @@ int manager_new(Manager **ret) { m->hostname_fd = -1; m->llmnr_support = RESOLVE_SUPPORT_YES; - m->mdns_support = RESOLVE_SUPPORT_NO; + m->mdns_support = RESOLVE_SUPPORT_YES; m->dnssec_mode = DEFAULT_DNSSEC_MODE; m->enable_cache = true; m->dns_stub_listener_mode = DNS_STUB_LISTENER_UDP; @@ -621,9 +670,13 @@ Manager *manager_free(Manager *m) { dns_resource_key_unref(m->llmnr_host_ipv4_key); dns_resource_key_unref(m->llmnr_host_ipv6_key); + dns_resource_key_unref(m->mdns_host_ipv4_key); + dns_resource_key_unref(m->mdns_host_ipv6_key); sd_event_source_unref(m->hostname_event_source); safe_close(m->hostname_fd); + + free(m->full_hostname); free(m->llmnr_hostname); free(m->mdns_hostname); @@ -1007,6 +1060,8 @@ void manager_refresh_rrs(Manager *m) { m->llmnr_host_ipv4_key = dns_resource_key_unref(m->llmnr_host_ipv4_key); m->llmnr_host_ipv6_key = dns_resource_key_unref(m->llmnr_host_ipv6_key); + m->mdns_host_ipv4_key = dns_resource_key_unref(m->mdns_host_ipv4_key); + m->mdns_host_ipv6_key = dns_resource_key_unref(m->mdns_host_ipv6_key); HASHMAP_FOREACH(l, m->links, i) { link_add_rrs(l, true); @@ -1146,8 +1201,14 @@ int manager_is_own_hostname(Manager *m, const char *name) { return r; } - if (m->mdns_hostname) - return dns_name_equal(name, m->mdns_hostname); + if (m->mdns_hostname) { + r = dns_name_equal(name, m->mdns_hostname); + if (r != 0) + return r; + } + + if (m->full_hostname) + return dns_name_equal(name, m->full_hostname); return 0; } diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 6b2208ed94..97c52b7729 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -109,10 +109,13 @@ struct Manager { sd_event_source *bus_retry_event_source; /* The hostname we publish on LLMNR and mDNS */ + char *full_hostname; char *llmnr_hostname; char *mdns_hostname; DnsResourceKey *llmnr_host_ipv4_key; DnsResourceKey *llmnr_host_ipv6_key; + DnsResourceKey *mdns_host_ipv4_key; + DnsResourceKey *mdns_host_ipv6_key; /* Watch the system hostname */ int hostname_fd; diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c index b13b1d0144..c40e8f75f0 100644 --- a/src/resolve/resolved-mdns.c +++ b/src/resolve/resolved-mdns.c @@ -67,6 +67,50 @@ eaddrinuse: return 0; } +static int mdns_scope_process_query(DnsScope *s, DnsPacket *p) { + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL; + DnsResourceKey *key = NULL; + bool tentative = false; + int r; + + assert(s); + assert(p); + + r = dns_packet_extract(p); + if (r < 0) + return log_debug_errno(r, "Failed to extract resource records from incoming packet: %m"); + + /* TODO: there might be more than one question in mDNS queries. */ + assert_return((dns_question_size(p->question) > 0), -EINVAL); + key = p->question->keys[0]; + + r = dns_zone_lookup(&s->zone, key, 0, &answer, &soa, &tentative); + if (r < 0) { + log_debug_errno(r, "Failed to lookup key: %m"); + return r; + } + if (r == 0) + return 0; + + r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, NULL, answer, NULL, false, &reply); + if (r < 0) { + log_debug_errno(r, "Failed to build reply packet: %m"); + return r; + } + + if (!ratelimit_test(&s->ratelimit)) + return 0; + + r = dns_scope_emit_udp(s, -1, reply); + if (r < 0) { + log_debug_errno(r, "Failed to send reply packet: %m"); + return r; + } + + return 0; +} + static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) { _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; Manager *m = userdata; @@ -77,6 +121,9 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us if (r <= 0) return r; + if (manager_our_packet(m, p)) + return 0; + scope = manager_find_scope(m, p); if (!scope) { log_warning("Got mDNS UDP packet on unknown scope. Ignoring."); @@ -115,9 +162,28 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us dns_name_endswith(name, "local") > 0)) return 0; + if (rr->ttl == 0) { + log_debug("Got a goodbye packet"); + /* See the section 10.1 of RFC6762 */ + rr->ttl = 1; + } + t = dns_scope_find_transaction(scope, rr->key, false); if (t) dns_transaction_process_reply(t, p); + + /* Also look for the various types of ANY transactions */ + t = dns_scope_find_transaction(scope, &DNS_RESOURCE_KEY_CONST(rr->key->class, DNS_TYPE_ANY, dns_resource_key_name(rr->key)), false); + if (t) + dns_transaction_process_reply(t, p); + + t = dns_scope_find_transaction(scope, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_ANY, rr->key->type, dns_resource_key_name(rr->key)), false); + if (t) + dns_transaction_process_reply(t, p); + + t = dns_scope_find_transaction(scope, &DNS_RESOURCE_KEY_CONST(DNS_CLASS_ANY, DNS_TYPE_ANY, dns_resource_key_name(rr->key)), false); + if (t) + dns_transaction_process_reply(t, p); } dns_cache_put(&scope->cache, NULL, DNS_PACKET_RCODE(p), p->answer, false, (uint32_t) -1, 0, p->family, &p->sender); @@ -125,7 +191,11 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us } else if (dns_packet_validate_query(p) > 0) { log_debug("Got mDNS query packet for id %u", DNS_PACKET_ID(p)); - dns_scope_process_query(scope, NULL, p); + r = mdns_scope_process_query(scope, p); + if (r < 0) { + log_debug_errno(r, "mDNS query processing failed: %m"); + return 0; + } } else log_debug("Invalid mDNS UDP packet."); diff --git a/src/resolve/resolved-mdns.h b/src/resolve/resolved-mdns.h index 5d274648f4..06bd3296be 100644 --- a/src/resolve/resolved-mdns.h +++ b/src/resolve/resolved-mdns.h @@ -22,6 +22,7 @@ #include "resolved-manager.h" #define MDNS_PORT 5353 +#define MDNS_ANNOUNCE_DELAY (1 * USEC_PER_SEC) int manager_mdns_ipv4_fd(Manager *m); int manager_mdns_ipv6_fd(Manager *m); diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c index 13f08f8a6c..3c62550872 100644 --- a/src/resolve/resolved-resolv-conf.c +++ b/src/resolve/resolved-resolv-conf.c @@ -203,13 +203,13 @@ static void write_resolv_conf_search( static int write_resolv_conf_contents(FILE *f, OrderedSet *dns, OrderedSet *domains) { Iterator i; - fputs("# This file is managed by systemd-resolved(8). Do not edit.\n#\n" + fputs("# This file is managed by man:systemd-resolved(8). Do not edit.\n#\n" "# This is a dynamic resolv.conf file for connecting local clients directly to\n" "# all known DNS servers.\n#\n" "# Third party programs must not access this file directly, but only through the\n" - "# symlink at /etc/resolv.conf. To manage resolv.conf(5) in a different way,\n" + "# symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a different way,\n" "# replace this symlink by a static file or a different symlink.\n#\n" - "# See systemd-resolved.service(8) for details about the supported modes of\n" + "# See man:systemd-resolved.service(8) for details about the supported modes of\n" "# operation for /etc/resolv.conf.\n\n", f); if (ordered_set_isempty(dns)) diff --git a/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts b/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts Binary files differdeleted file mode 100644 index a383c6286d..0000000000 --- a/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts +++ /dev/null diff --git a/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts b/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts Binary files differdeleted file mode 100644 index 15de02e997..0000000000 --- a/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts +++ /dev/null diff --git a/src/resolve/test-data/fake-caa.pkts b/src/resolve/test-data/fake-caa.pkts Binary files differdeleted file mode 100644 index 1c3ecc5491..0000000000 --- a/src/resolve/test-data/fake-caa.pkts +++ /dev/null diff --git a/src/resolve/test-data/fedoraproject.org.pkts b/src/resolve/test-data/fedoraproject.org.pkts Binary files differdeleted file mode 100644 index 17874844d9..0000000000 --- a/src/resolve/test-data/fedoraproject.org.pkts +++ /dev/null diff --git a/src/resolve/test-data/gandi.net.pkts b/src/resolve/test-data/gandi.net.pkts Binary files differdeleted file mode 100644 index 5ef51e0c8e..0000000000 --- a/src/resolve/test-data/gandi.net.pkts +++ /dev/null diff --git a/src/resolve/test-data/google.com.pkts b/src/resolve/test-data/google.com.pkts Binary files differdeleted file mode 100644 index f98c4cd855..0000000000 --- a/src/resolve/test-data/google.com.pkts +++ /dev/null diff --git a/src/resolve/test-data/kyhwana.org.pkts b/src/resolve/test-data/kyhwana.org.pkts Binary files differdeleted file mode 100644 index e28a725c9a..0000000000 --- a/src/resolve/test-data/kyhwana.org.pkts +++ /dev/null diff --git a/src/resolve/test-data/root.pkts b/src/resolve/test-data/root.pkts Binary files differdeleted file mode 100644 index 54ba668c75..0000000000 --- a/src/resolve/test-data/root.pkts +++ /dev/null diff --git a/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts b/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts Binary files differdeleted file mode 100644 index a854249532..0000000000 --- a/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts +++ /dev/null diff --git a/src/resolve/test-data/teamits.com.pkts b/src/resolve/test-data/teamits.com.pkts Binary files differdeleted file mode 100644 index 11deb39677..0000000000 --- a/src/resolve/test-data/teamits.com.pkts +++ /dev/null diff --git a/src/resolve/test-data/zbyszek@fedoraproject.org.pkts b/src/resolve/test-data/zbyszek@fedoraproject.org.pkts Binary files differdeleted file mode 100644 index f0a6f982df..0000000000 --- a/src/resolve/test-data/zbyszek@fedoraproject.org.pkts +++ /dev/null diff --git a/src/resolve/test-dns-packet.c b/src/resolve/test-dns-packet.c index 956b155872..8cbe492526 100644 --- a/src/resolve/test-dns-packet.c +++ b/src/resolve/test-dns-packet.c @@ -29,6 +29,7 @@ #include "resolved-dns-rr.h" #include "string-util.h" #include "strv.h" +#include "tests.h" #include "unaligned.h" #define HASH_KEY SD_ID128_MAKE(d3,1e,48,90,4b,fa,4c,fe,af,9d,d5,a1,d7,2e,8a,b1) @@ -115,7 +116,7 @@ int main(int argc, char **argv) { N = argc - 1; fnames = argv + 1; } else { - assert_se(glob(RESOLVE_TEST_DIR "/*.pkts", GLOB_NOSORT, NULL, &g) == 0); + assert_se(glob(get_testdata_dir("/test-resolve/*.pkts"), GLOB_NOSORT, NULL, &g) == 0); N = g.gl_pathc; fnames = g.gl_pathv; } diff --git a/src/run/run.c b/src/run/run.c index 08f7e12336..2e6765aa18 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -497,7 +497,7 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv, cons if (r < 0) return r; - if (arg_wait) { + if (arg_wait || arg_pty) { r = sd_bus_message_append(m, "(sv)", "AddRef", "b", 1); if (r < 0) return r; @@ -818,16 +818,18 @@ static int run_context_update(RunContext *c, const char *path) { {} }; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; r = bus_map_all_properties(c->bus, "org.freedesktop.systemd1", path, map, + &error, c); if (r < 0) { sd_event_exit(c->event, EXIT_FAILURE); - return log_error_errno(r, "Failed to query unit state: %m"); + return log_error_errno(r, "Failed to query unit state: %s", bus_error_message(&error, r)); } run_context_check_done(c); @@ -1024,7 +1026,6 @@ static int start_transient_service( pty_forward_set_handler(c.forward, pty_forward_handler, &c); } - path = unit_dbus_path_from_name(service); if (!path) return log_oom(); diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 20c1085697..aae69f6da5 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -554,7 +554,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen } else if (streq(field, "RestrictNamespaces")) { bool invert = false; - uint64_t flags = 0; + unsigned long flags = 0; if (eq[0] == '~') { invert = true; @@ -575,7 +575,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen if (invert) flags = (~flags) & NAMESPACE_FLAGS_ALL; - r = sd_bus_message_append(m, "v", "t", flags); + r = sd_bus_message_append(m, "v", "t", (uint64_t) flags); } else if ((dep = unit_dependency_from_string(field)) >= 0) r = sd_bus_message_append(m, "v", "as", 1, eq); else if (streq(field, "MountFlags")) { @@ -585,7 +585,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen if (r < 0) return log_error_errno(r, "Failed to parse mount propagation flags: %s", eq); - r = sd_bus_message_append(m, "v", "t", f); + r = sd_bus_message_append(m, "v", "t", (uint64_t) f); } else if (STR_IN_SET(field, "BindPaths", "BindReadOnlyPaths")) { const char *p = eq; @@ -862,7 +862,7 @@ static void log_job_error_with_service_result(const char* service, const char *r service_shell_quoted = shell_maybe_quote(service); - if (extra_args && extra_args[1]) { + if (extra_args) { _cleanup_free_ char *t; t = strv_join((char**) extra_args, " "); diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 6aebe18fc0..8ddfb584ea 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -1116,9 +1116,9 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_ int bus_message_map_all_properties( sd_bus_message *m, const struct bus_properties_map *map, + sd_bus_error *error, void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; assert(m); @@ -1156,9 +1156,9 @@ int bus_message_map_all_properties( v = (uint8_t *)userdata + prop->offset; if (map[i].set) - r = prop->set(sd_bus_message_get_bus(m), member, m, &error, v); + r = prop->set(sd_bus_message_get_bus(m), member, m, error, v); else - r = map_basic(sd_bus_message_get_bus(m), member, m, &error, v); + r = map_basic(sd_bus_message_get_bus(m), member, m, error, v); if (r < 0) return r; @@ -1184,6 +1184,7 @@ int bus_message_map_all_properties( int bus_message_map_properties_changed( sd_bus_message *m, const struct bus_properties_map *map, + sd_bus_error *error, void *userdata) { const char *member; @@ -1192,7 +1193,7 @@ int bus_message_map_properties_changed( assert(m); assert(map); - r = bus_message_map_all_properties(m, map, userdata); + r = bus_message_map_all_properties(m, map, error, userdata); if (r < 0) return r; @@ -1222,10 +1223,10 @@ int bus_map_all_properties( const char *destination, const char *path, const struct bus_properties_map *map, + sd_bus_error *error, void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; assert(bus); @@ -1239,13 +1240,13 @@ int bus_map_all_properties( path, "org.freedesktop.DBus.Properties", "GetAll", - &error, + error, &m, "s", ""); if (r < 0) return r; - return bus_message_map_all_properties(m, map, userdata); + return bus_message_map_all_properties(m, map, error, userdata); } int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **ret) { diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h index af5f133912..d9ce4263bb 100644 --- a/src/shared/bus-util.h +++ b/src/shared/bus-util.h @@ -50,9 +50,9 @@ struct bus_properties_map { int bus_map_id128(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata); -int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, void *userdata); -int bus_message_map_properties_changed(sd_bus_message *m, const struct bus_properties_map *map, void *userdata); -int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map, void *userdata); +int bus_message_map_all_properties(sd_bus_message *m, const struct bus_properties_map *map, sd_bus_error *error, void *userdata); +int bus_message_map_properties_changed(sd_bus_message *m, const struct bus_properties_map *map, sd_bus_error *error, void *userdata); +int bus_map_all_properties(sd_bus *bus, const char *destination, const char *path, const struct bus_properties_map *map, sd_bus_error *error, void *userdata); int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name); diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 410a7764ed..39e724c51a 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -61,12 +61,8 @@ static int probe_filesystem(const char *node, char **ret_fstype) { log_debug("Failed to identify any partition type on partition %s", node); goto not_found; } - if (r != 0) { - if (errno == 0) - return -EIO; - - return -errno; - } + if (r != 0) + return -errno ?: -EIO; (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); @@ -146,12 +142,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI errno = 0; r = blkid_probe_set_device(b, fd, 0, 0); - if (r != 0) { - if (errno == 0) - return -ENOMEM; - - return -errno; - } + if (r != 0) + return -errno ?: -ENOMEM; if ((flags & DISSECT_IMAGE_GPT_ONLY) == 0) { /* Look for file system superblocks, unless we only shall look for GPT partition tables */ @@ -168,12 +160,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI log_debug("Failed to identify any partition table."); return -ENOPKG; } - if (r != 0) { - if (errno == 0) - return -EIO; - - return -errno; - } + if (r != 0) + return -errno ?: -EIO; m = new0(DissectedImage, 1); if (!m) @@ -232,12 +220,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI errno = 0; pl = blkid_probe_get_partitions(b); - if (!pl) { - if (errno == 0) - return -ENOMEM; - - return -errno; - } + if (!pl) + return -errno ?: -ENOMEM; udev = udev_new(); if (!udev) diff --git a/src/shared/dropin.c b/src/shared/dropin.c index 3917eb8f23..15ccd1b6ca 100644 --- a/src/shared/dropin.c +++ b/src/shared/dropin.c @@ -43,11 +43,10 @@ int drop_in_file(const char *dir, const char *unit, unsigned level, const char *name, char **_p, char **_q) { + char prefix[DECIMAL_STR_MAX(unsigned)]; _cleanup_free_ char *b = NULL; char *p, *q; - char prefix[DECIMAL_STR_MAX(unsigned)]; - assert(unit); assert(name); assert(_p); @@ -128,9 +127,10 @@ static int unit_file_find_dir( assert(path); r = chase_symlinks(path, original_root, 0, &chased); + if (r == -ENOENT) /* Ignore -ENOENT, after all most units won't have a drop-in dir */ + return 0; if (r < 0) - return log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, - r, "Failed to canonicalize path %s: %m", path); + return log_full_errno(LOG_WARNING, r, "Failed to canonicalize path %s: %m", path); r = strv_push(dirs, chased); if (r < 0) @@ -148,16 +148,14 @@ static int unit_file_find_dirs( const char *suffix, char ***dirs) { - _cleanup_free_ char *path = NULL; + char *path; int r; assert(unit_path); assert(name); assert(suffix); - path = strjoin(unit_path, "/", name, suffix); - if (!path) - return log_oom(); + path = strjoina(unit_path, "/", name, suffix); if (!unit_path_cache || set_get(unit_path_cache, path)) { r = unit_file_find_dir(original_root, path, dirs); @@ -166,22 +164,15 @@ static int unit_file_find_dirs( } if (unit_name_is_valid(name, UNIT_NAME_INSTANCE)) { - _cleanup_free_ char *template = NULL, *p = NULL; /* Also try the template dir */ + _cleanup_free_ char *template = NULL; + r = unit_name_template(name, &template); if (r < 0) return log_error_errno(r, "Failed to generate template from unit name: %m"); - p = strjoin(unit_path, "/", template, suffix); - if (!p) - return log_oom(); - - if (!unit_path_cache || set_get(unit_path_cache, p)) { - r = unit_file_find_dir(original_root, p, dirs); - if (r < 0) - return r; - } + return unit_file_find_dirs(original_root, unit_path_cache, unit_path, template, suffix, dirs); } return 0; @@ -194,27 +185,30 @@ int unit_file_find_dropin_paths( const char *dir_suffix, const char *file_suffix, Set *names, - char ***paths) { + char ***ret) { _cleanup_strv_free_ char **dirs = NULL, **ans = NULL; Iterator i; char *t, **p; int r; - assert(paths); + assert(ret); SET_FOREACH(t, names, i) STRV_FOREACH(p, lookup_path) unit_file_find_dirs(original_root, unit_path_cache, *p, t, dir_suffix, &dirs); - if (strv_isempty(dirs)) + if (strv_isempty(dirs)) { + *ret = NULL; return 0; + } r = conf_files_list_strv(&ans, file_suffix, NULL, (const char**) dirs); if (r < 0) return log_warning_errno(r, "Failed to sort the list of configuration files: %m"); - *paths = ans; + *ret = ans; ans = NULL; + return 1; } diff --git a/src/shared/firewall-util.c b/src/shared/firewall-util.c index 9c29b0afca..952fc48c45 100644 --- a/src/shared/firewall-util.c +++ b/src/shared/firewall-util.c @@ -76,8 +76,11 @@ static int entry_fill_basics( } if (out_interface) { + size_t l = strlen(out_interface); + assert(l < sizeof entry->ip.outiface && l < sizeof entry->ip.outiface_mask); + strcpy(entry->ip.outiface, out_interface); - memset(entry->ip.outiface_mask, 0xFF, strlen(out_interface)+1); + memset(entry->ip.outiface_mask, 0xFF, l + 1); } if (destination) { entry->ip.dst = destination->in; diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c index 87b520b540..c3106f1ae9 100644 --- a/src/shared/fstab-util.c +++ b/src/shared/fstab-util.c @@ -213,7 +213,7 @@ static char *unquote(const char *s, const char* quotes) { * trailing quotes if there is one. Doesn't care about * escaping or anything. * - * DON'T USE THIS FOR NEW CODE ANYMORE!*/ + * DON'T USE THIS FOR NEW CODE ANYMORE! */ l = strlen(s); if (l < 2) diff --git a/src/shared/install.c b/src/shared/install.c index f25ed685f6..58c8e852b2 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -389,6 +389,12 @@ void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *chang verb, changes[i].path); logged = true; break; + + case -ENOENT: + log_error_errno(changes[i].type, "Failed to %s unit, unit %s does not exist.", verb, changes[i].path); + logged = true; + break; + default: assert(changes[i].type < 0); log_error_errno(changes[i].type, "Failed to %s unit, file %s: %m.", @@ -1807,7 +1813,9 @@ static int install_context_mark_for_removal( InstallContext *c, const LookupPaths *paths, Set **remove_symlinks_to, - const char *config_path) { + const char *config_path, + UnitFileChange **changes, + unsigned *n_changes) { UnitFileInstallInfo *i; int r; @@ -1833,19 +1841,26 @@ static int install_context_mark_for_removal( r = install_info_traverse(scope, c, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL); if (r == -ENOLINK) { - log_debug_errno(r, "Name %s leads to a dangling symlink, ignoring.", i->name); - continue; - } else if (r == -ENOENT && i->auxiliary) { - /* some unit specified in Also= or similar is missing */ - log_debug_errno(r, "Auxiliary unit %s not found, ignoring.", i->name); - continue; - } else if (r < 0) - return log_debug_errno(r, "Failed to find unit %s: %m", i->name); + log_debug_errno(r, "Name %s leads to a dangling symlink, removing name.", i->name); + unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_DANGLING, i->path ?: i->name, NULL); + } else if (r == -ENOENT) { - if (i->type != UNIT_FILE_TYPE_REGULAR) { - log_debug("Unit %s has type %s, ignoring.", - i->name, - unit_file_type_to_string(i->type) ?: "invalid"); + if (i->auxiliary) /* some unit specified in Also= or similar is missing */ + log_debug_errno(r, "Auxiliary unit of %s not found, removing name.", i->name); + else { + log_debug_errno(r, "Unit %s not found, removing name.", i->name); + unit_file_changes_add(changes, n_changes, r, i->path ?: i->name, NULL); + } + + } else if (r < 0) { + log_debug_errno(r, "Failed to find unit %s, removing name: %m", i->name); + unit_file_changes_add(changes, n_changes, r, i->path ?: i->name, NULL); + } else if (i->type == UNIT_FILE_TYPE_MASKED) { + log_debug("Unit file %s is masked, ignoring.", i->name); + unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, i->path ?: i->name, NULL); + continue; + } else if (i->type != UNIT_FILE_TYPE_REGULAR) { + log_debug("Unit %s has type %s, ignoring.", i->name, unit_file_type_to_string(i->type) ?: "invalid"); continue; } @@ -1878,6 +1893,8 @@ int unit_file_mask( return r; config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; + if (!config_path) + return -ENXIO; STRV_FOREACH(i, files) { _cleanup_free_ char *path = NULL; @@ -1926,6 +1943,9 @@ int unit_file_unmask( return r; config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; + if (!config_path) + return -ENXIO; + dry_run = !!(flags & UNIT_FILE_DRY_RUN); STRV_FOREACH(i, files) { @@ -2015,6 +2035,8 @@ int unit_file_link( return r; config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; + if (!config_path) + return -ENXIO; STRV_FOREACH(i, files) { _cleanup_free_ char *full = NULL; @@ -2282,6 +2304,8 @@ int unit_file_add_dependency( return r; config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; + if (!config_path) + return -ENXIO; r = install_info_discover(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info, changes, n_changes); @@ -2347,6 +2371,8 @@ int unit_file_enable( return r; config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; + if (!config_path) + return -ENXIO; STRV_FOREACH(f, files) { r = install_info_discover(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, @@ -2391,6 +2417,8 @@ int unit_file_disable( return r; config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; + if (!config_path) + return -ENXIO; STRV_FOREACH(i, files) { if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) @@ -2401,7 +2429,7 @@ int unit_file_disable( return r; } - r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path); + r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, changes, n_changes); if (r < 0) return r; @@ -2790,7 +2818,7 @@ static int execute_preset( if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) { _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path); + r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, changes, n_changes); if (r < 0) return r; @@ -2885,6 +2913,8 @@ int unit_file_preset( return r; config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; + if (!config_path) + return -ENXIO; r = read_presets(scope, root_dir, &presets); if (r < 0) @@ -2923,6 +2953,8 @@ int unit_file_preset_all( return r; config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; + if (!config_path) + return -ENXIO; r = read_presets(scope, root_dir, &presets); if (r < 0) diff --git a/src/shared/journal-util.c b/src/shared/journal-util.c new file mode 100644 index 0000000000..8479221a44 --- /dev/null +++ b/src/shared/journal-util.c @@ -0,0 +1,151 @@ +/*** + This file is part of systemd. + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "acl-util.h" +#include "fs-util.h" +#include "hashmap.h" +#include "journal-internal.h" +#include "journal-util.h" +#include "log.h" +#include "strv.h" +#include "user-util.h" + +static int access_check_var_log_journal(sd_journal *j) { +#ifdef HAVE_ACL + _cleanup_strv_free_ char **g = NULL; + const char* dir; +#endif + int r; + + assert(j); + + /* If we are root, we should have access, don't warn. */ + if (getuid() == 0) + return 0; + + /* If we are in the 'systemd-journal' group, we should have + * access too. */ + r = in_group("systemd-journal"); + if (r < 0) + return log_error_errno(r, "Failed to check if we are in the 'systemd-journal' group: %m"); + if (r > 0) + return 0; + +#ifdef HAVE_ACL + if (laccess("/run/log/journal", F_OK) >= 0) + dir = "/run/log/journal"; + else + dir = "/var/log/journal"; + + /* If we are in any of the groups listed in the journal ACLs, + * then all is good, too. Let's enumerate all groups from the + * default ACL of the directory, which generally should allow + * access to most journal files too. */ + r = acl_search_groups(dir, &g); + if (r < 0) + return log_error_errno(r, "Failed to search journal ACL: %m"); + if (r > 0) + return 0; + + /* Print a pretty list, if there were ACLs set. */ + if (!strv_isempty(g)) { + _cleanup_free_ char *s = NULL; + + /* Thre are groups in the ACL, let's list them */ + r = strv_extend(&g, "systemd-journal"); + if (r < 0) + return log_oom(); + + strv_sort(g); + strv_uniq(g); + + s = strv_join(g, "', '"); + if (!s) + return log_oom(); + + log_notice("Hint: You are currently not seeing messages from other users and the system.\n" + " Users in groups '%s' can see all messages.\n" + " Pass -q to turn off this notice.", s); + return 1; + } +#endif + + /* If no ACLs were found, print a short version of the message. */ + log_notice("Hint: You are currently not seeing messages from other users and the system.\n" + " Users in the 'systemd-journal' group can see all messages. Pass -q to\n" + " turn off this notice."); + + return 1; +} + +int journal_access_check_and_warn(sd_journal *j, bool quiet) { + Iterator it; + void *code; + char *path; + int r = 0; + + assert(j); + + if (hashmap_isempty(j->errors)) { + if (ordered_hashmap_isempty(j->files) && !quiet) + log_notice("No journal files were found."); + + return 0; + } + + if (hashmap_contains(j->errors, INT_TO_PTR(-EACCES))) { + if (!quiet) + (void) access_check_var_log_journal(j); + + if (ordered_hashmap_isempty(j->files)) + r = log_error_errno(EACCES, "No journal files were opened due to insufficient permissions."); + } + + HASHMAP_FOREACH_KEY(path, code, j->errors, it) { + int err; + + err = abs(PTR_TO_INT(code)); + + switch (err) { + case EACCES: + continue; + + case ENODATA: + log_warning_errno(err, "Journal file %s is truncated, ignoring file.", path); + break; + + case EPROTONOSUPPORT: + log_warning_errno(err, "Journal file %1$s uses an unsupported feature, ignoring file.\n" + "Use SYSTEMD_LOG_LEVEL=debug journalctl --file=%1$s to see the details.", + path); + break; + + case EBADMSG: + log_warning_errno(err, "Journal file %s corrupted, ignoring file.", path); + break; + + default: + log_warning_errno(err, "An error was encountered while opening journal file or directory %s, ignoring file: %m", path); + break; + } + } + + return r; +} diff --git a/src/shared/journal-util.h b/src/shared/journal-util.h new file mode 100644 index 0000000000..499e6c62ec --- /dev/null +++ b/src/shared/journal-util.h @@ -0,0 +1,25 @@ +/*** + This file is part of systemd. + + Copyright 2013 Zbigniew Jędrzejewski-Szmek + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdbool.h> + +#include "sd-journal.h" + +int journal_access_check_and_warn(sd_journal *j, bool quiet); diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index 7bc5c0a128..32a4c67590 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -594,7 +594,7 @@ static int clone_auxiliary_file(const char *path, const char *new_name, const ch if (!rs) return -ENOMEM; - return copy_file_atomic(path, rs, 0664, false, 0); + return copy_file_atomic(path, rs, 0664, 0, COPY_REFLINK); } int image_clone(Image *i, const char *new_name, bool read_only) { @@ -636,7 +636,7 @@ 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.*/ + * directory. */ new_path = strjoina("/var/lib/machines/", new_name); @@ -656,7 +656,7 @@ int image_clone(Image *i, const char *new_name, bool read_only) { case IMAGE_RAW: new_path = strjoina("/var/lib/machines/", new_name, ".raw"); - r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, false, FS_NOCOW_FL); + r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, FS_NOCOW_FL, COPY_REFLINK); break; default: @@ -712,7 +712,7 @@ int image_read_only(Image *i, bool b) { use the "immutable" flag, to at least make the top-level directory read-only. It's not as good as a read-only subvolume, but at least something, and - we can read the value back.*/ + we can read the value back. */ r = chattr_path(i->path, b ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL); if (r < 0) diff --git a/src/shared/pager.c b/src/shared/pager.c index 09672a4abf..f00ba9e1e7 100644 --- a/src/shared/pager.c +++ b/src/shared/pager.c @@ -44,7 +44,7 @@ static pid_t pager_pid = 0; noreturn static void pager_fallback(void) { int r; - r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (uint64_t) -1, false); + r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (uint64_t) -1, 0); if (r < 0) { log_error_errno(r, "Internal pager failed: %m"); _exit(EXIT_FAILURE); @@ -104,7 +104,8 @@ int pager_open(bool no_pager, bool jump_to_end) { less_opts = "FRSXMK"; if (jump_to_end) less_opts = strjoina(less_opts, " +G"); - setenv("LESS", less_opts, 1); + if (setenv("LESS", less_opts, 1) < 0) + _exit(EXIT_FAILURE); /* Initialize a good charset for less. This is * particularly important if we output UTF-8 @@ -112,8 +113,9 @@ int pager_open(bool no_pager, bool jump_to_end) { less_charset = getenv("SYSTEMD_LESSCHARSET"); if (!less_charset && is_locale_utf8()) less_charset = "utf-8"; - if (less_charset) - setenv("LESSCHARSET", less_charset, 1); + if (less_charset && + setenv("LESSCHARSET", less_charset, 1) < 0) + _exit(EXIT_FAILURE); /* Make sure the pager goes away when the parent dies */ if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c index 586ef64e72..e2b3f8b742 100644 --- a/src/shared/path-lookup.c +++ b/src/shared/path-lookup.c @@ -33,6 +33,7 @@ #include "stat-util.h" #include "string-util.h" #include "strv.h" +#include "user-util.h" #include "util.h" static int user_runtime_dir(char **ret, const char *suffix) { @@ -57,6 +58,7 @@ static int user_runtime_dir(char **ret, const char *suffix) { static int user_config_dir(char **ret, const char *suffix) { const char *e; char *j; + int r; assert(ret); @@ -64,11 +66,11 @@ static int user_config_dir(char **ret, const char *suffix) { if (e) j = strappend(e, suffix); else { - const char *home; + _cleanup_free_ char *home = NULL; - home = getenv("HOME"); - if (!home) - return -ENXIO; + r = get_home_dir(&home); + if (r < 0) + return r; j = strjoin(home, "/.config", suffix); } @@ -83,6 +85,7 @@ static int user_config_dir(char **ret, const char *suffix) { static int user_data_dir(char **ret, const char *suffix) { const char *e; char *j; + int r; assert(ret); assert(suffix); @@ -95,12 +98,11 @@ static int user_data_dir(char **ret, const char *suffix) { if (e) j = strappend(e, suffix); else { - const char *home; - - home = getenv("HOME"); - if (!home) - return -ENXIO; + _cleanup_free_ char *home = NULL; + r = get_home_dir(&home); + if (r < 0) + return r; j = strjoin(home, "/.local/share", suffix); } @@ -136,10 +138,10 @@ static char** user_dirs( NULL }; - const char *e; _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL; _cleanup_free_ char *data_home = NULL; _cleanup_strv_free_ char **res = NULL; + const char *e; char **tmp; int r; @@ -186,9 +188,8 @@ static char** user_dirs( if (strv_extend(&res, generator_early) < 0) return NULL; - if (!strv_isempty(config_dirs)) - if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0) - return NULL; + if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0) + return NULL; if (strv_extend(&res, persistent_config) < 0) return NULL; @@ -205,9 +206,8 @@ static char** user_dirs( if (strv_extend(&res, data_home) < 0) return NULL; - if (!strv_isempty(data_dirs)) - if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0) - return NULL; + if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0) + return NULL; if (strv_extend_strv(&res, (char**) data_unit_paths, false) < 0) return NULL; @@ -220,6 +220,7 @@ static char** user_dirs( tmp = res; res = NULL; + return tmp; } @@ -328,12 +329,18 @@ static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **ru case UNIT_FILE_USER: r = user_config_dir(&a, "/systemd/user"); - if (r < 0) + if (r < 0 && r != -ENXIO) return r; r = user_runtime_dir(runtime, "/systemd/user"); - if (r < 0) - return r; + if (r < 0) { + if (r != -ENXIO) + return r; + + /* If XDG_RUNTIME_DIR is not set, don't consider that fatal, simply initialize the runtime + * directory to NULL */ + *runtime = NULL; + } *persistent = a; a = NULL; @@ -382,12 +389,18 @@ static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **r case UNIT_FILE_USER: r = user_config_dir(&a, "/systemd/system.control"); - if (r < 0) + if (r < 0 && r != -ENXIO) return r; r = user_runtime_dir(runtime, "/systemd/system.control"); - if (r < 0) - return r; + if (r < 0) { + if (r != -ENXIO) + return r; + + /* If XDG_RUNTIME_DIR is not set, don't consider this fatal, simply initialize the directory to + * NULL */ + *runtime = NULL; + } break; @@ -474,22 +487,26 @@ int lookup_paths_init( return -ENOMEM; } + /* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_config to NULL */ r = acquire_config_dirs(scope, &persistent_config, &runtime_config); - if (r < 0 && r != -ENXIO) + if (r < 0) return r; if ((flags & LOOKUP_PATHS_EXCLUDE_GENERATED) == 0) { + /* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */ r = acquire_generator_dirs(scope, &generator, &generator_early, &generator_late); if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO) return r; } + /* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */ r = acquire_transient_dir(scope, &transient); if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO) return r; + /* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_control to NULL */ r = acquire_control_dirs(scope, &persistent_control, &runtime_control); - if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO) + if (r < 0 && r != -EOPNOTSUPP) return r; /* First priority is whatever has been passed to us via env vars */ @@ -503,8 +520,7 @@ int lookup_paths_init( append = true; } - /* FIXME: empty components in other places should be - * rejected. */ + /* FIXME: empty components in other places should be rejected. */ r = path_split_and_make_absolute(e, &paths); if (r < 0) diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index e35f18471c..2631856563 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -36,31 +36,72 @@ const uint32_t seccomp_local_archs[] = { -#if defined(__i386__) || defined(__x86_64__) + /* Note: always list the native arch we are compiled as last, so that users can blacklist seccomp(), but our own calls to it still succeed */ + +#if defined(__x86_64__) && defined(__ILP32__) SCMP_ARCH_X86, SCMP_ARCH_X86_64, + SCMP_ARCH_X32, /* native */ +#elif defined(__x86_64__) && !defined(__ILP32__) + SCMP_ARCH_X86, SCMP_ARCH_X32, - -#elif defined(__arm__) || defined(__aarch64__) + SCMP_ARCH_X86_64, /* native */ +#elif defined(__i386__) + SCMP_ARCH_X86, +#elif defined(__aarch64__) SCMP_ARCH_ARM, - SCMP_ARCH_AARCH64, - -#elif defined(__mips__) || defined(__mips64__) + SCMP_ARCH_AARCH64, /* native */ +#elif defined(__arm__) + SCMP_ARCH_ARM, +#elif defined(__mips__) && __BYTE_ORDER == __BIG_ENDIAN && _MIPS_SIM == _MIPS_SIM_ABI32 + SCMP_ARCH_MIPSEL, + SCMP_ARCH_MIPS, /* native */ +#elif defined(__mips__) && __BYTE_ORDER == __LITTLE_ENDIAN && _MIPS_SIM == _MIPS_SIM_ABI32 SCMP_ARCH_MIPS, - SCMP_ARCH_MIPS64, + SCMP_ARCH_MIPSEL, /* native */ +#elif defined(__mips__) && __BYTE_ORDER == __BIG_ENDIAN && _MIPS_SIM == _MIPS_SIM_ABI64 + SCMP_ARCH_MIPSEL, + SCMP_ARCH_MIPS, + SCMP_ARCH_MIPSEL64N32, + SCMP_ARCH_MIPS64N32, + SCMP_ARCH_MIPSEL64, + SCMP_ARCH_MIPS64, /* native */ +#elif defined(__mips__) && __BYTE_ORDER == __LITTLE_ENDIAN && _MIPS_SIM == _MIPS_SIM_ABI64 + SCMP_ARCH_MIPS, + SCMP_ARCH_MIPSEL, SCMP_ARCH_MIPS64N32, + SCMP_ARCH_MIPSEL64N32, + SCMP_ARCH_MIPS64, + SCMP_ARCH_MIPSEL64, /* native */ +#elif defined(__mips__) && __BYTE_ORDER == __BIG_ENDIAN && _MIPS_SIM == _MIPS_SIM_NABI32 SCMP_ARCH_MIPSEL, + SCMP_ARCH_MIPS, SCMP_ARCH_MIPSEL64, + SCMP_ARCH_MIPS64, SCMP_ARCH_MIPSEL64N32, - -#elif defined(__powerpc__) || defined(__powerpc64__) + SCMP_ARCH_MIPS64N32, /* native */ +#elif defined(__mips__) && __BYTE_ORDER == __LITTLE_ENDIAN && _MIPS_SIM == _MIPS_SIM_NABI32 + SCMP_ARCH_MIPS, + SCMP_ARCH_MIPSEL, + SCMP_ARCH_MIPS64, + SCMP_ARCH_MIPSEL64, + SCMP_ARCH_MIPS64N32, + SCMP_ARCH_MIPSEL64N32, /* native */ +#elif defined(__powerpc64__) && __BYTE_ORDER == __BIG_ENDIAN SCMP_ARCH_PPC, - SCMP_ARCH_PPC64, SCMP_ARCH_PPC64LE, - -#elif defined(__s390__) || defined(__s390x__) + SCMP_ARCH_PPC64, /* native */ +#elif defined(__powerpc64__) && __BYTE_ORDER == __LITTLE_ENDIAN + SCMP_ARCH_PPC, + SCMP_ARCH_PPC64, + SCMP_ARCH_PPC64LE, /* native */ +#elif defined(__powerpc__) + SCMP_ARCH_PPC, +#elif defined(__s390x__) + SCMP_ARCH_S390, + SCMP_ARCH_S390X, /* native */ +#elif defined(__s390__) SCMP_ARCH_S390, - SCMP_ARCH_S390X, #endif (uint32_t) -1 }; @@ -344,6 +385,7 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "mknodat\0" "mmap2\0" "mmap\0" + "munmap\0" "newfstatat\0" "open\0" "openat\0" @@ -760,6 +802,8 @@ int seccomp_restrict_namespaces(unsigned long retain) { case SCMP_ARCH_X86_64: case SCMP_ARCH_X86: case SCMP_ARCH_X32: + case SCMP_ARCH_PPC64: + case SCMP_ARCH_PPC64LE: clone_reversed_order = 0; break; @@ -771,8 +815,8 @@ int seccomp_restrict_namespaces(unsigned long retain) { /* Please add more definitions here, if you port systemd to other architectures! */ -#if !defined(__i386__) && !defined(__x86_64__) && !defined(__s390__) && !defined(__s390x__) -#warning "Consider adding the right clone() syscall definitions here!" +#if SECCOMP_RESTRICT_NAMESPACES_BROKEN +# warning "Consider adding the right clone() syscall definitions here!" #endif } @@ -906,17 +950,42 @@ int seccomp_protect_sysctl(void) { } int seccomp_restrict_address_families(Set *address_families, bool whitelist) { - -#if !SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN uint32_t arch; int r; SECCOMP_FOREACH_LOCAL_ARCH(arch) { _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; + bool supported; Iterator i; log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch)); + switch (arch) { + + case SCMP_ARCH_X86_64: + case SCMP_ARCH_X32: + case SCMP_ARCH_ARM: + case SCMP_ARCH_AARCH64: + /* These we know we support (i.e. are the ones that do not use socketcall()) */ + supported = true; + break; + + case SCMP_ARCH_X86: + case SCMP_ARCH_S390: + case SCMP_ARCH_S390X: + case SCMP_ARCH_PPC: + case SCMP_ARCH_PPC64: + case SCMP_ARCH_PPC64LE: + default: + /* These we either know we don't support (i.e. are the ones that do use socketcall()), or we + * don't know */ + supported = false; + break; + } + + if (!supported) + continue; + r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW); if (r < 0) return r; @@ -1036,7 +1105,6 @@ int seccomp_restrict_address_families(Set *address_families, bool whitelist) { if (r < 0) log_debug_errno(r, "Failed to install socket family rules for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); } -#endif return 0; } diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h index 61f94de638..b56ac3f763 100644 --- a/src/shared/seccomp-util.h +++ b/src/shared/seccomp-util.h @@ -92,7 +92,7 @@ int seccomp_memory_deny_write_execute(void); #endif /* we don't know the right order of the clone() parameters except for these archs, for now */ -#if defined(__x86_64__) || defined(__i386__) || defined(__s390x__) || defined(__s390__) +#if defined(__x86_64__) || defined(__i386__) || defined(__s390x__) || defined(__s390__) || defined(__powerpc64__) #define SECCOMP_RESTRICT_NAMESPACES_BROKEN 0 #else #define SECCOMP_RESTRICT_NAMESPACES_BROKEN 1 diff --git a/src/shared/tests.c b/src/shared/tests.c index 409116290d..f300bbc66f 100644 --- a/src/shared/tests.c +++ b/src/shared/tests.c @@ -17,10 +17,14 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <alloc-util.h> +#include <fs-util.h> +#include <libgen.h> #include <stdlib.h> #include <util.h> #include "tests.h" +#include "path-util.h" char* setup_fake_runtime_dir(void) { char t[] = "/tmp/fake-xdg-runtime-XXXXXX", *p; @@ -31,3 +35,39 @@ char* setup_fake_runtime_dir(void) { return p; } + +const char* get_testdata_dir(const char *suffix) { + const char *env; + /* convenience: caller does not need to free result */ + static char testdir[PATH_MAX]; + + /* if the env var is set, use that */ + env = getenv("SYSTEMD_TEST_DATA"); + testdir[sizeof(testdir) - 1] = '\0'; + if (env) { + if (access(env, F_OK) < 0) { + fputs("ERROR: $SYSTEMD_TEST_DATA directory does not exist\n", stderr); + exit(1); + } + strncpy(testdir, env, sizeof(testdir) - 1); + } else { + _cleanup_free_ char *exedir = NULL; + assert_se(readlink_and_make_absolute("/proc/self/exe", &exedir) >= 0); + + /* Check if we're running from the builddir. If so, use the compiled in path. */ + if (path_startswith(exedir, ABS_BUILD_DIR)) + assert_se(snprintf(testdir, sizeof(testdir), "%s/test", ABS_SRC_DIR) > 0); + else + /* Try relative path, according to the install-test layout */ + assert_se(snprintf(testdir, sizeof(testdir), "%s/testdata", dirname(exedir)) > 0); + + /* test this without the suffix, as it may contain a glob */ + if (access(testdir, F_OK) < 0) { + fputs("ERROR: Cannot find testdata directory, set $SYSTEMD_TEST_DATA\n", stderr); + exit(1); + } + } + + strncpy(testdir + strlen(testdir), suffix, sizeof(testdir) - strlen(testdir) - 1); + return testdir; +} diff --git a/src/shared/tests.h b/src/shared/tests.h index 93f09013a1..7055124990 100644 --- a/src/shared/tests.h +++ b/src/shared/tests.h @@ -20,3 +20,4 @@ ***/ char* setup_fake_runtime_dir(void); +const char* get_testdata_dir(const char *suffix); diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index c8f0742183..3bac78b3e4 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -25,6 +25,7 @@ #include "sd-messages.h" #include "def.h" +#include "exec-util.h" #include "fd-util.h" #include "fileio.h" #include "log.h" @@ -106,10 +107,10 @@ static int execute(char **modes, char **states) { if (r < 0) return r; - execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments); log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START), + "MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR, LOG_MESSAGE("Suspending system..."), "SLEEP=%s", arg_verb, NULL); @@ -119,13 +120,13 @@ static int execute(char **modes, char **states) { return r; log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_STOP), + "MESSAGE_ID=" SD_MESSAGE_SLEEP_STOP_STR, LOG_MESSAGE("System resumed."), "SLEEP=%s", arg_verb, NULL); arguments[1] = (char*) "post"; - execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments); return r; } diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 2809dece50..d78e56d777 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -1922,7 +1922,7 @@ static int get_machine_properties(sd_bus *bus, struct machine_info *mi) { bus = container; } - r = bus_map_all_properties(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", machine_info_property_map, mi); + r = bus_map_all_properties(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", machine_info_property_map, NULL, mi); if (r < 0) return r; @@ -1957,7 +1957,7 @@ static int get_machine_list( machine_infos[c].name = hn; hn = NULL; - get_machine_properties(bus, &machine_infos[c]); + (void) get_machine_properties(bus, &machine_infos[c]); c++; } @@ -1987,7 +1987,7 @@ static int get_machine_list( return log_oom(); } - get_machine_properties(NULL, &machine_infos[c]); + (void) get_machine_properties(NULL, &machine_infos[c]); c++; } @@ -3482,6 +3482,8 @@ static int set_exit_code(uint8_t code) { static int start_special(int argc, char *argv[], void *userdata) { enum action a; int r; + bool termination_action; /* an action that terminates the manager, + * can be performed also by signal. */ assert(argv); @@ -3521,40 +3523,43 @@ static int start_special(int argc, char *argv[], void *userdata) { return r; } - if (arg_force >= 2 && - IN_SET(a, - ACTION_HALT, - ACTION_POWEROFF, - ACTION_REBOOT)) + termination_action = IN_SET(a, + ACTION_HALT, + ACTION_POWEROFF, + ACTION_REBOOT); + if (termination_action && arg_force >= 2) return halt_now(a); if (arg_force >= 1 && - IN_SET(a, - ACTION_HALT, - ACTION_POWEROFF, - ACTION_REBOOT, - ACTION_KEXEC, - ACTION_EXIT)) - return trivial_method(argc, argv, userdata); + (termination_action || IN_SET(a, ACTION_KEXEC, ACTION_EXIT))) + r = trivial_method(argc, argv, userdata); + else { + /* First try logind, to allow authentication with polkit */ + if (IN_SET(a, + ACTION_POWEROFF, + ACTION_REBOOT, + ACTION_SUSPEND, + ACTION_HIBERNATE, + ACTION_HYBRID_SLEEP)) { + + r = logind_reboot(a); + if (r >= 0) + return r; + if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS)) + /* requested operation is not supported or already in progress */ + return r; - /* First try logind, to allow authentication with polkit */ - if (IN_SET(a, - ACTION_POWEROFF, - ACTION_REBOOT, - ACTION_SUSPEND, - ACTION_HIBERNATE, - ACTION_HYBRID_SLEEP)) { - r = logind_reboot(a); - if (r >= 0) - return r; - if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS)) - /* requested operation is not supported or already in progress */ - return r; + /* On all other errors, try low-level operation */ + } - /* On all other errors, try low-level operation */ + r = start_unit(argc, argv, userdata); } - return start_unit(argc, argv, userdata); + if (termination_action && arg_force < 2 && + IN_SET(r, -ENOENT, -ETIMEDOUT)) + log_notice("It is possible to perform action directly, see discussion of --force --force in man:systemctl(1)."); + + return r; } static int start_system_special(int argc, char *argv[], void *userdata) { @@ -4953,7 +4958,7 @@ static int show_one( return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r)); if (unit) { - r = bus_message_map_all_properties(reply, property_map, &info); + r = bus_message_map_all_properties(reply, property_map, &error, &info); if (r < 0) return log_error_errno(r, "Failed to map properties: %s", bus_error_message(&error, r)); @@ -5125,8 +5130,9 @@ static int show_all( static int show_system_status(sd_bus *bus) { char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], since2[FORMAT_TIMESTAMP_MAX]; - _cleanup_free_ char *hn = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(machine_info_clear) struct machine_info mi = {}; + _cleanup_free_ char *hn = NULL; const char *on, *off; int r; @@ -5134,9 +5140,9 @@ static int show_system_status(sd_bus *bus) { if (!hn) return log_oom(); - r = bus_map_all_properties(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", machine_info_property_map, &mi); + r = bus_map_all_properties(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", machine_info_property_map, &error, &mi); if (r < 0) - return log_error_errno(r, "Failed to read server status: %m"); + return log_error_errno(r, "Failed to read server status: %s", bus_error_message(&error, r)); if (streq_ptr(mi.state, "degraded")) { on = ansi_highlight_red(); @@ -5299,7 +5305,7 @@ static int cat_file(const char *filename, bool newline) { ansi_normal()); fflush(stdout); - return copy_bytes(fd, STDOUT_FILENO, (uint64_t) -1, false); + return copy_bytes(fd, STDOUT_FILENO, (uint64_t) -1, 0); } static int cat(int argc, char *argv[], void *userdata) { @@ -5958,6 +5964,7 @@ static int mangle_names(char **original_names, char ***mangled_names) { } else { r = unit_name_mangle(*name, UNIT_NAME_NOGLOB, i); if (r < 0) { + *i = NULL; strv_free(l); return log_error_errno(r, "Failed to mangle unit name: %m"); } @@ -6028,7 +6035,7 @@ static int unit_exists(const char *unit) { if (r < 0) return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r)); - r = bus_message_map_all_properties(reply, property_map, &info); + r = bus_message_map_all_properties(reply, property_map, &error, &info); if (r < 0) return log_error_errno(r, "Failed to map properties: %s", bus_error_message(&error, r)); @@ -6581,7 +6588,7 @@ static int create_edit_temp_file(const char *new_path, const char *original_path if (r < 0) return log_error_errno(r, "Failed to create directories for \"%s\": %m", new_path); - r = copy_file(original_path, t, 0, 0644, 0); + r = copy_file(original_path, t, 0, 0644, 0, COPY_REFLINK); if (r == -ENOENT) { r = touch(t); @@ -6805,29 +6812,54 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) { return r; STRV_FOREACH(name, names) { - _cleanup_free_ char *path = NULL, *new_path = NULL, *tmp_path = NULL; + _cleanup_free_ char *path = NULL, *new_path = NULL, *tmp_path = NULL, *tmp_name = NULL; + const char *unit_name; r = unit_find_paths(bus, *name, &lp, &path, NULL); if (r < 0) return r; - else if (!arg_force) { - if (r == 0) { - log_error("Run 'systemctl edit --force %s' to create a new unit.", *name); - return -ENOENT; - } else if (!path) { - // FIXME: support units with path==NULL (no FragmentPath) - log_error("No fragment exists for %s.", *name); + + if (r == 0) { + assert(!path); + + if (!arg_force) { + log_error("Run 'systemctl edit%s --force %s' to create a new unit.", + arg_scope == UNIT_FILE_GLOBAL ? " --global" : + arg_scope == UNIT_FILE_USER ? " --user" : "", + *name); return -ENOENT; } - } - if (path) { + /* Create a new unit from scratch */ + unit_name = *name; + r = unit_file_create_new(&lp, unit_name, + arg_full ? NULL : ".d/override.conf", + &new_path, &tmp_path); + } else { + assert(path); + + unit_name = basename(path); + /* We follow unit aliases, but we need to propagate the instance */ + if (unit_name_is_valid(*name, UNIT_NAME_INSTANCE) && + unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) { + _cleanup_free_ char *instance = NULL; + + r = unit_name_to_instance(*name, &instance); + if (r < 0) + return r; + + r = unit_name_replace_instance(unit_name, instance, &tmp_name); + if (r < 0) + return r; + + unit_name = tmp_name; + } + if (arg_full) - r = unit_file_create_copy(&lp, basename(path), path, &new_path, &tmp_path); + r = unit_file_create_copy(&lp, unit_name, path, &new_path, &tmp_path); else - r = unit_file_create_new(&lp, basename(path), ".d/override.conf", &new_path, &tmp_path); - } else - r = unit_file_create_new(&lp, *name, NULL, &new_path, &tmp_path); + r = unit_file_create_new(&lp, unit_name, ".d/override.conf", &new_path, &tmp_path); + } if (r < 0) return r; @@ -7299,7 +7331,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { case 't': { if (isempty(optarg)) { - log_error("--type requires arguments."); + log_error("--type= requires arguments."); return -EINVAL; } @@ -7539,7 +7571,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { case ARG_STATE: { if (isempty(optarg)) { - log_error("--signal requires arguments."); + log_error("--state= requires arguments."); return -EINVAL; } @@ -7548,7 +7580,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { r = extract_first_word(&p, &s, ",", 0); if (r < 0) - return log_error_errno(r, "Failed to parse signal: %s", optarg); + return log_error_errno(r, "Failed to parse state: %s", optarg); if (r == 0) break; diff --git a/src/systemd/sd-bus-vtable.h b/src/systemd/sd-bus-vtable.h index e8f84eb545..3563a2b126 100644 --- a/src/systemd/sd-bus-vtable.h +++ b/src/systemd/sd-bus-vtable.h @@ -86,18 +86,26 @@ struct sd_bus_vtable { { \ .type = _SD_BUS_VTABLE_START, \ .flags = _flags, \ - .x.start.element_size = sizeof(sd_bus_vtable), \ + .x = { \ + .start = { \ + .element_size = sizeof(sd_bus_vtable) \ + }, \ + }, \ } #define SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, _offset, _flags) \ { \ .type = _SD_BUS_VTABLE_METHOD, \ .flags = _flags, \ - .x.method.member = _member, \ - .x.method.signature = _signature, \ - .x.method.result = _result, \ - .x.method.handler = _handler, \ - .x.method.offset = _offset, \ + .x = { \ + .method = { \ + .member = _member, \ + .signature = _signature, \ + .result = _result, \ + .handler = _handler, \ + .offset = _offset, \ + }, \ + }, \ } #define SD_BUS_METHOD(_member, _signature, _result, _handler, _flags) \ SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, 0, _flags) @@ -106,29 +114,41 @@ struct sd_bus_vtable { { \ .type = _SD_BUS_VTABLE_SIGNAL, \ .flags = _flags, \ - .x.signal.member = _member, \ - .x.signal.signature = _signature, \ + .x = { \ + .signal = { \ + .member = _member, \ + .signature = _signature, \ + }, \ + }, \ } #define SD_BUS_PROPERTY(_member, _signature, _get, _offset, _flags) \ { \ .type = _SD_BUS_VTABLE_PROPERTY, \ .flags = _flags, \ - .x.property.member = _member, \ - .x.property.signature = _signature, \ - .x.property.get = _get, \ - .x.property.offset = _offset, \ + .x = { \ + .property = { \ + .member = _member, \ + .signature = _signature, \ + .get = _get, \ + .offset = _offset, \ + }, \ + }, \ } #define SD_BUS_WRITABLE_PROPERTY(_member, _signature, _get, _set, _offset, _flags) \ { \ .type = _SD_BUS_VTABLE_WRITABLE_PROPERTY, \ .flags = _flags, \ - .x.property.member = _member, \ - .x.property.signature = _signature, \ - .x.property.get = _get, \ - .x.property.set = _set, \ - .x.property.offset = _offset, \ + .x = { \ + .property = { \ + .member = _member, \ + .signature = _signature, \ + .get = _get, \ + .set = _set, \ + .offset = _offset, \ + }, \ + }, \ } #define SD_BUS_VTABLE_END \ diff --git a/src/systemd/sd-id128.h b/src/systemd/sd-id128.h index 6cc8e4ac0e..9b38969b77 100644 --- a/src/systemd/sd-id128.h +++ b/src/systemd/sd-id128.h @@ -100,6 +100,9 @@ int sd_id128_get_invocation(sd_id128_t *ret); ((x).bytes[15] & 15) >= 10 ? 'a' + ((x).bytes[15] & 15) - 10 : '0' + ((x).bytes[15] & 15), \ 0 }) +#define SD_ID128_MAKE_STR(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \ + #a #b #c #d #e #f #g #h #i #j #k #l #m #n #o #p + _sd_pure_ static __inline__ int sd_id128_equal(sd_id128_t a, sd_id128_t b) { return memcmp(&a, &b, 16) == 0; } diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h index db1a21be05..f466d9b062 100644 --- a/src/systemd/sd-messages.h +++ b/src/systemd/sd-messages.h @@ -33,60 +33,109 @@ _SD_BEGIN_DECLARATIONS; * with journalctl --new-id128. Do not use any other IDs, and do not * count them up manually. */ -#define SD_MESSAGE_JOURNAL_START SD_ID128_MAKE(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b) -#define SD_MESSAGE_JOURNAL_STOP SD_ID128_MAKE(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b) -#define SD_MESSAGE_JOURNAL_DROPPED SD_ID128_MAKE(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e) -#define SD_MESSAGE_JOURNAL_MISSED SD_ID128_MAKE(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06) -#define SD_MESSAGE_JOURNAL_USAGE SD_ID128_MAKE(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6) - -#define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1) -#define SD_MESSAGE_TRUNCATED_CORE SD_ID128_MAKE(5a,ad,d8,e9,54,dc,4b,1a,8c,95,4d,63,fd,9e,11,37) - -#define SD_MESSAGE_SESSION_START SD_ID128_MAKE(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66) -#define SD_MESSAGE_SESSION_STOP SD_ID128_MAKE(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a) -#define SD_MESSAGE_SEAT_START SD_ID128_MAKE(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b) -#define SD_MESSAGE_SEAT_STOP SD_ID128_MAKE(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5) -#define SD_MESSAGE_MACHINE_START SD_ID128_MAKE(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2) -#define SD_MESSAGE_MACHINE_STOP SD_ID128_MAKE(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58) - -#define SD_MESSAGE_TIME_CHANGE SD_ID128_MAKE(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27) -#define SD_MESSAGE_TIMEZONE_CHANGE SD_ID128_MAKE(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90) - -#define SD_MESSAGE_STARTUP_FINISHED SD_ID128_MAKE(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff) -#define SD_MESSAGE_USER_STARTUP_FINISHED SD_ID128_MAKE(ee,d0,0a,68,ff,d8,4e,31,88,21,05,fd,97,3a,bd,d1) - -#define SD_MESSAGE_SLEEP_START SD_ID128_MAKE(6b,bd,95,ee,97,79,41,e4,97,c4,8b,e2,7c,25,41,28) -#define SD_MESSAGE_SLEEP_STOP SD_ID128_MAKE(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14) - -#define SD_MESSAGE_SHUTDOWN SD_ID128_MAKE(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40) - -#define SD_MESSAGE_UNIT_STARTING SD_ID128_MAKE(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5) -#define SD_MESSAGE_UNIT_STARTED SD_ID128_MAKE(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf) -#define SD_MESSAGE_UNIT_STOPPING SD_ID128_MAKE(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f) -#define SD_MESSAGE_UNIT_STOPPED SD_ID128_MAKE(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86) -#define SD_MESSAGE_UNIT_FAILED SD_ID128_MAKE(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d) -#define SD_MESSAGE_UNIT_RELOADING SD_ID128_MAKE(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25) -#define SD_MESSAGE_UNIT_RELOADED SD_ID128_MAKE(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54) - -#define SD_MESSAGE_SPAWN_FAILED SD_ID128_MAKE(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7) - -#define SD_MESSAGE_FORWARD_SYSLOG_MISSED SD_ID128_MAKE(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e) - -#define SD_MESSAGE_OVERMOUNTING SD_ID128_MAKE(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7) - -#define SD_MESSAGE_LID_OPENED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f) -#define SD_MESSAGE_LID_CLOSED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,70) -#define SD_MESSAGE_SYSTEM_DOCKED SD_ID128_MAKE(f5,f4,16,b8,62,07,4b,28,92,7a,48,c3,ba,7d,51,ff) -#define SD_MESSAGE_SYSTEM_UNDOCKED SD_ID128_MAKE(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53) -#define SD_MESSAGE_POWER_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71) -#define SD_MESSAGE_SUSPEND_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72) -#define SD_MESSAGE_HIBERNATE_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73) - -#define SD_MESSAGE_INVALID_CONFIGURATION SD_ID128_MAKE(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01) - -#define SD_MESSAGE_DNSSEC_FAILURE SD_ID128_MAKE(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d) -#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED SD_ID128_MAKE(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65) -#define SD_MESSAGE_DNSSEC_DOWNGRADE SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57) +#define SD_MESSAGE_JOURNAL_START SD_ID128_MAKE(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b) +#define SD_MESSAGE_JOURNAL_START_STR SD_ID128_MAKE_STR(f7,73,79,a8,49,0b,40,8b,be,5f,69,40,50,5a,77,7b) +#define SD_MESSAGE_JOURNAL_STOP SD_ID128_MAKE(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b) +#define SD_MESSAGE_JOURNAL_STOP_STR SD_ID128_MAKE_STR(d9,3f,b3,c9,c2,4d,45,1a,97,ce,a6,15,ce,59,c0,0b) +#define SD_MESSAGE_JOURNAL_DROPPED SD_ID128_MAKE(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e) +#define SD_MESSAGE_JOURNAL_DROPPED_STR SD_ID128_MAKE_STR(a5,96,d6,fe,7b,fa,49,94,82,8e,72,30,9e,95,d6,1e) +#define SD_MESSAGE_JOURNAL_MISSED SD_ID128_MAKE(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06) +#define SD_MESSAGE_JOURNAL_MISSED_STR SD_ID128_MAKE_STR(e9,bf,28,e6,e8,34,48,1b,b6,f4,8f,54,8a,d1,36,06) +#define SD_MESSAGE_JOURNAL_USAGE SD_ID128_MAKE(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6) +#define SD_MESSAGE_JOURNAL_USAGE_STR SD_ID128_MAKE_STR(ec,38,7f,57,7b,84,4b,8f,a9,48,f3,3c,ad,9a,75,e6) + +#define SD_MESSAGE_COREDUMP SD_ID128_MAKE(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1) +#define SD_MESSAGE_COREDUMP_STR SD_ID128_MAKE_STR(fc,2e,22,bc,6e,e6,47,b6,b9,07,29,ab,34,a2,50,b1) +#define SD_MESSAGE_TRUNCATED_CORE SD_ID128_MAKE(5a,ad,d8,e9,54,dc,4b,1a,8c,95,4d,63,fd,9e,11,37) +#define SD_MESSAGE_TRUNCATED_CORE_STR SD_ID128_MAKE_STR(5a,ad,d8,e9,54,dc,4b,1a,8c,95,4d,63,fd,9e,11,37) +#define SD_MESSAGE_BACKTRACE SD_ID128_MAKE(1f,4e,0a,44,a8,86,49,93,9a,ae,a3,4f,c6,da,8c,95) +#define SD_MESSAGE_BACKTRACE_STR SD_ID128_MAKE_STR(1f,4e,0a,44,a8,86,49,93,9a,ae,a3,4f,c6,da,8c,95) + +#define SD_MESSAGE_SESSION_START SD_ID128_MAKE(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66) +#define SD_MESSAGE_SESSION_START_STR SD_ID128_MAKE_STR(8d,45,62,0c,1a,43,48,db,b1,74,10,da,57,c6,0c,66) +#define SD_MESSAGE_SESSION_STOP SD_ID128_MAKE(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a) +#define SD_MESSAGE_SESSION_STOP_STR SD_ID128_MAKE_STR(33,54,93,94,24,b4,45,6d,98,02,ca,83,33,ed,42,4a) +#define SD_MESSAGE_SEAT_START SD_ID128_MAKE(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b) +#define SD_MESSAGE_SEAT_START_STR SD_ID128_MAKE_STR(fc,be,fc,5d,a2,3d,42,80,93,f9,7c,82,a9,29,0f,7b) +#define SD_MESSAGE_SEAT_STOP SD_ID128_MAKE(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5) +#define SD_MESSAGE_SEAT_STOP_STR SD_ID128_MAKE_STR(e7,85,2b,fe,46,78,4e,d0,ac,cd,e0,4b,c8,64,c2,d5) +#define SD_MESSAGE_MACHINE_START SD_ID128_MAKE(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2) +#define SD_MESSAGE_MACHINE_START_STR SD_ID128_MAKE_STR(24,d8,d4,45,25,73,40,24,96,06,83,81,a6,31,2d,f2) +#define SD_MESSAGE_MACHINE_STOP SD_ID128_MAKE(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58) +#define SD_MESSAGE_MACHINE_STOP_STR SD_ID128_MAKE_STR(58,43,2b,d3,ba,ce,47,7c,b5,14,b5,63,81,b8,a7,58) + +#define SD_MESSAGE_TIME_CHANGE SD_ID128_MAKE(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27) +#define SD_MESSAGE_TIME_CHANGE_STR SD_ID128_MAKE_STR(c7,a7,87,07,9b,35,4e,aa,a9,e7,7b,37,18,93,cd,27) +#define SD_MESSAGE_TIMEZONE_CHANGE SD_ID128_MAKE(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90) +#define SD_MESSAGE_TIMEZONE_CHANGE_STR SD_ID128_MAKE_STR(45,f8,2f,4a,ef,7a,4b,bf,94,2c,e8,61,d1,f2,09,90) + +#define SD_MESSAGE_STARTUP_FINISHED SD_ID128_MAKE(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff) +#define SD_MESSAGE_STARTUP_FINISHED_STR SD_ID128_MAKE_STR(b0,7a,24,9c,d0,24,41,4a,82,dd,00,cd,18,13,78,ff) +#define SD_MESSAGE_USER_STARTUP_FINISHED \ + SD_ID128_MAKE(ee,d0,0a,68,ff,d8,4e,31,88,21,05,fd,97,3a,bd,d1) +#define SD_MESSAGE_USER_STARTUP_FINISHED_STR \ + SD_ID128_MAKE_STR(ee,d0,0a,68,ff,d8,4e,31,88,21,05,fd,97,3a,bd,d1) + +#define SD_MESSAGE_SLEEP_START SD_ID128_MAKE(6b,bd,95,ee,97,79,41,e4,97,c4,8b,e2,7c,25,41,28) +#define SD_MESSAGE_SLEEP_START_STR SD_ID128_MAKE_STR(6b,bd,95,ee,97,79,41,e4,97,c4,8b,e2,7c,25,41,28) +#define SD_MESSAGE_SLEEP_STOP SD_ID128_MAKE(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14) +#define SD_MESSAGE_SLEEP_STOP_STR SD_ID128_MAKE_STR(88,11,e6,df,2a,8e,40,f5,8a,94,ce,a2,6f,8e,bf,14) + +#define SD_MESSAGE_SHUTDOWN SD_ID128_MAKE(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40) +#define SD_MESSAGE_SHUTDOWN_STR SD_ID128_MAKE_STR(98,26,88,66,d1,d5,4a,49,9c,4e,98,92,1d,93,bc,40) + +#define SD_MESSAGE_UNIT_STARTING SD_ID128_MAKE(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5) +#define SD_MESSAGE_UNIT_STARTING_STR SD_ID128_MAKE_STR(7d,49,58,e8,42,da,4a,75,8f,6c,1c,dc,7b,36,dc,c5) +#define SD_MESSAGE_UNIT_STARTED SD_ID128_MAKE(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf) +#define SD_MESSAGE_UNIT_STARTED_STR SD_ID128_MAKE_STR(39,f5,34,79,d3,a0,45,ac,8e,11,78,62,48,23,1f,bf) +#define SD_MESSAGE_UNIT_STOPPING SD_ID128_MAKE(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f) +#define SD_MESSAGE_UNIT_STOPPING_STR SD_ID128_MAKE_STR(de,5b,42,6a,63,be,47,a7,b6,ac,3e,aa,c8,2e,2f,6f) +#define SD_MESSAGE_UNIT_STOPPED SD_ID128_MAKE(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86) +#define SD_MESSAGE_UNIT_STOPPED_STR SD_ID128_MAKE_STR(9d,1a,aa,27,d6,01,40,bd,96,36,54,38,aa,d2,02,86) +#define SD_MESSAGE_UNIT_FAILED SD_ID128_MAKE(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d) +#define SD_MESSAGE_UNIT_FAILED_STR SD_ID128_MAKE_STR(be,02,cf,68,55,d2,42,8b,a4,0d,f7,e9,d0,22,f0,3d) +#define SD_MESSAGE_UNIT_RELOADING SD_ID128_MAKE(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25) +#define SD_MESSAGE_UNIT_RELOADING_STR SD_ID128_MAKE_STR(d3,4d,03,7f,ff,18,47,e6,ae,66,9a,37,0e,69,47,25) +#define SD_MESSAGE_UNIT_RELOADED SD_ID128_MAKE(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54) +#define SD_MESSAGE_UNIT_RELOADED_STR SD_ID128_MAKE_STR(7b,05,eb,c6,68,38,42,22,ba,a8,88,11,79,cf,da,54) + +#define SD_MESSAGE_SPAWN_FAILED SD_ID128_MAKE(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7) +#define SD_MESSAGE_SPAWN_FAILED_STR SD_ID128_MAKE_STR(64,12,57,65,1c,1b,4e,c9,a8,62,4d,7a,40,a9,e1,e7) + +#define SD_MESSAGE_FORWARD_SYSLOG_MISSED SD_ID128_MAKE(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e) +#define SD_MESSAGE_FORWARD_SYSLOG_MISSED_STR \ + SD_ID128_MAKE_STR(00,27,22,9c,a0,64,41,81,a7,6c,4e,92,45,8a,fa,2e) + +#define SD_MESSAGE_OVERMOUNTING SD_ID128_MAKE(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7) +#define SD_MESSAGE_OVERMOUNTING_STR SD_ID128_MAKE_STR(1d,ee,03,69,c7,fc,47,36,b7,09,9b,38,ec,b4,6e,e7) + +#define SD_MESSAGE_LID_OPENED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f) +#define SD_MESSAGE_LID_OPENED_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,6f) +#define SD_MESSAGE_LID_CLOSED SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,70) +#define SD_MESSAGE_LID_CLOSED_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,70) +#define SD_MESSAGE_SYSTEM_DOCKED SD_ID128_MAKE(f5,f4,16,b8,62,07,4b,28,92,7a,48,c3,ba,7d,51,ff) +#define SD_MESSAGE_SYSTEM_DOCKED_STR SD_ID128_MAKE_STR(f5,f4,16,b8,62,07,4b,28,92,7a,48,c3,ba,7d,51,ff) +#define SD_MESSAGE_SYSTEM_UNDOCKED SD_ID128_MAKE(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53) +#define SD_MESSAGE_SYSTEM_UNDOCKED_STR SD_ID128_MAKE_STR(51,e1,71,bd,58,52,48,56,81,10,14,4c,51,7c,ca,53) +#define SD_MESSAGE_POWER_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71) +#define SD_MESSAGE_POWER_KEY_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,71) +#define SD_MESSAGE_SUSPEND_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72) +#define SD_MESSAGE_SUSPEND_KEY_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,72) +#define SD_MESSAGE_HIBERNATE_KEY SD_ID128_MAKE(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73) +#define SD_MESSAGE_HIBERNATE_KEY_STR SD_ID128_MAKE_STR(b7,2e,a4,a2,88,15,45,a0,b5,0e,20,0e,55,b9,b0,73) + +#define SD_MESSAGE_INVALID_CONFIGURATION SD_ID128_MAKE(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01) +#define SD_MESSAGE_INVALID_CONFIGURATION_STR \ + SD_ID128_MAKE_STR(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01) + +#define SD_MESSAGE_DNSSEC_FAILURE SD_ID128_MAKE(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d) +#define SD_MESSAGE_DNSSEC_FAILURE_STR SD_ID128_MAKE_STR(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d) +#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED \ + SD_ID128_MAKE(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65) +#define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED_STR \ + SD_ID128_MAKE_STR(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65) +#define SD_MESSAGE_DNSSEC_DOWNGRADE SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57) +#define SD_MESSAGE_DNSSEC_DOWNGRADE_STR SD_ID128_MAKE_STR(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57) _SD_END_DECLARATIONS; diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 17b966eb52..4a0a49f2bb 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -211,7 +211,7 @@ static int make_backup(const char *target, const char *x) { if (r < 0) return r; - r = copy_bytes(src, fileno(dst), (uint64_t) -1, true); + r = copy_bytes(src, fileno(dst), (uint64_t) -1, COPY_REFLINK); if (r < 0) goto fail; diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c index 1f34a91b10..f90b73aeaf 100644 --- a/src/test/test-calendarspec.c +++ b/src/test/test-calendarspec.c @@ -192,6 +192,7 @@ int main(int argc, char* argv[]) { test_one("00..07-*-*", "2000..2007-*-* 00:00:00"); test_one("*:20..39/5", "*-*-* *:20..35/5:00"); test_one("00:00:20..40/1", "*-*-* 00:00:20..40"); + test_one("*~03/1,03..05", "*-*~03/1,03..05 00:00:00"); test_next("2016-03-27 03:17:00", "", 12345, 1459048620000000); test_next("2016-03-27 03:17:00", "CET", 12345, 1459041420000000); diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c index a027eb0fd2..b42088c680 100644 --- a/src/test/test-cgroup-mask.c +++ b/src/test/test-cgroup-mask.c @@ -27,6 +27,7 @@ #include "unit.h" static int test_cgroup_mask(void) { + _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; Manager *m = NULL; Unit *son, *daughter, *parent, *root, *grandchild, *parent_deep; FILE *serial = NULL; @@ -34,7 +35,8 @@ static int test_cgroup_mask(void) { int r; /* Prepare the manager. */ - assert_se(set_unit_path(TEST_DIR) >= 0); + assert_se(set_unit_path(get_testdata_dir("")) >= 0); + assert_se(runtime_dir = setup_fake_runtime_dir()); r = manager_new(UNIT_FILE_USER, true, &m); if (r == -EPERM || r == -EACCES) { puts("manager_new: Permission denied. Skipping test."); @@ -110,10 +112,8 @@ static int test_cgroup_mask(void) { } int main(int argc, char* argv[]) { - _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; int rc = 0; - assert_se(runtime_dir = setup_fake_runtime_dir()); TEST_REQ_RUNNING_SYSTEMD(rc = test_cgroup_mask()); return rc; diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c index c60fb631fa..30cd463722 100644 --- a/src/test/test-cgroup-util.c +++ b/src/test/test-cgroup-util.c @@ -18,11 +18,13 @@ ***/ #include "alloc-util.h" +#include "build.h" #include "cgroup-util.h" #include "dirent-util.h" #include "fd-util.h" #include "format-util.h" #include "parse-util.h" +#include "proc-cmdline.h" #include "process-util.h" #include "stat-util.h" #include "string-util.h" @@ -332,7 +334,49 @@ static void test_fd_is_cgroup_fs(void) { fd = safe_close(fd); } +static void test_is_wanted_print(bool header) { + _cleanup_free_ char *cmdline = NULL; + + log_info("-- %s --", __func__); + assert_se(proc_cmdline(&cmdline) >= 0); + log_info("cmdline: %s", cmdline); + if (header) { + + log_info(_CGROUP_HIEARCHY_); + (void) system("findmnt -n /sys/fs/cgroup"); + } + + log_info("is_unified_wanted() → %s", yes_no(cg_is_unified_wanted())); + log_info("is_hybrid_wanted() → %s", yes_no(cg_is_hybrid_wanted())); + log_info("is_legacy_wanted() → %s", yes_no(cg_is_legacy_wanted())); + log_info(" "); +} + +static void test_is_wanted(void) { + assert_se(setenv("SYSTEMD_PROC_CMDLINE", + "systemd.unified_cgroup_hierarchy", 1) >= 0); + test_is_wanted_print(false); + + assert_se(setenv("SYSTEMD_PROC_CMDLINE", + "systemd.unified_cgroup_hierarchy=0", 1) >= 0); + test_is_wanted_print(false); + + assert_se(setenv("SYSTEMD_PROC_CMDLINE", + "systemd.unified_cgroup_hierarchy=0 " + "systemd.legacy_systemd_cgroup_controller", 1) >= 0); + test_is_wanted_print(false); + + assert_se(setenv("SYSTEMD_PROC_CMDLINE", + "systemd.unified_cgroup_hierarchy=0 " + "systemd.legacy_systemd_cgroup_controller=0", 1) >= 0); + test_is_wanted_print(false); +} + int main(void) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + test_path_decode_unit(); test_path_get_unit(); test_path_get_user_unit(); @@ -349,6 +393,9 @@ int main(void) { TEST_REQ_RUNNING_SYSTEMD(test_mask_supported()); TEST_REQ_RUNNING_SYSTEMD(test_is_cgroup_fs()); TEST_REQ_RUNNING_SYSTEMD(test_fd_is_cgroup_fs()); + test_is_wanted_print(true); + test_is_wanted_print(false); /* run twice to test caching */ + test_is_wanted(); return 0; } diff --git a/src/test/test-conf-files.c b/src/test/test-conf-files.c index 03b3a9fa5c..22b7c61204 100644 --- a/src/test/test-conf-files.c +++ b/src/test/test-conf-files.c @@ -47,13 +47,16 @@ static void setup_test_dir(char *tmp_dir, const char *files, ...) { static void test_conf_files_list(bool use_root) { char tmp_dir[] = "/tmp/test-conf-files-XXXXXX"; - _cleanup_strv_free_ char **found_files = NULL; - const char *root_dir, *search_1, *search_2, *expect_a, *expect_b; + _cleanup_strv_free_ char **found_files = NULL, **found_files2 = NULL; + const char *root_dir, *search_1, *search_2, *expect_a, *expect_b, *expect_c; + + log_debug("/* %s */", __func__); setup_test_dir(tmp_dir, "/dir1/a.conf", "/dir2/a.conf", "/dir2/b.conf", + "/dir2/c.foo", NULL); if (use_root) { @@ -68,6 +71,9 @@ static void test_conf_files_list(bool use_root) { expect_a = strjoina(tmp_dir, "/dir1/a.conf"); expect_b = strjoina(tmp_dir, "/dir2/b.conf"); + expect_c = strjoina(tmp_dir, "/dir2/c.foo"); + + log_debug("/* Check when filtered by suffix */"); assert_se(conf_files_list(&found_files, ".conf", root_dir, search_1, search_2, NULL) == 0); strv_print(found_files); @@ -77,10 +83,24 @@ static void test_conf_files_list(bool use_root) { assert_se(streq_ptr(found_files[1], expect_b)); assert_se(found_files[2] == NULL); + log_debug("/* Check when unfiltered */"); + assert_se(conf_files_list(&found_files2, NULL, root_dir, search_1, search_2, NULL) == 0); + strv_print(found_files2); + + assert_se(found_files2); + assert_se(streq_ptr(found_files2[0], expect_a)); + assert_se(streq_ptr(found_files2[1], expect_b)); + assert_se(streq_ptr(found_files2[2], expect_c)); + assert_se(found_files2[3] == NULL); + assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0); } int main(int argc, char **argv) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + test_conf_files_list(false); test_conf_files_list(true); return 0; diff --git a/src/test/test-copy.c b/src/test/test-copy.c index e65516f080..ed6725611d 100644 --- a/src/test/test-copy.c +++ b/src/test/test-copy.c @@ -31,6 +31,7 @@ #include "rm-rf.h" #include "string-util.h" #include "strv.h" +#include "user-util.h" #include "util.h" static void test_copy_file(void) { @@ -52,7 +53,7 @@ static void test_copy_file(void) { assert_se(write_string_file(fn, "foo bar bar bar foo", WRITE_STRING_FILE_CREATE) == 0); - assert_se(copy_file(fn, fn_copy, 0, 0644, 0) == 0); + assert_se(copy_file(fn, fn_copy, 0, 0644, 0, COPY_REFLINK) == 0); assert_se(read_full_file(fn_copy, &buf, &sz) == 0); assert_se(streq(buf, "foo bar bar bar foo\n")); @@ -77,8 +78,8 @@ static void test_copy_file_fd(void) { assert_se(out_fd >= 0); assert_se(write_string_file(in_fn, text, WRITE_STRING_FILE_CREATE) == 0); - assert_se(copy_file_fd("/a/file/which/does/not/exist/i/guess", out_fd, true) < 0); - assert_se(copy_file_fd(in_fn, out_fd, true) >= 0); + assert_se(copy_file_fd("/a/file/which/does/not/exist/i/guess", out_fd, COPY_REFLINK) < 0); + assert_se(copy_file_fd(in_fn, out_fd, COPY_REFLINK) >= 0); assert_se(lseek(out_fd, SEEK_SET, 0) == 0); assert_se(read(out_fd, buf, sizeof(buf)) == sizeof(text) - 1); @@ -125,7 +126,7 @@ static void test_copy_tree(void) { unixsockp = strjoina(original_dir, "unixsock"); assert_se(mknod(unixsockp, S_IFSOCK|0644, 0) >= 0); - assert_se(copy_tree(original_dir, copy_dir, true) == 0); + assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK|COPY_MERGE) == 0); STRV_FOREACH(p, files) { _cleanup_free_ char *buf = NULL, *f; @@ -152,8 +153,8 @@ static void test_copy_tree(void) { assert_se(stat(unixsockp, &st) >= 0); assert_se(S_ISSOCK(st.st_mode)); - assert_se(copy_tree(original_dir, copy_dir, false) < 0); - assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, false) < 0); + assert_se(copy_tree(original_dir, copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK) < 0); + assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, UID_INVALID, GID_INVALID, COPY_REFLINK) < 0); (void) rm_rf(copy_dir, REMOVE_ROOT|REMOVE_PHYSICAL); (void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL); @@ -172,7 +173,7 @@ static void test_copy_bytes(void) { assert_se(pipe2(pipefd, O_CLOEXEC) == 0); - r = copy_bytes(infd, pipefd[1], (uint64_t) -1, false); + r = copy_bytes(infd, pipefd[1], (uint64_t) -1, 0); assert_se(r == 0); r = read(pipefd[0], buf, sizeof(buf)); @@ -185,13 +186,13 @@ static void test_copy_bytes(void) { assert_se(strneq(buf, buf2, r)); /* test copy_bytes with invalid descriptors */ - r = copy_bytes(pipefd[0], pipefd[0], 1, false); + r = copy_bytes(pipefd[0], pipefd[0], 1, 0); assert_se(r == -EBADF); - r = copy_bytes(pipefd[1], pipefd[1], 1, false); + r = copy_bytes(pipefd[1], pipefd[1], 1, 0); assert_se(r == -EBADF); - r = copy_bytes(pipefd[1], infd, 1, false); + r = copy_bytes(pipefd[1], infd, 1, 0); assert_se(r == -EBADF); } @@ -213,7 +214,7 @@ static void test_copy_bytes_regular_file(const char *src, bool try_reflink, uint fd3 = mkostemp_safe(fn3); assert_se(fd3 >= 0); - r = copy_bytes(fd, fd2, max_bytes, try_reflink); + r = copy_bytes(fd, fd2, max_bytes, try_reflink ? COPY_REFLINK : 0); if (max_bytes == (uint64_t) -1) assert_se(r == 0); else @@ -221,7 +222,7 @@ static void test_copy_bytes_regular_file(const char *src, bool try_reflink, uint assert_se(lseek(fd2, 0, SEEK_SET) == 0); - r = copy_bytes(fd2, fd3, max_bytes, try_reflink); + r = copy_bytes(fd2, fd3, max_bytes, try_reflink ? COPY_REFLINK : 0); if (max_bytes == (uint64_t) -1) assert_se(r == 0); else diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index b4db4a6702..a7cd8e4b51 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -373,16 +373,16 @@ static void test_dns_name_is_valid(void) { test_dns_name_is_valid_one("ä", 1); test_dns_name_is_valid_one("\n", 0); - /* 256 characters*/ + /* 256 characters */ test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345", 0); - /* 255 characters*/ + /* 255 characters */ test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a1234", 0); - /* 254 characters*/ + /* 254 characters */ test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a123", 0); - /* 253 characters*/ + /* 253 characters */ test_dns_name_is_valid_one("a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12345678.a12", 1); /* label of 64 chars length */ diff --git a/src/test/test-engine.c b/src/test/test-engine.c index a651f6b683..8133343fb3 100644 --- a/src/test/test-engine.c +++ b/src/test/test-engine.c @@ -37,10 +37,9 @@ int main(int argc, char *argv[]) { Job *j; int r; - assert_se(runtime_dir = setup_fake_runtime_dir()); - /* prepare the test */ - assert_se(set_unit_path(TEST_DIR) >= 0); + assert_se(set_unit_path(get_testdata_dir("")) >= 0); + assert_se(runtime_dir = setup_fake_runtime_dir()); r = manager_new(UNIT_FILE_USER, true, &m); if (MANAGER_SKIP_TEST(r)) { log_notice_errno(r, "Skipping test: manager_new: %m"); diff --git a/src/test/test-env-util.c b/src/test/test-env-util.c index 35bb62906e..e5cc2a2df8 100644 --- a/src/test/test-env-util.c +++ b/src/test/test-env-util.c @@ -45,6 +45,16 @@ static void test_strv_env_delete(void) { assert_se(strv_length(d) == 2); } +static void test_strv_env_get(void) { + char **l; + + l = STRV_MAKE("ONE_OR_TWO=1", "THREE=3", "ONE_OR_TWO=2", "FOUR=4"); + + assert_se(streq(strv_env_get(l, "ONE_OR_TWO"), "2")); + assert_se(streq(strv_env_get(l, "THREE"), "3")); + assert_se(streq(strv_env_get(l, "FOUR"), "4")); +} + static void test_strv_env_unset(void) { _cleanup_strv_free_ char **l = NULL; @@ -102,7 +112,90 @@ static void test_strv_env_merge(void) { assert_se(strv_length(r) == 5); } -static void test_replace_env_arg(void) { +static void test_env_strv_get_n(void) { + const char *_env[] = { + "FOO=NO NO NO", + "FOO=BAR BAR", + "BAR=waldo", + "PATH=unset", + NULL + }; + char **env = (char**) _env; + + assert_se(streq(strv_env_get_n(env, "FOO__", 3, 0), "BAR BAR")); + assert_se(streq(strv_env_get_n(env, "FOO__", 3, REPLACE_ENV_USE_ENVIRONMENT), "BAR BAR")); + assert_se(streq(strv_env_get_n(env, "FOO", 3, 0), "BAR BAR")); + assert_se(streq(strv_env_get_n(env, "FOO", 3, REPLACE_ENV_USE_ENVIRONMENT), "BAR BAR")); + + assert_se(streq(strv_env_get_n(env, "PATH__", 4, 0), "unset")); + assert_se(streq(strv_env_get_n(env, "PATH", 4, 0), "unset")); + assert_se(streq(strv_env_get_n(env, "PATH__", 4, REPLACE_ENV_USE_ENVIRONMENT), "unset")); + assert_se(streq(strv_env_get_n(env, "PATH", 4, REPLACE_ENV_USE_ENVIRONMENT), "unset")); + + env[3] = NULL; /* kill our $PATH */ + + assert_se(!strv_env_get_n(env, "PATH__", 4, 0)); + assert_se(!strv_env_get_n(env, "PATH", 4, 0)); + assert_se(streq(strv_env_get_n(env, "PATH__", 4, REPLACE_ENV_USE_ENVIRONMENT), + getenv("PATH"))); + assert_se(streq(strv_env_get_n(env, "PATH", 4, REPLACE_ENV_USE_ENVIRONMENT), + getenv("PATH"))); +} + +static void test_replace_env(bool braceless) { + const char *env[] = { + "FOO=BAR BAR", + "BAR=waldo", + NULL + }; + _cleanup_free_ char *t = NULL, *s = NULL, *q = NULL, *r = NULL, *p = NULL; + unsigned flags = REPLACE_ENV_ALLOW_BRACELESS*braceless; + + t = replace_env("FOO=$FOO=${FOO}", (char**) env, flags); + assert_se(streq(t, braceless ? "FOO=BAR BAR=BAR BAR" : "FOO=$FOO=BAR BAR")); + + s = replace_env("BAR=$BAR=${BAR}", (char**) env, flags); + assert_se(streq(s, braceless ? "BAR=waldo=waldo" : "BAR=$BAR=waldo")); + + q = replace_env("BARBAR=$BARBAR=${BARBAR}", (char**) env, flags); + assert_se(streq(q, braceless ? "BARBAR==" : "BARBAR=$BARBAR=")); + + r = replace_env("BAR=$BAR$BAR${BAR}${BAR}", (char**) env, flags); + assert_se(streq(r, braceless ? "BAR=waldowaldowaldowaldo" : "BAR=$BAR$BARwaldowaldo")); + + p = replace_env("${BAR}$BAR$BAR", (char**) env, flags); + assert_se(streq(p, braceless ? "waldowaldowaldo" : "waldo$BAR$BAR")); +} + +static void test_replace_env2(bool extended) { + const char *env[] = { + "FOO=foo", + "BAR=bar", + NULL + }; + _cleanup_free_ char *t = NULL, *s = NULL, *q = NULL, *r = NULL, *p = NULL, *x = NULL; + unsigned flags = REPLACE_ENV_ALLOW_EXTENDED*extended; + + t = replace_env("FOO=${FOO:-${BAR}}", (char**) env, flags); + assert_se(streq(t, extended ? "FOO=foo" : "FOO=${FOO:-bar}")); + + s = replace_env("BAR=${XXX:-${BAR}}", (char**) env, flags); + assert_se(streq(s, extended ? "BAR=bar" : "BAR=${XXX:-bar}")); + + q = replace_env("XXX=${XXX:+${BAR}}", (char**) env, flags); + assert_se(streq(q, extended ? "XXX=" : "XXX=${XXX:+bar}")); + + r = replace_env("FOO=${FOO:+${BAR}}", (char**) env, flags); + assert_se(streq(r, extended ? "FOO=bar" : "FOO=${FOO:+bar}")); + + p = replace_env("FOO=${FOO:-${BAR}post}", (char**) env, flags); + assert_se(streq(p, extended ? "FOO=foo" : "FOO=${FOO:-barpost}")); + + x = replace_env("XXX=${XXX:+${BAR}post}", (char**) env, flags); + assert_se(streq(x, extended ? "XXX=" : "XXX=${XXX:+barpost}")); +} + +static void test_replace_env_argv(void) { const char *env[] = { "FOO=BAR BAR", "BAR=waldo", @@ -120,6 +213,12 @@ static void test_replace_env_arg(void) { "${FOO", "FOO$$${FOO}", "$$FOO${FOO}", + "${FOO:-${BAR}}", + "${QUUX:-${FOO}}", + "${FOO:+${BAR}}", + "${QUUX:+${BAR}}", + "${FOO:+|${BAR}|}}", + "${FOO:+|${BAR}{|}", NULL }; _cleanup_strv_free_ char **r = NULL; @@ -137,7 +236,13 @@ static void test_replace_env_arg(void) { assert_se(streq(r[8], "${FOO")); assert_se(streq(r[9], "FOO$BAR BAR")); assert_se(streq(r[10], "$FOOBAR BAR")); - assert_se(strv_length(r) == 11); + assert_se(streq(r[11], "${FOO:-waldo}")); + assert_se(streq(r[12], "${QUUX:-BAR BAR}")); + assert_se(streq(r[13], "${FOO:+waldo}")); + assert_se(streq(r[14], "${QUUX:+waldo}")); + assert_se(streq(r[15], "${FOO:+|waldo|}}")); + assert_se(streq(r[16], "${FOO:+|waldo{|}")); + assert_se(strv_length(r) == 17); } static void test_env_clean(void) { @@ -211,10 +316,16 @@ static void test_env_assignment_is_valid(void) { int main(int argc, char *argv[]) { test_strv_env_delete(); + test_strv_env_get(); test_strv_env_unset(); test_strv_env_set(); test_strv_env_merge(); - test_replace_env_arg(); + test_env_strv_get_n(); + test_replace_env(false); + test_replace_env(true); + test_replace_env2(false); + test_replace_env2(true); + test_replace_env_argv(); test_env_clean(); test_env_name_is_valid(); test_env_value_is_valid(); diff --git a/src/test/test-exec-util.c b/src/test/test-exec-util.c new file mode 100644 index 0000000000..482b0751b9 --- /dev/null +++ b/src/test/test-exec-util.c @@ -0,0 +1,348 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + Copyright 2013 Thomas H.P. Andersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <errno.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "alloc-util.h" +#include "copy.h" +#include "def.h" +#include "env-util.h" +#include "exec-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" +#include "log.h" +#include "macro.h" +#include "rm-rf.h" +#include "string-util.h" +#include "strv.h" + +static int here = 0, here2 = 0, here3 = 0; +void *ignore_stdout_args[] = {&here, &here2, &here3}; + +/* noop handlers, just check that arguments are passed correctly */ +static int ignore_stdout_func(int fd, void *arg) { + assert(fd >= 0); + assert(arg == &here); + safe_close(fd); + + return 0; +} +static int ignore_stdout_func2(int fd, void *arg) { + assert(fd >= 0); + assert(arg == &here2); + safe_close(fd); + + return 0; +} +static int ignore_stdout_func3(int fd, void *arg) { + assert(fd >= 0); + assert(arg == &here3); + safe_close(fd); + + return 0; +} + +static const gather_stdout_callback_t ignore_stdout[] = { + ignore_stdout_func, + ignore_stdout_func2, + ignore_stdout_func3, +}; + +static void test_execute_directory(bool gather_stdout) { + char template_lo[] = "/tmp/test-exec-util.XXXXXXX"; + char template_hi[] = "/tmp/test-exec-util.XXXXXXX"; + const char * dirs[] = {template_hi, template_lo, NULL}; + const char *name, *name2, *name3, *overridden, *override, *masked, *mask; + + log_info("/* %s (%s) */", __func__, gather_stdout ? "gathering stdout" : "asynchronous"); + + assert_se(mkdtemp(template_lo)); + assert_se(mkdtemp(template_hi)); + + name = strjoina(template_lo, "/script"); + name2 = strjoina(template_hi, "/script2"); + name3 = strjoina(template_lo, "/useless"); + overridden = strjoina(template_lo, "/overridden"); + override = strjoina(template_hi, "/overridden"); + masked = strjoina(template_lo, "/masked"); + mask = strjoina(template_hi, "/masked"); + + assert_se(write_string_file(name, + "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works", + WRITE_STRING_FILE_CREATE) == 0); + assert_se(write_string_file(name2, + "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works2", + WRITE_STRING_FILE_CREATE) == 0); + assert_se(write_string_file(overridden, + "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed", + WRITE_STRING_FILE_CREATE) == 0); + assert_se(write_string_file(override, + "#!/bin/sh\necho 'Executing '$0", + WRITE_STRING_FILE_CREATE) == 0); + assert_se(write_string_file(masked, + "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed", + WRITE_STRING_FILE_CREATE) == 0); + assert_se(symlink("/dev/null", mask) == 0); + assert_se(touch(name3) >= 0); + + assert_se(chmod(name, 0755) == 0); + assert_se(chmod(name2, 0755) == 0); + assert_se(chmod(overridden, 0755) == 0); + assert_se(chmod(override, 0755) == 0); + assert_se(chmod(masked, 0755) == 0); + + if (gather_stdout) + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL); + else + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, NULL); + + assert_se(chdir(template_lo) == 0); + assert_se(access("it_works", F_OK) >= 0); + assert_se(access("failed", F_OK) < 0); + + assert_se(chdir(template_hi) == 0); + assert_se(access("it_works2", F_OK) >= 0); + assert_se(access("failed", F_OK) < 0); + + (void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL); + (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL); +} + +static void test_execution_order(void) { + char template_lo[] = "/tmp/test-exec-util-lo.XXXXXXX"; + char template_hi[] = "/tmp/test-exec-util-hi.XXXXXXX"; + const char *dirs[] = {template_hi, template_lo, NULL}; + const char *name, *name2, *name3, *overridden, *override, *masked, *mask; + const char *output, *t; + _cleanup_free_ char *contents = NULL; + + assert_se(mkdtemp(template_lo)); + assert_se(mkdtemp(template_hi)); + + output = strjoina(template_hi, "/output"); + + log_info("/* %s >>%s */", __func__, output); + + /* write files in "random" order */ + name2 = strjoina(template_lo, "/90-bar"); + name = strjoina(template_hi, "/80-foo"); + name3 = strjoina(template_lo, "/last"); + overridden = strjoina(template_lo, "/30-override"); + override = strjoina(template_hi, "/30-override"); + masked = strjoina(template_lo, "/10-masked"); + mask = strjoina(template_hi, "/10-masked"); + + t = strjoina("#!/bin/sh\necho $(basename $0) >>", output); + assert_se(write_string_file(name, t, WRITE_STRING_FILE_CREATE) == 0); + + t = strjoina("#!/bin/sh\necho $(basename $0) >>", output); + assert_se(write_string_file(name2, t, WRITE_STRING_FILE_CREATE) == 0); + + t = strjoina("#!/bin/sh\necho $(basename $0) >>", output); + assert_se(write_string_file(name3, t, WRITE_STRING_FILE_CREATE) == 0); + + t = strjoina("#!/bin/sh\necho OVERRIDDEN >>", output); + assert_se(write_string_file(overridden, t, WRITE_STRING_FILE_CREATE) == 0); + + t = strjoina("#!/bin/sh\necho $(basename $0) >>", output); + assert_se(write_string_file(override, t, WRITE_STRING_FILE_CREATE) == 0); + + t = strjoina("#!/bin/sh\necho MASKED >>", output); + assert_se(write_string_file(masked, t, WRITE_STRING_FILE_CREATE) == 0); + + assert_se(symlink("/dev/null", mask) == 0); + + assert_se(chmod(name, 0755) == 0); + assert_se(chmod(name2, 0755) == 0); + assert_se(chmod(name3, 0755) == 0); + assert_se(chmod(overridden, 0755) == 0); + assert_se(chmod(override, 0755) == 0); + assert_se(chmod(masked, 0755) == 0); + + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL); + + assert_se(read_full_file(output, &contents, NULL) >= 0); + assert_se(streq(contents, "30-override\n80-foo\n90-bar\nlast\n")); + + (void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL); + (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL); +} + +static int gather_stdout_one(int fd, void *arg) { + char ***s = arg, *t; + char buf[128] = {}; + + assert_se(s); + assert_se(read(fd, buf, sizeof buf) >= 0); + safe_close(fd); + + assert_se(t = strndup(buf, sizeof buf)); + assert_se(strv_push(s, t) >= 0); + + return 0; +} +static int gather_stdout_two(int fd, void *arg) { + char ***s = arg, **t; + + STRV_FOREACH(t, *s) + assert_se(write(fd, *t, strlen(*t)) == (ssize_t) strlen(*t)); + safe_close(fd); + + return 0; +} +static int gather_stdout_three(int fd, void *arg) { + char **s = arg; + char buf[128] = {}; + + assert_se(read(fd, buf, sizeof buf - 1) > 0); + safe_close(fd); + assert_se(*s = strndup(buf, sizeof buf)); + + return 0; +} + +const gather_stdout_callback_t const gather_stdout[] = { + gather_stdout_one, + gather_stdout_two, + gather_stdout_three, +}; + + +static void test_stdout_gathering(void) { + char template[] = "/tmp/test-exec-util.XXXXXXX"; + const char *dirs[] = {template, NULL}; + const char *name, *name2, *name3; + int r; + + char **tmp = NULL; /* this is only used in the forked process, no cleanup here */ + _cleanup_free_ char *output = NULL; + + void* args[] = {&tmp, &tmp, &output}; + + assert_se(mkdtemp(template)); + + log_info("/* %s */", __func__); + + /* write files */ + name = strjoina(template, "/10-foo"); + name2 = strjoina(template, "/20-bar"); + name3 = strjoina(template, "/30-last"); + + assert_se(write_string_file(name, + "#!/bin/sh\necho a\necho b\necho c\n", + WRITE_STRING_FILE_CREATE) == 0); + assert_se(write_string_file(name2, + "#!/bin/sh\necho d\n", + WRITE_STRING_FILE_CREATE) == 0); + assert_se(write_string_file(name3, + "#!/bin/sh\nsleep 1", + WRITE_STRING_FILE_CREATE) == 0); + + assert_se(chmod(name, 0755) == 0); + assert_se(chmod(name2, 0755) == 0); + assert_se(chmod(name3, 0755) == 0); + + r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_stdout, args, NULL); + assert_se(r >= 0); + + log_info("got: %s", output); + + assert_se(streq(output, "a\nb\nc\nd\n")); +} + +static void test_environment_gathering(void) { + char template[] = "/tmp/test-exec-util.XXXXXXX", **p; + const char *dirs[] = {template, NULL}; + const char *name, *name2, *name3; + int r; + + char **tmp = NULL; /* this is only used in the forked process, no cleanup here */ + _cleanup_strv_free_ char **env = NULL; + + void* const args[] = { &tmp, &tmp, &env }; + + assert_se(mkdtemp(template)); + + log_info("/* %s */", __func__); + + /* write files */ + name = strjoina(template, "/10-foo"); + name2 = strjoina(template, "/20-bar"); + name3 = strjoina(template, "/30-last"); + + assert_se(write_string_file(name, + "#!/bin/sh\n" + "echo A=23\n", + WRITE_STRING_FILE_CREATE) == 0); + assert_se(write_string_file(name2, + "#!/bin/sh\n" + "echo A=22:$A\n\n\n", /* substitution from previous generator */ + WRITE_STRING_FILE_CREATE) == 0); + assert_se(write_string_file(name3, + "#!/bin/sh\n" + "echo A=$A:24\n" + "echo B=12\n" + "echo C=000\n" + "echo C=001\n" /* variable overwriting */ + /* various invalid entries */ + "echo unset A\n" + "echo unset A=\n" + "echo unset A=B\n" + "echo unset \n" + "echo A B=C\n" + "echo A\n" + /* test variable assignment without newline */ + "echo PATH=$PATH:/no/such/file", /* no newline */ + WRITE_STRING_FILE_CREATE) == 0); + + assert_se(chmod(name, 0755) == 0); + assert_se(chmod(name2, 0755) == 0); + assert_se(chmod(name3, 0755) == 0); + + r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL); + assert_se(r >= 0); + + STRV_FOREACH(p, env) + log_info("got env: \"%s\"", *p); + + assert_se(streq(strv_env_get(env, "A"), "22:23:24")); + assert_se(streq(strv_env_get(env, "B"), "12")); + assert_se(streq(strv_env_get(env, "C"), "001")); + assert_se(endswith(strv_env_get(env, "PATH"), ":/no/such/file")); +} + +int main(int argc, char *argv[]) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + test_execute_directory(true); + test_execute_directory(false); + test_execution_order(); + test_stdout_gathering(); + test_environment_gathering(); + + return 0; +} diff --git a/src/test/test-execute.c b/src/test/test-execute.c index bc9a2021f9..90540b884b 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -35,6 +35,7 @@ #endif #include "stat-util.h" #include "test-helper.h" +#include "tests.h" #include "unit.h" #include "util.h" #include "virt.h" @@ -145,11 +146,11 @@ static void test_exec_privatetmp(Manager *m) { static void test_exec_privatedevices(Manager *m) { if (detect_container() > 0) { - log_notice("testing in container, skipping private device tests"); + log_notice("testing in container, skipping %s", __func__); return; } if (!is_inaccessible_available()) { - log_notice("testing without inaccessible, skipping private device tests"); + log_notice("testing without inaccessible, skipping %s", __func__); return; } @@ -158,12 +159,22 @@ static void test_exec_privatedevices(Manager *m) { } static void test_exec_privatedevices_capabilities(Manager *m) { + int r; + if (detect_container() > 0) { - log_notice("testing in container, skipping private device tests"); + log_notice("testing in container, skipping %s", __func__); return; } if (!is_inaccessible_available()) { - log_notice("testing without inaccessible, skipping private device tests"); + log_notice("testing without inaccessible, skipping %s", __func__); + return; + } + + /* We use capsh to test if the capabilities are + * properly set, so be sure that it exists */ + r = find_binary("capsh", NULL); + if (r < 0) { + log_error_errno(r, "Skipping %s, could not find capsh binary: %m", __func__); return; } @@ -174,15 +185,24 @@ static void test_exec_privatedevices_capabilities(Manager *m) { } static void test_exec_protectkernelmodules(Manager *m) { + int r; + if (detect_container() > 0) { - log_notice("testing in container, skipping protectkernelmodules tests"); + log_notice("testing in container, skipping %s", __func__); return; } if (!is_inaccessible_available()) { - log_notice("testing without inaccessible, skipping protectkernelmodules tests"); + log_notice("testing without inaccessible, skipping %s", __func__); return; } + r = find_binary("capsh", NULL); + if (r < 0) { + log_error_errno(r, "Skipping %s, could not find capsh binary: %m", __func__); + return; + } + + test(m, "exec-protectkernelmodules-no-capabilities.service", 0, CLD_EXITED); test(m, "exec-protectkernelmodules-yes-capabilities.service", 0, CLD_EXITED); test(m, "exec-protectkernelmodules-yes-mount-propagation.service", 0, CLD_EXITED); @@ -253,7 +273,7 @@ static void test_exec_systemcall_system_mode_with_user(Manager *m) { else if (getpwnam("nfsnobody")) test(m, "exec-systemcallfilter-system-user-nfsnobody.service", 0, CLD_EXITED); else - log_error_errno(errno, "Skipping test_exec_systemcall_system_mode_with_user, could not find nobody/nfsnobody user: %m"); + log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody user: %m", __func__); #endif } @@ -263,7 +283,7 @@ static void test_exec_user(Manager *m) { else if (getpwnam("nfsnobody")) test(m, "exec-user-nfsnobody.service", 0, CLD_EXITED); else - log_error_errno(errno, "Skipping test_exec_user, could not find nobody/nfsnobody user: %m"); + log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody user: %m", __func__); } static void test_exec_group(Manager *m) { @@ -272,7 +292,7 @@ static void test_exec_group(Manager *m) { else if (getgrnam("nfsnobody")) test(m, "exec-group-nfsnobody.service", 0, CLD_EXITED); else - log_error_errno(errno, "Skipping test_exec_group, could not find nobody/nfsnobody group: %m"); + log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody group: %m", __func__); } static void test_exec_supplementary_groups(Manager *m) { @@ -353,17 +373,15 @@ static void test_exec_runtimedirectory(Manager *m) { else if (getgrnam("nfsnobody")) test(m, "exec-runtimedirectory-owner-nfsnobody.service", 0, CLD_EXITED); else - log_error_errno(errno, "Skipping test_exec_runtimedirectory-owner, could not find nobody/nfsnobody group: %m"); + log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody group: %m", __func__); } static void test_exec_capabilityboundingset(Manager *m) { int r; - /* We use capsh to test if the capabilities are - * properly set, so be sure that it exists */ r = find_binary("capsh", NULL); if (r < 0) { - log_error_errno(r, "Skipping test_exec_capabilityboundingset, could not find capsh binary: %m"); + log_error_errno(r, "Skipping %s, could not find capsh binary: %m", __func__); return; } @@ -389,9 +407,9 @@ static void test_exec_capabilityambientset(Manager *m) { test(m, "exec-capabilityambientset-nfsnobody.service", 0, CLD_EXITED); test(m, "exec-capabilityambientset-merge-nfsnobody.service", 0, CLD_EXITED); } else - log_error_errno(errno, "Skipping test_exec_capabilityambientset, could not find nobody/nfsnobody user: %m"); + log_error_errno(errno, "Skipping %s, could not find nobody/nfsnobody user: %m", __func__); } else - log_error_errno(errno, "Skipping test_exec_capabilityambientset, the kernel does not support ambient capabilities: %m"); + log_error_errno(errno, "Skipping %s, the kernel does not support ambient capabilities: %m", __func__); } static void test_exec_privatenetwork(Manager *m) { @@ -399,7 +417,7 @@ static void test_exec_privatenetwork(Manager *m) { r = find_binary("ip", NULL); if (r < 0) { - log_error_errno(r, "Skipping test_exec_privatenetwork, could not find ip binary: %m"); + log_error_errno(r, "Skipping %s, could not find ip binary: %m", __func__); return; } @@ -422,6 +440,10 @@ static void test_exec_spec_interpolation(Manager *m) { test(m, "exec-spec-interpolation.service", 0, CLD_EXITED); } +static void test_exec_read_only_path_suceed(Manager *m) { + test(m, "exec-read-only-path-succeed.service", 0, CLD_EXITED); +} + static int run_tests(UnitFileScope scope, const test_function_t *tests) { const test_function_t *test = NULL; Manager *m = NULL; @@ -475,6 +497,7 @@ int main(int argc, char *argv[]) { test_exec_oomscoreadjust, test_exec_ioschedulingclass, test_exec_spec_interpolation, + test_exec_read_only_path_suceed, NULL, }; static const test_function_t system_tests[] = { @@ -494,7 +517,7 @@ int main(int argc, char *argv[]) { } assert_se(setenv("XDG_RUNTIME_DIR", "/tmp/", 1) == 0); - assert_se(set_unit_path(TEST_DIR "/test-execute/") >= 0); + assert_se(set_unit_path(get_testdata_dir("/test-execute")) >= 0); /* Unset VAR1, VAR2 and VAR3 which are used in the PassEnvironment test * cases, otherwise (and if they are present in the environment), diff --git a/src/test/test-fd-util.c b/src/test/test-fd-util.c index f555bb976c..4425b5fe5f 100644 --- a/src/test/test-fd-util.c +++ b/src/test/test-fd-util.c @@ -94,10 +94,20 @@ static void test_same_fd(void) { assert_se(same_fd(b, a) == 0); } +static void test_open_serialization_fd(void) { + _cleanup_close_ int fd = -1; + + fd = open_serialization_fd("test"); + assert_se(fd >= 0); + + write(fd, "test\n", 5); +} + int main(int argc, char *argv[]) { test_close_many(); test_close_nointr(); test_same_fd(); + test_open_serialization_fd(); return 0; } diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index 56316904a3..b1d688c89e 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -71,6 +71,8 @@ static void test_parse_env_file(void) { "seven=\"sevenval\" #nocomment\n" "eight=eightval #nocomment\n" "export nine=nineval\n" + "ten=ignored\n" + "ten=ignored\n" "ten=", f); fflush(f); @@ -204,6 +206,113 @@ static void test_parse_multiline_env_file(void) { unlink(p); } +static void test_merge_env_file(void) { + char t[] = "/tmp/test-fileio-XXXXXX"; + int fd, r; + FILE *f; + _cleanup_strv_free_ char **a = NULL; + char **i; + + fd = mkostemp_safe(t); + assert_se(fd >= 0); + + log_info("/* %s (%s) */", __func__, t); + + f = fdopen(fd, "w"); + assert_se(f); + + r = write_string_stream(f, + "one=1 \n" + "twelve=${one}2\n" + "twentyone=2${one}\n" + "one=2\n" + "twentytwo=2${one}\n" + "xxx_minus_three=$xxx - 3\n" + "xxx=0x$one$one$one\n" + "yyy=${one:-fallback}\n" + "zzz=${one:+replacement}\n" + "zzzz=${foobar:-${nothing}}\n" + "zzzzz=${nothing:+${nothing}}\n" + , false); + assert(r >= 0); + + r = merge_env_file(&a, NULL, t); + assert_se(r >= 0); + strv_sort(a); + + STRV_FOREACH(i, a) + log_info("Got: <%s>", *i); + + assert_se(streq(a[0], "one=2")); + assert_se(streq(a[1], "twelve=12")); + assert_se(streq(a[2], "twentyone=21")); + assert_se(streq(a[3], "twentytwo=22")); + assert_se(streq(a[4], "xxx=0x222")); + assert_se(streq(a[5], "xxx_minus_three= - 3")); + assert_se(streq(a[6], "yyy=2")); + assert_se(streq(a[7], "zzz=replacement")); + assert_se(streq(a[8], "zzzz=")); + assert_se(streq(a[9], "zzzzz=")); + assert_se(a[10] == NULL); + + r = merge_env_file(&a, NULL, t); + assert_se(r >= 0); + strv_sort(a); + + STRV_FOREACH(i, a) + log_info("Got2: <%s>", *i); + + assert_se(streq(a[0], "one=2")); + assert_se(streq(a[1], "twelve=12")); + assert_se(streq(a[2], "twentyone=21")); + assert_se(streq(a[3], "twentytwo=22")); + assert_se(streq(a[4], "xxx=0x222")); + assert_se(streq(a[5], "xxx_minus_three=0x222 - 3")); + assert_se(streq(a[6], "yyy=2")); + assert_se(streq(a[7], "zzz=replacement")); + assert_se(streq(a[8], "zzzz=")); + assert_se(streq(a[9], "zzzzz=")); + assert_se(a[10] == NULL); +} + +static void test_merge_env_file_invalid(void) { + char t[] = "/tmp/test-fileio-XXXXXX"; + int fd, r; + FILE *f; + _cleanup_strv_free_ char **a = NULL; + char **i; + + fd = mkostemp_safe(t); + assert_se(fd >= 0); + + log_info("/* %s (%s) */", __func__, t); + + f = fdopen(fd, "w"); + assert_se(f); + + r = write_string_stream(f, + "unset one \n" + "unset one= \n" + "unset one=1 \n" + "one \n" + "one = \n" + "one two =\n" + "\x20two=\n" + "#comment=comment\n" + ";comment2=comment2\n" + "#\n" + "\n\n" /* empty line */ + , false); + assert(r >= 0); + + r = merge_env_file(&a, NULL, t); + assert_se(r >= 0); + + STRV_FOREACH(i, a) + log_info("Got: <%s>", *i); + + assert_se(strv_isempty(a)); +} static void test_executable_is_script(void) { char t[] = "/tmp/test-executable-XXXXXX"; @@ -555,11 +664,14 @@ static void test_tempfn(void) { } int main(int argc, char *argv[]) { + log_set_max_level(LOG_DEBUG); log_parse_environment(); log_open(); test_parse_env_file(); test_parse_multiline_env_file(); + test_merge_env_file(); + test_merge_env_file_invalid(); test_executable_is_script(); test_status_field(); test_capeff(); diff --git a/src/test/test-ipcrm.c b/src/test/test-ipcrm.c index 463e135e2b..ce6c7aa18a 100644 --- a/src/test/test-ipcrm.c +++ b/src/test/test-ipcrm.c @@ -24,7 +24,7 @@ int main(int argc, char *argv[]) { uid_t uid; int r; - const char* name = argv[1] ?: "nfsnobody"; + const char* name = argv[1] ?: NOBODY_USER_NAME; r = get_user_creds(&name, &uid, NULL, NULL, NULL); if (r < 0) { diff --git a/src/test/test-journal-importer.c b/src/test/test-journal-importer.c new file mode 100644 index 0000000000..a61212ce7b --- /dev/null +++ b/src/test/test-journal-importer.c @@ -0,0 +1,90 @@ +/*** + This file is part of systemd. + + Copyright 2016 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "log.h" +#include "journal-importer.h" +#include "string-util.h" +#include "tests.h" + +static void assert_iovec_entry(const struct iovec *iovec, const char* content) { + assert_se(strlen(content) == iovec->iov_len); + assert_se(memcmp(content, iovec->iov_base, iovec->iov_len) == 0); +} + +#define COREDUMP_PROC_GROUP \ + "COREDUMP_PROC_CGROUP=1:name=systemd:/\n" \ + "0::/user.slice/user-1002.slice/user@1002.service/gnome-terminal-server.service\n" + +static void test_basic_parsing(void) { + _cleanup_(journal_importer_cleanup) JournalImporter imp = {}; + int r; + + imp.fd = open(get_testdata_dir("/journal-data/journal-1.txt"), O_RDONLY|O_CLOEXEC); + assert_se(imp.fd >= 0); + + do + r = journal_importer_process_data(&imp); + while (r == 0 && !journal_importer_eof(&imp)); + assert_se(r == 1); + + /* We read one entry, so we should get EOF on next read, but not yet */ + assert_se(!journal_importer_eof(&imp)); + + assert_se(imp.iovw.count == 6); + assert_iovec_entry(&imp.iovw.iovec[0], "_BOOT_ID=1531fd22ec84429e85ae888b12fadb91"); + assert_iovec_entry(&imp.iovw.iovec[1], "_TRANSPORT=journal"); + assert_iovec_entry(&imp.iovw.iovec[2], COREDUMP_PROC_GROUP); + assert_iovec_entry(&imp.iovw.iovec[3], "COREDUMP_RLIMIT=-1"); + assert_iovec_entry(&imp.iovw.iovec[4], COREDUMP_PROC_GROUP); + assert_iovec_entry(&imp.iovw.iovec[5], "_SOURCE_REALTIME_TIMESTAMP=1478389147837945"); + + /* Let's check if we get EOF now */ + r = journal_importer_process_data(&imp); + assert_se(r == 0); + assert_se(journal_importer_eof(&imp)); +} + +static void test_bad_input(void) { + _cleanup_(journal_importer_cleanup) JournalImporter imp = {}; + int r; + + imp.fd = open(get_testdata_dir("/journal-data/journal-2.txt"), O_RDONLY|O_CLOEXEC); + assert_se(imp.fd >= 0); + + do + r = journal_importer_process_data(&imp); + while (!journal_importer_eof(&imp)); + assert_se(r == 0); /* If we don't have enough input, 0 is returned */ + + assert_se(journal_importer_eof(&imp)); +} + +int main(int argc, char **argv) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + + test_basic_parsing(); + test_bad_input(); + + return 0; +} diff --git a/src/test/test-path.c b/src/test/test-path.c index 5e99d478ee..70ac6b3df3 100644 --- a/src/test/test-path.c +++ b/src/test/test-path.c @@ -262,8 +262,8 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); + assert_se(set_unit_path(get_testdata_dir("/test-path")) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - assert_se(set_unit_path(TEST_DIR "/test-path/") >= 0); for (test = tests; test && *test; test++) { int r; diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c index 7b37910c33..81d9abc2d5 100644 --- a/src/test/test-sched-prio.c +++ b/src/test/test-sched-prio.c @@ -34,10 +34,9 @@ int main(int argc, char *argv[]) { FDSet *fdset = NULL; int r; - assert_se(runtime_dir = setup_fake_runtime_dir()); - /* prepare the test */ - assert_se(set_unit_path(TEST_DIR) >= 0); + assert_se(set_unit_path(get_testdata_dir("")) >= 0); + assert_se(runtime_dir = setup_fake_runtime_dir()); r = manager_new(UNIT_FILE_USER, true, &m); if (MANAGER_SKIP_TEST(r)) { log_notice_errno(r, "Skipping test: manager_new: %m"); diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c index d80613dc84..8ac1d7989f 100644 --- a/src/test/test-socket-util.c +++ b/src/test/test-socket-util.c @@ -456,6 +456,23 @@ static void test_sockaddr_un_len(void) { assert_se(SOCKADDR_UN_LEN(abstract) == offsetof(struct sockaddr_un, sun_path) + 1 + strlen(abstract.sun_path + 1)); } +static void test_in_addr_is_multicast(void) { + union in_addr_union a, b; + int f; + + assert_se(in_addr_from_string_auto("192.168.3.11", &f, &a) >= 0); + assert_se(in_addr_is_multicast(f, &a) == 0); + + assert_se(in_addr_from_string_auto("224.0.0.1", &f, &a) >= 0); + assert_se(in_addr_is_multicast(f, &a) == 1); + + assert_se(in_addr_from_string_auto("FF01:0:0:0:0:0:0:1", &f, &b) >= 0); + assert_se(in_addr_is_multicast(f, &b) == 1); + + assert_se(in_addr_from_string_auto("2001:db8::c:69b:aeff:fe53:743e", &f, &b) >= 0); + assert_se(in_addr_is_multicast(f, &b) == 0); +} + int main(int argc, char *argv[]) { log_set_max_level(LOG_DEBUG); @@ -482,5 +499,7 @@ int main(int argc, char *argv[]) { test_sockaddr_un_len(); + test_in_addr_is_multicast(); + return 0; } diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c index a48dca99e1..3ff2aadea4 100644 --- a/src/test/test-stat-util.c +++ b/src/test/test-stat-util.c @@ -26,6 +26,7 @@ #include "fileio.h" #include "macro.h" #include "missing.h" +#include "mount-util.h" #include "stat-util.h" static void test_files_same(void) { @@ -69,8 +70,11 @@ static void test_path_is_os_tree(void) { } static void test_path_check_fstype(void) { - assert_se(path_check_fstype("/run", TMPFS_MAGIC) > 0); - assert_se(path_check_fstype("/run", BTRFS_SUPER_MAGIC) == 0); + /* run might not be a mount point in build chroots */ + if (path_is_mount_point("/run", NULL, AT_SYMLINK_FOLLOW) > 0) { + assert_se(path_check_fstype("/run", TMPFS_MAGIC) > 0); + assert_se(path_check_fstype("/run", BTRFS_SUPER_MAGIC) == 0); + } assert_se(path_check_fstype("/proc", PROC_SUPER_MAGIC) > 0); assert_se(path_check_fstype("/proc", BTRFS_SUPER_MAGIC) == 0); assert_se(path_check_fstype("/proc", BTRFS_SUPER_MAGIC) == 0); @@ -78,7 +82,9 @@ static void test_path_check_fstype(void) { } static void test_path_is_temporary_fs(void) { - assert_se(path_is_temporary_fs("/run") > 0); + /* run might not be a mount point in build chroots */ + if (path_is_mount_point("/run", NULL, AT_SYMLINK_FOLLOW) > 0) + assert_se(path_is_temporary_fs("/run") > 0); assert_se(path_is_temporary_fs("/proc") == 0); assert_se(path_is_temporary_fs("/i-dont-exist") == -ENOENT); } diff --git a/src/test/test-util.c b/src/test/test-util.c index 1b5cba86c1..f8bf0cb875 100644 --- a/src/test/test-util.c +++ b/src/test/test-util.c @@ -195,50 +195,6 @@ static void test_log2i(void) { assert_se(log2i(INT_MAX) == sizeof(int)*8-2); } -static void test_execute_directory(void) { - char template_lo[] = "/tmp/test-readlink_and_make_absolute-lo.XXXXXXX"; - char template_hi[] = "/tmp/test-readlink_and_make_absolute-hi.XXXXXXX"; - const char * dirs[] = {template_hi, template_lo, NULL}; - const char *name, *name2, *name3, *overridden, *override, *masked, *mask; - - assert_se(mkdtemp(template_lo)); - assert_se(mkdtemp(template_hi)); - - name = strjoina(template_lo, "/script"); - name2 = strjoina(template_hi, "/script2"); - name3 = strjoina(template_lo, "/useless"); - overridden = strjoina(template_lo, "/overridden"); - override = strjoina(template_hi, "/overridden"); - masked = strjoina(template_lo, "/masked"); - mask = strjoina(template_hi, "/masked"); - - assert_se(write_string_file(name, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works", WRITE_STRING_FILE_CREATE) == 0); - assert_se(write_string_file(name2, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works2", WRITE_STRING_FILE_CREATE) == 0); - assert_se(write_string_file(overridden, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed", WRITE_STRING_FILE_CREATE) == 0); - assert_se(write_string_file(override, "#!/bin/sh\necho 'Executing '$0", WRITE_STRING_FILE_CREATE) == 0); - assert_se(write_string_file(masked, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/failed", WRITE_STRING_FILE_CREATE) == 0); - assert_se(symlink("/dev/null", mask) == 0); - assert_se(chmod(name, 0755) == 0); - assert_se(chmod(name2, 0755) == 0); - assert_se(chmod(overridden, 0755) == 0); - assert_se(chmod(override, 0755) == 0); - assert_se(chmod(masked, 0755) == 0); - assert_se(touch(name3) >= 0); - - execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL); - - assert_se(chdir(template_lo) == 0); - assert_se(access("it_works", F_OK) >= 0); - assert_se(access("failed", F_OK) < 0); - - assert_se(chdir(template_hi) == 0); - assert_se(access("it_works2", F_OK) >= 0); - assert_se(access("failed", F_OK) < 0); - - (void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL); - (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL); -} - static void test_raw_clone(void) { pid_t parent, pid, pid2; @@ -359,7 +315,6 @@ int main(int argc, char *argv[]) { test_protect_errno(); test_in_set(); test_log2i(); - test_execute_directory(); test_raw_clone(); test_physical_memory(); test_physical_memory_scale(); diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c index 553ef67011..281b1534a3 100644 --- a/src/timedate/timedatectl.c +++ b/src/timedate/timedatectl.c @@ -165,6 +165,8 @@ static int show_status(sd_bus *bus, char **args, unsigned n) { { "RTCTimeUSec", "t", NULL, offsetof(StatusInfo, rtc_time) }, {} }; + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; assert(bus); @@ -173,9 +175,10 @@ static int show_status(sd_bus *bus, char **args, unsigned n) { "org.freedesktop.timedate1", "/org/freedesktop/timedate1", map, + &error, &info); if (r < 0) - return log_error_errno(r, "Failed to query server: %m"); + return log_error_errno(r, "Failed to query server: %s", bus_error_message(&error, r)); print_status_info(&info); diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c index 490929e93b..1061b094d3 100644 --- a/src/timedate/timedated.c +++ b/src/timedate/timedated.c @@ -413,7 +413,7 @@ static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error * } log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_TIMEZONE_CHANGE), + "MESSAGE_ID=" SD_MESSAGE_TIMEZONE_CHANGE_STR, "TIMEZONE=%s", c->zone, LOG_MESSAGE("Changed time zone to '%s'.", c->zone), NULL); @@ -591,7 +591,7 @@ static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *erro clock_set_hwclock(tm); log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_TIME_CHANGE), + "MESSAGE_ID=" SD_MESSAGE_TIME_CHANGE_STR, "REALTIME="USEC_FMT, timespec_load(&ts), LOG_MESSAGE("Changed local time to %s", ctime(&ts.tv_sec)), NULL); diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index c4f4d46ca1..7326597b8c 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -872,7 +872,7 @@ static int parse_attribute_from_arg(Item *item) { { 's', FS_SECRM_FL }, /* Secure deletion */ { 'u', FS_UNRM_FL }, /* Undelete */ { 't', FS_NOTAIL_FL }, /* file tail should not be merged */ - { 'T', FS_TOPDIR_FL }, /* Top of directory hierarchies*/ + { 'T', FS_TOPDIR_FL }, /* Top of directory hierarchies */ { 'C', FS_NOCOW_FL }, /* Do not cow file */ }; @@ -1170,7 +1170,7 @@ static int create_item(Item *i) { return log_error_errno(r, "Failed to substitute specifiers in copy source %s: %m", i->argument); log_debug("Copying tree \"%s\" to \"%s\".", resolved, i->path); - r = copy_tree(resolved, i->path, false); + r = copy_tree(resolved, i->path, i->uid_set ? i->uid : UID_INVALID, i->gid_set ? i->gid : GID_INVALID, COPY_REFLINK); if (r == -EROFS && stat(i->path, &st) == 0) r = -EEXIST; diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c index 3c58445836..9037aa1304 100644 --- a/src/udev/udev-builtin-blkid.c +++ b/src/udev/udev-builtin-blkid.c @@ -122,7 +122,7 @@ static int find_gpt_root(struct udev_device *dev, blkid_probe pr, bool test) { errno = 0; pl = blkid_probe_get_partitions(pr); if (!pl) - return errno > 0 ? -errno : -ENOMEM; + return -errno ?: -ENOMEM; nvals = blkid_partlist_numof_partitions(pl); for (i = 0; i < nvals; i++) { @@ -193,7 +193,7 @@ static int probe_superblocks(blkid_probe pr) { int rc; if (fstat(blkid_probe_get_fd(pr), &st)) - return -1; + return -errno; blkid_probe_enable_partitions(pr, 1); diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c index 5be158f527..bd7b789cad 100644 --- a/src/udev/udev-builtin-net_id.c +++ b/src/udev/udev-builtin-net_id.c @@ -34,7 +34,8 @@ * * Type of names: * b<number> — BCMA bus core number - * c<bus_id> — CCW bus group name, without leading zeros [s390] + * c<bus_id> — bus id of a grouped CCW or CCW device, + * with all leading zeros stripped [s390] * o<index>[n<phys_port_name>|d<dev_port>] * — on-board device index number * s<slot>[f<function>][n<phys_port_name>|d<dev_port>] @@ -87,6 +88,11 @@ * /sys/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.2/2-1.2:1.0/net/enp0s29u1u2 * ID_NET_NAME_MAC=enxd626b3450fb5 * ID_NET_NAME_PATH=enp0s29u1u2 + * + * s390 grouped CCW interface: + * /sys/devices/css0/0.0.0007/0.0.f5f0/group_device/net/encf5f0 + * ID_NET_NAME_MAC=enx026d3c00000a + * ID_NET_NAME_PATH=encf5f0 */ #include <errno.h> @@ -115,7 +121,7 @@ enum netname_type{ NET_USB, NET_BCMA, NET_VIRTIO, - NET_CCWGROUP, + NET_CCW, }; struct netnames { @@ -132,9 +138,21 @@ struct netnames { char usb_ports[IFNAMSIZ]; char bcma_core[IFNAMSIZ]; - char ccw_group[IFNAMSIZ]; + char ccw_busid[IFNAMSIZ]; }; +/* skip intermediate virtio devices */ +static struct udev_device *skip_virtio(struct udev_device *dev) { + struct udev_device *parent = dev; + + /* there can only ever be one virtio bus per parent device, so we can + safely ignore any virtio buses. see + <http://lists.linuxfoundation.org/pipermail/virtualization/2015-August/030331.html> */ + while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent))) + parent = udev_device_get_parent(parent); + return parent; +} + /* retrieve on-board index number and label from firmware */ static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) { unsigned dev_port = 0; @@ -308,12 +326,8 @@ static int names_pci(struct udev_device *dev, struct netnames *names) { assert(names); parent = udev_device_get_parent(dev); - - /* there can only ever be one virtio bus per parent device, so we can - safely ignore any virtio buses. see - <http://lists.linuxfoundation.org/pipermail/virtualization/2015-August/030331.html> */ - while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent))) - parent = udev_device_get_parent(parent); + /* skip virtio subsystem if present */ + parent = skip_virtio(parent); if (!parent) return -ENOENT; @@ -412,8 +426,9 @@ static int names_bcma(struct udev_device *dev, struct netnames *names) { static int names_ccw(struct udev_device *dev, struct netnames *names) { struct udev_device *cdev; - const char *bus_id; + const char *bus_id, *subsys; size_t bus_id_len; + size_t bus_id_start; int rc; assert(dev); @@ -421,14 +436,17 @@ static int names_ccw(struct udev_device *dev, struct netnames *names) { /* Retrieve the associated CCW device */ cdev = udev_device_get_parent(dev); + /* skip virtio subsystem if present */ + cdev = skip_virtio(cdev); if (!cdev) return -ENOENT; - /* Network devices are always grouped CCW devices */ - if (!streq_ptr("ccwgroup", udev_device_get_subsystem(cdev))) + /* Network devices are either single or grouped CCW devices */ + subsys = udev_device_get_subsystem(cdev); + if (!STRPTR_IN_SET(subsys, "ccwgroup", "ccw")) return -ENOENT; - /* Retrieve bus-ID of the grouped CCW device. The bus-ID uniquely + /* Retrieve bus-ID of the CCW device. The bus-ID uniquely * identifies the network device on the Linux on System z channel * subsystem. Note that the bus-ID contains lowercase characters. */ @@ -447,14 +465,15 @@ static int names_ccw(struct udev_device *dev, struct netnames *names) { /* Strip leading zeros from the bus id for aesthetic purposes. This * keeps the ccw names stable, yet much shorter in general case of * bus_id 0.0.0600 -> 600. This is similar to e.g. how PCI domain is - * not prepended when it is zero. + * not prepended when it is zero. Preserve the last 0 for 0.0.0000. */ - bus_id += strspn(bus_id, ".0"); + bus_id_start = strspn(bus_id, ".0"); + bus_id += bus_id_start < bus_id_len ? bus_id_start : bus_id_len - 1; /* Store the CCW bus-ID for use as network device name */ - rc = snprintf(names->ccw_group, sizeof(names->ccw_group), "c%s", bus_id); - if (rc >= 0 && rc < (int)sizeof(names->ccw_group)) - names->type = NET_CCWGROUP; + rc = snprintf(names->ccw_busid, sizeof(names->ccw_busid), "c%s", bus_id); + if (rc >= 0 && rc < (int)sizeof(names->ccw_busid)) + names->type = NET_CCW; return 0; } @@ -564,10 +583,10 @@ static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool /* get path names for Linux on System z network devices */ err = names_ccw(dev, &names); - if (err >= 0 && names.type == NET_CCWGROUP) { + if (err >= 0 && names.type == NET_CCW) { char str[IFNAMSIZ]; - if (snprintf(str, sizeof(str), "%s%s", prefix, names.ccw_group) < (int)sizeof(str)) + if (snprintf(str, sizeof(str), "%s%s", prefix, names.ccw_busid) < (int)sizeof(str)) udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str); goto out; } diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c index 527f0bff2d..8cb330dba1 100644 --- a/src/udev/udev-builtin-path_id.c +++ b/src/udev/udev-builtin-path_id.c @@ -664,11 +664,8 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool parent = skip_subsystem(parent, "xen"); supported_parent = true; } else if (streq(subsys, "virtio")) { - while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent))) - parent = udev_device_get_parent(parent); - path_prepend(&path, "virtio-pci-%s", udev_device_get_sysname(parent)); + parent = skip_subsystem(parent, "virtio"); supported_transport = true; - supported_parent = true; } else if (streq(subsys, "scm")) { path_prepend(&path, "scm-%s", udev_device_get_sysname(parent)); parent = skip_subsystem(parent, "scm"); diff --git a/src/udev/udevd.c b/src/udev/udevd.c index dd23054b0d..ce2ff89b85 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -1210,7 +1210,7 @@ static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, voi else log_warning("worker ["PID_FMT"] exited with return code %i", pid, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { - log_warning("worker ["PID_FMT"] terminated by signal %i (%s)", pid, WTERMSIG(status), strsignal(WTERMSIG(status))); + log_warning("worker ["PID_FMT"] terminated by signal %i (%s)", pid, WTERMSIG(status), signal_to_string(WTERMSIG(status))); } else if (WIFSTOPPED(status)) { log_info("worker ["PID_FMT"] stopped", pid); continue; diff --git a/src/update-done/update-done.c b/src/update-done/update-done.c index 48c2a3fff4..d466e1b759 100644 --- a/src/update-done/update-done.c +++ b/src/update-done/update-done.c @@ -26,7 +26,7 @@ #define MESSAGE \ "# This file was created by systemd-update-done. Its only \n" \ "# purpose is to hold a timestamp of the time this directory\n" \ - "# was updated. See systemd-update-done.service(8).\n" + "# was updated. See man:systemd-update-done.service(8).\n" static int apply_timestamp(const char *path, struct timespec *ts) { struct timespec twice[2] = { |