diff options
Diffstat (limited to 'src')
148 files changed, 3166 insertions, 1314 deletions
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 51d881c5fb..a9402fdb28 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)); diff --git a/src/basic/MurmurHash2.c b/src/basic/MurmurHash2.c index 9020793930..a282a21201 100644 --- a/src/basic/MurmurHash2.c +++ b/src/basic/MurmurHash2.c @@ -69,9 +69,9 @@ uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed ) switch(len) { - case 3: h ^= data[2] << 16; - case 2: h ^= data[1] << 8; - case 1: h ^= data[0]; + case 3: h ^= data[2] << 16; /* fall through */ + case 2: h ^= data[1] << 8; /* fall through */ + case 1: h ^= data[0]; /* fall through */ h *= m; }; 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..d6b8603b06 100644 --- a/src/basic/architecture.h +++ b/src/basic/architecture.h @@ -194,7 +194,8 @@ int uname_architecture(void); #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/calendarspec.c b/src/basic/calendarspec.c index 2e5622699d..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; } @@ -1020,7 +1019,7 @@ static int find_end_of_month(struct tm *tm, bool utc, int day) { t.tm_mon++; t.tm_mday = 1 - day; - if (mktime_or_timegm(&t, utc) == (time_t) -1 || + if (mktime_or_timegm(&t, utc) < 0 || t.tm_mon != tm->tm_mon) return -1; @@ -1086,7 +1085,7 @@ static bool tm_out_of_bounds(const struct tm *tm, bool utc) { t = *tm; - if (mktime_or_timegm(&t, utc) == (time_t) -1) + if (mktime_or_timegm(&t, utc) < 0) return true; /* @@ -1115,7 +1114,7 @@ static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) { return true; t = *tm; - if (mktime_or_timegm(&t, utc) == (time_t) -1) + if (mktime_or_timegm(&t, utc) < 0) return false; k = t.tm_wday == 0 ? 6 : t.tm_wday - 1; @@ -1228,6 +1227,9 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) assert(spec); assert(next); + if (usec > USEC_TIMESTAMP_FORMATTABLE_MAX) + return -EINVAL; + usec++; t = (time_t) (usec / USEC_PER_SEC); assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc)); @@ -1238,7 +1240,7 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) return r; t = mktime_or_timegm(&tm, spec->utc); - if (t == (time_t) -1) + if (t < 0) return -EINVAL; *next = (usec_t) t * USEC_PER_SEC + tm_usec; diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index d2d18f13f0..6948ed3931 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -182,8 +182,7 @@ int cg_read_subgroup(DIR *d, char **fn) { if (de->d_type != DT_DIR) continue; - if (streq(de->d_name, ".") || - streq(de->d_name, "..")) + if (dot_or_dot_dot(de->d_name)) continue; b = strdup(de->d_name); diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c index c0c22610d7..b5780194df 100644 --- a/src/basic/conf-files.c +++ b/src/basic/conf-files.c @@ -43,7 +43,6 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char int r; assert(path); - assert(suffix); dirpath = prefix_roota(root, path); @@ -94,7 +93,6 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const int r; assert(strv); - assert(suffix); /* This alters the dirs string array */ if (!path_strv_resolve_uniq(dirs, root)) @@ -126,7 +124,6 @@ int conf_files_list_strv(char ***strv, const char *suffix, const char *root, con _cleanup_strv_free_ char **copy = NULL; assert(strv); - assert(suffix); copy = strv_copy((char**) dirs); if (!copy) diff --git a/src/basic/copy.c b/src/basic/copy.c index 9883f5fa31..e9a7efd232 100644 --- a/src/basic/copy.c +++ b/src/basic/copy.c @@ -331,7 +331,7 @@ static int fd_copy_directory( struct stat buf; int q; - if (STR_IN_SET(de->d_name, ".", "..")) + if (dot_or_dot_dot(de->d_name)) continue; if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) { diff --git a/src/basic/dirent-util.c b/src/basic/dirent-util.c index 59067121b7..6b9d26773e 100644 --- a/src/basic/dirent-util.c +++ b/src/basic/dirent-util.c @@ -70,5 +70,8 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) { if (de->d_name[0] == '.') return false; + if (!suffix) + return true; + return endswith(de->d_name, suffix); } diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index e31fa2711a..8fe19ee4e4 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -723,6 +723,8 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, return -errno; if (S_ISLNK(st.st_mode)) { + char *joined; + _cleanup_free_ char *destination = NULL; /* This is a symlink, in this case read the destination. But let's make sure we don't follow @@ -746,9 +748,6 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fd < 0) return -errno; - free_and_replace(buffer, destination); - - todo = buffer; free(done); /* Note that we do not revalidate the root, we take it as is. */ @@ -760,19 +759,17 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, return -ENOMEM; } - } else { - char *joined; + } - /* A relative destination. If so, this is what we'll prefix what's left to do with what - * we just read, and start the loop again, but remain in the current directory. */ + /* Prefix what's left to do with what we just read, and start the loop again, + * but remain in the current directory. */ - joined = strjoin("/", destination, todo); - if (!joined) - return -ENOMEM; + joined = strjoin("/", destination, todo); + if (!joined) + return -ENOMEM; - free(buffer); - todo = buffer = joined; - } + free(buffer); + todo = buffer = joined; continue; } diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c index 6843aedd0a..2d6e377f0a 100644 --- a/src/basic/hexdecoct.c +++ b/src/basic/hexdecoct.c @@ -72,10 +72,10 @@ int unhexchar(char c) { } char *hexmem(const void *p, size_t l) { - char *r, *z; const uint8_t *x; + char *r, *z; - z = r = malloc(l * 2 + 1); + z = r = new(char, l * 2 + 1); if (!r) return NULL; diff --git a/src/basic/khash.c b/src/basic/khash.c index 9a2a3edb75..84648dc1c9 100644 --- a/src/basic/khash.c +++ b/src/basic/khash.c @@ -143,7 +143,7 @@ int khash_dup(khash *h, khash **ret) { copy->fd = -1; copy->algorithm = strdup(h->algorithm); - if (!copy) + if (!copy->algorithm) return -ENOMEM; copy->fd = accept4(h->fd, NULL, 0, SOCK_CLOEXEC); diff --git a/src/basic/nss-util.h b/src/basic/nss-util.h index e7844fff96..9d927a8227 100644 --- a/src/basic/nss-util.h +++ b/src/basic/nss-util.h @@ -27,6 +27,10 @@ #define NSS_SIGNALS_BLOCK SIGALRM,SIGVTALRM,SIGPIPE,SIGCHLD,SIGTSTP,SIGIO,SIGHUP,SIGUSR1,SIGUSR2,SIGPROF,SIGURG,SIGWINCH +#ifndef DEPRECATED_RES_USE_INET6 +# define DEPRECATED_RES_USE_INET6 0x00002000 +#endif + #define NSS_GETHOSTBYNAME_PROTOTYPES(module) \ enum nss_status _nss_##module##_gethostbyname4_r( \ const char *name, \ @@ -92,7 +96,7 @@ enum nss_status _nss_##module##_gethostbyname_r( \ int *errnop, int *h_errnop) { \ enum nss_status ret = NSS_STATUS_NOTFOUND; \ \ - if (_res.options & RES_USE_INET6) \ + if (_res.options & DEPRECATED_RES_USE_INET6) \ ret = _nss_##module##_gethostbyname3_r( \ name, \ AF_INET6, \ diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 9a51e0d8bc..1313a52c9c 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -699,10 +699,7 @@ bool filename_is_valid(const char *p) { if (isempty(p)) return false; - if (streq(p, ".")) - return false; - - if (streq(p, "..")) + if (dot_or_dot_dot(p)) return false; e = strchrnul(p, '/'); @@ -720,14 +717,17 @@ bool path_is_safe(const char *p) { if (isempty(p)) return false; - if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../")) + if (dot_or_dot_dot(p)) + return false; + + if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../")) return false; if (strlen(p)+1 > PATH_MAX) return false; /* The following two checks are not really dangerous, but hey, they still are confusing */ - if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./")) + if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./")) return false; if (strstr(p, "//")) @@ -892,3 +892,16 @@ int systemd_installation_has_version(const char *root, unsigned minimal_version) return false; } + +bool dot_or_dot_dot(const char *path) { + if (!path) + return false; + if (path[0] != '.') + return false; + if (path[1] == 0) + return true; + if (path[1] != '.') + return false; + + return path[2] == 0; +} diff --git a/src/basic/path-util.h b/src/basic/path-util.h index 349cdac7d6..35aef3adc8 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -141,3 +141,5 @@ bool is_device_path(const char *path); bool is_deviceallow_pattern(const char *path); int systemd_installation_has_version(const char *root, unsigned minimal_version); + +bool dot_or_dot_dot(const char *path); diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c index 6ecb6c3f0d..f703e7f145 100644 --- a/src/basic/proc-cmdline.c +++ b/src/basic/proc-cmdline.c @@ -148,7 +148,7 @@ int proc_cmdline_get_key(const char *key, unsigned flags, char **value) { * a) The "value" parameter is used. In this case a parameter beginning with the "key" string followed by "=" * is searched, and the value following this is returned in "value". * - * b) as above, but the PROC_CMDLINE_VALUE_OPTIONAL flag is set. In this case if the the key is found as a + * b) as above, but the PROC_CMDLINE_VALUE_OPTIONAL flag is set. In this case if the key is found as a * separate word (i.e. not followed by "=" but instead by whitespace or the end of the command line), then * this is also accepted, and "value" is returned as NULL. * diff --git a/src/basic/raw-clone.h b/src/basic/raw-clone.h index d473828999..c6e531ada4 100644 --- a/src/basic/raw-clone.h +++ b/src/basic/raw-clone.h @@ -47,8 +47,8 @@ static inline int raw_clone(unsigned long flags) { assert((flags & (CLONE_VM|CLONE_PARENT_SETTID|CLONE_CHILD_SETTID| CLONE_CHILD_CLEARTID|CLONE_SETTLS)) == 0); -#if defined(__s390__) || defined(__CRIS__) - /* On s390 and cris the order of the first and second arguments +#if defined(__s390x__) || defined(__s390__) || defined(__CRIS__) + /* On s390/s390x and cris the order of the first and second arguments * of the raw clone() system call is reversed. */ return (int) syscall(__NR_clone, NULL, flags); #elif defined(__sparc__) && defined(__arch64__) diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c index 07d42f78dd..08497af729 100644 --- a/src/basic/rm-rf.c +++ b/src/basic/rm-rf.c @@ -83,7 +83,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) { bool is_dir; struct stat st; - if (streq(de->d_name, ".") || streq(de->d_name, "..")) + if (dot_or_dot_dot(de->d_name)) continue; if (de->d_type == DT_UNKNOWN || diff --git a/src/basic/siphash24.c b/src/basic/siphash24.c index 8c1cdc3db6..4bb41786c8 100644 --- a/src/basic/siphash24.c +++ b/src/basic/siphash24.c @@ -127,18 +127,25 @@ void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) { switch (left) { case 7: state->padding |= ((uint64_t) in[6]) << 48; + /* fall through */ case 6: state->padding |= ((uint64_t) in[5]) << 40; + /* fall through */ case 5: state->padding |= ((uint64_t) in[4]) << 32; + /* fall through */ case 4: state->padding |= ((uint64_t) in[3]) << 24; + /* fall through */ case 3: state->padding |= ((uint64_t) in[2]) << 16; + /* fall through */ case 2: state->padding |= ((uint64_t) in[1]) << 8; + /* fall through */ case 1: state->padding |= ((uint64_t) in[0]); + /* fall through */ case 0: break; } diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index 77f81a60ba..17e90a8994 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -877,7 +877,7 @@ bool ifname_valid(const char *p) { if (strlen(p) >= IFNAMSIZ) return false; - if (STR_IN_SET(p, ".", "..")) + if (dot_or_dot_dot(p)) return false; while (*p) { diff --git a/src/basic/string-util.c b/src/basic/string-util.c index 2ba3604ba0..9d2f4bc8f9 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -821,6 +821,7 @@ int free_and_strdup(char **p, const char *s) { return 1; } +#if !HAVE_DECL_EXPLICIT_BZERO /* * Pointer to memset is volatile so that compiler must de-reference * the pointer and can't assume that it points to any function in @@ -831,19 +832,19 @@ typedef void *(*memset_t)(void *,int,size_t); static volatile memset_t memset_func = memset; -void* memory_erase(void *p, size_t l) { - return memset_func(p, 'x', l); +void explicit_bzero(void *p, size_t l) { + memset_func(p, '\0', l); } +#endif char* string_erase(char *x) { - if (!x) return NULL; /* A delicious drop of snake-oil! To be called on memory where * we stored passphrases or so, after we used them. */ - - return memory_erase(x, strlen(x)); + explicit_bzero(x, strlen(x)); + return x; } char *string_free_erase(char *s) { diff --git a/src/basic/string-util.h b/src/basic/string-util.h index e99f7964be..be44dedff4 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -189,7 +189,10 @@ static inline void *memmem_safe(const void *haystack, size_t haystacklen, const return memmem(haystack, haystacklen, needle, needlelen); } -void* memory_erase(void *p, size_t l); +#if !HAVE_DECL_EXPLICIT_BZERO +void explicit_bzero(void *p, size_t l); +#endif + char *string_erase(char *x); char *string_free_erase(char *s); diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 7a5b29d77e..a0db97c41a 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -185,7 +185,7 @@ usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) { usec_t timespec_load(const struct timespec *ts) { assert(ts); - if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1) + if (ts->tv_sec < 0 || ts->tv_nsec < 0) return USEC_INFINITY; if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC) @@ -199,7 +199,7 @@ usec_t timespec_load(const struct timespec *ts) { nsec_t timespec_load_nsec(const struct timespec *ts) { assert(ts); - if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1) + if (ts->tv_sec < 0 || ts->tv_nsec < 0) return NSEC_INFINITY; if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC) @@ -211,7 +211,8 @@ nsec_t timespec_load_nsec(const struct timespec *ts) { struct timespec *timespec_store(struct timespec *ts, usec_t u) { assert(ts); - if (u == USEC_INFINITY) { + if (u == USEC_INFINITY || + u / USEC_PER_SEC >= TIME_T_MAX) { ts->tv_sec = (time_t) -1; ts->tv_nsec = (long) -1; return ts; @@ -226,8 +227,7 @@ struct timespec *timespec_store(struct timespec *ts, usec_t u) { usec_t timeval_load(const struct timeval *tv) { assert(tv); - if (tv->tv_sec == (time_t) -1 && - tv->tv_usec == (suseconds_t) -1) + if (tv->tv_sec < 0 || tv->tv_usec < 0) return USEC_INFINITY; if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC) @@ -241,7 +241,8 @@ usec_t timeval_load(const struct timeval *tv) { struct timeval *timeval_store(struct timeval *tv, usec_t u) { assert(tv); - if (u == USEC_INFINITY) { + if (u == USEC_INFINITY|| + u / USEC_PER_SEC > TIME_T_MAX) { tv->tv_sec = (time_t) -1; tv->tv_usec = (suseconds_t) -1; } else { @@ -288,9 +289,11 @@ static char *format_timestamp_internal( if (t <= 0 || t == USEC_INFINITY) return NULL; /* Timestamp is unset */ + /* Let's not format times with years > 9999 */ + if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) + return NULL; + sec = (time_t) (t / USEC_PER_SEC); /* Round down */ - if ((usec_t) sec != (t / USEC_PER_SEC)) - return NULL; /* overflow? */ if (!localtime_or_gmtime_r(&sec, &tm, utc)) return NULL; @@ -551,12 +554,12 @@ void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) { } int dual_timestamp_deserialize(const char *value, dual_timestamp *t) { - unsigned long long a, b; + uint64_t a, b; assert(value); assert(t); - if (sscanf(value, "%llu %llu", &a, &b) != 2) { + if (sscanf(value, "%" PRIu64 "%" PRIu64, &a, &b) != 2) { log_debug("Failed to parse dual timestamp value \"%s\": %m", value); return -EINVAL; } @@ -830,16 +833,23 @@ parse_usec: from_tm: x = mktime_or_timegm(&tm, utc); - if (x == (time_t) -1) + if (x < 0) return -EINVAL; if (weekday >= 0 && tm.tm_wday != weekday) return -EINVAL; ret = (usec_t) x * USEC_PER_SEC + x_usec; + if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX) + return -EINVAL; finish: + if (ret + plus < ret) /* overflow? */ + return -EINVAL; ret += plus; + if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX) + return -EINVAL; + if (ret > minus) ret -= minus; else @@ -1271,7 +1281,7 @@ bool clock_supported(clockid_t clock) { if (!clock_boottime_supported()) return false; - /* fall through, after checking the cached value for CLOCK_BOOTTIME. */ + /* fall through */ default: /* For everything else, check properly */ diff --git a/src/basic/time-util.h b/src/basic/time-util.h index f67a4474ed..7463507f51 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -181,3 +181,14 @@ static inline usec_t usec_sub(usec_t timestamp, int64_t delta) { return timestamp - delta; } + +#if SIZEOF_TIME_T == 8 +/* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit year + * territory. However, since we want to stay away from this in all timezones we take one day off. */ +#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 253402214399000000) +#elif SIZEOF_TIME_T == 4 +/* With a 32bit time_t we can't go beyond 2038... */ +#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 2147483647000000) +#else +#error "Yuck, time_t is neither 4 not 8 bytes wide?" +#endif diff --git a/src/basic/virt.c b/src/basic/virt.c index 33641e6886..03ce71a728 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -49,7 +49,7 @@ static int detect_vm_cpuid(void) { { "KVMKVMKVM", VIRTUALIZATION_KVM }, /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ { "VMwareVMware", VIRTUALIZATION_VMWARE }, - /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */ + /* https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/tlfs */ { "Microsoft Hv", VIRTUALIZATION_MICROSOFT }, /* https://wiki.freebsd.org/bhyve */ { "bhyve bhyve ", VIRTUALIZATION_BHYVE }, diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 44ea6215dc..7cc54a8cdd 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -1647,7 +1647,7 @@ static EFI_STATUS image_start(EFI_HANDLE parent_image, const Config *config, con loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16); #ifdef SD_BOOT_LOG_TPM - /* Try to log any options to the TPM, escpecially to catch manually edited options */ + /* Try to log any options to the TPM, especially to catch manually edited options */ err = tpm_log_event(SD_TPM_PCR, (EFI_PHYSICAL_ADDRESS) loaded_image->LoadOptions, loaded_image->LoadOptionsSize, loaded_image->LoadOptions); diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 1e250f34f4..7c1ffb1bca 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -100,7 +100,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { cmdline = line; #ifdef SD_BOOT_LOG_TPM - /* Try to log any options to the TPM, escpecially manually edited options */ + /* Try to log any options to the TPM, especially manually edited options */ err = tpm_log_event(SD_TPM_PCR, (EFI_PHYSICAL_ADDRESS) loaded_image->LoadOptions, loaded_image->LoadOptionsSize, loaded_image->LoadOptions); diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c index b55aa86a40..5574c14555 100644 --- a/src/cgls/cgls.c +++ b/src/cgls/cgls.c @@ -26,7 +26,6 @@ #include "sd-bus.h" #include "alloc-util.h" -#include "bus-error.h" #include "bus-util.h" #include "cgroup-show.h" #include "cgroup-util.h" @@ -35,12 +34,21 @@ #include "output-mode.h" #include "pager.h" #include "path-util.h" +#include "strv.h" #include "unit-name.h" #include "util.h" static bool arg_no_pager = false; static bool arg_kernel_threads = false; static bool arg_all = false; + +static enum { + SHOW_UNIT_NONE, + SHOW_UNIT_SYSTEM, + SHOW_UNIT_USER, +} arg_show_unit = SHOW_UNIT_NONE; +static char **arg_names = NULL; + static int arg_full = -1; static char* arg_machine = NULL; @@ -51,6 +59,8 @@ static void help(void) { " --version Show package version\n" " --no-pager Do not pipe output into a pager\n" " -a --all Show all groups, including empty\n" + " -u --unit Show the subtrees of specifified system units\n" + " --user-unit Show the subtrees of specifified user units\n" " -l --full Do not ellipsize output\n" " -k Include kernel threads in output\n" " -M --machine= Show container\n" @@ -62,15 +72,18 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_NO_PAGER = 0x100, ARG_VERSION, + ARG_USER_UNIT, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, - { "all", no_argument, NULL, 'a' }, - { "full", no_argument, NULL, 'l' }, - { "machine", required_argument, NULL, 'M' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "all", no_argument, NULL, 'a' }, + { "full", no_argument, NULL, 'l' }, + { "machine", required_argument, NULL, 'M' }, + { "unit", optional_argument, NULL, 'u' }, + { "user-unit", optional_argument, NULL, ARG_USER_UNIT }, {} }; @@ -79,7 +92,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 1); assert(argv); - while ((c = getopt_long(argc, argv, "hkalM:", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "-hkalM:u::", options, NULL)) >= 0) switch (c) { @@ -98,6 +111,24 @@ static int parse_argv(int argc, char *argv[]) { arg_all = true; break; + case 'u': + arg_show_unit = SHOW_UNIT_SYSTEM; + if (strv_push(&arg_names, optarg) < 0) /* push optarg if not empty */ + return log_oom(); + break; + + case ARG_USER_UNIT: + arg_show_unit = SHOW_UNIT_USER; + if (strv_push(&arg_names, optarg) < 0) /* push optarg if not empty */ + return log_oom(); + break; + + case 1: + /* positional argument */ + if (strv_push(&arg_names, optarg) < 0) + return log_oom(); + break; + case 'l': arg_full = true; break; @@ -117,51 +148,12 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } - return 1; -} - -static int get_cgroup_root(char **ret) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - _cleanup_free_ char *unit = NULL, *path = NULL; - const char *m; - int r; - - if (!arg_machine) { - r = cg_get_root_path(ret); - if (r == -ENOMEDIUM) - return log_error_errno(r, "Failed to get root control group path: No cgroup filesystem mounted on /sys/fs/cgroup"); - else if (r < 0) - return log_error_errno(r, "Failed to get root control group path: %m"); - - return 0; + if (arg_machine && arg_show_unit != SHOW_UNIT_NONE) { + log_error("Cannot combine --unit or --user-unit with --machine=."); + return -EINVAL; } - m = strjoina("/run/systemd/machines/", arg_machine); - r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL); - if (r < 0) - return log_error_errno(r, "Failed to load machine data: %m"); - - path = unit_dbus_path_from_name(unit); - if (!path) - return log_oom(); - - r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, false, &bus); - if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); - - r = sd_bus_get_property_string( - bus, - "org.freedesktop.systemd1", - path, - unit_dbus_interface_from_name(unit), - "ControlGroup", - &error, - ret); - if (r < 0) - return log_error_errno(r, "Failed to query unit control group path: %s", bus_error_message(&error, r)); - - return 0; + return 1; } static void show_cg_info(const char *controller, const char *path) { @@ -194,31 +186,65 @@ int main(int argc, char *argv[]) { (arg_full > 0) * OUTPUT_FULL_WIDTH | arg_kernel_threads * OUTPUT_KERNEL_THREADS; - if (optind < argc) { + if (arg_names) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_free_ char *root = NULL; - int i; + char **name; - r = get_cgroup_root(&root); - if (r < 0) - goto finish; - - for (i = optind; i < argc; i++) { + STRV_FOREACH(name, arg_names) { int q; - if (path_startswith(argv[i], "/sys/fs/cgroup")) { + if (arg_show_unit != SHOW_UNIT_NONE) { + /* Command line arguments are unit names */ + _cleanup_free_ char *cgroup = NULL; + + if (!bus) { + /* Connect to the bus only if necessary */ + r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, + arg_show_unit == SHOW_UNIT_USER, + &bus); + if (r < 0) { + log_error_errno(r, "Failed to create bus connection: %m"); + goto finish; + } + } + + q = show_cgroup_get_unit_path_and_warn(bus, *name, &cgroup); + if (q < 0) + goto failed; + + if (isempty(cgroup)) { + log_warning("Unit %s not found.", *name); + q = -ENOENT; + goto failed; + } + + printf("Unit %s (%s):\n", *name, cgroup); + fflush(stdout); + + q = show_cgroup_by_path(cgroup, NULL, 0, output_flags); - printf("Directory %s:\n", argv[i]); + } else if (path_startswith(*name, "/sys/fs/cgroup")) { + + printf("Directory %s:\n", *name); fflush(stdout); - q = show_cgroup_by_path(argv[i], NULL, 0, output_flags); + q = show_cgroup_by_path(*name, NULL, 0, output_flags); } else { _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL; const char *controller, *path; - r = cg_split_spec(argv[i], &c, &p); - if (r < 0) { - log_error_errno(r, "Failed to split argument %s: %m", argv[i]); - goto finish; + if (!root) { + /* Query root only if needed, treat error as fatal */ + r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root); + if (r < 0) + goto finish; + } + + q = cg_split_spec(*name, &c, &p); + if (q < 0) { + log_error_errno(q, "Failed to split argument %s: %m", *name); + goto failed; } controller = c ?: SYSTEMD_CGROUP_CONTROLLER; @@ -239,7 +265,8 @@ int main(int argc, char *argv[]) { q = show_cgroup(controller, path, NULL, 0, output_flags); } - if (q < 0) + failed: + if (q < 0 && r >= 0) r = q; } @@ -267,7 +294,7 @@ int main(int argc, char *argv[]) { if (!done) { _cleanup_free_ char *root = NULL; - r = get_cgroup_root(&root); + r = show_cgroup_get_path_and_warn(arg_machine, NULL, &root); if (r < 0) goto finish; @@ -283,6 +310,7 @@ int main(int argc, char *argv[]) { finish: pager_close(); + free(arg_names); /* don't free the strings */ return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index af5c3d8695..50ac6a58b0 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -31,6 +31,7 @@ #include "alloc-util.h" #include "bus-error.h" #include "bus-util.h" +#include "cgroup-show.h" #include "cgroup-util.h" #include "fd-util.h" #include "fileio.h" @@ -862,13 +863,9 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } - if (optind == argc-1) { - if (arg_machine) { - log_error("Specifying a control group path together with the -M option is not allowed"); - return -EINVAL; - } + if (optind == argc - 1) arg_root = argv[optind]; - } else if (optind < argc) { + else if (optind < argc) { log_error("Too many arguments."); return -EINVAL; } @@ -890,59 +887,6 @@ static const char* counting_what(void) { return "userspace processes (excl. kernel)"; } -static int get_cgroup_root(char **ret) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - _cleanup_free_ char *unit = NULL, *path = NULL; - const char *m; - int r; - - if (arg_root) { - char *aux; - - aux = strdup(arg_root); - if (!aux) - return log_oom(); - - *ret = aux; - return 0; - } - - if (!arg_machine) { - r = cg_get_root_path(ret); - if (r < 0) - return log_error_errno(r, "Failed to get root control group path: %m"); - - return 0; - } - - m = strjoina("/run/systemd/machines/", arg_machine); - r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL); - if (r < 0) - return log_error_errno(r, "Failed to load machine data: %m"); - - path = unit_dbus_path_from_name(unit); - if (!path) - return log_oom(); - - r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, false, &bus); - if (r < 0) - return log_error_errno(r, "Failed to create bus connection: %m"); - - r = sd_bus_get_property_string( - bus, - "org.freedesktop.systemd1", - path, - unit_dbus_interface_from_name(unit), - "ControlGroup", - &error, - ret); - if (r < 0) - return log_error_errno(r, "Failed to query unit control group path: %s", bus_error_message(&error, r)); - - return 0; -} - int main(int argc, char *argv[]) { int r; Hashmap *a = NULL, *b = NULL; @@ -967,11 +911,12 @@ int main(int argc, char *argv[]) { if (r <= 0) goto finish; - r = get_cgroup_root(&root); + r = show_cgroup_get_path_and_warn(arg_machine, arg_root, &root); if (r < 0) { log_error_errno(r, "Failed to get root control group path: %m"); goto finish; - } + } else + log_debug("Cgroup path: %s", root); a = hashmap_new(&string_hash_ops); b = hashmap_new(&string_hash_ops); diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 9960b7a811..7df4cab3f6 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -758,6 +758,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("LimitRTTIMESoft", "t", bus_property_get_rlimit, offsetof(ExecContext, rlimit[RLIMIT_RTTIME]), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("WorkingDirectory", "s", property_get_working_directory, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(ExecContext, root_directory), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RootImage", "s", NULL, offsetof(ExecContext, root_image), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("OOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Nice", "i", property_get_nice, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("IOScheduling", "i", property_get_ioprio, 0, SD_BUS_VTABLE_PROPERTY_CONST), @@ -828,6 +829,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("RestrictNamespaces", "t", bus_property_get_ulong, offsetof(ExecContext, restrict_namespaces), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("BindPaths", "a(ssbt)", property_get_bind_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("BindReadOnlyPaths", "a(ssbt)", property_get_bind_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("MountAPIVFS", "b", bus_property_get_bool, offsetof(ExecContext, mount_apivfs), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_VTABLE_END }; @@ -1047,7 +1049,7 @@ int bus_exec_context_set_transient_property( return 1; - } else if (STR_IN_SET(name, "TTYPath", "RootDirectory")) { + } else if (STR_IN_SET(name, "TTYPath", "RootDirectory", "RootImage")) { const char *s; r = sd_bus_message_read(message, "s", &s); @@ -1060,6 +1062,8 @@ int bus_exec_context_set_transient_property( if (mode != UNIT_CHECK) { if (streq(name, "TTYPath")) r = free_and_strdup(&c->tty_path, s); + else if (streq(name, "RootImage")) + r = free_and_strdup(&c->root_image, s); else { assert(streq(name, "RootDirectory")); r = free_and_strdup(&c->root_directory, s); @@ -1207,7 +1211,7 @@ int bus_exec_context_set_transient_property( "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC", "ProtectKernelTunables", - "ProtectKernelModules", "ProtectControlGroups")) { + "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS")) { int b; r = sd_bus_message_read(message, "b", &b); @@ -1247,6 +1251,8 @@ int bus_exec_context_set_transient_property( c->protect_kernel_modules = b; else if (streq(name, "ProtectControlGroups")) c->protect_control_groups = b; + else if (streq(name, "MountAPIVFS")) + c->mount_apivfs = b; unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, yes_no(b)); } @@ -1382,7 +1388,7 @@ int bus_exec_context_set_transient_property( _cleanup_free_ char *joined = NULL; _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char **l = NULL; + _cleanup_strv_free_ char **l = NULL; size_t size = 0; char **i; @@ -1495,12 +1501,15 @@ int bus_exec_context_set_transient_property( return r; STRV_FOREACH(p, l) { - int offset; - if (!utf8_is_valid(*p)) + const char *i = *p; + size_t offset; + + if (!utf8_is_valid(i)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name); - offset = **p == '-'; - if (!path_is_absolute(*p + offset)) + offset = i[0] == '-'; + offset += i[offset] == '+'; + if (!path_is_absolute(i + offset)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name); } @@ -1519,7 +1528,6 @@ int bus_exec_context_set_transient_property( unit_write_drop_in_private_format(u, mode, name, "%s=", name); } else { r = strv_extend_strv(dirs, l, true); - if (r < 0) return -ENOMEM; @@ -1698,7 +1706,7 @@ int bus_exec_context_set_transient_property( if (!path_is_absolute(source)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path %s is not absolute.", source); if (!path_is_absolute(destination)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is not absolute.", source); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path %s is not absolute.", destination); if (!IN_SET(mount_flags, 0, MS_REC)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown mount flags."); diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 9876251438..f87b52a266 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -19,6 +19,7 @@ #include <errno.h> #include <sys/prctl.h> +#include <sys/statvfs.h> #include <unistd.h> #include "alloc-util.h" @@ -38,6 +39,7 @@ #include "fs-util.h" #include "install.h" #include "log.h" +#include "parse-util.h" #include "path-util.h" #include "selinux-access.h" #include "stat-util.h" @@ -48,6 +50,10 @@ #include "virt.h" #include "watchdog.h" +/* Require 16MiB free in /run/systemd for reloading/reexecing. After all we need to serialize our state there, and if + * we can't we'll fail badly. */ +#define RELOAD_DISK_SPACE_MIN (UINT64_C(16) * UINT64_C(1024) * UINT64_C(1024)) + static UnitFileFlags unit_file_bools_to_flags(bool runtime, bool force) { return (runtime ? UNIT_FILE_RUNTIME : 0) | (force ? UNIT_FILE_FORCE : 0); @@ -842,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); } @@ -1312,6 +1314,40 @@ static int method_refuse_snapshot(sd_bus_message *message, void *userdata, sd_bu return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Support for snapshots has been removed."); } +static int verify_run_space(const char *message, sd_bus_error *error) { + struct statvfs svfs; + uint64_t available; + + if (statvfs("/run/systemd", &svfs) < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to statvfs(/run/systemd): %m"); + + available = (uint64_t) svfs.f_bfree * (uint64_t) svfs.f_bsize; + + if (available < RELOAD_DISK_SPACE_MIN) { + char fb_available[FORMAT_BYTES_MAX], fb_need[FORMAT_BYTES_MAX]; + return sd_bus_error_setf(error, + BUS_ERROR_DISK_FULL, + "%s, not enough space available on /run/systemd. " + "Currently, %s are free, but a safety buffer of %s is enforced.", + message, + format_bytes(fb_available, sizeof(fb_available), available), + format_bytes(fb_need, sizeof(fb_need), RELOAD_DISK_SPACE_MIN)); + } + + return 0; +} + +int verify_run_space_and_log(const char *message) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + r = verify_run_space(message, &error); + if (r < 0) + log_error_errno(r, "%s", bus_error_message(&error, r)); + + return r; +} + static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; int r; @@ -1319,6 +1355,10 @@ static int method_reload(sd_bus_message *message, void *userdata, sd_bus_error * assert(message); assert(m); + r = verify_run_space("Refusing to reload", error); + if (r < 0) + return r; + r = mac_selinux_access_check(message, "reload", error); if (r < 0) return r; @@ -1351,6 +1391,10 @@ static int method_reexecute(sd_bus_message *message, void *userdata, sd_bus_erro assert(message); assert(m); + r = verify_run_space("Refusing to reexecute", error); + if (r < 0) + return r; + r = mac_selinux_access_check(message, "reload", error); if (r < 0) return r; @@ -1469,11 +1513,26 @@ static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_er char *ri = NULL, *rt = NULL; const char *root, *init; Manager *m = userdata; + struct statvfs svfs; + uint64_t available; int r; assert(message); assert(m); + if (statvfs("/run/systemd", &svfs) < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to statvfs(/run/systemd): %m"); + + available = (uint64_t) svfs.f_bfree * (uint64_t) svfs.f_bsize; + + if (available < RELOAD_DISK_SPACE_MIN) { + char fb_available[FORMAT_BYTES_MAX], fb_need[FORMAT_BYTES_MAX]; + log_warning("Dangerously low amount of free space on /run/systemd, root switching operation might not complete successfuly. " + "Currently, %s are free, but %s are suggested. Proceeding anyway.", + format_bytes(fb_available, sizeof(fb_available), available), + format_bytes(fb_need, sizeof(fb_need), RELOAD_DISK_SPACE_MIN)); + } + r = mac_selinux_access_check(message, "reboot", error); if (r < 0) return r; @@ -1853,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. @@ -1923,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, @@ -1939,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, @@ -1998,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) { @@ -2067,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( @@ -2102,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) { @@ -2137,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) { @@ -2168,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) { @@ -2211,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) { @@ -2251,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-manager.h b/src/core/dbus-manager.h index 36a2e9481b..9f3222da28 100644 --- a/src/core/dbus-manager.h +++ b/src/core/dbus-manager.h @@ -26,3 +26,5 @@ extern const sd_bus_vtable bus_manager_vtable[]; void bus_manager_send_finished(Manager *m, usec_t firmware_usec, usec_t loader_usec, usec_t kernel_usec, usec_t initrd_usec, usec_t userspace_usec, usec_t total_usec); void bus_manager_send_reloading(Manager *m, bool active); void bus_manager_send_change_signal(Manager *m); + +int verify_run_space_and_log(const char *message); diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 2adc1d9288..f1306a023f 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; @@ -1218,7 +1222,7 @@ int bus_unit_queue_job( (type == JOB_STOP && u->refuse_manual_stop) || ((type == JOB_RESTART || type == JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop)) || (type == JOB_RELOAD_OR_START && job_type_collapse(type, u) == JOB_START && u->refuse_manual_start)) - return sd_bus_error_setf(error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, unit %s may be requested by dependency only.", u->id); + return sd_bus_error_setf(error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, unit %s may be requested by dependency only (it is configured to refuse manual start/stop).", u->id); r = manager_add_job(u->manager, type, u, mode, error, &j); if (r < 0) diff --git a/src/core/dbus.c b/src/core/dbus.c index 07ab21f199..0493e5786c 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -175,7 +175,7 @@ static int signal_activation_request(sd_bus_message *message, void *userdata, sd goto failed; if (u->refuse_manual_start) { - r = sd_bus_error_setf(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, %s may be requested by dependency only.", u->id); + r = sd_bus_error_setf(&error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, %s may be requested by dependency only (it is configured to refuse manual start/stop).", u->id); goto failed; } @@ -477,7 +477,7 @@ static int bus_kill_context_find(sd_bus *bus, const char *path, const char *inte } static int bus_job_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { - _cleanup_free_ char **l = NULL; + _cleanup_strv_free_ char **l = NULL; Manager *m = userdata; unsigned k = 0; Iterator i; @@ -504,7 +504,7 @@ static int bus_job_enumerate(sd_bus *bus, const char *path, void *userdata, char } static int bus_unit_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { - _cleanup_free_ char **l = NULL; + _cleanup_strv_free_ char **l = NULL; Manager *m = userdata; unsigned k = 0; Iterator i; @@ -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 06a291fd39..4c2968f971 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -1643,6 +1643,9 @@ static bool exec_needs_mount_namespace( assert(context); assert(params); + if (context->root_image) + return true; + if (!strv_isempty(context->read_write_paths) || !strv_isempty(context->read_only_paths) || !strv_isempty(context->inaccessible_paths)) @@ -1665,6 +1668,9 @@ static bool exec_needs_mount_namespace( context->protect_control_groups) return true; + if (context->mount_apivfs) + return true; + return false; } @@ -1685,25 +1691,31 @@ static int setup_private_users(uid_t uid, gid_t gid) { * child then writes the UID mapping, under full privileges. The parent waits for the child to finish and * continues execution normally. */ - if (uid != 0 && uid_is_valid(uid)) - asprintf(&uid_map, - "0 0 1\n" /* Map root → root */ - UID_FMT " " UID_FMT " 1\n", /* Map $UID → $UID */ - uid, uid); - else + if (uid != 0 && uid_is_valid(uid)) { + r = asprintf(&uid_map, + "0 0 1\n" /* Map root → root */ + UID_FMT " " UID_FMT " 1\n", /* Map $UID → $UID */ + uid, uid); + if (r < 0) + return -ENOMEM; + } else { uid_map = strdup("0 0 1\n"); /* The case where the above is the same */ - if (!uid_map) - return -ENOMEM; + if (!uid_map) + return -ENOMEM; + } - if (gid != 0 && gid_is_valid(gid)) - asprintf(&gid_map, - "0 0 1\n" /* Map root → root */ - GID_FMT " " GID_FMT " 1\n", /* Map $GID → $GID */ - gid, gid); - else + if (gid != 0 && gid_is_valid(gid)) { + r = asprintf(&gid_map, + "0 0 1\n" /* Map root → root */ + GID_FMT " " GID_FMT " 1\n", /* Map $GID → $GID */ + gid, gid); + if (r < 0) + return -ENOMEM; + } else { gid_map = strdup("0 0 1\n"); /* The case where the above is the same */ - if (!gid_map) - return -ENOMEM; + if (!gid_map) + return -ENOMEM; + } /* Create a communication channel so that the parent can tell the child when it finished creating the user * namespace. */ @@ -1896,8 +1908,8 @@ static int compile_read_write_paths( _cleanup_strv_free_ char **l = NULL; char **rt; - /* Compile the list of writable paths. This is the combination of the explicitly configured paths, plus all - * runtime directories. */ + /* Compile the list of writable paths. This is the combination of + * the explicitly configured paths, plus all runtime directories. */ if (strv_isempty(context->read_write_paths) && strv_isempty(context->runtime_directory)) { @@ -1926,20 +1938,26 @@ 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; - _cleanup_free_ char **rw = NULL; +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; + const char *root_dir = NULL, *root_image = NULL; NameSpaceInfo ns_info = { .ignore_protect_paths = false, .private_dev = context->private_devices, .protect_control_groups = context->protect_control_groups, .protect_kernel_tunables = context->protect_kernel_tunables, .protect_kernel_modules = context->protect_kernel_modules, + .mount_apivfs = context->mount_apivfs, }; + bool apply_restrictions; + int r; assert(context); @@ -1958,8 +1976,12 @@ static int apply_mount_namespace(Unit *u, const ExecContext *context, if (r < 0) return r; - if (params->flags & EXEC_APPLY_CHROOT) - root_dir = context->root_directory; + if (params->flags & EXEC_APPLY_CHROOT) { + root_image = context->root_image; + + if (!root_image) + root_dir = context->root_directory; + } /* * If DynamicUser=no and RootDirectory= is set then lets pass a relaxed @@ -1969,16 +1991,20 @@ static int apply_mount_namespace(Unit *u, const ExecContext *context, if (!context->dynamic_user && root_dir) ns_info.ignore_protect_paths = true; - r = setup_namespace(root_dir, &ns_info, rw, - context->read_only_paths, - context->inaccessible_paths, + apply_restrictions = (params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged; + + r = setup_namespace(root_dir, root_image, + &ns_info, rw, + 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, - context->mount_flags); + apply_restrictions ? context->protect_home : PROTECT_HOME_NO, + apply_restrictions ? context->protect_system : PROTECT_SYSTEM_NO, + context->mount_flags, + DISSECT_IMAGE_DISCARD_ON_LOOP); /* If we couldn't set up the namespace this is probably due to a * missing capability. In this case, silently proceeed. */ @@ -1992,33 +2018,47 @@ static int apply_mount_namespace(Unit *u, const ExecContext *context, return r; } -static int apply_working_directory(const ExecContext *context, - const ExecParameters *params, - const char *home, - const bool needs_mount_ns) { - const char *d; - const char *wd; +static int apply_working_directory( + const ExecContext *context, + const ExecParameters *params, + const char *home, + const bool needs_mount_ns, + int *exit_status) { + + const char *d, *wd; assert(context); + assert(exit_status); + + if (context->working_directory_home) { + + if (!home) { + *exit_status = EXIT_CHDIR; + return -ENXIO; + } - if (context->working_directory_home) wd = home; - else if (context->working_directory) + + } else if (context->working_directory) wd = context->working_directory; else wd = "/"; if (params->flags & EXEC_APPLY_CHROOT) { if (!needs_mount_ns && context->root_directory) - if (chroot(context->root_directory) < 0) + if (chroot(context->root_directory) < 0) { + *exit_status = EXIT_CHROOT; return -errno; + } d = wd; } else - d = strjoina(strempty(context->root_directory), "/", strempty(wd)); + d = prefix_roota(context->root_directory, wd); - if (chdir(d) < 0 && !context->working_directory_missing_ok) + if (chdir(d) < 0 && !context->working_directory_missing_ok) { + *exit_status = EXIT_CHDIR; return -errno; + } return 0; } @@ -2160,6 +2200,35 @@ static int send_user_lookup( return 0; } +static int acquire_home(const ExecContext *c, uid_t uid, const char** home, char **buf) { + int r; + + assert(c); + assert(home); + assert(buf); + + /* If WorkingDirectory=~ is set, try to acquire a usable home directory. */ + + if (*home) + return 0; + + if (!c->working_directory_home) + return 0; + + if (uid == 0) { + /* Hardcode /root as home directory for UID 0 */ + *home = "/root"; + return 1; + } + + r = get_home_dir(buf); + if (r < 0) + return r; + + *home = *buf; + return 1; +} + static int exec_child( Unit *unit, ExecCommand *command, @@ -2177,7 +2246,7 @@ static int exec_child( char **error_message) { _cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **final_argv = NULL; - _cleanup_free_ char *mac_selinux_context_net = NULL; + _cleanup_free_ char *mac_selinux_context_net = NULL, *home_buffer = NULL; _cleanup_free_ gid_t *supplementary_gids = NULL; const char *username = NULL, *groupname = NULL; const char *home = NULL, *shell = NULL; @@ -2330,6 +2399,13 @@ static int exec_child( user_lookup_fd = safe_close(user_lookup_fd); + r = acquire_home(context, uid, &home, &home_buffer); + if (r < 0) { + *exit_status = EXIT_CHDIR; + *error_message = strdup("Failed to determine $HOME for user"); + return r; + } + /* If a socket is connected to STDIN/STDOUT/STDERR, we * must sure to drop O_NONBLOCK */ if (socket_fd >= 0) @@ -2433,11 +2509,12 @@ static int exec_child( } if (context->utmp_id) - utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path, + utmp_put_init_process(context->utmp_id, getpid(), getsid(0), + context->tty_path, context->utmp_mode == EXEC_UTMP_INIT ? INIT_PROCESS : context->utmp_mode == EXEC_UTMP_LOGIN ? LOGIN_PROCESS : USER_PROCESS, - username ? "root" : context->user); + username); if (context->user) { r = chown_terminal(STDIN_FILENO, uid); @@ -2536,7 +2613,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; @@ -2544,11 +2621,9 @@ static int exec_child( } /* Apply just after mount namespace setup */ - r = apply_working_directory(context, params, home, needs_mount_namespace); - if (r < 0) { - *exit_status = EXIT_CHROOT; + r = apply_working_directory(context, params, home, needs_mount_namespace, exit_status); + if (r < 0) return r; - } /* Drop groups as early as possbile */ if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) { @@ -2975,6 +3050,7 @@ void exec_context_done(ExecContext *c) { c->working_directory = mfree(c->working_directory); c->root_directory = mfree(c->root_directory); + c->root_image = mfree(c->root_image); c->tty_path = mfree(c->tty_path); c->syslog_identifier = mfree(c->syslog_identifier); c->user = mfree(c->user); @@ -3096,7 +3172,7 @@ const char* exec_context_fdname(const ExecContext *c, int fd_index) { int exec_context_named_iofds(Unit *unit, const ExecContext *c, const ExecParameters *p, int named_iofds[3]) { unsigned i, targets; - const char *stdio_fdname[3]; + const char* stdio_fdname[3]; assert(c); assert(p); @@ -3109,18 +3185,32 @@ int exec_context_named_iofds(Unit *unit, const ExecContext *c, const ExecParamet stdio_fdname[i] = exec_context_fdname(c, i); for (i = 0; i < p->n_fds && targets > 0; i++) - if (named_iofds[STDIN_FILENO] < 0 && c->std_input == EXEC_INPUT_NAMED_FD && stdio_fdname[STDIN_FILENO] && streq(p->fd_names[i], stdio_fdname[STDIN_FILENO])) { + if (named_iofds[STDIN_FILENO] < 0 && + c->std_input == EXEC_INPUT_NAMED_FD && + stdio_fdname[STDIN_FILENO] && + streq(p->fd_names[i], stdio_fdname[STDIN_FILENO])) { + named_iofds[STDIN_FILENO] = p->fds[i]; targets--; - } else if (named_iofds[STDOUT_FILENO] < 0 && c->std_output == EXEC_OUTPUT_NAMED_FD && stdio_fdname[STDOUT_FILENO] && streq(p->fd_names[i], stdio_fdname[STDOUT_FILENO])) { + + } else if (named_iofds[STDOUT_FILENO] < 0 && + c->std_output == EXEC_OUTPUT_NAMED_FD && + stdio_fdname[STDOUT_FILENO] && + streq(p->fd_names[i], stdio_fdname[STDOUT_FILENO])) { + named_iofds[STDOUT_FILENO] = p->fds[i]; targets--; - } else if (named_iofds[STDERR_FILENO] < 0 && c->std_error == EXEC_OUTPUT_NAMED_FD && stdio_fdname[STDERR_FILENO] && streq(p->fd_names[i], stdio_fdname[STDERR_FILENO])) { + + } else if (named_iofds[STDERR_FILENO] < 0 && + c->std_error == EXEC_OUTPUT_NAMED_FD && + stdio_fdname[STDERR_FILENO] && + streq(p->fd_names[i], stdio_fdname[STDERR_FILENO])) { + named_iofds[STDERR_FILENO] = p->fds[i]; targets--; } - return (targets == 0 ? 0 : -ENOENT); + return targets == 0 ? 0 : -ENOENT; } int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l) { @@ -3276,6 +3366,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { "%sPrivateUsers: %s\n" "%sProtectHome: %s\n" "%sProtectSystem: %s\n" + "%sMountAPIVFS: %s\n" "%sIgnoreSIGPIPE: %s\n" "%sMemoryDenyWriteExecute: %s\n" "%sRestrictRealtime: %s\n", @@ -3292,10 +3383,14 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { prefix, yes_no(c->private_users), prefix, protect_home_to_string(c->protect_home), prefix, protect_system_to_string(c->protect_system), + prefix, yes_no(c->mount_apivfs), prefix, yes_no(c->ignore_sigpipe), prefix, yes_no(c->memory_deny_write_execute), prefix, yes_no(c->restrict_realtime)); + if (c->root_image) + fprintf(f, "%sRootImage: %s\n", prefix, c->root_image); + STRV_FOREACH(e, c->environment) fprintf(f, "%sEnvironment: %s\n", prefix, *e); diff --git a/src/core/execute.h b/src/core/execute.h index f8694ef520..9f2b6fd39e 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -106,7 +106,7 @@ struct ExecContext { char **pass_environment; struct rlimit *rlimit[_RLIMIT_MAX]; - char *working_directory, *root_directory; + char *working_directory, *root_directory, *root_image; bool working_directory_missing_ok; bool working_directory_home; @@ -183,6 +183,7 @@ struct ExecContext { bool protect_kernel_tunables; bool protect_kernel_modules; bool protect_control_groups; + bool mount_apivfs; bool no_new_privileges; diff --git a/src/core/job.c b/src/core/job.c index f7c4c59c32..00f7d7998f 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -697,7 +697,7 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR static void job_print_status_message(Unit *u, JobType t, JobResult result) { static const struct { const char *color, *word; - } const statuses[_JOB_RESULT_MAX] = { + } statuses[_JOB_RESULT_MAX] = { [JOB_DONE] = { ANSI_GREEN, " OK " }, [JOB_TIMEOUT] = { ANSI_HIGHLIGHT_RED, " TIME " }, [JOB_FAILED] = { ANSI_HIGHLIGHT_RED, "FAILED" }, diff --git a/src/core/killall.c b/src/core/killall.c index b3aa22adc5..7a9df546ee 100644 --- a/src/core/killall.c +++ b/src/core/killall.c @@ -66,29 +66,26 @@ static bool ignore_proc(pid_t pid, bool warn_rootfs) { if (count <= 0) return true; - /* Processes with argv[0][0] = '@' we ignore from the killing - * spree. + /* Processes with argv[0][0] = '@' we ignore from the killing spree. * * http://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons */ - if (c == '@' && warn_rootfs) { - _cleanup_free_ char *comm = NULL; + if (c != '@') + return false; - r = pid_from_same_root_fs(pid); - if (r < 0) - return true; + if (warn_rootfs && + pid_from_same_root_fs(pid) == 0) { + + _cleanup_free_ char *comm = NULL; get_process_comm(pid, &comm); - if (r) - log_notice("Process " PID_FMT " (%s) has been marked to be excluded from killing. It is " - "running from the root file system, and thus likely to block re-mounting of the " - "root file system to read-only. Please consider moving it into an initrd file " - "system instead.", pid, strna(comm)); - return true; - } else if (c == '@') - return true; + log_notice("Process " PID_FMT " (%s) has been marked to be excluded from killing. It is " + "running from the root file system, and thus likely to block re-mounting of the " + "root file system to read-only. Please consider moving it into an initrd file " + "system instead.", pid, strna(comm)); + } - return false; + return true; } static void wait_for_children(Set *pids, sigset_t *mask) { diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c index f83fa09301..ff3636149a 100644 --- a/src/core/load-dropin.c +++ b/src/core/load-dropin.c @@ -19,51 +19,121 @@ #include "conf-parser.h" +#include "fs-util.h" #include "load-dropin.h" #include "load-fragment.h" #include "log.h" +#include "stat-util.h" +#include "string-util.h" #include "strv.h" #include "unit-name.h" #include "unit.h" -static int add_dependency_consumer( - UnitDependency dependency, - const char *entry, - const char* filepath, - void *arg) { - Unit *u = arg; +static bool unit_name_compatible(const char *a, const char *b) { + _cleanup_free_ char *prefix = NULL; int r; - assert(u); + /* the straightforward case: the symlink name matches the target */ + if (streq(a, b)) + return true; + + r = unit_name_template(a, &prefix); + if (r < 0) { + log_oom(); + return true; + } + + /* an instance name points to a target that is just the template name */ + if (streq(prefix, b)) + return true; + + return false; +} + +static int process_deps(Unit *u, UnitDependency dependency, const char *dir_suffix) { + _cleanup_strv_free_ char **paths = NULL; + char **p; + int r; - r = unit_add_dependency_by_name(u, dependency, entry, filepath, true); + r = unit_file_find_dropin_paths(NULL, + u->manager->lookup_paths.search_path, + u->manager->unit_path_cache, + dir_suffix, + NULL, + u->names, + &paths); if (r < 0) - log_error_errno(r, "Cannot add dependency %s to %s, ignoring: %m", entry, u->id); + return r; + + STRV_FOREACH(p, paths) { + const char *entry; + _cleanup_free_ char *target = NULL; + + entry = basename(*p); + + if (null_or_empty_path(*p) > 0) { + /* an error usually means an invalid symlink, which is not a mask */ + log_unit_debug(u, "%s dependency on %s is masked by %s, ignoring.", + unit_dependency_to_string(dependency), entry, *p); + continue; + } + + r = is_symlink(*p); + if (r < 0) { + log_unit_warning_errno(u, r, "%s dropin %s unreadable, ignoring: %m", + unit_dependency_to_string(dependency), *p); + continue; + } + if (r == 0) { + log_unit_warning(u, "%s dependency dropin %s is not a symlink, ignoring.", + unit_dependency_to_string(dependency), *p); + continue; + } + + if (!unit_name_is_valid(entry, UNIT_NAME_ANY)) { + log_unit_warning(u, "%s dependency dropin %s is not a valid unit name, ignoring.", + unit_dependency_to_string(dependency), *p); + continue; + } + + r = readlink_malloc(*p, &target); + if (r < 0) { + log_unit_warning_errno(u, r, "readlink(\"%s\") failed, ignoring: %m", *p); + continue; + } + + /* We don't treat this as an error, especially because we didn't check this for a + * long time. Nevertheless, we warn, because such mismatch can be mighty confusing. */ + if (!unit_name_compatible(entry, basename(target))) + log_unit_warning(u, "%s dependency dropin %s target %s has different name", + unit_dependency_to_string(dependency), *p, target); + + r = unit_add_dependency_by_name(u, dependency, entry, *p, true); + if (r < 0) + log_unit_error_errno(u, r, "cannot add %s dependency on %s, ignoring: %m", + unit_dependency_to_string(dependency), entry); + } return 0; } int unit_load_dropin(Unit *u) { _cleanup_strv_free_ char **l = NULL; - Iterator i; - char *t, **f; + char **f; int r; assert(u); - /* Load dependencies from supplementary drop-in directories */ - - SET_FOREACH(t, u->names, i) { - char **p; + /* Load dependencies from .wants and .requires directories */ + r = process_deps(u, UNIT_WANTS, ".wants"); + if (r < 0) + return r; - STRV_FOREACH(p, u->manager->lookup_paths.search_path) { - unit_file_process_dir(u->manager->unit_path_cache, *p, t, ".wants", UNIT_WANTS, - add_dependency_consumer, u, NULL); - unit_file_process_dir(u->manager->unit_path_cache, *p, t, ".requires", UNIT_REQUIRES, - add_dependency_consumer, u, NULL); - } - } + r = process_deps(u, UNIT_REQUIRES, ".requires"); + if (r < 0) + return r; + /* Load .conf dropins */ r = unit_find_dropin_paths(u, &l); if (r <= 0) return 0; diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h index 942d26724e..5828a223ce 100644 --- a/src/core/load-dropin.h +++ b/src/core/load-dropin.h @@ -25,10 +25,11 @@ /* Read service data supplementary drop-in directories */ static inline int unit_find_dropin_paths(Unit *u, char ***paths) { - return unit_file_find_dropin_paths(u->manager->lookup_paths.search_path, - u->manager->unit_path_cache, - u->names, - paths); + return unit_file_find_dropin_conf_paths(NULL, + u->manager->lookup_paths.search_path, + u->manager->unit_path_cache, + u->names, + paths); } int unit_load_dropin(Unit *u); diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 15f22a2681..cb9e6fea27 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -19,6 +19,7 @@ m4_dnl Define the context options only once m4_define(`EXEC_CONTEXT_CONFIG_ITEMS', `$1.WorkingDirectory, config_parse_working_directory, 0, offsetof($1, exec_context) $1.RootDirectory, config_parse_unit_path_printf, 0, offsetof($1, exec_context.root_directory) +$1.RootImage, config_parse_unit_path_printf, 0, offsetof($1, exec_context.root_image) $1.User, config_parse_user_group, 0, offsetof($1, exec_context.user) $1.Group, config_parse_user_group, 0, offsetof($1, exec_context.group) $1.SupplementaryGroups, config_parse_user_group_strv, 0, offsetof($1, exec_context.supplementary_groups) @@ -101,6 +102,7 @@ $1.PrivateUsers, config_parse_bool, 0, $1.ProtectSystem, config_parse_protect_system, 0, offsetof($1, exec_context) $1.ProtectHome, config_parse_protect_home, 0, offsetof($1, exec_context) $1.MountFlags, config_parse_exec_mount_flags, 0, offsetof($1, exec_context) +$1.MountAPIVFS, config_parse_bool, 0, offsetof($1, exec_context.mount_apivfs) $1.Personality, config_parse_personality, 0, offsetof($1, exec_context.personality) $1.RuntimeDirectoryMode, config_parse_mode, 0, offsetof($1, exec_context.runtime_directory_mode) $1.RuntimeDirectory, config_parse_runtime_directory, 0, offsetof($1, exec_context.runtime_directory) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index bc0cf73d39..5b7471c0d0 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -620,7 +620,7 @@ int config_parse_exec( * If it's prefixed with - and the path doesn't exist, * we ignore it instead of erroring out; * if it's prefixed with @, we allow overriding of argv[0]; - * and if it's prefixed with !, it will be run with full privileges */ + * and if it's prefixed with +, it will be run with full privileges */ if (*f == '-' && !ignore) ignore = true; else if (*f == '@' && !separate_argv0) @@ -3839,7 +3839,8 @@ int config_parse_namespace_path_strv( cur = rvalue; for (;;) { _cleanup_free_ char *word = NULL, *resolved = NULL, *joined = NULL; - bool ignore_enoent; + const char *w; + bool ignore_enoent = false, shall_prefix = false; r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES); if (r == 0) @@ -3856,9 +3857,17 @@ int config_parse_namespace_path_strv( continue; } - ignore_enoent = word[0] == '-'; + w = word; + if (startswith(w, "-")) { + ignore_enoent = true; + w++; + } + if (startswith(w, "+")) { + shall_prefix = true; + w++; + } - r = unit_full_printf(u, word + ignore_enoent, &resolved); + r = unit_full_printf(u, w, &resolved); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers in %s: %m", word); continue; @@ -3871,7 +3880,9 @@ int config_parse_namespace_path_strv( path_kill_slashes(resolved); - joined = strjoin(ignore_enoent ? "-" : "", resolved); + joined = strjoin(ignore_enoent ? "-" : "", + shall_prefix ? "+" : "", + resolved); r = strv_push(sv, joined); if (r < 0) diff --git a/src/core/manager.c b/src/core/manager.c index d83c5ef5e2..e4da945777 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -1984,7 +1984,9 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t if (MANAGER_IS_SYSTEM(m)) { /* This is for compatibility with the * original sysvinit */ - m->exit_code = MANAGER_REEXECUTE; + r = verify_run_space_and_log("Refusing to reexecute"); + if (r >= 0) + m->exit_code = MANAGER_REEXECUTE; break; } @@ -2061,7 +2063,9 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t } case SIGHUP: - m->exit_code = MANAGER_RELOAD; + r = verify_run_space_and_log("Refusing to reload"); + if (r >= 0) + m->exit_code = MANAGER_RELOAD; break; default: { @@ -2432,18 +2436,22 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) { } int manager_open_serialization(Manager *m, FILE **_f) { - const char *path; int fd = -1; FILE *f; assert(_f); - path = MANAGER_IS_SYSTEM(m) ? "/run/systemd" : "/tmp"; - fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC); - if (fd < 0) - return -errno; + fd = memfd_create("systemd-serialization", MFD_CLOEXEC); + if (fd < 0) { + const char *path; - log_debug("Serializing state to %s", 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."); f = fdopen(fd, "w+"); if (!f) { diff --git a/src/core/mount.c b/src/core/mount.c index 8192a3616f..ca0c4b0d5e 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -1241,7 +1241,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { if (f == MOUNT_SUCCESS || m->from_proc_self_mountinfo) /* If /bin/mount returned success, or if we see the mount point in /proc/self/mountinfo we are - * happy. If we see the first condition first, we should see the the second condition + * happy. If we see the first condition first, we should see the second condition * immediately after – or /bin/mount lies to us and is broken. */ mount_enter_mounted(m, f); else @@ -1408,7 +1408,7 @@ static int mount_setup_new_unit( u->source_path = strdup("/proc/self/mountinfo"); MOUNT(u)->where = strdup(where); - if (!u->source_path && !MOUNT(u)->where) + if (!u->source_path || !MOUNT(u)->where) return -ENOMEM; /* Make sure to initialize those fields before mount_is_extrinsic(). */ @@ -1435,6 +1435,7 @@ static int mount_setup_new_unit( return r; } + unit_add_to_load_queue(u); flags->is_mounted = true; flags->just_mounted = true; flags->just_changed = true; diff --git a/src/core/namespace.c b/src/core/namespace.c index 834883267c..75dca5b791 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -30,6 +30,7 @@ #include "dev-setup.h" #include "fd-util.h" #include "fs-util.h" +#include "loop-util.h" #include "loopback-setup.h" #include "missing.h" #include "mkdir.h" @@ -52,10 +53,13 @@ typedef enum MountMode { INACCESSIBLE, BIND_MOUNT, BIND_MOUNT_RECURSIVE, - READONLY, PRIVATE_TMP, PRIVATE_VAR_TMP, PRIVATE_DEV, + BIND_DEV, + SYSFS, + PROCFS, + READONLY, READWRITE, } MountMode; @@ -70,13 +74,13 @@ typedef struct MountEntry { char *source_malloc; } MountEntry; -/* - * The following Protect tables are to protect paths and mark some of them - * READONLY, in case a path is covered by an option from another table, then - * it is marked READWRITE in the current one, and the more restrictive mode is - * applied from that other table. This way all options can be combined in a - * safe and comprehensible way for users. - */ +/* If MountAPIVFS= is used, let's mount /sys and /proc into the it, but only as a fallback if the user hasn't mounted + * something there already. These mounts are hence overriden by any other explicitly configured mounts. */ +static const MountEntry apivfs_table[] = { + { "/proc", PROCFS, false }, + { "/dev", BIND_DEV, false }, + { "/sys", SYSFS, false }, +}; /* ProtectKernelTunables= option and the related filesystem APIs */ static const MountEntry protect_kernel_tunables_table[] = { @@ -176,6 +180,13 @@ static const char *mount_entry_source(const MountEntry *p) { return p->source_malloc ?: p->source_const; } +static void mount_entry_done(MountEntry *p) { + assert(p); + + p->path_malloc = mfree(p->path_malloc); + p->source_malloc = mfree(p->source_malloc); +} + static int append_access_mounts(MountEntry **p, char **strv, MountMode mode) { char **i; @@ -351,7 +362,7 @@ static void drop_duplicates(MountEntry *m, unsigned *n) { if (previous && path_equal(mount_entry_path(f), mount_entry_path(previous))) { log_debug("%s is duplicate.", mount_entry_path(f)); previous->read_only = previous->read_only || mount_entry_read_only(f); /* Propagate the read-only flag to the remaining entry */ - f->path_malloc = mfree(f->path_malloc); + mount_entry_done(f); continue; } @@ -379,7 +390,7 @@ static void drop_inaccessible(MountEntry *m, unsigned *n) { * it, as inaccessible paths really should drop the entire subtree. */ if (clear && path_startswith(mount_entry_path(f), clear)) { log_debug("%s is masked by %s.", mount_entry_path(f), clear); - f->path_malloc = mfree(f->path_malloc); + mount_entry_done(f); continue; } @@ -419,7 +430,7 @@ static void drop_nop(MountEntry *m, unsigned *n) { /* We found it, let's see if it's the same mode, if so, we can drop this entry */ if (found && p->mode == f->mode) { log_debug("%s is redundant by %s", mount_entry_path(f), mount_entry_path(p)); - f->path_malloc = mfree(f->path_malloc); + mount_entry_done(f); continue; } } @@ -447,7 +458,7 @@ static void drop_outside_root(const char *root_directory, MountEntry *m, unsigne if (!path_startswith(mount_entry_path(f), root_directory)) { log_debug("%s is outside of root directory.", mount_entry_path(f)); - f->path_malloc = mfree(f->path_malloc); + mount_entry_done(f); continue; } @@ -458,7 +469,7 @@ static void drop_outside_root(const char *root_directory, MountEntry *m, unsigne *n = t - m; } -static int mount_dev(MountEntry *m) { +static int mount_private_dev(MountEntry *m) { static const char devnodes[] = "/dev/null\0" "/dev/zero\0" @@ -597,6 +608,62 @@ fail: return r; } +static int mount_bind_dev(MountEntry *m) { + int r; + + assert(m); + + /* Implements the little brother of mount_private_dev(): simply bind mounts the host's /dev into the service's + * /dev. This is only used when RootDirectory= is set. */ + + r = path_is_mount_point(mount_entry_path(m), NULL, 0); + if (r < 0) + return log_debug_errno(r, "Unable to determine whether /dev is already mounted: %m"); + if (r > 0) /* make this a NOP if /dev is already a mount point */ + return 0; + + if (mount("/dev", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL) < 0) + return log_debug_errno(errno, "Failed to bind mount %s: %m", mount_entry_path(m)); + + return 1; +} + +static int mount_sysfs(MountEntry *m) { + int r; + + assert(m); + + r = path_is_mount_point(mount_entry_path(m), NULL, 0); + if (r < 0) + return log_debug_errno(r, "Unable to determine whether /sys is already mounted: %m"); + if (r > 0) /* make this a NOP if /sys is already a mount point */ + return 0; + + /* Bind mount the host's version so that we get all child mounts of it, too. */ + if (mount("/sys", mount_entry_path(m), NULL, MS_BIND|MS_REC, NULL) < 0) + return log_debug_errno(errno, "Failed to mount %s: %m", mount_entry_path(m)); + + return 1; +} + +static int mount_procfs(MountEntry *m) { + int r; + + assert(m); + + r = path_is_mount_point(mount_entry_path(m), NULL, 0); + if (r < 0) + return log_debug_errno(r, "Unable to determine whether /proc is already mounted: %m"); + if (r > 0) /* make this a NOP if /proc is already a mount point */ + return 0; + + /* Mount a new instance, so that we get the one that matches our user namespace, if we are running in one */ + if (mount("proc", mount_entry_path(m), "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) < 0) + return log_debug_errno(errno, "Failed to mount %s: %m", mount_entry_path(m)); + + return 1; +} + static int mount_entry_chase( const char *root_directory, MountEntry *m, @@ -684,6 +751,7 @@ static int apply_mount( case BIND_MOUNT_RECURSIVE: /* Also chase the source mount */ + r = mount_entry_chase(root_directory, m, mount_entry_source(m), &m->source_malloc); if (r <= 0) return r; @@ -700,7 +768,16 @@ static int apply_mount( break; case PRIVATE_DEV: - return mount_dev(m); + return mount_private_dev(m); + + case BIND_DEV: + return mount_bind_dev(m); + + case SYSFS: + return mount_sysfs(m); + + case PROCFS: + return mount_procfs(m); default: assert_not_reached("Unknown mode"); @@ -722,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) { /* 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 @@ -738,6 +815,17 @@ static int make_read_only(MountEntry *m, char **blacklist) { return r; } +static bool namespace_info_mount_apivfs(const NameSpaceInfo *ns_info) { + assert(ns_info); + + /* ProtectControlGroups= and ProtectKernelTunables= imply MountAPIVFS=, since to protect the API VFS mounts, + * they need to be around in the first place... */ + + return ns_info->mount_apivfs || + ns_info->protect_control_groups || + ns_info->protect_kernel_tunables; +} + static unsigned namespace_calculate_mounts( const NameSpaceInfo *ns_info, char** read_write_paths, @@ -774,11 +862,13 @@ static unsigned namespace_calculate_mounts( (ns_info->protect_kernel_tunables ? ELEMENTSOF(protect_kernel_tunables_table) : 0) + (ns_info->protect_control_groups ? 1 : 0) + (ns_info->protect_kernel_modules ? ELEMENTSOF(protect_kernel_modules_table) : 0) + - protect_home_cnt + protect_system_cnt; + protect_home_cnt + protect_system_cnt + + (namespace_info_mount_apivfs(ns_info) ? ELEMENTSOF(apivfs_table) : 0); } int setup_namespace( const char* root_directory, + const char* root_image, const NameSpaceInfo *ns_info, char** read_write_paths, char** read_only_paths, @@ -789,16 +879,57 @@ int setup_namespace( const char* var_tmp_dir, ProtectHome protect_home, ProtectSystem protect_system, - unsigned long mount_flags) { + unsigned long mount_flags, + DissectImageFlags dissect_image_flags) { + _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; + _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL; + _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL; + _cleanup_free_ void *root_hash = NULL; MountEntry *m, *mounts = NULL; + size_t root_hash_size = 0; bool make_slave = false; unsigned n_mounts; int r = 0; + assert(ns_info); + if (mount_flags == 0) mount_flags = MS_SHARED; + if (root_image) { + dissect_image_flags |= DISSECT_IMAGE_REQUIRE_ROOT; + + if (protect_system == PROTECT_SYSTEM_STRICT && strv_isempty(read_write_paths)) + dissect_image_flags |= DISSECT_IMAGE_READ_ONLY; + + r = loop_device_make_by_path(root_image, + dissect_image_flags & DISSECT_IMAGE_READ_ONLY ? O_RDONLY : O_RDWR, + &loop_device); + if (r < 0) + return r; + + r = root_hash_load(root_image, &root_hash, &root_hash_size); + if (r < 0) + return r; + + r = dissect_image(loop_device->fd, root_hash, root_hash_size, dissect_image_flags, &dissected_image); + if (r < 0) + return r; + + r = dissected_image_decrypt(dissected_image, NULL, root_hash, root_hash_size, dissect_image_flags, &decrypted_image); + if (r < 0) + return r; + + if (!root_directory) { + /* Create a mount point for the image, if it's still missing. We use the same mount point for + * all images, which is safe, since they all live in their own namespaces after all, and hence + * won't see each other. */ + root_directory = "/run/systemd/unit-root"; + (void) mkdir(root_directory, 0700); + } + } + n_mounts = namespace_calculate_mounts( ns_info, read_write_paths, @@ -878,6 +1009,12 @@ int setup_namespace( if (r < 0) goto finish; + if (namespace_info_mount_apivfs(ns_info)) { + r = append_static_mounts(&m, apivfs_table, ELEMENTSOF(apivfs_table), ns_info->ignore_protect_paths); + if (r < 0) + goto finish; + } + assert(mounts + n_mounts == m); /* Prepend the root directory where that's necessary */ @@ -907,7 +1044,19 @@ int setup_namespace( } } - if (root_directory) { + if (root_image) { + r = dissected_image_mount(dissected_image, root_directory, dissect_image_flags); + if (r < 0) + goto finish; + + r = decrypted_image_relinquish(decrypted_image); + if (r < 0) + goto finish; + + loop_device_relinquish(loop_device); + + } else if (root_directory) { + /* Turn directory into bind mount, if it isn't one yet */ r = path_is_mount_point(root_directory, NULL, AT_SYMLINK_FOLLOW); if (r < 0) @@ -964,7 +1113,7 @@ int setup_namespace( finish: for (m = mounts; m < mounts + n_mounts; m++) - free(m->path_malloc); + mount_entry_done(m); return r; } diff --git a/src/core/namespace.h b/src/core/namespace.h index de3edc419c..f54954bd86 100644 --- a/src/core/namespace.h +++ b/src/core/namespace.h @@ -25,6 +25,7 @@ typedef struct BindMount BindMount; #include <stdbool.h> +#include "dissect-image.h" #include "macro.h" typedef enum ProtectHome { @@ -50,6 +51,7 @@ struct NameSpaceInfo { bool protect_control_groups:1; bool protect_kernel_tunables:1; bool protect_kernel_modules:1; + bool mount_apivfs:1; }; struct BindMount { @@ -62,6 +64,7 @@ struct BindMount { int setup_namespace( const char *root_directory, + const char *root_image, const NameSpaceInfo *ns_info, char **read_write_paths, char **read_only_paths, @@ -72,7 +75,8 @@ int setup_namespace( const char *var_tmp_dir, ProtectHome protect_home, ProtectSystem protect_system, - unsigned long mount_flags); + unsigned long mount_flags, + DissectImageFlags dissected_image_flags); int setup_tmp_dirs( const char *id, diff --git a/src/core/socket.c b/src/core/socket.c index 3cae6b31bb..a7b9ada65c 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -2668,6 +2668,7 @@ const char* socket_port_type_to_string(SocketPort *p) { if (socket_address_family(&p->address) == AF_NETLINK) return "Netlink"; + /* fall through */ default: return NULL; } diff --git a/src/core/timer.c b/src/core/timer.c index c6b28dd9c5..af67b7591a 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -232,7 +232,7 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) { if (v->base == TIMER_CALENDAR) { _cleanup_free_ char *p = NULL; - calendar_spec_to_string(v->calendar_spec, &p); + (void) calendar_spec_to_string(v->calendar_spec, &p); fprintf(f, "%s%s: %s\n", @@ -350,7 +350,7 @@ static void add_random(Timer *t, usec_t *v) { else *v += add; - log_unit_info(UNIT(t), "Adding %s random time.", format_timespan(s, sizeof(s), add, 0)); + log_unit_debug(UNIT(t), "Adding %s random time.", format_timespan(s, sizeof(s), add, 0)); } static void timer_enter_waiting(Timer *t, bool initial) { @@ -422,7 +422,8 @@ static void timer_enter_waiting(Timer *t, bool initial) { } /* In a container we don't want to include the time the host * was already up when the container started, so count from - * our own startup. Fall through. */ + * our own startup. */ + /* fall through */ case TIMER_STARTUP: base = UNIT(t)->manager->userspace_timestamp.monotonic; break; diff --git a/src/core/transaction.c b/src/core/transaction.c index e22e3b30c2..b6d1062414 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -907,7 +907,10 @@ int transaction_add_job_and_dependencies( SET_FOREACH(dep, following, i) { r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, false, false, ignore_order, e); if (r < 0) { - log_unit_warning(dep, "Cannot add dependency job for, ignoring: %s", bus_error_message(e, r)); + log_unit_full(dep, + r == -ERFKILL ? LOG_INFO : LOG_WARNING, + r, "Cannot add dependency job, ignoring: %s", + bus_error_message(e, r)); sd_bus_error_free(e); } } diff --git a/src/core/unit.c b/src/core/unit.c index 409668f6d2..5e4b1567d8 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -862,6 +862,12 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { return r; } + if (c->root_image) { + r = unit_require_mounts_for(u, c->root_image); + if (r < 0) + return r; + } + if (!MANAGER_IS_SYSTEM(u->manager)) return 0; @@ -1083,6 +1089,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { /* Common implementation for multiple backends */ int unit_load_fragment_and_dropin(Unit *u) { + Unit *t; int r; assert(u); @@ -1095,16 +1102,18 @@ int unit_load_fragment_and_dropin(Unit *u) { if (u->load_state == UNIT_STUB) return -ENOENT; - /* Load drop-in directory data */ - r = unit_load_dropin(unit_follow_merge(u)); - if (r < 0) - return r; + /* If the unit is an alias and the final unit has already been + * loaded, there's no point in reloading the dropins one more time. */ + t = unit_follow_merge(u); + if (t != u && t->load_state != UNIT_STUB) + return 0; - return 0; + return unit_load_dropin(t); } /* Common implementation for multiple backends */ int unit_load_fragment_and_dropin_optional(Unit *u) { + Unit *t; int r; assert(u); @@ -1120,12 +1129,13 @@ int unit_load_fragment_and_dropin_optional(Unit *u) { if (u->load_state == UNIT_STUB) u->load_state = UNIT_LOADED; - /* Load drop-in directory data */ - r = unit_load_dropin(unit_follow_merge(u)); - if (r < 0) - return r; + /* If the unit is an alias and the final unit has already been + * loaded, there's no point in reloading the dropins one more time. */ + t = unit_follow_merge(u); + if (t != u && t->load_state != UNIT_STUB) + return 0; - return 0; + return unit_load_dropin(t); } int unit_add_default_target_dependency(Unit *u, Unit *target) { @@ -2640,7 +2650,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); } diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index d55d896df4..1f6fb5de1e 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -642,7 +642,11 @@ static int get_process_container_parent_cmdline(pid_t pid, char** cmdline) { if (r < 0) return r; - return get_process_cmdline(container_pid, 0, false, cmdline); + r = get_process_cmdline(container_pid, 0, false, cmdline); + if (r < 0) + return r; + + return 1; } static int change_uid_gid(const char *context[]) { diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index cefd1b85df..91c653312a 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -594,7 +594,7 @@ static int help(void) { int main(int argc, char *argv[]) { struct crypt_device *cd = NULL; - int r; + int r = -EINVAL; if (argc <= 1) { r = help(); @@ -603,7 +603,6 @@ int main(int argc, char *argv[]) { if (argc < 3) { log_error("This program requires at least two arguments."); - r = -EINVAL; goto finish; } @@ -750,7 +749,6 @@ int main(int argc, char *argv[]) { } else { log_error("Unknown verb %s.", argv[1]); - r = -EINVAL; goto finish; } diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index fd9db5ba87..59bd7d9e84 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -191,6 +191,14 @@ int main(int argc, char *argv[]) { goto finish; } + if (!arg_root_hash) { + r = root_hash_load(arg_image, &arg_root_hash, &arg_root_hash_size); + if (r < 0) { + log_error_errno(r, "Failed to read root hash file for %s: %m", arg_image); + goto finish; + } + } + r = dissect_image(d->fd, arg_root_hash, arg_root_hash_size, arg_flags, &m); if (r == -ENOPKG) { log_error_errno(r, "Couldn't identify a suitable partition table or file system in %s.", arg_image); diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 3c601a63e2..d97bafd1fb 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -57,6 +57,34 @@ static char *arg_usr_fstype = NULL; static char *arg_usr_options = NULL; static VolatileMode arg_volatile_mode = _VOLATILE_MODE_INVALID; +static int write_options(FILE *f, const char *options) { + _cleanup_free_ char *o = NULL; + + if (isempty(options)) + return 0; + + if (streq(options, "defaults")) + return 0; + + o = strreplace(options, "%", "%%"); + if (!o) + return log_oom(); + + fprintf(f, "Options=%s\n", o); + return 1; +} + +static int write_what(FILE *f, const char *what) { + _cleanup_free_ char *w = NULL; + + w = strreplace(what, "%", "%%"); + if (!w) + return log_oom(); + + fprintf(f, "What=%s\n", w); + return 1; +} + static int add_swap( const char *what, struct mntent *me, @@ -96,17 +124,19 @@ static int add_swap( "Failed to create unit file %s: %m", unit); - fprintf(f, - "# Automatically generated by systemd-fstab-generator\n\n" - "[Unit]\n" - "SourcePath=/etc/fstab\n" - "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n" - "[Swap]\n" - "What=%s\n", - what); + fputs("# Automatically generated by systemd-fstab-generator\n\n" + "[Unit]\n" + "SourcePath=/etc/fstab\n" + "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n" + "[Swap]\n", f); + + r = write_what(f, what); + if (r < 0) + return r; - if (!isempty(me->mnt_opts) && !streq(me->mnt_opts, "defaults")) - fprintf(f, "Options=%s\n", me->mnt_opts); + r = write_options(f, me->mnt_opts); + if (r < 0) + return r; r = fflush_and_check(f); if (r < 0) @@ -331,11 +361,13 @@ static int add_mount( fprintf(f, "\n" "[Mount]\n" - "What=%s\n" "Where=%s\n", - what, where); + r = write_what(f, what); + if (r < 0) + return r; + if (!isempty(fstype) && !streq(fstype, "auto")) fprintf(f, "Type=%s\n", fstype); @@ -347,8 +379,9 @@ static int add_mount( if (r < 0) return r; - if (!isempty(filtered) && !streq(filtered, "defaults")) - fprintf(f, "Options=%s\n", filtered); + r = write_options(f, filtered); + if (r < 0) + return r; r = fflush_and_check(f); if (r < 0) diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index e61ef8f249..b958070c90 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -658,7 +658,7 @@ static int get_block_device_harder(const char *path, dev_t *dev) { FOREACH_DIRENT_ALL(de, d, return -errno) { - if (STR_IN_SET(de->d_name, ".", "..")) + if (dot_or_dot_dot(de->d_name)) continue; if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN)) 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 74256e4444..4657cf8c77 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -283,7 +283,7 @@ static int context_update_kernel_hostname(Context *c) { /* ... and the ultimate fallback */ else - hn = "localhost"; + hn = FALLBACK_HOSTNAME; if (sethostname_idempotent(hn) < 0) return -errno; @@ -419,7 +419,7 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error * name = c->data[PROP_STATIC_HOSTNAME]; if (isempty(name)) - name = "localhost"; + name = FALLBACK_HOSTNAME; if (!hostname_is_valid(name, false)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", name); diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index 8ad9738edf..9a1c5b76ca 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -912,7 +912,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "hD:", options, NULL)) >= 0) switch(c) { @@ -958,6 +958,7 @@ static int parse_argv(int argc, char *argv[]) { break; #else log_error("Option --trust is not available."); + return -EINVAL; #endif case 'D': arg_directory = optarg; @@ -1028,10 +1029,9 @@ int main(int argc, char *argv[]) { { MHD_OPTION_END, 0, NULL }}; int opts_pos = 2; - /* We force MHD_USE_PIPE_FOR_SHUTDOWN here, in order - * to make sure libmicrohttpd doesn't use shutdown() - * on our listening socket, which would break socket - * re-activation. See + /* We force MHD_USE_ITC here, in order to make sure + * libmicrohttpd doesn't use shutdown() on our listening + * socket, which would break socket re-activation. See * * https://lists.gnu.org/archive/html/libmicrohttpd/2015-09/msg00014.html * https://github.com/systemd/systemd/pull/1286 @@ -1040,7 +1040,7 @@ int main(int argc, char *argv[]) { int flags = MHD_USE_DEBUG | MHD_USE_DUAL_STACK | - MHD_USE_PIPE_FOR_SHUTDOWN | + MHD_USE_ITC | MHD_USE_POLL | MHD_USE_THREAD_PER_CONNECTION; diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c index d86c3681b1..d0d8d936e3 100644 --- a/src/journal-remote/journal-remote.c +++ b/src/journal-remote/journal-remote.c @@ -648,9 +648,9 @@ static int setup_microhttpd_server(RemoteServer *s, int flags = MHD_USE_DEBUG | MHD_USE_DUAL_STACK | - MHD_USE_EPOLL_LINUX_ONLY | + MHD_USE_EPOLL | MHD_USE_PEDANTIC_CHECKS | - MHD_USE_PIPE_FOR_SHUTDOWN; + MHD_USE_ITC; const union MHD_DaemonInfo *info; int r, epoll_fd; diff --git a/src/journal-remote/microhttpd-util.h b/src/journal-remote/microhttpd-util.h index af26ab69fe..49def4f630 100644 --- a/src/journal-remote/microhttpd-util.h +++ b/src/journal-remote/microhttpd-util.h @@ -24,13 +24,25 @@ #include "macro.h" +/* Those defines are added when options are renamed, hence the check for the *old* name. */ + /* Compatiblity with libmicrohttpd < 0.9.38 */ #ifndef MHD_HTTP_NOT_ACCEPTABLE -#define MHD_HTTP_NOT_ACCEPTABLE MHD_HTTP_METHOD_NOT_ACCEPTABLE +# define MHD_HTTP_NOT_ACCEPTABLE MHD_HTTP_METHOD_NOT_ACCEPTABLE +#endif + +/* Renamed in µhttpd 0.9.52 */ +#ifndef MHD_USE_EPOLL_LINUX_ONLY +# define MHD_USE_EPOLL MHD_USE_EPOLL_LINUX_ONLY +#endif + +/* Renamed in µhttpd 0.9.51 */ +#ifndef MHD_USE_PIPE_FOR_SHUTDOWN +# define MHD_USE_ITC MHD_USE_PIPE_FOR_SHUTDOWN #endif #if MHD_VERSION < 0x00094203 -#define MHD_create_response_from_fd_at_offset64 MHD_create_response_from_fd_at_offset +# define MHD_create_response_from_fd_at_offset64 MHD_create_response_from_fd_at_offset #endif void microhttpd_logger(void *arg, const char *fmt, va_list ap) _printf_(2, 0); diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index d3e0214731..ef87b176fa 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -42,6 +42,7 @@ #include "sd-event.h" #include "set.h" #include "string-util.h" +#include "strv.h" #include "xattr-util.h" #define DEFAULT_DATA_HASH_TABLE_SIZE (2047ULL*sizeof(HashItem)) @@ -285,7 +286,7 @@ static int journal_file_set_online(JournalFile *f) { continue; /* Canceled restart from offlining, must wait for offlining to complete however. */ - /* fall through to wait */ + /* fall through */ default: { int r; @@ -507,42 +508,58 @@ static int journal_file_refresh_header(JournalFile *f) { return r; } -static int journal_file_verify_header(JournalFile *f) { +static bool warn_wrong_flags(const JournalFile *f, bool compatible) { + const uint32_t any = compatible ? HEADER_COMPATIBLE_ANY : HEADER_INCOMPATIBLE_ANY, + supported = compatible ? HEADER_COMPATIBLE_SUPPORTED : HEADER_INCOMPATIBLE_SUPPORTED; + const char *type = compatible ? "compatible" : "incompatible"; uint32_t flags; + flags = le32toh(compatible ? f->header->compatible_flags : f->header->incompatible_flags); + + if (flags & ~supported) { + if (flags & ~any) + log_debug("Journal file %s has unknown %s flags 0x%"PRIx32, + f->path, type, flags & ~any); + flags = (flags & any) & ~supported; + if (flags) { + const char* strv[3]; + unsigned n = 0; + _cleanup_free_ char *t = NULL; + + if (compatible && (flags & HEADER_COMPATIBLE_SEALED)) + strv[n++] = "sealed"; + if (!compatible && (flags & HEADER_INCOMPATIBLE_COMPRESSED_XZ)) + strv[n++] = "xz-compressed"; + if (!compatible && (flags & HEADER_INCOMPATIBLE_COMPRESSED_LZ4)) + strv[n++] = "lz4-compressed"; + strv[n] = NULL; + assert(n < ELEMENTSOF(strv)); + + t = strv_join((char**) strv, ", "); + log_debug("Journal file %s uses %s %s %s disabled at compilation time.", + f->path, type, n > 1 ? "flags" : "flag", strnull(t)); + } + return true; + } + + return false; +} + +static int journal_file_verify_header(JournalFile *f) { assert(f); assert(f->header); if (memcmp(f->header->signature, HEADER_SIGNATURE, 8)) return -EBADMSG; - /* In both read and write mode we refuse to open files with - * incompatible flags we don't know */ - flags = le32toh(f->header->incompatible_flags); - if (flags & ~HEADER_INCOMPATIBLE_SUPPORTED) { - if (flags & ~HEADER_INCOMPATIBLE_ANY) - log_debug("Journal file %s has unknown incompatible flags %"PRIx32, - f->path, flags & ~HEADER_INCOMPATIBLE_ANY); - flags = (flags & HEADER_INCOMPATIBLE_ANY) & ~HEADER_INCOMPATIBLE_SUPPORTED; - if (flags) - log_debug("Journal file %s uses incompatible flags %"PRIx32 - " disabled at compilation time.", f->path, flags); + /* In both read and write mode we refuse to open files with incompatible + * flags we don't know. */ + if (warn_wrong_flags(f, false)) return -EPROTONOSUPPORT; - } - /* When open for writing we refuse to open files with - * compatible flags, too */ - flags = le32toh(f->header->compatible_flags); - if (f->writable && (flags & ~HEADER_COMPATIBLE_SUPPORTED)) { - if (flags & ~HEADER_COMPATIBLE_ANY) - log_debug("Journal file %s has unknown compatible flags %"PRIx32, - f->path, flags & ~HEADER_COMPATIBLE_ANY); - flags = (flags & HEADER_COMPATIBLE_ANY) & ~HEADER_COMPATIBLE_SUPPORTED; - if (flags) - log_debug("Journal file %s uses compatible flags %"PRIx32 - " disabled at compilation time.", f->path, flags); + /* When open for writing we refuse to open files with compatible flags, too. */ + if (f->writable && warn_wrong_flags(f, true)) return -EPROTONOSUPPORT; - } if (f->header->state >= _STATE_MAX) return -EBADMSG; @@ -580,12 +597,12 @@ static int journal_file_verify_header(JournalFile *f) { state = f->header->state; - if (state == STATE_ONLINE) { + if (state == STATE_ARCHIVED) + return -ESHUTDOWN; /* Already archived */ + else if (state == STATE_ONLINE) { log_debug("Journal file %s is already online. Assuming unclean closing.", f->path); return -EBUSY; - } else if (state == STATE_ARCHIVED) - return -ESHUTDOWN; - else if (state != STATE_OFFLINE) { + } else if (state != STATE_OFFLINE) { log_debug("Journal file %s has unknown state %i.", f->path, state); return -EBUSY; } @@ -3087,13 +3104,18 @@ int journal_file_open( } } - if (fname) + if (fname) { f->path = strdup(fname); - else /* If we don't know the path, fill in something explanatory and vaguely useful */ - asprintf(&f->path, "/proc/self/%i", fd); - if (!f->path) { - r = -ENOMEM; - goto fail; + if (!f->path) { + r = -ENOMEM; + goto fail; + } + } else { + /* If we don't know the path, fill in something explanatory and vaguely useful */ + if (asprintf(&f->path, "/proc/self/%i", fd) < 0) { + r = -ENOMEM; + goto fail; + } } f->chain_cache = ordered_hashmap_new(&uint64_hash_ops); @@ -3330,12 +3352,12 @@ int journal_file_open_reliably( r = journal_file_open(-1, fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret); if (!IN_SET(r, - -EBADMSG, /* corrupted */ - -ENODATA, /* truncated */ - -EHOSTDOWN, /* other machine */ - -EPROTONOSUPPORT, /* incompatible feature */ - -EBUSY, /* unclean shutdown */ - -ESHUTDOWN, /* already archived */ + -EBADMSG, /* Corrupted */ + -ENODATA, /* Truncated */ + -EHOSTDOWN, /* Other machine */ + -EPROTONOSUPPORT, /* Incompatible feature */ + -EBUSY, /* Unclean shutdown */ + -ESHUTDOWN, /* Already archived */ -EIO, /* IO error, including SIGBUS on mmap */ -EIDRM, /* File has been deleted */ -ETXTBSY)) /* File is from the future */ diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 2a5f2b37e8..9ad6f115a1 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -686,7 +686,9 @@ static int parse_argv(int argc, char *argv[]) { r = free_and_strdup(&arg_verify_key, optarg); if (r < 0) return r; - string_erase(optarg); + /* Use memset not string_erase so this doesn't look confusing + * in ps or htop output. */ + memset(optarg, 'x', strlen(optarg)); arg_merge = false; break; @@ -1910,7 +1912,9 @@ static int access_check(sd_journal *j) { break; case EPROTONOSUPPORT: - log_warning_errno(err, "Journal file %s uses an unsupported feature, ignoring file.", path); + 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: diff --git a/src/journal/lookup3.c b/src/journal/lookup3.c index d8f1a4977d..ec725ce46c 100644 --- a/src/journal/lookup3.c +++ b/src/journal/lookup3.c @@ -48,6 +48,10 @@ on 1 byte), but shoehorning those bytes into integers efficiently is messy. # include <endian.h> /* attempt to define endianness */ #endif +#if __GNUC__ >= 7 +_Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"") +#endif + /* * My best guess at if you are big-endian or little-endian. This may * need adjustment. diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 5462e03476..f8056dbc4b 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -339,7 +339,7 @@ int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char ***str_arr) { size_t pos = 0, idx = 0; - _cleanup_free_ char **names = NULL; + _cleanup_strv_free_ char **names = NULL; int r; assert_return(optlen > 1, -ENODATA); diff --git a/src/libsystemd-network/lldp-network.c b/src/libsystemd-network/lldp-network.c index 59c25598e9..ae2f6744d5 100644 --- a/src/libsystemd-network/lldp-network.c +++ b/src/libsystemd-network/lldp-network.c @@ -47,6 +47,13 @@ int lldp_network_bind_raw_socket(int ifindex) { .filter = (struct sock_filter*) filter, }; + struct packet_mreq mreq = { + .mr_ifindex = ifindex, + .mr_type = PACKET_MR_MULTICAST, + .mr_alen = ETH_ALEN, + .mr_address = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 } + }; + union sockaddr_union saddrll = { .ll.sll_family = AF_PACKET, .ll.sll_ifindex = ifindex, @@ -66,6 +73,20 @@ int lldp_network_bind_raw_socket(int ifindex) { if (r < 0) return -errno; + r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + if (r < 0) + return -errno; + + mreq.mr_address[ETH_ALEN - 1] = 0x03; + r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + if (r < 0) + return -errno; + + mreq.mr_address[ETH_ALEN - 1] = 0x0E; + r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + if (r < 0) + return -errno; + r = bind(fd, &saddrll.sa, sizeof(saddrll.ll)); if (r < 0) return -errno; diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index 0e57ab6b69..2890681561 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -857,7 +857,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, if (!existing_lease) { lease = new0(DHCPLease, 1); - lease->address = req->requested_ip; + lease->address = address; lease->client_id.data = memdup(req->client_id.data, req->client_id.length); if (!lease->client_id.data) { diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index e81215f7d7..6444b0ce94 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -999,7 +999,7 @@ static int client_receive_message( break; } - /* fall through for Soliciation Rapid Commit option check */ + /* fall through */ /* for Soliciation Rapid Commit option check */ case DHCP6_STATE_REQUEST: case DHCP6_STATE_RENEW: case DHCP6_STATE_REBIND: diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index ab59977a3f..8396407e9f 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -226,7 +226,7 @@ int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, if (r < 0) return 0; - free(lease->domains); + strv_free(lease->domains); lease->domains = domains; lease->domains_count = r; diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c index d2a826bf6e..b40ba2520c 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ b/src/libsystemd/sd-bus/bus-common-errors.c @@ -38,7 +38,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, EDEADLK), SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, EDEADLK), SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, EDEADLK), - SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_MASKED, ESHUTDOWN), + SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_MASKED, ERFKILL), SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_GENERATED, EADDRNOTAVAIL), SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_LINKED, ELOOP), SD_BUS_ERROR_MAP(BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, EBADR), @@ -47,6 +47,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_SCOPE_NOT_RUNNING, EHOSTDOWN), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_DYNAMIC_USER, ESRCH), SD_BUS_ERROR_MAP(BUS_ERROR_NOT_REFERENCED, EUNATCH), + SD_BUS_ERROR_MAP(BUS_ERROR_DISK_FULL, ENOSPC), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_MACHINE, ENXIO), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_IMAGE, ENOENT), diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index 525b79fa77..4523be05ce 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -43,6 +43,7 @@ #define BUS_ERROR_SCOPE_NOT_RUNNING "org.freedesktop.systemd1.ScopeNotRunning" #define BUS_ERROR_NO_SUCH_DYNAMIC_USER "org.freedesktop.systemd1.NoSuchDynamicUser" #define BUS_ERROR_NOT_REFERENCED "org.freedesktop.systemd1.NotReferenced" +#define BUS_ERROR_DISK_FULL "org.freedesktop.systemd1.DiskFull" #define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine" #define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage" diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index bc5e92f8fe..efeadf0cd4 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -560,7 +560,7 @@ int device_read_uevent_file(sd_device *device) { value = &uevent[i]; state = VALUE; - /* fall through to handle empty property */ + /* fall through */ /* to handle empty property */ case VALUE: if (strchr(NEWLINE, uevent[i])) { uevent[i] = '\0'; diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index f94959adac..b4686d0065 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -730,7 +730,6 @@ static void event_unmask_signal_data(sd_event *e, struct signal_data *d, int sig /* If all the mask is all-zero we can get rid of the structure */ hashmap_remove(e->signal_data, &d->priority); - assert(!d->current); safe_close(d->fd); free(d); return; @@ -2226,11 +2225,16 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) { } static int source_dispatch(sd_event_source *s) { + EventSourceType saved_type; int r = 0; assert(s); assert(s->pending || s->type == SOURCE_EXIT); + /* Save the event source type, here, so that we still know it after the event callback which might invalidate + * the event. */ + saved_type = s->type; + if (s->type != SOURCE_DEFER && s->type != SOURCE_EXIT) { r = source_set_pending(s, false); if (r < 0) @@ -2318,7 +2322,7 @@ static int source_dispatch(sd_event_source *s) { if (r < 0) log_debug_errno(r, "Event source %s (type %s) returned error, disabling: %m", - strna(s->description), event_source_type_to_string(s->type)); + strna(s->description), event_source_type_to_string(saved_type)); if (s->n_ref == 0) source_free(s); diff --git a/src/libudev/libudev-util.c b/src/libudev/libudev-util.c index a9819b9db3..1d73d8f090 100644 --- a/src/libudev/libudev-util.c +++ b/src/libudev/libudev-util.c @@ -161,6 +161,20 @@ void util_remove_trailing_chars(char *path, char c) path[--len] = '\0'; } +/* + * Copy from 'str' to 'to', while removing all leading and trailing whitespace, + * and replacing each run of consecutive whitespace with a single underscore. + * The chars from 'str' are copied up to the \0 at the end of the string, or + * at most 'len' chars. This appends \0 to 'to', at the end of the copied + * characters. + * + * If 'len' chars are copied into 'to', the final \0 is placed at len+1 + * (i.e. 'to[len] = \0'), so the 'to' buffer must have at least len+1 + * chars available. + * + * Note this may be called with 'str' == 'to', i.e. to replace whitespace + * in-place in a buffer. This function can handle that situation. + */ int util_replace_whitespace(const char *str, char *to, size_t len) { size_t i, j; 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 4c618ed19e..7dea5c0859 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -240,7 +240,6 @@ static int list_seats(int argc, char *argv[], void *userdata) { sd_bus *bus = userdata; unsigned k = 0; int r; - assert(bus); assert(argv); @@ -280,35 +279,17 @@ static int list_seats(int argc, char *argv[], void *userdata) { } static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit, pid_t leader) { + _cleanup_free_ char *cgroup = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_free_ char *path = NULL; - const char *cgroup; unsigned c; int r; assert(bus); assert(unit); - path = unit_dbus_path_from_name(unit); - if (!path) - return log_oom(); - - r = sd_bus_get_property( - bus, - "org.freedesktop.systemd1", - path, - interface, - "ControlGroup", - &error, - &reply, - "s"); - if (r < 0) - return log_error_errno(r, "Failed to query ControlGroup: %s", bus_error_message(&error, r)); - - r = sd_bus_message_read(reply, "s", &cgroup); + r = show_cgroup_get_unit_path_and_warn(bus, unit, &cgroup); if (r < 0) - return bus_log_parse_error(r); + return r; if (isempty(cgroup)) return 0; @@ -501,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"); @@ -630,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"); @@ -704,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-dbus.c b/src/login/logind-dbus.c index 3873bf3e96..ad44ca290e 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -2396,13 +2396,9 @@ static int method_set_wall_message( if (r == 0) return 1; /* Will call us back */ - if (isempty(wall_message)) - m->wall_message = mfree(m->wall_message); - else { - r = free_and_strdup(&m->wall_message, wall_message); - if (r < 0) - return log_oom(); - } + r = free_and_strdup(&m->wall_message, empty_to_null(wall_message)); + if (r < 0) + return log_oom(); m->enable_wall_messages = enable_wall_messages; diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c index f0d8a1f554..bfbd07309d 100644 --- a/src/login/logind-seat.c +++ b/src/login/logind-seat.c @@ -541,8 +541,6 @@ int seat_attach_session(Seat *s, Session *session) { LIST_PREPEND(sessions_by_seat, s->sessions, session); seat_assign_position(s, session); - seat_send_changed(s, "Sessions", NULL); - /* On seats with VTs, the VT logic defines which session is active. On * seats without VTs, we automatically activate new sessions. */ if (!seat_has_vts(s)) diff --git a/src/login/logind-session.c b/src/login/logind-session.c index d8bb1ea0a8..fd7fcf7f2c 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -586,12 +586,10 @@ int session_start(Session *s) { /* Send signals */ session_send_signal(s, true); - user_send_changed(s->user, "Sessions", "Display", NULL); + user_send_changed(s->user, "Display", NULL); if (s->seat) { if (s->seat->active == s) - seat_send_changed(s->seat, "Sessions", "ActiveSession", NULL); - else - seat_send_changed(s->seat, "Sessions", NULL); + seat_send_changed(s->seat, "ActiveSession", NULL); } return 0; @@ -698,11 +696,10 @@ int session_finalize(Session *s) { seat_set_active(s->seat, NULL); seat_save(s->seat); - seat_send_changed(s->seat, "Sessions", NULL); } user_save(s->user); - user_send_changed(s->user, "Sessions", "Display", NULL); + user_send_changed(s->user, "Display", NULL); return 0; } diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 31a40d47c3..fe4f1b7726 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -477,35 +477,17 @@ static int list_images(int argc, char *argv[], void *userdata) { } static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { + _cleanup_free_ char *cgroup = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_free_ char *path = NULL; - const char *cgroup; int r; unsigned c; assert(bus); assert(unit); - path = unit_dbus_path_from_name(unit); - if (!path) - return log_oom(); - - r = sd_bus_get_property( - bus, - "org.freedesktop.systemd1", - path, - unit_dbus_interface_from_name(unit), - "ControlGroup", - &error, - &reply, - "s"); + r = show_cgroup_get_unit_path_and_warn(bus, unit, &cgroup); if (r < 0) - return log_error_errno(r, "Failed to query ControlGroup: %s", bus_error_message(&error, r)); - - r = sd_bus_message_read(reply, "s", &cgroup); - if (r < 0) - return bus_log_parse_error(r); + return r; if (isempty(cgroup)) return 0; @@ -790,6 +772,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; @@ -802,9 +785,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"); @@ -980,6 +964,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; @@ -991,9 +976,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"); @@ -1047,6 +1033,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); @@ -1055,9 +1043,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/mount/mount-tool.c b/src/mount/mount-tool.c index 9d79e81918..4b3cac8a22 100644 --- a/src/mount/mount-tool.c +++ b/src/mount/mount-tool.c @@ -40,6 +40,7 @@ enum { ACTION_DEFAULT, ACTION_MOUNT, ACTION_AUTOMOUNT, + ACTION_UMOUNT, ACTION_LIST, } arg_action = ACTION_DEFAULT; @@ -99,6 +100,7 @@ static void help(void) { " Set automount unit property\n" " --bind-device Bind automount unit to device\n" " --list List mountable block devices\n" + " -u --umount Unmount mount points\n" , program_invocation_short_name); } @@ -137,6 +139,7 @@ static int parse_argv(int argc, char *argv[]) { { "discover", no_argument, NULL, ARG_DISCOVER }, { "type", required_argument, NULL, 't' }, { "options", required_argument, NULL, 'o' }, + { "fsck", required_argument, NULL, ARG_FSCK }, { "description", required_argument, NULL, ARG_DESCRIPTION }, { "property", required_argument, NULL, 'p' }, { "automount", required_argument, NULL, ARG_AUTOMOUNT }, @@ -144,6 +147,8 @@ static int parse_argv(int argc, char *argv[]) { { "automount-property", required_argument, NULL, ARG_AUTOMOUNT_PROPERTY }, { "bind-device", no_argument, NULL, ARG_BIND_DEVICE }, { "list", no_argument, NULL, ARG_LIST }, + { "umount", no_argument, NULL, 'u' }, + { "unmount", no_argument, NULL, 'u' }, {}, }; @@ -152,7 +157,10 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hqH:M:t:o:p:A", options, NULL)) >= 0) + if (strstr(program_invocation_short_name, "systemd-umount")) + arg_action = ACTION_UMOUNT; + + while ((c = getopt_long(argc, argv, "hqH:M:t:o:p:Au", options, NULL)) >= 0) switch (c) { @@ -263,6 +271,10 @@ static int parse_argv(int argc, char *argv[]) { arg_action = ACTION_LIST; break; + case 'u': + arg_action = ACTION_UMOUNT; + break; + case '?': return -EINVAL; @@ -607,6 +619,89 @@ static int start_transient_automount( return 0; } +static int stop_mount( + sd_bus *bus, + char **argv, + const char *suffix) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; + _cleanup_free_ char *mount_unit = NULL; + int r; + + if (!arg_no_block) { + r = bus_wait_for_jobs_new(bus, &w); + if (r < 0) + return log_error_errno(r, "Could not watch jobs: %m"); + } + + r = unit_name_from_path(arg_mount_where, suffix, &mount_unit); + if (r < 0) + return log_error_errno(r, "Failed to make mount unit name: %m"); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StopUnit"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password); + if (r < 0) + return bus_log_create_error(r); + + /* Name and mode */ + r = sd_bus_message_append(m, "ss", mount_unit, "fail"); + if (r < 0) + return bus_log_create_error(r); + + polkit_agent_open_if_enabled(); + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) + return log_error_errno(r, "Failed to stop mount unit: %s", bus_error_message(&error, r)); + + if (w) { + const char *object; + + r = sd_bus_message_read(reply, "o", &object); + if (r < 0) + return bus_log_parse_error(r); + + r = bus_wait_for_jobs_one(w, object, arg_quiet); + if (r < 0) + return r; + } + + if (!arg_quiet) + log_info("Stopped unit %s%s%s for mount point: %s%s%s", + ansi_highlight(), mount_unit, ansi_normal(), + ansi_highlight(), arg_mount_where, ansi_normal()); + + return 0; +} + +static int stop_mounts( + sd_bus *bus, + char **argv) { + + int r; + + r = stop_mount(bus, argv + optind, ".mount"); + if (r < 0) + return r; + + r = stop_mount(bus, argv + optind, ".automount"); + if (r < 0) + return r; + + return 0; +} + static int acquire_mount_type(struct udev_device *d) { const char *v; @@ -1093,6 +1188,10 @@ int main(int argc, char* argv[]) { r = start_transient_automount(bus, argv + optind); break; + case ACTION_UMOUNT: + r = stop_mounts(bus, argv + optind); + break; + default: assert_not_reached("Unexpected action."); } diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 2b698d9531..ffd2e18a45 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -809,8 +809,8 @@ int config_parse_label( if (r < 0) return r; - if (!ifname_valid(rvalue)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is not valid or too long, ignoring assignment: %s", rvalue); + if (strlen(rvalue) >= IFNAMSIZ) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is too long, ignoring assignment: %s", rvalue); return 0; } 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..92062ca00c 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -70,6 +70,7 @@ 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) @@ -152,6 +153,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 +226,7 @@ void network_free(Network *network) { Route *route; Address *address; FdbEntry *fdb_entry; + IPv6ProxyNDPAddress *ipv6_proxy_ndp_address; Iterator i; if (!network) @@ -268,6 +271,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); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index b7da9d22d4..f06828a899 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" @@ -188,10 +189,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/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf index c0fa4bfa1f..e5fdf63162 100644 --- a/src/nspawn/nspawn-gperf.gperf +++ b/src/nspawn/nspawn-gperf.gperf @@ -26,6 +26,7 @@ Exec.KillSignal, config_parse_signal, 0, offsetof(Settings, Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality) Exec.MachineID, config_parse_id128, 0, offsetof(Settings, machine_id) Exec.WorkingDirectory, config_parse_path, 0, offsetof(Settings, working_directory) +Exec.PivotRoot, config_parse_pivot_root, 0, 0 Exec.PrivateUsers, config_parse_private_users, 0, 0 Exec.NotifyReady, config_parse_bool, 0, offsetof(Settings, notify_ready) Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only) diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 72c007f204..4b2838b752 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -1349,3 +1349,116 @@ fail: (void) rmdir(template); return r; } + +/* Expects *pivot_root_new and *pivot_root_old to be initialised to allocated memory or NULL. */ +int pivot_root_parse(char **pivot_root_new, char **pivot_root_old, const char *s) { + _cleanup_free_ char *root_new = NULL, *root_old = NULL; + const char *p = s; + int r; + + assert(pivot_root_new); + assert(pivot_root_old); + + r = extract_first_word(&p, &root_new, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + + if (isempty(p)) + root_old = NULL; + else { + root_old = strdup(p); + if (!root_old) + return -ENOMEM; + } + + if (!path_is_absolute(root_new)) + return -EINVAL; + if (root_old && !path_is_absolute(root_old)) + return -EINVAL; + + free_and_replace(*pivot_root_new, root_new); + free_and_replace(*pivot_root_old, root_old); + + return 0; +} + +int setup_pivot_root(const char *directory, const char *pivot_root_new, const char *pivot_root_old) { + _cleanup_free_ char *directory_pivot_root_new = NULL; + _cleanup_free_ char *pivot_tmp_pivot_root_old = NULL; + char pivot_tmp[] = "/tmp/nspawn-pivot-XXXXXX"; + bool remove_pivot_tmp = false; + int r; + + assert(directory); + + if (!pivot_root_new) + return 0; + + /* Pivot pivot_root_new to / and the existing / to pivot_root_old. + * If pivot_root_old is NULL, the existing / disappears. + * This requires a temporary directory, pivot_tmp, which is + * not a child of either. + * + * This is typically used for OSTree-style containers, where + * the root partition contains several sysroots which could be + * run. Normally, one would be chosen by the bootloader and + * pivoted to / by initramfs. + * + * For example, for an OSTree deployment, pivot_root_new + * would be: /ostree/deploy/$os/deploy/$checksum. Note that this + * code doesn’t do the /var mount which OSTree expects: use + * --bind +/sysroot/ostree/deploy/$os/var:/var for that. + * + * So in the OSTree case, we’ll end up with something like: + * - directory = /tmp/nspawn-root-123456 + * - pivot_root_new = /ostree/deploy/os/deploy/123abc + * - pivot_root_old = /sysroot + * - directory_pivot_root_new = + * /tmp/nspawn-root-123456/ostree/deploy/os/deploy/123abc + * - pivot_tmp = /tmp/nspawn-pivot-123456 + * - pivot_tmp_pivot_root_old = /tmp/nspawn-pivot-123456/sysroot + * + * Requires all file systems at directory and below to be mounted + * MS_PRIVATE or MS_SLAVE so they can be moved. + */ + directory_pivot_root_new = prefix_root(directory, pivot_root_new); + + /* Remount directory_pivot_root_new to make it movable. */ + r = mount_verbose(LOG_ERR, directory_pivot_root_new, directory_pivot_root_new, NULL, MS_BIND, NULL); + if (r < 0) + goto done; + + if (pivot_root_old) { + if (!mkdtemp(pivot_tmp)) { + r = log_error_errno(errno, "Failed to create temporary directory: %m"); + goto done; + } + + remove_pivot_tmp = true; + pivot_tmp_pivot_root_old = prefix_root(pivot_tmp, pivot_root_old); + + r = mount_verbose(LOG_ERR, directory_pivot_root_new, pivot_tmp, NULL, MS_MOVE, NULL); + if (r < 0) + goto done; + + r = mount_verbose(LOG_ERR, directory, pivot_tmp_pivot_root_old, NULL, MS_MOVE, NULL); + if (r < 0) + goto done; + + r = mount_verbose(LOG_ERR, pivot_tmp, directory, NULL, MS_MOVE, NULL); + if (r < 0) + goto done; + } else { + r = mount_verbose(LOG_ERR, directory_pivot_root_new, directory, NULL, MS_MOVE, NULL); + if (r < 0) + goto done; + } + +done: + if (remove_pivot_tmp) + (void) rmdir(pivot_tmp); + + return r; +} diff --git a/src/nspawn/nspawn-mount.h b/src/nspawn/nspawn-mount.h index 6b33fbff57..2777d2169b 100644 --- a/src/nspawn/nspawn-mount.h +++ b/src/nspawn/nspawn-mount.h @@ -70,3 +70,6 @@ int mount_custom(const char *dest, CustomMount *mounts, unsigned n, bool userns, int setup_volatile(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); int setup_volatile_state(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); + +int pivot_root_parse(char **pivot_root_new, char **pivot_root_old, const char *s); +int setup_pivot_root(const char *directory, const char *pivot_root_new, const char *pivot_root_old); diff --git a/src/nspawn/nspawn-patch-uid.c b/src/nspawn/nspawn-patch-uid.c index ded5866d05..1a3f129db0 100644 --- a/src/nspawn/nspawn-patch-uid.c +++ b/src/nspawn/nspawn-patch-uid.c @@ -375,7 +375,7 @@ static int recurse_fd(int fd, bool donate_fd, const struct stat *st, uid_t shift FOREACH_DIRENT_ALL(de, d, r = -errno; goto finish) { struct stat fst; - if (STR_IN_SET(de->d_name, ".", "..")) + if (dot_or_dot_dot(de->d_name)) continue; if (fstatat(dirfd(d), de->d_name, &fst, AT_SYMLINK_NOFOLLOW) < 0) { diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c index 22b74d88e4..5217d10665 100644 --- a/src/nspawn/nspawn-settings.c +++ b/src/nspawn/nspawn-settings.c @@ -90,6 +90,8 @@ Settings* settings_free(Settings *s) { strv_free(s->parameters); strv_free(s->environment); free(s->user); + free(s->pivot_root_new); + free(s->pivot_root_old); free(s->working_directory); strv_free(s->network_interfaces); @@ -237,6 +239,34 @@ int config_parse_id128( return 0; } +int config_parse_pivot_root( + 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) { + + Settings *settings = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = pivot_root_parse(&settings->pivot_root_new, &settings->pivot_root_old, rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid pivot root mount specification %s: %m", rvalue); + return 0; + } + + return 0; +} + int config_parse_bind( const char *unit, const char *filename, diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index e9ea087191..021403258f 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -57,7 +57,8 @@ typedef enum SettingsMask { SETTING_WORKING_DIRECTORY = 1 << 12, SETTING_USERNS = 1 << 13, SETTING_NOTIFY_READY = 1 << 14, - _SETTINGS_MASK_ALL = (1 << 15) -1 + SETTING_PIVOT_ROOT = 1 << 15, + _SETTINGS_MASK_ALL = (1 << 16) -1 } SettingsMask; typedef struct Settings { @@ -72,6 +73,8 @@ typedef struct Settings { unsigned long personality; sd_id128_t machine_id; char *working_directory; + char *pivot_root_new; + char *pivot_root_old; UserNamespaceMode userns_mode; uid_t uid_shift, uid_range; bool notify_ready; @@ -109,6 +112,7 @@ int config_parse_capability(const char *unit, const char *filename, unsigned lin int config_parse_id128(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_expose_port(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_volatile_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_pivot_root(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_bind(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_tmpfs(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_overlay(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/nspawn/nspawn-stub-pid1.c b/src/nspawn/nspawn-stub-pid1.c index 38ab37367e..7f2a53a245 100644 --- a/src/nspawn/nspawn-stub-pid1.c +++ b/src/nspawn/nspawn-stub-pid1.c @@ -87,7 +87,7 @@ int stub_pid1(sd_id128_t uuid) { /* Flush out /proc/self/environ, so that we don't leak the environment from the host into the container. Also, * set $container= and $container_uuid= so that clients in the container that query it from /proc/1/environ - * find them set set. */ + * find them set. */ sd_id128_to_string(uuid, new_environment + sizeof(new_environment) - SD_ID128_STRING_MAX); reset_environ(new_environment, sizeof(new_environment)); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 532be148a6..b172b44933 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -132,6 +132,8 @@ typedef enum LinkJournal { static char *arg_directory = NULL; static char *arg_template = NULL; static char *arg_chdir = NULL; +static char *arg_pivot_root_new = NULL; +static char *arg_pivot_root_old = NULL; static char *arg_user = NULL; static sd_id128_t arg_uuid = {}; static char *arg_machine = NULL; @@ -221,6 +223,8 @@ static void help(void) { " -a --as-pid2 Maintain a stub init as PID1, invoke binary as PID2\n" " -b --boot Boot up full system (i.e. invoke init)\n" " --chdir=PATH Set working directory in the container\n" + " --pivot-root=PATH[:PATH]\n" + " Pivot root to given directory in the container\n" " -u --user=USER Run the command under specified user or uid\n" " -M --machine=NAME Set the machine name for the container\n" " --uuid=UUID Set a specific machine UUID for the container\n" @@ -427,6 +431,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_KILL_SIGNAL, ARG_SETTINGS, ARG_CHDIR, + ARG_PIVOT_ROOT, ARG_PRIVATE_USERS_CHOWN, ARG_NOTIFY_READY, ARG_ROOT_HASH, @@ -478,6 +483,7 @@ static int parse_argv(int argc, char *argv[]) { { "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL }, { "settings", required_argument, NULL, ARG_SETTINGS }, { "chdir", required_argument, NULL, ARG_CHDIR }, + { "pivot-root", required_argument, NULL, ARG_PIVOT_ROOT }, { "notify-ready", required_argument, NULL, ARG_NOTIFY_READY }, { "root-hash", required_argument, NULL, ARG_ROOT_HASH }, {} @@ -676,9 +682,8 @@ static int parse_argv(int argc, char *argv[]) { r = free_and_strdup(&arg_machine, optarg); if (r < 0) return log_oom(); - - break; } + break; case 'Z': arg_selinux_context = optarg; @@ -1013,6 +1018,14 @@ static int parse_argv(int argc, char *argv[]) { arg_settings_mask |= SETTING_WORKING_DIRECTORY; break; + case ARG_PIVOT_ROOT: + r = pivot_root_parse(&arg_pivot_root_new, &arg_pivot_root_old, optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --pivot-root= argument %s: %m", optarg); + + arg_settings_mask |= SETTING_PIVOT_ROOT; + break; + case ARG_NOTIFY_READY: r = parse_boolean(optarg); if (r < 0) { @@ -1918,7 +1931,7 @@ static int wait_for_container(pid_t pid, ContainerStatus *container) { return 0; } - /* CLD_KILLED fallthrough */ + /* fall through */ case CLD_DUMPED: log_error("Container %s terminated by signal %s.", arg_machine, signal_to_string(status.si_status)); @@ -2131,6 +2144,7 @@ static int inner_child( NULL, /* NOTIFY_SOCKET */ NULL }; + const char *exec_target; _cleanup_strv_free_ char **env_use = NULL; int r; @@ -2325,20 +2339,25 @@ static int inner_child( a[0] = (char*) "/sbin/init"; execve(a[0], a, env_use); - } else if (!strv_isempty(arg_parameters)) + + exec_target = "/usr/lib/systemd/systemd, /lib/systemd/systemd, /sbin/init"; + } else if (!strv_isempty(arg_parameters)) { + exec_target = arg_parameters[0]; execvpe(arg_parameters[0], arg_parameters, env_use); - else { + } else { if (!arg_chdir) /* If we cannot change the directory, we'll end up in /, that is expected. */ (void) chdir(home ?: "/root"); execle("/bin/bash", "-bash", NULL, env_use); execle("/bin/sh", "-sh", NULL, env_use); + + exec_target = "/bin/bash, /bin/sh"; } r = -errno; (void) log_open(); - return log_error_errno(r, "execv() failed: %m"); + return log_error_errno(r, "execv(%s) failed: %m", exec_target); } static int setup_sd_notify_child(void) { @@ -2488,6 +2507,13 @@ static int outer_child( if (r < 0) return r; + r = setup_pivot_root( + directory, + arg_pivot_root_new, + arg_pivot_root_old); + if (r < 0) + return r; + r = setup_volatile( directory, arg_volatile_mode, @@ -2910,6 +2936,12 @@ static int load_settings(void) { settings->parameters = NULL; } + if ((arg_settings_mask & SETTING_PIVOT_ROOT) == 0 && + settings->pivot_root_new) { + free_and_replace(arg_pivot_root_new, settings->pivot_root_new); + free_and_replace(arg_pivot_root_old, settings->pivot_root_old); + } + if ((arg_settings_mask & SETTING_WORKING_DIRECTORY) == 0 && settings->working_directory) { free(arg_chdir); @@ -3475,53 +3507,6 @@ static int run(int master, return 1; /* loop again */ } -static int load_root_hash(const char *image) { - _cleanup_free_ char *text = NULL, *fn = NULL; - char *n, *e; - void *k; - size_t l; - int r; - - assert_se(image); - - /* Try to load the root hash from a file next to the image file if it exists. */ - - if (arg_root_hash) - return 0; - - fn = new(char, strlen(image) + strlen(".roothash") + 1); - if (!fn) - return log_oom(); - - n = stpcpy(fn, image); - e = endswith(fn, ".raw"); - if (e) - n = e; - - strcpy(n, ".roothash"); - - r = read_one_line_file(fn, &text); - if (r == -ENOENT) - return 0; - if (r < 0) { - log_warning_errno(r, "Failed to read %s, ignoring: %m", fn); - return 0; - } - - r = unhexmem(text, strlen(text), &k, &l); - if (r < 0) - return log_error_errno(r, "Invalid root hash: %s", text); - if (l < sizeof(sd_id128_t)) { - free(k); - return log_error_errno(r, "Root hash too short: %s", text); - } - - arg_root_hash = k; - arg_root_hash_size = l; - - return 0; -} - int main(int argc, char *argv[]) { _cleanup_free_ char *console = NULL; @@ -3737,9 +3722,13 @@ int main(int argc, char *argv[]) { goto finish; } - r = load_root_hash(arg_image); - if (r < 0) - goto finish; + if (!arg_root_hash) { + r = root_hash_load(arg_image, &arg_root_hash, &arg_root_hash_size); + if (r < 0) { + log_error_errno(r, "Failed to load root hash file for %s: %m", arg_image); + goto finish; + } + } } if (!mkdtemp(tmprootdir)) { @@ -3910,6 +3899,8 @@ finish: free(arg_image); free(arg_machine); free(arg_user); + free(arg_pivot_root_new); + free(arg_pivot_root_old); free(arg_chdir); strv_free(arg_setenv); free(arg_network_bridge); diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c index 11c27575c0..326672cab5 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,9 +111,6 @@ 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) { @@ -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)); diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c index d46a3afe91..ce8d59d390 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_INET)); + 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, @@ -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)); diff --git a/src/reply-password/reply-password.c b/src/reply-password/reply-password.c index 17eab9772e..a17c8a62b8 100644 --- a/src/reply-password/reply-password.c +++ b/src/reply-password/reply-password.c @@ -90,7 +90,7 @@ int main(int argc, char *argv[]) { r = send_on_socket(fd, argv[2], packet, length); finish: - memory_erase(packet, sizeof(packet)); + explicit_bzero(packet, sizeof(packet)); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c index 0c62e5b385..3cec36817a 100644 --- a/src/resolve/resolve-tool.c +++ b/src/resolve/resolve-tool.c @@ -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; } 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-query.c b/src/resolve/resolved-dns-query.c index e03db4d003..c58845c3b6 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -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. */ @@ -1028,6 +1029,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 +1119,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-server.c b/src/resolve/resolved-dns-server.c index 22c64e8491..10562d03ec 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -451,18 +451,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 @@ -779,6 +783,15 @@ 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); +} + 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..406282d864 100644 --- a/src/resolve/resolved-dns-server.h +++ b/src/resolve/resolved-dns-server.h @@ -144,6 +144,8 @@ 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; diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c index 932e5b58a2..7d43825960 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,46 @@ static int dns_stub_make_reply_packet( continue; add: - r = dns_packet_append_rr(p, rr, 0, 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 the client didn't do EDNS, clamp the rcode to 4 bit */ + if (!add_opt && rcode > 0xF) + rcode = DNS_RCODE_SERVFAIL; + + 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 +117,6 @@ static int dns_stub_make_reply_packet( return r; } - *ret = p; - p = NULL; - return 0; } @@ -155,7 +169,11 @@ static int dns_stub_send_failure(Manager *m, DnsStream *s, DnsPacket *p, int rco 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), false); if (r < 0) return log_debug_errno(r, "Failed to build failure packet: %m"); @@ -170,26 +188,40 @@ 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_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); + break; + } + if (r < 0) { + log_debug_errno(r, "Failed to process CNAME: %m"); + break; + } + if (r == DNS_QUERY_RESTARTED) + return; - r = dns_stub_make_reply_packet( + 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_PACKET_DO(q->request_dns_packet) && 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); @@ -301,7 +333,7 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) { 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); @@ -328,7 +360,7 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) { goto fail; } - log_info("Processing query..."); + log_debug("Processing query..."); return; fail: diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 81131df91e..0c7a8867fb 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -926,7 +926,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; diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c index 364812250f..59cd6cf1cb 100644 --- a/src/resolve/resolved-link-bus.c +++ b/src/resolve/resolved-link-bus.c @@ -462,7 +462,7 @@ int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_e int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_set_free_free_ Set *ns = NULL; - _cleanup_free_ char **ntas = NULL; + _cleanup_strv_free_ char **ntas = NULL; Link *l = userdata; int r; char **i; diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index f81d6f0a55..791c125613 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -322,12 +322,13 @@ 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); @@ -374,32 +375,33 @@ static int determine_hostname(char **llmnr_hostname, char **mdns_hostname) { *llmnr_hostname = n; n = NULL; + *full_hostname = h; + h = NULL; + 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); - - free(m->llmnr_hostname); - free(m->mdns_hostname); + log_info("System hostname changed to '%s'.", full_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,9 +430,14 @@ 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->full_hostname = strdup("linux"); + if (!m->full_hostname) + return log_oom(); + m->llmnr_hostname = strdup("linux"); if (!m->llmnr_hostname) return log_oom(); @@ -439,7 +446,7 @@ static int manager_watch_hostname(Manager *m) { if (!m->mdns_hostname) return log_oom(); } else - log_info("Using system hostname '%s'.", m->llmnr_hostname); + log_info("Using system hostname '%s'.", m->full_hostname); return 0; } @@ -626,6 +633,8 @@ Manager *manager_free(Manager *m) { 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); @@ -1150,8 +1159,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; } @@ -1353,7 +1368,7 @@ void manager_cleanup_saved_user(Manager *m) { if (!IN_SET(de->d_type, DT_UNKNOWN, DT_REG)) continue; - if (STR_IN_SET(de->d_name, ".", "..")) + if (dot_or_dot_dot(de->d_name)) continue; r = parse_ifindex(de->d_name, &ifindex); diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 0b52ca594a..97c52b7729 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -109,6 +109,7 @@ 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; diff --git a/src/run/run.c b/src/run/run.c index 99f03465b0..f8257abc93 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -403,6 +403,11 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } + if (arg_pty && arg_no_block) { + log_error("--pty is not compatible with --no-block."); + return -EINVAL; + } + if (arg_scope && with_timer()) { log_error("Timer options are not supported in --scope mode."); return -EINVAL; @@ -784,21 +789,23 @@ static void run_context_free(RunContext *c) { } static void run_context_check_done(RunContext *c) { - bool done = true; + bool done; assert(c); if (c->match) - done = done && (c->active_state && STR_IN_SET(c->active_state, "inactive", "failed")); + done = STRPTR_IN_SET(c->active_state, "inactive", "failed"); + else + done = true; - if (c->forward) - done = done && pty_forward_is_done(c->forward); + if (c->forward && done) /* If the service is gone, it's time to drain the output */ + done = pty_forward_drain(c->forward); if (done) sd_event_exit(c->event, EXIT_SUCCESS); } -static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) { +static int run_context_update(RunContext *c, const char *path) { static const struct bus_properties_map map[] = { { "ActiveState", "s", NULL, offsetof(RunContext, active_state) }, @@ -811,23 +818,33 @@ static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error {} }; - RunContext *c = userdata; + _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", - sd_bus_message_get_path(m), + 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); return 0; } +static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) { + RunContext *c = userdata; + + assert(m); + assert(c); + + return run_context_update(c, sd_bus_message_get_path(m)); +} + static int pty_forward_handler(PTYForward *f, int rcode, void *userdata) { RunContext *c = userdata; @@ -985,6 +1002,8 @@ static int start_transient_service( if (arg_wait || master >= 0) { _cleanup_(run_context_free) RunContext c = {}; + _cleanup_free_ char *path = NULL; + const char *mt; c.bus = sd_bus_ref(bus); @@ -1007,27 +1026,27 @@ static int start_transient_service( pty_forward_set_handler(c.forward, pty_forward_handler, &c); } - if (arg_wait) { - _cleanup_free_ char *path = NULL; - const char *mt; - path = unit_dbus_path_from_name(service); - if (!path) - return log_oom(); + path = unit_dbus_path_from_name(service); + if (!path) + return log_oom(); - mt = strjoina("type='signal'," - "sender='org.freedesktop.systemd1'," - "path='", path, "'," - "interface='org.freedesktop.DBus.Properties'," - "member='PropertiesChanged'"); - r = sd_bus_add_match(bus, &c.match, mt, on_properties_changed, &c); - if (r < 0) - return log_error_errno(r, "Failed to add properties changed signal."); + mt = strjoina("type='signal'," + "sender='org.freedesktop.systemd1'," + "path='", path, "'," + "interface='org.freedesktop.DBus.Properties'," + "member='PropertiesChanged'"); + r = sd_bus_add_match(bus, &c.match, mt, on_properties_changed, &c); + if (r < 0) + return log_error_errno(r, "Failed to add properties changed signal."); - r = sd_bus_attach_event(bus, c.event, 0); - if (r < 0) - return log_error_errno(r, "Failed to attach bus to event loop."); - } + r = sd_bus_attach_event(bus, c.event, 0); + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop."); + + r = run_context_update(&c, path); + if (r < 0) + return r; r = sd_event_loop(c.event); if (r < 0) @@ -1041,7 +1060,13 @@ static int start_transient_service( fputc('\n', stdout); } - if (!arg_quiet) { + if (arg_wait && !arg_quiet) { + + /* Explicitly destroy the PTY forwarder, so that the PTY device is usable again, in its + * original settings (i.e. proper line breaks), so that we can show the summary in a pretty + * way. */ + c.forward = pty_forward_free(c.forward); + if (!isempty(c.result)) log_info("Finished with result: %s", strna(c.result)); @@ -1416,7 +1441,7 @@ int main(int argc, char* argv[]) { /* If --wait is used connect via the bus, unconditionally, as ref/unref is not supported via the limited direct * connection */ - if (arg_wait) + if (arg_wait || arg_pty) r = bus_connect_transport(arg_transport, arg_host, arg_user, &bus); else r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus); diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index 3e877920da..e3b29e390c 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -95,7 +95,7 @@ static int retrieve_key(key_serial_t serial, char ***ret) { if (n < m) break; - memory_erase(p, n); + explicit_bzero(p, n); free(p); m *= 2; } @@ -104,7 +104,7 @@ static int retrieve_key(key_serial_t serial, char ***ret) { if (!l) return -ENOMEM; - memory_erase(p, n); + explicit_bzero(p, n); *ret = l; return 0; @@ -140,7 +140,7 @@ static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **pa return r; serial = add_key("user", keyname, p, n, KEY_SPEC_USER_KEYRING); - memory_erase(p, n); + explicit_bzero(p, n); if (serial == -1) return -errno; @@ -390,7 +390,7 @@ int ask_password_tty( } x = strndup(passphrase, p); - memory_erase(passphrase, p); + explicit_bzero(passphrase, p); if (!x) { r = -ENOMEM; goto finish; @@ -647,7 +647,7 @@ int ask_password_agent( l = strv_new("", NULL); else l = strv_parse_nulstr(passphrase+1, n-1); - memory_erase(passphrase, n); + explicit_bzero(passphrase, n); if (!l) { r = -ENOMEM; goto finish; diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 829be2c6da..4ca614f647 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -208,7 +208,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges", "SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC", "ProtectKernelTunables", - "ProtectKernelModules", "ProtectControlGroups")) { + "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS")) { r = parse_boolean(eq); if (r < 0) @@ -266,7 +266,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen "StandardInput", "StandardOutput", "StandardError", "Description", "Slice", "Type", "WorkingDirectory", "RootDirectory", "SyslogIdentifier", "ProtectSystem", - "ProtectHome", "SELinuxContext", "Restart")) + "ProtectHome", "SELinuxContext", "Restart", "RootImage")) r = sd_bus_message_append(m, "v", "s", eq); else if (streq(field, "SyslogLevel")) { @@ -484,7 +484,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen for (p = eq;;) { _cleanup_free_ char *word = NULL; - int offset; + size_t offset; r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); if (r < 0) { @@ -500,6 +500,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen } offset = word[0] == '-'; + offset += word[offset] == '+'; + if (!path_is_absolute(word + offset)) { log_error("Failed to parse %s value %s", field, eq); return -EINVAL; @@ -552,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; @@ -573,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")) { @@ -583,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; 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/cgroup-show.c b/src/shared/cgroup-show.c index 5d21742a09..8765cf2f49 100644 --- a/src/shared/cgroup-show.c +++ b/src/shared/cgroup-show.c @@ -24,10 +24,15 @@ #include <stdlib.h> #include <string.h> +#include <systemd/sd-bus.h> + #include "alloc-util.h" +#include "bus-error.h" +#include "bus-util.h" #include "cgroup-show.h" #include "cgroup-util.h" #include "fd-util.h" +#include "fileio.h" #include "format-util.h" #include "locale-util.h" #include "macro.h" @@ -36,6 +41,7 @@ #include "process-util.h" #include "string-util.h" #include "terminal-util.h" +#include "unit-name.h" static void show_pid_array( pid_t pids[], @@ -73,7 +79,7 @@ static void show_pid_array( for (i = 0; i < n_pids; i++) { _cleanup_free_ char *t = NULL; - get_process_cmdline(pids[i], n_columns, true, &t); + (void) get_process_cmdline(pids[i], n_columns, true, &t); if (extra) printf("%s%s ", prefix, special_glyph(TRIANGULAR_BULLET)); @@ -310,3 +316,81 @@ int show_cgroup_and_extra_by_spec( return show_cgroup_and_extra(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags); } + +int show_cgroup_get_unit_path_and_warn( + sd_bus *bus, + const char *unit, + char **ret) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *path = NULL; + int r; + + path = unit_dbus_path_from_name(unit); + if (!path) + return log_oom(); + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + path, + unit_dbus_interface_from_name(unit), + "ControlGroup", + &error, + ret); + if (r < 0) + return log_error_errno(r, "Failed to query unit control group path: %s", + bus_error_message(&error, r)); + + return 0; +} + +int show_cgroup_get_path_and_warn( + const char *machine, + const char *prefix, + char **ret) { + + int r; + _cleanup_free_ char *root = NULL; + + if (machine) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_free_ char *unit = NULL; + const char *m; + + m = strjoina("/run/systemd/machines/", machine); + r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL); + if (r < 0) + return log_error_errno(r, "Failed to load machine data: %m"); + + r = bus_connect_transport_systemd(BUS_TRANSPORT_LOCAL, NULL, false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); + + r = show_cgroup_get_unit_path_and_warn(bus, unit, &root); + if (r < 0) + return r; + } else { + r = cg_get_root_path(&root); + if (r == -ENOMEDIUM) + return log_error_errno(r, "Failed to get root control group path.\n" + "No cgroup filesystem mounted on /sys/fs/cgroup"); + else if (r < 0) + return log_error_errno(r, "Failed to get root control group path: %m"); + } + + if (prefix) { + char *t; + + t = strjoin(root, prefix); + if (!t) + return log_oom(); + + *ret = t; + } else { + *ret = root; + root = NULL; + } + + return 0; +} diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h index 5c1d6e6d98..736f0f34c8 100644 --- a/src/shared/cgroup-show.h +++ b/src/shared/cgroup-show.h @@ -22,6 +22,8 @@ #include <stdbool.h> #include <sys/types.h> +#include <systemd/sd-bus.h> + #include "logs-show.h" #include "output-mode.h" @@ -30,3 +32,12 @@ int show_cgroup(const char *controller, const char *path, const char *prefix, un int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags); int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags); + +int show_cgroup_get_unit_path_and_warn( + sd_bus *bus, + const char *unit, + char **ret); +int show_cgroup_get_path_and_warn( + const char *machine, + const char *prefix, + char **ret); diff --git a/src/shared/clean-ipc.c b/src/shared/clean-ipc.c index f2d1555c13..f59f6f23ae 100644 --- a/src/shared/clean-ipc.c +++ b/src/shared/clean-ipc.c @@ -225,7 +225,7 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid, gid_t gid) { FOREACH_DIRENT_ALL(de, dir, goto fail) { struct stat st; - if (STR_IN_SET(de->d_name, "..", ".")) + if (dot_or_dot_dot(de->d_name)) continue; if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { @@ -310,7 +310,7 @@ static int clean_posix_mq(uid_t uid, gid_t gid) { struct stat st; char fn[1+strlen(de->d_name)+1]; - if (STR_IN_SET(de->d_name, "..", ".")) + if (dot_or_dot_dot(de->d_name)) continue; if (fstatat(dirfd(dir), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 878cb008aa..410a7764ed 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -28,14 +28,19 @@ #include "blkid-util.h" #include "dissect-image.h" #include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" #include "gpt.h" +#include "hexdecoct.h" #include "mount-util.h" #include "path-util.h" #include "stat-util.h" #include "stdio-util.h" #include "string-table.h" #include "string-util.h" +#include "strv.h" #include "udev-util.h" +#include "xattr-util.h" static int probe_filesystem(const char *node, char **ret_fstype) { #ifdef HAVE_BLKID @@ -347,9 +352,6 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI sd_id128_t type_id, id; bool rw = true; - if (pflags & GPT_FLAG_NO_AUTO) - continue; - sid = blkid_partition_get_uuid(pp); if (!sid) continue; @@ -363,18 +365,37 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI continue; if (sd_id128_equal(type_id, GPT_HOME)) { + + if (pflags & GPT_FLAG_NO_AUTO) + continue; + designator = PARTITION_HOME; rw = !(pflags & GPT_FLAG_READ_ONLY); } else if (sd_id128_equal(type_id, GPT_SRV)) { + + if (pflags & GPT_FLAG_NO_AUTO) + continue; + designator = PARTITION_SRV; rw = !(pflags & GPT_FLAG_READ_ONLY); } else if (sd_id128_equal(type_id, GPT_ESP)) { + + /* Note that we don't check the GPT_FLAG_NO_AUTO flag for the ESP, as it is not defined + * there. We instead check the GPT_FLAG_NO_BLOCK_IO_PROTOCOL, as recommended by the + * UEFI spec (See "12.3.3 Number and Location of System Partitions"). */ + + if (pflags & GPT_FLAG_NO_BLOCK_IO_PROTOCOL) + continue; + designator = PARTITION_ESP; fstype = "vfat"; } #ifdef GPT_ROOT_NATIVE else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) { + if (pflags & GPT_FLAG_NO_AUTO) + continue; + /* If a root ID is specified, ignore everything but the root id */ if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id)) continue; @@ -384,6 +405,9 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI rw = !(pflags & GPT_FLAG_READ_ONLY); } else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY)) { + if (pflags & GPT_FLAG_NO_AUTO) + continue; + m->can_verity = true; /* Ignore verity unless a root hash is specified */ @@ -399,6 +423,9 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI #ifdef GPT_ROOT_SECONDARY else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) { + if (pflags & GPT_FLAG_NO_AUTO) + continue; + /* If a root ID is specified, ignore everything but the root id */ if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id)) continue; @@ -407,6 +434,10 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI architecture = SECONDARY_ARCHITECTURE; rw = !(pflags & GPT_FLAG_READ_ONLY); } else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY)) { + + if (pflags & GPT_FLAG_NO_AUTO) + continue; + m->can_verity = true; /* Ignore verity unless root has is specified */ @@ -420,10 +451,17 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI } #endif else if (sd_id128_equal(type_id, GPT_SWAP)) { + + if (pflags & GPT_FLAG_NO_AUTO) + continue; + designator = PARTITION_SWAP; fstype = "swap"; } else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) { + if (pflags & GPT_FLAG_NO_AUTO) + continue; + if (generic_node) multiple_generic = true; else { @@ -624,7 +662,9 @@ static int mount_partition( DissectImageFlags flags) { const char *p, *options = NULL, *node, *fstype; + _cleanup_free_ char *chased = NULL; bool rw; + int r; assert(m); assert(where); @@ -641,9 +681,13 @@ static int mount_partition( rw = m->rw && !(flags & DISSECT_IMAGE_READ_ONLY); - if (directory) - p = strjoina(where, directory); - else + if (directory) { + r = chase_symlinks(directory, where, CHASE_PREFIX_ROOT, &chased); + if (r < 0) + return r; + + p = chased; + } else p = where; /* If requested, turn on discard support. */ @@ -677,22 +721,23 @@ int dissected_image_mount(DissectedImage *m, const char *where, DissectImageFlag return r; if (m->partitions[PARTITION_ESP].found) { - const char *mp, *x; + const char *mp; /* Mount the ESP to /efi if it exists and is empty. If it doesn't exist, use /boot instead. */ - mp = "/efi"; - x = strjoina(where, mp); - r = dir_is_empty(x); - if (r == -ENOENT) { - mp = "/boot"; - x = strjoina(where, mp); - r = dir_is_empty(x); - } - if (r > 0) { - r = mount_partition(m->partitions + PARTITION_ESP, where, mp, flags); + FOREACH_STRING(mp, "/efi", "/boot") { + _cleanup_free_ char *p = NULL; + + r = chase_symlinks(mp, where, CHASE_PREFIX_ROOT, &p); if (r < 0) - return r; + continue; + + r = dir_is_empty(p); + if (r > 0) { + r = mount_partition(m->partitions + PARTITION_ESP, where, mp, flags); + if (r < 0) + return r; + } } } @@ -1078,6 +1123,62 @@ int decrypted_image_relinquish(DecryptedImage *d) { return 0; } +int root_hash_load(const char *image, void **ret, size_t *ret_size) { + _cleanup_free_ char *text = NULL; + _cleanup_free_ void *k = NULL; + size_t l; + int r; + + assert(image); + assert(ret); + assert(ret_size); + + if (is_device_path(image)) { + /* If we are asked to load the root hash for a device node, exit early */ + *ret = NULL; + *ret_size = 0; + return 0; + } + + r = getxattr_malloc(image, "user.verity.roothash", &text, true); + if (r < 0) { + char *fn, *e, *n; + + if (!IN_SET(r, -ENODATA, -EOPNOTSUPP, -ENOENT)) + return r; + + fn = newa(char, strlen(image) + strlen(".roothash") + 1); + n = stpcpy(fn, image); + e = endswith(fn, ".raw"); + if (e) + n = e; + + strcpy(n, ".roothash"); + + r = read_one_line_file(fn, &text); + if (r == -ENOENT) { + *ret = NULL; + *ret_size = 0; + return 0; + } + if (r < 0) + return r; + } + + r = unhexmem(text, strlen(text), &k, &l); + if (r < 0) + return r; + if (l < sizeof(sd_id128_t)) + return -EINVAL; + + *ret = k; + *ret_size = l; + + k = NULL; + + return 1; +} + static const char *const partition_designator_table[] = { [PARTITION_ROOT] = "root", [PARTITION_ROOT_SECONDARY] = "root-secondary", diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 26319bd8e7..cdb083be6f 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -94,3 +94,5 @@ int decrypted_image_relinquish(DecryptedImage *d); const char* partition_designator_to_string(int i) _const_; int partition_designator_from_string(const char *name) _pure_; + +int root_hash_load(const char *image, void **ret, size_t *ret_size); diff --git a/src/shared/dropin.c b/src/shared/dropin.c index 3cbfe13f4c..15ccd1b6ca 100644 --- a/src/shared/dropin.c +++ b/src/shared/dropin.c @@ -29,6 +29,7 @@ #include "escape.h" #include "fd-util.h" #include "fileio-label.h" +#include "fs-util.h" #include "hashmap.h" #include "log.h" #include "macro.h" @@ -42,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); @@ -116,125 +116,99 @@ int write_drop_in_format(const char *dir, const char *unit, unsigned level, return write_drop_in(dir, unit, level, name, p); } -static int iterate_dir( +static int unit_file_find_dir( + const char *original_root, const char *path, - UnitDependency dependency, - dependency_consumer_t consumer, - void *arg, - char ***strv) { + char ***dirs) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; + _cleanup_free_ char *chased = NULL; int r; assert(path); - /* The config directories are special, since the order of the - * drop-ins matters */ - if (dependency < 0) { - r = strv_extend(strv, path); - if (r < 0) - return log_oom(); - + 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; - } - - assert(consumer); - - d = opendir(path); - if (!d) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open directory %s: %m", path); - } - - FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read directory %s: %m", path)) { - _cleanup_free_ char *f = NULL; - - f = strjoin(path, "/", de->d_name); - if (!f) - return log_oom(); + if (r < 0) + return log_full_errno(LOG_WARNING, r, "Failed to canonicalize path %s: %m", path); - r = consumer(dependency, de->d_name, f, arg); - if (r < 0) - return r; - } + r = strv_push(dirs, chased); + if (r < 0) + return log_oom(); + chased = NULL; return 0; } -int unit_file_process_dir( +static int unit_file_find_dirs( + const char *original_root, Set *unit_path_cache, const char *unit_path, const char *name, const char *suffix, - UnitDependency dependency, - dependency_consumer_t consumer, - void *arg, - char ***strv) { + 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)) - (void) iterate_dir(path, dependency, consumer, arg, strv); + if (!unit_path_cache || set_get(unit_path_cache, path)) { + r = unit_file_find_dir(original_root, path, dirs); + if (r < 0) + return r; + } 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)) - (void) iterate_dir(p, dependency, consumer, arg, strv); + return unit_file_find_dirs(original_root, unit_path_cache, unit_path, template, suffix, dirs); } return 0; } int unit_file_find_dropin_paths( + const char *original_root, char **lookup_path, Set *unit_path_cache, + const char *dir_suffix, + const char *file_suffix, Set *names, - char ***paths) { + char ***ret) { - _cleanup_strv_free_ char **strv = NULL, **ans = NULL; + _cleanup_strv_free_ char **dirs = NULL, **ans = NULL; Iterator i; - char *t; + char *t, **p; int r; - assert(paths); - - SET_FOREACH(t, names, i) { - char **p; + assert(ret); + SET_FOREACH(t, names, i) STRV_FOREACH(p, lookup_path) - unit_file_process_dir(unit_path_cache, *p, t, ".d", _UNIT_DEPENDENCY_INVALID, NULL, NULL, &strv); - } + unit_file_find_dirs(original_root, unit_path_cache, *p, t, dir_suffix, &dirs); - if (strv_isempty(strv)) + if (strv_isempty(dirs)) { + *ret = NULL; return 0; + } - r = conf_files_list_strv(&ans, ".conf", NULL, (const char**) strv); + r = conf_files_list_strv(&ans, file_suffix, NULL, (const char**) dirs); if (r < 0) - return log_warning_errno(r, "Failed to get list of configuration files: %m"); + 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/dropin.h b/src/shared/dropin.h index c1936f397b..a2b8cdce61 100644 --- a/src/shared/dropin.h +++ b/src/shared/dropin.h @@ -33,29 +33,24 @@ int write_drop_in(const char *dir, const char *unit, unsigned level, int write_drop_in_format(const char *dir, const char *unit, unsigned level, const char *name, const char *format, ...) _printf_(5, 6); -/** - * This callback will be called for each directory entry @entry, - * with @filepath being the full path to the entry. - * - * If return value is negative, loop will be aborted. - */ -typedef int (*dependency_consumer_t)(UnitDependency dependency, - const char *entry, - const char* filepath, - void *arg); - -int unit_file_process_dir( - Set * unit_path_cache, - const char *unit_path, - const char *name, - const char *suffix, - UnitDependency dependency, - dependency_consumer_t consumer, - void *arg, - char ***strv); - int unit_file_find_dropin_paths( + const char *original_root, char **lookup_path, Set *unit_path_cache, + const char *dir_suffix, + const char *file_suffix, Set *names, char ***paths); + +static inline int unit_file_find_dropin_conf_paths( + const char *original_root, + char **lookup_path, + Set *unit_path_cache, + Set *names, + char ***paths) { + return unit_file_find_dropin_paths(original_root, + lookup_path, + unit_path_cache, + ".d", ".conf", + names, paths); +} diff --git a/src/shared/gpt.h b/src/shared/gpt.h index 13d80d611c..cc752006fa 100644 --- a/src/shared/gpt.h +++ b/src/shared/gpt.h @@ -71,6 +71,8 @@ # define GPT_ROOT_NATIVE_VERITY GPT_ROOT_ARM_VERITY #endif +#define GPT_FLAG_NO_BLOCK_IO_PROTOCOL (1ULL << 1) + /* Flags we recognize on the root, swap, home and srv partitions when * doing auto-discovery. These happen to be identical to what * Microsoft defines for its own Basic Data Partitions, but that's diff --git a/src/shared/install.c b/src/shared/install.c index 478abac8ab..58c8e852b2 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -208,7 +208,7 @@ static int path_is_control(const LookupPaths *p, const char *path) { path_equal_ptr(parent, p->runtime_control); } -static int path_is_config(const LookupPaths *p, const char *path) { +static int path_is_config(const LookupPaths *p, const char *path, bool check_parent) { _cleanup_free_ char *parent = NULL; assert(p); @@ -217,15 +217,19 @@ static int path_is_config(const LookupPaths *p, const char *path) { /* Note that we do *not* have generic checks for /etc or /run in place, since with * them we couldn't discern configuration from transient or generated units */ - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; + if (check_parent) { + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; + + path = parent; + } - return path_equal_ptr(parent, p->persistent_config) || - path_equal_ptr(parent, p->runtime_config); + return path_equal_ptr(path, p->persistent_config) || + path_equal_ptr(path, p->runtime_config); } -static int path_is_runtime(const LookupPaths *p, const char *path) { +static int path_is_runtime(const LookupPaths *p, const char *path, bool check_parent) { _cleanup_free_ char *parent = NULL; const char *rpath; @@ -239,16 +243,20 @@ static int path_is_runtime(const LookupPaths *p, const char *path) { if (rpath && path_startswith(rpath, "/run")) return true; - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; + if (check_parent) { + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; - return path_equal_ptr(parent, p->runtime_config) || - path_equal_ptr(parent, p->generator) || - path_equal_ptr(parent, p->generator_early) || - path_equal_ptr(parent, p->generator_late) || - path_equal_ptr(parent, p->transient) || - path_equal_ptr(parent, p->runtime_control); + path = parent; + } + + return path_equal_ptr(path, p->runtime_config) || + path_equal_ptr(path, p->generator) || + path_equal_ptr(path, p->generator_early) || + path_equal_ptr(path, p->generator_late) || + path_equal_ptr(path, p->transient) || + path_equal_ptr(path, p->runtime_control); } static int path_is_vendor(const LookupPaths *p, const char *path) { @@ -381,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.", @@ -677,7 +691,6 @@ static int find_symlinks_fd( int fd, const char *path, const char *config_path, - const LookupPaths *lp, bool *same_name_link) { _cleanup_closedir_ DIR *d = NULL; @@ -688,7 +701,6 @@ static int find_symlinks_fd( assert(fd >= 0); assert(path); assert(config_path); - assert(lp); assert(same_name_link); d = fdopendir(fd); @@ -722,7 +734,7 @@ static int find_symlinks_fd( } /* This will close nfd, regardless whether it succeeds or not */ - q = find_symlinks_fd(root_dir, name, nfd, p, config_path, lp, same_name_link); + q = find_symlinks_fd(root_dir, name, nfd, p, config_path, same_name_link); if (q > 0) return 1; if (r == 0) @@ -800,7 +812,6 @@ static int find_symlinks( const char *root_dir, const char *name, const char *config_path, - const LookupPaths *lp, bool *same_name_link) { int fd; @@ -817,44 +828,82 @@ static int find_symlinks( } /* This takes possession of fd and closes it */ - return find_symlinks_fd(root_dir, name, fd, config_path, config_path, lp, same_name_link); + return find_symlinks_fd(root_dir, name, fd, config_path, config_path, same_name_link); } static int find_symlinks_in_scope( - UnitFileScope scope, const LookupPaths *paths, const char *name, UnitFileState *state) { - bool same_name_link_runtime = false, same_name_link = false; + bool same_name_link_runtime = false, same_name_link_config = false; + bool enabled_in_runtime = false, enabled_at_all = false; + char **p; int r; - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); assert(paths); assert(name); - /* First look in the persistent config path */ - r = find_symlinks(paths->root_dir, name, paths->persistent_config, paths, &same_name_link); - if (r < 0) - return r; - if (r > 0) { - *state = UNIT_FILE_ENABLED; - return r; + STRV_FOREACH(p, paths->search_path) { + bool same_name_link = false; + + r = find_symlinks(paths->root_dir, name, *p, &same_name_link); + if (r < 0) + return r; + if (r > 0) { + /* We found symlinks in this dir? Yay! Let's see where precisely it is enabled. */ + + r = path_is_config(paths, *p, false); + if (r < 0) + return r; + if (r > 0) { + /* This is the best outcome, let's return it immediately. */ + *state = UNIT_FILE_ENABLED; + return 1; + } + + r = path_is_runtime(paths, *p, false); + if (r < 0) + return r; + if (r > 0) + enabled_in_runtime = true; + else + enabled_at_all = true; + + } else if (same_name_link) { + + r = path_is_config(paths, *p, false); + if (r < 0) + return r; + if (r > 0) + same_name_link_config = true; + else { + r = path_is_runtime(paths, *p, false); + if (r < 0) + return r; + if (r > 0) + same_name_link_runtime = true; + } + } } - /* Then look in runtime config path */ - r = find_symlinks(paths->root_dir, name, paths->runtime_config, paths, &same_name_link_runtime); - if (r < 0) - return r; - if (r > 0) { + if (enabled_in_runtime) { *state = UNIT_FILE_ENABLED_RUNTIME; - return r; + return 1; + } + + /* Here's a special rule: if the unit we are looking for is an instance, and it symlinked in the search path + * outside of runtime and configuration directory, then we consider it statically enabled. Note we do that only + * for instance, not for regular names, as those are merely aliases, while instances explicitly instantiate + * something, and hence are a much stronger concept. */ + if (enabled_at_all && unit_name_is_valid(name, UNIT_NAME_INSTANCE)) { + *state = UNIT_FILE_STATIC; + return 1; } /* Hmm, we didn't find it, but maybe we found the same name * link? */ - if (same_name_link) { + if (same_name_link_config) { *state = UNIT_FILE_LINKED; return 1; } @@ -1354,7 +1403,8 @@ static int install_info_follow( InstallContext *c, UnitFileInstallInfo *i, const char *root_dir, - SearchFlags flags) { + SearchFlags flags, + bool ignore_different_name) { assert(c); assert(i); @@ -1367,7 +1417,7 @@ static int install_info_follow( /* If the basename doesn't match, the caller should add a * complete new entry for this. */ - if (!streq(basename(i->symlink_target), i->name)) + if (!ignore_different_name && !streq(basename(i->symlink_target), i->name)) return -EXDEV; free_and_replace(i->path, i->symlink_target); @@ -1408,14 +1458,14 @@ static int install_info_traverse( return -ELOOP; if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS)) { - r = path_is_config(paths, i->path); + r = path_is_config(paths, i->path, true); if (r < 0) return r; if (r > 0) return -ELOOP; } - r = install_info_follow(c, i, paths->root_dir, flags); + r = install_info_follow(c, i, paths->root_dir, flags, false); if (r == -EXDEV) { _cleanup_free_ char *buffer = NULL; const char *bn; @@ -1439,6 +1489,18 @@ static int install_info_traverse( if (r < 0) return r; + if (streq(buffer, i->name)) { + + /* We filled in the instance, and the target stayed the same? If so, then let's + * honour the link as it is. */ + + r = install_info_follow(c, i, paths->root_dir, flags, true); + if (r < 0) + return r; + + continue; + } + bn = buffer; } @@ -1751,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; @@ -1777,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->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); + } - 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"); + } 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; } @@ -1822,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; @@ -1870,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) { @@ -1959,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; @@ -2027,7 +2105,7 @@ static int path_shall_revert(const LookupPaths *paths, const char *path) { /* Checks whether the path is one where the drop-in directories shall be removed. */ - r = path_is_config(paths, path); + r = path_is_config(paths, path, true); if (r != 0) return r; @@ -2135,7 +2213,7 @@ int unit_file_revert( if (errno != ENOENT) return -errno; } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { - r = path_is_config(&paths, path); + r = path_is_config(&paths, path, true); if (r < 0) return r; if (r > 0) { @@ -2226,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); @@ -2291,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, @@ -2335,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)) @@ -2345,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; @@ -2481,7 +2565,7 @@ static int unit_file_lookup_state( switch (i->type) { case UNIT_FILE_TYPE_MASKED: - r = path_is_runtime(paths, i->path); + r = path_is_runtime(paths, i->path, true); if (r < 0) return r; @@ -2505,7 +2589,7 @@ static int unit_file_lookup_state( break; } - r = find_symlinks_in_scope(scope, paths, i->name, &state); + r = find_symlinks_in_scope(paths, i->name, &state); if (r < 0) return r; if (r == 0) { @@ -2734,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; @@ -2829,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) @@ -2867,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/logs-show.c b/src/shared/logs-show.c index 75ea25c8ac..72c43e80cb 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -246,6 +246,11 @@ static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, Ou if (r < 0) return log_error_errno(r, "Failed to get realtime timestamp: %m"); + if (x > USEC_TIMESTAMP_FORMATTABLE_MAX) { + log_error("Timestamp cannot be printed"); + return -EINVAL; + } + if (mode == OUTPUT_SHORT_FULL) { const char *k; diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c index 586ef64e72..09a44534e2 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 */ diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c index 293c6673fc..59b541d519 100644 --- a/src/shared/ptyfwd.c +++ b/src/shared/ptyfwd.c @@ -69,6 +69,7 @@ struct PTYForward { bool read_from_master:1; bool done:1; + bool drain:1; bool last_char_set:1; char last_char; @@ -302,6 +303,11 @@ static int shovel(PTYForward *f) { return pty_forward_done(f, 0); } + /* If we were asked to drain, and there's nothing more to handle from the master, then call the callback + * too. */ + if (f->drain && f->out_buffer_full == 0 && !f->master_readable) + return pty_forward_done(f, 0); + return 0; } @@ -438,6 +444,9 @@ int pty_forward_new( r = sd_event_add_io(f->event, &f->stdin_event_source, STDIN_FILENO, EPOLLIN|EPOLLET, on_stdin_event, f); if (r < 0 && r != -EPERM) return r; + + if (r >= 0) + (void) sd_event_source_set_description(f->stdin_event_source, "ptyfwd-stdin"); } r = sd_event_add_io(f->event, &f->stdout_event_source, STDOUT_FILENO, EPOLLOUT|EPOLLET, on_stdout_event, f); @@ -446,15 +455,21 @@ int pty_forward_new( f->stdout_writable = true; else if (r < 0) return r; + else + (void) sd_event_source_set_description(f->stdout_event_source, "ptyfwd-stdout"); r = sd_event_add_io(f->event, &f->master_event_source, master, EPOLLIN|EPOLLOUT|EPOLLET, on_master_event, f); if (r < 0) return r; + (void) sd_event_source_set_description(f->master_event_source, "ptyfwd-master"); + r = sd_event_add_signal(f->event, &f->sigwinch_event_source, SIGWINCH, on_sigwinch_event, f); if (r < 0) return r; + (void) sd_event_source_set_description(f->sigwinch_event_source, "ptyfwd-sigwinch"); + *ret = f; f = NULL; @@ -519,3 +534,18 @@ void pty_forward_set_handler(PTYForward *f, PTYForwardHandler cb, void *userdata f->handler = cb; f->userdata = userdata; } + +bool pty_forward_drain(PTYForward *f) { + assert(f); + + /* Starts draining the forwarder. Specifically: + * + * - Returns true if there are no unprocessed bytes from the pty, false otherwise + * + * - Makes sure the handler function is called the next time the number of unprocessed bytes hits zero + */ + + f->drain = true; + + return f->out_buffer_full == 0 && !f->master_readable; +} diff --git a/src/shared/ptyfwd.h b/src/shared/ptyfwd.h index bd5d5fec0d..3fad1d3b26 100644 --- a/src/shared/ptyfwd.h +++ b/src/shared/ptyfwd.h @@ -51,4 +51,6 @@ bool pty_forward_is_done(PTYForward *f); void pty_forward_set_handler(PTYForward *f, PTYForwardHandler handler, void *userdata); +bool pty_forward_drain(PTYForward *f); + DEFINE_TRIVIAL_CLEANUP_FUNC(PTYForward*, pty_forward_free); diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index 2c73cb8fa4..84964f750f 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 }; @@ -171,11 +212,11 @@ int seccomp_init_for_arch(scmp_filter_ctx *ret, uint32_t arch, uint32_t default_ if (arch != SCMP_ARCH_NATIVE && arch != seccomp_arch_native()) { - r = seccomp_arch_add(seccomp, arch); + r = seccomp_arch_remove(seccomp, seccomp_arch_native()); if (r < 0) goto finish; - r = seccomp_arch_remove(seccomp, seccomp_arch_native()); + r = seccomp_arch_add(seccomp, arch); if (r < 0) goto finish; @@ -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" @@ -750,10 +792,35 @@ int seccomp_restrict_namespaces(unsigned long retain) { SECCOMP_FOREACH_LOCAL_ARCH(arch) { _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; + int clone_reversed_order = -1; unsigned i; log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch)); + switch (arch) { + + case SCMP_ARCH_X86_64: + case SCMP_ARCH_X86: + case SCMP_ARCH_X32: + clone_reversed_order = 0; + break; + + case SCMP_ARCH_S390: + case SCMP_ARCH_S390X: + /* On s390/s390x the first two parameters to clone are switched */ + clone_reversed_order = 1; + break; + + /* 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!" +#endif + } + + if (clone_reversed_order < 0) /* we don't know the right order, let's ignore this arch... */ + continue; + r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW); if (r < 0) return r; @@ -802,12 +869,20 @@ int seccomp_restrict_namespaces(unsigned long retain) { break; } - r = seccomp_rule_add_exact( - seccomp, - SCMP_ACT_ERRNO(EPERM), - SCMP_SYS(clone), - 1, - SCMP_A0(SCMP_CMP_MASKED_EQ, f, f)); + if (clone_reversed_order == 0) + r = seccomp_rule_add_exact( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(clone), + 1, + SCMP_A0(SCMP_CMP_MASKED_EQ, f, f)); + else + r = seccomp_rule_add_exact( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(clone), + 1, + SCMP_A1(SCMP_CMP_MASKED_EQ, f, f)); if (r < 0) { log_debug_errno(r, "Failed to add clone() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); break; @@ -878,10 +953,37 @@ int seccomp_restrict_address_families(Set *address_families, bool whitelist) { 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; @@ -1083,27 +1185,81 @@ int seccomp_restrict_realtime(void) { } int seccomp_memory_deny_write_execute(void) { + uint32_t arch; int r; SECCOMP_FOREACH_LOCAL_ARCH(arch) { _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; + int filter_syscall = 0, block_syscall = 0, shmat_syscall = 0; log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch)); + switch (arch) { + + case SCMP_ARCH_X86: + filter_syscall = SCMP_SYS(mmap2); + block_syscall = SCMP_SYS(mmap); + + /* Note that shmat() isn't available on i386, where the call is multiplexed through ipc(). We + * ignore that here, which means there's still a way to get writable/executable memory, if an + * IPC key is mapped like this on i386. That's a pity, but no total loss. */ + break; + + case SCMP_ARCH_X86_64: + case SCMP_ARCH_X32: + filter_syscall = SCMP_SYS(mmap); + shmat_syscall = SCMP_SYS(shmat); + break; + + /* Please add more definitions here, if you port systemd to other architectures! */ + +#if !defined(__i386__) && !defined(__x86_64__) +#warning "Consider adding the right mmap() syscall definitions here!" +#endif + } + + /* Can't filter mmap() on this arch, then skip it */ + if (filter_syscall == 0) + continue; + r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW); if (r < 0) return r; - r = seccomp_rule_add_exact( - seccomp, - SCMP_ACT_ERRNO(EPERM), - SCMP_SYS(mmap), - 1, - SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC|PROT_WRITE, PROT_EXEC|PROT_WRITE)); - if (r < 0) { - log_debug_errno(r, "Failed to add mmap() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); - continue; + if (filter_syscall != 0) { + r = seccomp_rule_add_exact( + seccomp, + SCMP_ACT_ERRNO(EPERM), + filter_syscall, + 1, + SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC|PROT_WRITE, PROT_EXEC|PROT_WRITE)); + if (r < 0) { + _cleanup_free_ char *n = NULL; + + n = seccomp_syscall_resolve_num_arch(arch, filter_syscall); + log_debug_errno(r, "Failed to add %s() rule for architecture %s, skipping: %m", + strna(n), + seccomp_arch_to_string(arch)); + continue; + } + } + + if (block_syscall != 0) { + r = seccomp_rule_add_exact( + seccomp, + SCMP_ACT_ERRNO(EPERM), + block_syscall, + 0); + if (r < 0) { + _cleanup_free_ char *n = NULL; + + n = seccomp_syscall_resolve_num_arch(arch, block_syscall); + log_debug_errno(r, "Failed to add %s() rule for architecture %s, skipping: %m", + strna(n), + seccomp_arch_to_string(arch)); + continue; + } } r = seccomp_rule_add_exact( @@ -1117,15 +1273,17 @@ int seccomp_memory_deny_write_execute(void) { continue; } - r = seccomp_rule_add_exact( - seccomp, - SCMP_ACT_ERRNO(EPERM), - SCMP_SYS(shmat), - 1, - SCMP_A2(SCMP_CMP_MASKED_EQ, SHM_EXEC, SHM_EXEC)); - if (r < 0) { - log_debug_errno(r, "Failed to add shmat() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); - continue; + if (shmat_syscall != 0) { + r = seccomp_rule_add_exact( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(shmat), + 1, + SCMP_A2(SCMP_CMP_MASKED_EQ, SHM_EXEC, SHM_EXEC)); + if (r < 0) { + log_debug_errno(r, "Failed to add shmat() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); + continue; + } } r = seccomp_load(seccomp); diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h index 4438e87fa6..61f94de638 100644 --- a/src/shared/seccomp-util.h +++ b/src/shared/seccomp-util.h @@ -76,6 +76,28 @@ int seccomp_restrict_address_families(Set *address_families, bool whitelist); int seccomp_restrict_realtime(void); int seccomp_memory_deny_write_execute(void); +#if defined(__i386__) || defined(__s390x__) || defined(__s390__) || defined(__powerpc64__) || defined(__powerpc__) || defined (__mips__) +/* On these archs, socket() is implemented via the socketcall() syscall multiplexer, and we can't restrict it hence via + * seccomp */ +#define SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN 1 +#else +#define SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN 0 +#endif + +/* mmap() blocking is only available on some archs for now */ +#if defined(__x86_64__) || defined(__i386__) +#define SECCOMP_MEMORY_DENY_WRITE_EXECUTE_BROKEN 0 +#else +#define SECCOMP_MEMORY_DENY_WRITE_EXECUTE_BROKEN 1 +#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__) +#define SECCOMP_RESTRICT_NAMESPACES_BROKEN 0 +#else +#define SECCOMP_RESTRICT_NAMESPACES_BROKEN 1 +#endif + extern const uint32_t seccomp_local_archs[]; #define SECCOMP_FOREACH_LOCAL_ARCH(arch) \ diff --git a/src/system-update-generator/system-update-generator.c b/src/system-update-generator/system-update-generator.c index 6ffb169b21..f1514c9638 100644 --- a/src/system-update-generator/system-update-generator.c +++ b/src/system-update-generator/system-update-generator.c @@ -22,6 +22,7 @@ #include "fs-util.h" #include "log.h" +#include "proc-cmdline.h" #include "special.h" #include "string-util.h" #include "util.h" @@ -47,11 +48,27 @@ static int generate_symlink(void) { if (symlink(SYSTEM_DATA_UNIT_PATH "/system-update.target", p) < 0) return log_error_errno(errno, "Failed to create symlink %s: %m", p); + return 1; +} + +static int parse_proc_cmdline_item(const char *key, const char *value, void *data) { + assert(key); + + /* Check if a run level is specified on the kernel command line. The + * command line has higher priority than any on-disk configuration, so + * it'll make any symlink we create moot. + */ + + if (streq(key, "systemd.unit") && !proc_cmdline_value_missing(key, value)) + log_warning("Offline system update overriden by kernel command line systemd.unit= setting"); + else if (!value && runlevel_to_target(key)) + log_warning("Offline system update overriden by runlevel \"%s\" on the kernel command line", key); + return 0; } int main(int argc, char *argv[]) { - int r; + int r, k; if (argc > 1 && argc != 4) { log_error("This program takes three or no arguments."); @@ -69,5 +86,11 @@ int main(int argc, char *argv[]) { r = generate_symlink(); + if (r > 0) { + k = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0); + if (k < 0) + log_warning_errno(k, "Failed to parse kernel command line, ignoring: %m"); + } + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 41e8d6075a..2336ae34f4 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++; } @@ -2484,7 +2484,7 @@ static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **un _cleanup_free_ char *path = NULL, *lpath = NULL; int r; - path = path_join(arg_root, *p, unit_name); + path = path_join(NULL, *p, unit_name); if (!path) return log_oom(); @@ -2601,7 +2601,8 @@ static int unit_find_paths( return log_error_errno(r, "Failed to add unit name: %m"); if (dropin_paths) { - r = unit_file_find_dropin_paths(lp->search_path, NULL, names, &dropins); + r = unit_file_find_dropin_conf_paths(arg_root, lp->search_path, + NULL, names, &dropins); if (r < 0) return r; } @@ -4952,7 +4953,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)); @@ -5029,13 +5030,10 @@ static int show_one( r = 0; if (show_properties) { char **pp; - int not_found_level = streq(verb, "show") ? LOG_DEBUG : LOG_WARNING; STRV_FOREACH(pp, arg_properties) - if (!set_contains(found_properties, *pp)) { - log_full(not_found_level, "Property %s does not exist.", *pp); - r = -ENXIO; - } + if (!set_contains(found_properties, *pp)) + log_debug("Property %s does not exist.", *pp); } else if (streq(verb, "help")) show_unit_help(&info); @@ -5127,8 +5125,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; @@ -5136,9 +5135,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(); @@ -5655,16 +5654,22 @@ static int switch_root(int argc, char *argv[], void *userdata) { } /* Instruct PID1 to exclude us from its killing spree applied during - * the transition from the initrd to the main system otherwise we would - * exit with a failure status even though the switch to the new root - * has succeed. */ - if (in_initrd()) - argv_cmdline[0] = '@'; + * the transition. Otherwise we would exit with a failure status even + * though the switch to the new root has succeed. */ + argv_cmdline[0] = '@'; r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) return r; + /* If we are slow to exit after the root switch, the new systemd instance + * will send us a signal to terminate. Just ignore it and exit normally. + * This way the unit does not end up as failed. + */ + r = ignore_signals(SIGTERM, -1); + if (r < 0) + log_warning_errno(r, "Failed to change disposition of SIGTERM to ignore: %m"); + log_debug("Switching root - root: %s; init: %s", root, strna(init)); r = sd_bus_call_method( @@ -5676,8 +5681,11 @@ static int switch_root(int argc, char *argv[], void *userdata) { &error, NULL, "ss", root, init); - if (r < 0) + if (r < 0) { + (void) default_signals(SIGTERM, -1); + return log_error_errno(r, "Failed to switch root: %s", bus_error_message(&error, r)); + } return 0; } @@ -6021,7 +6029,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)); @@ -6211,21 +6219,25 @@ static int enable_unit(int argc, char *argv[], void *userdata) { "4) In case of template units, the unit is meant to be enabled with some\n" " instance name specified."); - if (arg_now && n_changes > 0 && STR_IN_SET(argv[0], "enable", "disable", "mask")) { - char *new_args[n_changes + 2]; + if (arg_now && STR_IN_SET(argv[0], "enable", "disable", "mask")) { sd_bus *bus; - unsigned i; + unsigned len, i; r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) goto finish; - new_args[0] = (char*) (streq(argv[0], "enable") ? "start" : "stop"); - for (i = 0; i < n_changes; i++) - new_args[i + 1] = basename(changes[i].path); - new_args[i + 1] = NULL; + len = strv_length(names); + { + char *new_args[len + 2]; + + new_args[0] = (char*) (streq(argv[0], "enable") ? "start" : "stop"); + for (i = 0; i < len; i++) + new_args[i + 1] = basename(names[i]); + new_args[i + 1] = NULL; - r = start_unit(strv_length(new_args), new_args, userdata); + r = start_unit(len + 1, new_args, userdata); + } } finish: @@ -6928,6 +6940,7 @@ static void systemctl_help(void) { " -a --all Show all properties/all units currently in memory,\n" " including dead/empty ones. To list all units installed on\n" " the system, use the 'list-unit-files' command instead.\n" + " --failed Same as --state=failed\n" " -l --full Don't ellipsize unit names on output\n" " -r --recursive Show unit list of host and local containers\n" " --reverse Show reverse dependencies with 'list-dependencies'\n" diff --git a/src/test/test-af-list.c b/src/test/test-af-list.c index e2479133de..e5ca54c8e7 100644 --- a/src/test/test-af-list.c +++ b/src/test/test-af-list.c @@ -24,6 +24,7 @@ #include "string-util.h" #include "util.h" +_unused_ \ static const struct af_name* lookup_af(register const char *str, register GPERF_LEN_TYPE len); #include "af-from-name.h" diff --git a/src/test/test-arphrd-list.c b/src/test/test-arphrd-list.c index 8f4f342faa..bb51518c9c 100644 --- a/src/test/test-arphrd-list.c +++ b/src/test/test-arphrd-list.c @@ -24,6 +24,7 @@ #include "string-util.h" #include "util.h" +_unused_ \ static const struct arphrd_name* lookup_arphrd(register const char *str, register GPERF_LEN_TYPE len); #include "arphrd-from-name.h" 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-date.c b/src/test/test-date.c index a8d3f1e083..b77598c81d 100644 --- a/src/test/test-date.c +++ b/src/test/test-date.c @@ -95,6 +95,16 @@ int main(int argc, char *argv[]) { test_one_noutc("@1395716396"); test_should_parse("today UTC"); test_should_fail("today UTC UTC"); + test_should_parse("1970-1-1 UTC"); + test_should_fail("1969-1-1 UTC"); +#if SIZEOF_TIME_T == 8 + test_should_parse("9999-12-30 23:59:59 UTC"); + test_should_fail("9999-12-31 00:00:00 UTC"); + test_should_fail("10000-01-01 00:00:00 UTC"); +#elif SIZEOF_TIME_T == 4 + test_should_parse("2038-01-19 03:14:07 UTC"); + test_should_fail( "2038-01-19 03:14:08 UTC"); +#endif return 0; } diff --git a/src/test/test-execute.c b/src/test/test-execute.c index bc9a2021f9..1e479b9843 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -422,6 +422,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 +479,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[] = { diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index ae68587be9..e774f567e0 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -186,6 +186,13 @@ static void test_chase_symlinks(void) { r = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &result); assert_se(r == -ENOENT); + p = strjoina(temp, "/target"); + q = strjoina(temp, "/top"); + assert_se(symlink(q, p) >= 0); + p = strjoina(temp, "/target/idontexist"); + r = chase_symlinks(p, NULL, 0, &result); + assert_se(r == -ENOENT); + assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); } @@ -298,12 +305,23 @@ static void test_var_tmp(void) { } } +static void test_dot_or_dot_dot(void) { + assert_se(!dot_or_dot_dot(NULL)); + assert_se(!dot_or_dot_dot("")); + assert_se(!dot_or_dot_dot("xxx")); + assert_se(dot_or_dot_dot(".")); + assert_se(dot_or_dot_dot("..")); + assert_se(!dot_or_dot_dot(".foo")); + assert_se(!dot_or_dot_dot("..foo")); +} + int main(int argc, char *argv[]) { test_unlink_noerrno(); test_readlink_and_make_absolute(); test_get_files_in_directory(); test_var_tmp(); test_chase_symlinks(); + test_dot_or_dot_dot(); return 0; } diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c index d0bc8004f3..575401cb91 100644 --- a/src/test/test-install-root.c +++ b/src/test/test-install-root.c @@ -736,6 +736,28 @@ static void test_preset_order(const char *root) { assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); } +static void test_static_instance(const char *root) { + UnitFileState state; + const char *p; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@.service", &state) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@foo.service", &state) == -ENOENT); + + p = strjoina(root, "/usr/lib/systemd/system/static-instance@.service"); + assert_se(write_string_file(p, + "[Install]\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + p = strjoina(root, "/usr/lib/systemd/system/static-instance@foo.service"); + assert_se(symlink("static-instance@.service", p) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@foo.service", &state) >= 0 && state == UNIT_FILE_STATIC); +} + int main(int argc, char *argv[]) { char root[] = "/tmp/rootXXXXXX"; const char *p; @@ -766,6 +788,7 @@ int main(int argc, char *argv[]) { test_preset_and_list(root); test_preset_order(root); test_revert(root); + test_static_instance(root); assert_se(rm_rf(root, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); diff --git a/src/test/test-ns.c b/src/test/test-ns.c index c99bcb371b..0125d905a6 100644 --- a/src/test/test-ns.c +++ b/src/test/test-ns.c @@ -77,6 +77,7 @@ int main(int argc, char *argv[]) { log_info("Not chrooted"); r = setup_namespace(root_directory, + NULL, &ns_info, (char **) writable, (char **) readonly, @@ -86,6 +87,7 @@ int main(int argc, char *argv[]) { var_tmp_dir, PROTECT_HOME_NO, PROTECT_SYSTEM_NO, + 0, 0); if (r < 0) { log_error_errno(r, "Failed to setup namespace: %m"); diff --git a/src/test/test-nss.c b/src/test/test-nss.c index 4ccfe75147..b59cb7aa69 100644 --- a/src/test/test-nss.c +++ b/src/test/test-nss.c @@ -511,7 +511,7 @@ int main(int argc, char **argv) { _cleanup_free_ char *dir = NULL; _cleanup_strv_free_ char **modules = NULL, **names = NULL; _cleanup_free_ struct local_address *addresses = NULL; - int n_addresses; + int n_addresses = 0; char **module; int r; diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c index 6f15879c45..34a1275162 100644 --- a/src/test/test-seccomp.c +++ b/src/test/test-seccomp.c @@ -158,6 +158,8 @@ static void test_restrict_namespace(void) { assert_se(streq(s, "cgroup ipc net mnt pid user uts")); assert_se(namespace_flag_from_string_many(s, &ul) == 0 && ul == NAMESPACE_FLAGS_ALL); +#if SECCOMP_RESTRICT_NAMESPACES_BROKEN == 0 + if (!is_seccomp_available()) return; if (geteuid() != 0) @@ -216,6 +218,7 @@ static void test_restrict_namespace(void) { } assert_se(wait_for_terminate_and_warn("nsseccomp", pid, true) == EXIT_SUCCESS); +#endif } static void test_protect_sysctl(void) { @@ -283,8 +286,14 @@ static void test_restrict_address_families(void) { assert_se(fd >= 0); safe_close(fd); +#if SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN + fd = socket(AF_UNIX, SOCK_DGRAM, 0); + assert_se(fd >= 0); + safe_close(fd); +#else assert_se(socket(AF_UNIX, SOCK_DGRAM, 0) < 0); assert_se(errno == EAFNOSUPPORT); +#endif fd = socket(AF_NETLINK, SOCK_DGRAM, 0); assert_se(fd >= 0); @@ -300,11 +309,21 @@ static void test_restrict_address_families(void) { assert_se(fd >= 0); safe_close(fd); +#if SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN + fd = socket(AF_UNIX, SOCK_DGRAM, 0); + assert_se(fd >= 0); + safe_close(fd); + + fd = socket(AF_NETLINK, SOCK_DGRAM, 0); + assert_se(fd >= 0); + safe_close(fd); +#else assert_se(socket(AF_UNIX, SOCK_DGRAM, 0) < 0); assert_se(errno == EAFNOSUPPORT); assert_se(socket(AF_NETLINK, SOCK_DGRAM, 0) < 0); assert_se(errno == EAFNOSUPPORT); +#endif _exit(EXIT_SUCCESS); } @@ -368,11 +387,21 @@ static void test_memory_deny_write_execute(void) { assert_se(p != MAP_FAILED); assert_se(munmap(p, page_size()) >= 0); - seccomp_memory_deny_write_execute(); + p = mmap(NULL, page_size(), PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1,0); + assert_se(p != MAP_FAILED); + assert_se(munmap(p, page_size()) >= 0); + assert_se(seccomp_memory_deny_write_execute() >= 0); + +#if SECCOMP_MEMORY_DENY_WRITE_EXECUTE_BROKEN + p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1,0); + assert_se(p != MAP_FAILED); + assert_se(munmap(p, page_size()) >= 0); +#else p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1,0); assert_se(p == MAP_FAILED); assert_se(errno == EPERM); +#endif p = mmap(NULL, page_size(), PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1,0); assert_se(p != MAP_FAILED); diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c index e43373b0f5..4b3e924cfb 100644 --- a/src/test/test-string-util.c +++ b/src/test/test-string-util.c @@ -29,31 +29,20 @@ static void test_string_erase(void) { assert_se(streq(string_erase(x), "")); x = strdupa("1"); - assert_se(streq(string_erase(x), "x")); - - x = strdupa("12"); - assert_se(streq(string_erase(x), "xx")); - - x = strdupa("123"); - assert_se(streq(string_erase(x), "xxx")); - - x = strdupa("1234"); - assert_se(streq(string_erase(x), "xxxx")); - - x = strdupa("12345"); - assert_se(streq(string_erase(x), "xxxxx")); - - x = strdupa("123456"); - assert_se(streq(string_erase(x), "xxxxxx")); - - x = strdupa("1234567"); - assert_se(streq(string_erase(x), "xxxxxxx")); - - x = strdupa("12345678"); - assert_se(streq(string_erase(x), "xxxxxxxx")); + assert_se(streq(string_erase(x), "")); x = strdupa("123456789"); - assert_se(streq(string_erase(x), "xxxxxxxxx")); + assert_se(streq(string_erase(x), "")); + + assert_se(x[1] == '\0'); + assert_se(x[2] == '\0'); + assert_se(x[3] == '\0'); + assert_se(x[4] == '\0'); + assert_se(x[5] == '\0'); + assert_se(x[6] == '\0'); + assert_se(x[7] == '\0'); + assert_se(x[8] == '\0'); + assert_se(x[9] == '\0'); } static void test_ascii_strcasecmp_n(void) { diff --git a/src/test/test-time.c b/src/test/test-time.c index 8259491813..911282bf0c 100644 --- a/src/test/test-time.c +++ b/src/test/test-time.c @@ -17,9 +17,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "random-util.h" +#include "string-util.h" #include "strv.h" #include "time-util.h" -#include "random-util.h" static void test_parse_sec(void) { usec_t u; @@ -248,6 +249,30 @@ static void test_format_timestamp(void) { } } +static void test_format_timestamp_utc_one(usec_t t, const char *result) { + char buf[FORMAT_TIMESTAMP_MAX]; + + assert_se(!format_timestamp_utc(buf, sizeof(buf), t) == !result); + + if (result) + assert_se(streq(result, buf)); +} + +static void test_format_timestamp_utc(void) { + test_format_timestamp_utc_one(0, NULL); + test_format_timestamp_utc_one(1, "Thu 1970-01-01 00:00:00 UTC"); + test_format_timestamp_utc_one(USEC_PER_SEC, "Thu 1970-01-01 00:00:01 UTC"); + +#if SIZEOF_TIME_T == 8 + test_format_timestamp_utc_one(USEC_TIMESTAMP_FORMATTABLE_MAX, "Thu 9999-12-30 23:59:59 UTC"); +#elif SIZEOF_TIME_T == 4 + test_format_timestamp_utc_one(USEC_TIMESTAMP_FORMATTABLE_MAX, "Tue 2038-01-19 03:14:07 UTC"); +#endif + + test_format_timestamp_utc_one(USEC_TIMESTAMP_FORMATTABLE_MAX+1, NULL); + test_format_timestamp_utc_one(USEC_INFINITY, NULL); +} + int main(int argc, char *argv[]) { uintmax_t x; @@ -262,6 +287,7 @@ int main(int argc, char *argv[]) { test_usec_add(); test_usec_sub(); test_format_timestamp(); + test_format_timestamp_utc(); /* Ensure time_t is signed */ assert_cc((time_t) -1 < (time_t) 1); 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/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index f4ce9791fb..c4f4d46ca1 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -385,7 +385,7 @@ static int dir_cleanup( usec_t age; _cleanup_free_ char *sub_path = NULL; - if (STR_IN_SET(dent->d_name, ".", "..")) + if (dot_or_dot_dot(dent->d_name)) continue; if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) { @@ -1070,9 +1070,7 @@ static int item_do_children(Item *i, const char *path, action_t action) { _cleanup_free_ char *p = NULL; int q; - errno = 0; - - if (STR_IN_SET(de->d_name, ".", "..")) + if (dot_or_dot_dot(de->d_name)) continue; p = strjoin(path, "/", de->d_name); diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index b45490be1a..a17c006d57 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -243,7 +243,7 @@ static int ask_password_plymouth( r = 0; finish: - memory_erase(buffer, sizeof(buffer)); + explicit_bzero(buffer, sizeof(buffer)); return r; } @@ -283,7 +283,7 @@ static int send_passwords(const char *socket_name, char **passwords) { r = log_debug_errno(errno, "sendto(): %m"); finish: - memory_erase(packet, packet_length); + explicit_bzero(packet, packet_length); return r; } diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index deffefd60b..3f9c3ed0cf 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -73,29 +73,219 @@ void udev_event_unref(struct udev_event *event) { free(event); } +enum subst_type { + SUBST_UNKNOWN, + SUBST_DEVNODE, + SUBST_ATTR, + SUBST_ENV, + SUBST_KERNEL, + SUBST_KERNEL_NUMBER, + SUBST_DRIVER, + SUBST_DEVPATH, + SUBST_ID, + SUBST_MAJOR, + SUBST_MINOR, + SUBST_RESULT, + SUBST_PARENT, + SUBST_NAME, + SUBST_LINKS, + SUBST_ROOT, + SUBST_SYS, +}; + +static size_t subst_format_var(struct udev_event *event, struct udev_device *dev, + enum subst_type type, char *attr, + char *dest, size_t l) { + char *s = dest; + + switch (type) { + case SUBST_DEVPATH: + l = strpcpy(&s, l, udev_device_get_devpath(dev)); + break; + case SUBST_KERNEL: + l = strpcpy(&s, l, udev_device_get_sysname(dev)); + break; + case SUBST_KERNEL_NUMBER: + if (udev_device_get_sysnum(dev) == NULL) + break; + l = strpcpy(&s, l, udev_device_get_sysnum(dev)); + break; + case SUBST_ID: + if (event->dev_parent == NULL) + break; + l = strpcpy(&s, l, udev_device_get_sysname(event->dev_parent)); + break; + case SUBST_DRIVER: { + const char *driver; + + if (event->dev_parent == NULL) + break; + + driver = udev_device_get_driver(event->dev_parent); + if (driver == NULL) + break; + l = strpcpy(&s, l, driver); + break; + } + case SUBST_MAJOR: { + char num[UTIL_PATH_SIZE]; + + sprintf(num, "%u", major(udev_device_get_devnum(dev))); + l = strpcpy(&s, l, num); + break; + } + case SUBST_MINOR: { + char num[UTIL_PATH_SIZE]; + + sprintf(num, "%u", minor(udev_device_get_devnum(dev))); + l = strpcpy(&s, l, num); + break; + } + case SUBST_RESULT: { + char *rest; + int i; + + if (event->program_result == NULL) + break; + /* get part of the result string */ + i = 0; + if (attr != NULL) + i = strtoul(attr, &rest, 10); + if (i > 0) { + char result[UTIL_PATH_SIZE]; + char tmp[UTIL_PATH_SIZE]; + char *cpos; + + strscpy(result, sizeof(result), event->program_result); + cpos = result; + while (--i) { + while (cpos[0] != '\0' && !isspace(cpos[0])) + cpos++; + while (isspace(cpos[0])) + cpos++; + if (cpos[0] == '\0') + break; + } + if (i > 0) { + log_error("requested part of result string not found"); + break; + } + strscpy(tmp, sizeof(tmp), cpos); + /* %{2+}c copies the whole string from the second part on */ + if (rest[0] != '+') { + cpos = strchr(tmp, ' '); + if (cpos) + cpos[0] = '\0'; + } + l = strpcpy(&s, l, tmp); + } else { + l = strpcpy(&s, l, event->program_result); + } + break; + } + case SUBST_ATTR: { + const char *value = NULL; + char vbuf[UTIL_NAME_SIZE]; + size_t len; + int count; + + if (attr == NULL) { + log_error("missing file parameter for attr"); + break; + } + + /* try to read the value specified by "[dmi/id]product_name" */ + if (util_resolve_subsys_kernel(event->udev, attr, vbuf, sizeof(vbuf), 1) == 0) + value = vbuf; + + /* try to read the attribute the device */ + if (value == NULL) + value = udev_device_get_sysattr_value(event->dev, attr); + + /* try to read the attribute of the parent device, other matches have selected */ + if (value == NULL && event->dev_parent != NULL && event->dev_parent != event->dev) + value = udev_device_get_sysattr_value(event->dev_parent, attr); + + if (value == NULL) + break; + + /* strip trailing whitespace, and replace unwanted characters */ + if (value != vbuf) + strscpy(vbuf, sizeof(vbuf), value); + len = strlen(vbuf); + while (len > 0 && isspace(vbuf[--len])) + vbuf[len] = '\0'; + count = util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT); + if (count > 0) + log_debug("%i character(s) replaced" , count); + l = strpcpy(&s, l, vbuf); + break; + } + case SUBST_PARENT: { + struct udev_device *dev_parent; + const char *devnode; + + dev_parent = udev_device_get_parent(event->dev); + if (dev_parent == NULL) + break; + devnode = udev_device_get_devnode(dev_parent); + if (devnode != NULL) + l = strpcpy(&s, l, devnode + strlen("/dev/")); + break; + } + case SUBST_DEVNODE: + if (udev_device_get_devnode(dev) != NULL) + l = strpcpy(&s, l, udev_device_get_devnode(dev)); + break; + case SUBST_NAME: + if (event->name != NULL) + l = strpcpy(&s, l, event->name); + else if (udev_device_get_devnode(dev) != NULL) + l = strpcpy(&s, l, udev_device_get_devnode(dev) + strlen("/dev/")); + else + l = strpcpy(&s, l, udev_device_get_sysname(dev)); + break; + case SUBST_LINKS: { + struct udev_list_entry *list_entry; + + list_entry = udev_device_get_devlinks_list_entry(dev); + if (list_entry == NULL) + break; + l = strpcpy(&s, l, udev_list_entry_get_name(list_entry) + strlen("/dev/")); + udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry)) + l = strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry) + strlen("/dev/"), NULL); + break; + } + case SUBST_ROOT: + l = strpcpy(&s, l, "/dev"); + break; + case SUBST_SYS: + l = strpcpy(&s, l, "/sys"); + break; + case SUBST_ENV: + if (attr == NULL) { + break; + } else { + const char *value; + + value = udev_device_get_property_value(event->dev, attr); + if (value == NULL) + break; + l = strpcpy(&s, l, value); + break; + } + default: + log_error("unknown substitution type=%i", type); + break; + } + + return s - dest; +} + size_t udev_event_apply_format(struct udev_event *event, const char *src, char *dest, size_t size, bool replace_whitespace) { struct udev_device *dev = event->dev; - enum subst_type { - SUBST_UNKNOWN, - SUBST_DEVNODE, - SUBST_ATTR, - SUBST_ENV, - SUBST_KERNEL, - SUBST_KERNEL_NUMBER, - SUBST_DRIVER, - SUBST_DEVPATH, - SUBST_ID, - SUBST_MAJOR, - SUBST_MINOR, - SUBST_RESULT, - SUBST_PARENT, - SUBST_NAME, - SUBST_LINKS, - SUBST_ROOT, - SUBST_SYS, - }; static const struct subst_map { const char *name; const char fmt; @@ -132,10 +322,9 @@ size_t udev_event_apply_format(struct udev_event *event, for (;;) { enum subst_type type = SUBST_UNKNOWN; - char attrbuf[UTIL_PATH_SIZE], sbuf[UTIL_PATH_SIZE]; - char *attr = NULL, *_s; - size_t _l; - bool replws = replace_whitespace; + char attrbuf[UTIL_PATH_SIZE]; + char *attr = NULL; + size_t subst_len; while (from[0] != '\0') { if (from[0] == '$') { @@ -204,213 +393,17 @@ subst: attr = NULL; } - /* result subst handles space as field separator */ - if (type == SUBST_RESULT) - replws = false; - - if (replws) { - /* store dest string ptr and remaining len */ - _s = s; - _l = l; - /* temporarily use sbuf */ - s = sbuf; - l = UTIL_PATH_SIZE; - } - - switch (type) { - case SUBST_DEVPATH: - l = strpcpy(&s, l, udev_device_get_devpath(dev)); - break; - case SUBST_KERNEL: - l = strpcpy(&s, l, udev_device_get_sysname(dev)); - break; - case SUBST_KERNEL_NUMBER: - if (udev_device_get_sysnum(dev) == NULL) - break; - l = strpcpy(&s, l, udev_device_get_sysnum(dev)); - break; - case SUBST_ID: - if (event->dev_parent == NULL) - break; - l = strpcpy(&s, l, udev_device_get_sysname(event->dev_parent)); - break; - case SUBST_DRIVER: { - const char *driver; - - if (event->dev_parent == NULL) - break; - - driver = udev_device_get_driver(event->dev_parent); - if (driver == NULL) - break; - l = strpcpy(&s, l, driver); - break; - } - case SUBST_MAJOR: { - char num[UTIL_PATH_SIZE]; - - sprintf(num, "%u", major(udev_device_get_devnum(dev))); - l = strpcpy(&s, l, num); - break; - } - case SUBST_MINOR: { - char num[UTIL_PATH_SIZE]; - - sprintf(num, "%u", minor(udev_device_get_devnum(dev))); - l = strpcpy(&s, l, num); - break; - } - case SUBST_RESULT: { - char *rest; - int i; - - if (event->program_result == NULL) - break; - /* get part of the result string */ - i = 0; - if (attr != NULL) - i = strtoul(attr, &rest, 10); - if (i > 0) { - char result[UTIL_PATH_SIZE]; - char tmp[UTIL_PATH_SIZE]; - char *cpos; - - strscpy(result, sizeof(result), event->program_result); - cpos = result; - while (--i) { - while (cpos[0] != '\0' && !isspace(cpos[0])) - cpos++; - while (isspace(cpos[0])) - cpos++; - if (cpos[0] == '\0') - break; - } - if (i > 0) { - log_error("requested part of result string not found"); - break; - } - strscpy(tmp, sizeof(tmp), cpos); - /* %{2+}c copies the whole string from the second part on */ - if (rest[0] != '+') { - cpos = strchr(tmp, ' '); - if (cpos) - cpos[0] = '\0'; - } - l = strpcpy(&s, l, tmp); - } else { - l = strpcpy(&s, l, event->program_result); - } - break; - } - case SUBST_ATTR: { - const char *value = NULL; - char vbuf[UTIL_NAME_SIZE]; - size_t len; - int count; - - if (attr == NULL) { - log_error("missing file parameter for attr"); - break; - } - - /* try to read the value specified by "[dmi/id]product_name" */ - if (util_resolve_subsys_kernel(event->udev, attr, vbuf, sizeof(vbuf), 1) == 0) - value = vbuf; - - /* try to read the attribute the device */ - if (value == NULL) - value = udev_device_get_sysattr_value(event->dev, attr); - - /* try to read the attribute of the parent device, other matches have selected */ - if (value == NULL && event->dev_parent != NULL && event->dev_parent != event->dev) - value = udev_device_get_sysattr_value(event->dev_parent, attr); - - if (value == NULL) - break; + subst_len = subst_format_var(event, dev, type, attr, s, l); - /* strip trailing whitespace, and replace unwanted characters */ - if (value != vbuf) - strscpy(vbuf, sizeof(vbuf), value); - len = strlen(vbuf); - while (len > 0 && isspace(vbuf[--len])) - vbuf[len] = '\0'; - count = util_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT); - if (count > 0) - log_debug("%i character(s) replaced" , count); - l = strpcpy(&s, l, vbuf); - break; - } - case SUBST_PARENT: { - struct udev_device *dev_parent; - const char *devnode; - - dev_parent = udev_device_get_parent(event->dev); - if (dev_parent == NULL) - break; - devnode = udev_device_get_devnode(dev_parent); - if (devnode != NULL) - l = strpcpy(&s, l, devnode + strlen("/dev/")); - break; - } - case SUBST_DEVNODE: - if (udev_device_get_devnode(dev) != NULL) - l = strpcpy(&s, l, udev_device_get_devnode(dev)); - break; - case SUBST_NAME: - if (event->name != NULL) - l = strpcpy(&s, l, event->name); - else if (udev_device_get_devnode(dev) != NULL) - l = strpcpy(&s, l, udev_device_get_devnode(dev) + strlen("/dev/")); - else - l = strpcpy(&s, l, udev_device_get_sysname(dev)); - break; - case SUBST_LINKS: { - struct udev_list_entry *list_entry; + /* SUBST_RESULT handles spaces itself */ + if (replace_whitespace && type != SUBST_RESULT) + /* util_replace_whitespace can replace in-place, + * and does nothing if subst_len == 0 + */ + subst_len = util_replace_whitespace(s, s, subst_len); - list_entry = udev_device_get_devlinks_list_entry(dev); - if (list_entry == NULL) - break; - l = strpcpy(&s, l, udev_list_entry_get_name(list_entry) + strlen("/dev/")); - udev_list_entry_foreach(list_entry, udev_list_entry_get_next(list_entry)) - l = strpcpyl(&s, l, " ", udev_list_entry_get_name(list_entry) + strlen("/dev/"), NULL); - break; - } - case SUBST_ROOT: - l = strpcpy(&s, l, "/dev"); - break; - case SUBST_SYS: - l = strpcpy(&s, l, "/sys"); - break; - case SUBST_ENV: - if (attr == NULL) { - break; - } else { - const char *value; - - value = udev_device_get_property_value(event->dev, attr); - if (value == NULL) - break; - l = strpcpy(&s, l, value); - break; - } - default: - log_error("unknown substitution type=%i", type); - break; - } - - /* replace whitespace in sbuf and copy to dest */ - if (replws) { - size_t tmplen = UTIL_PATH_SIZE - l; - - /* restore s and l to dest string values */ - s = _s; - l = _l; - - /* copy ws-replaced value to s */ - tmplen = util_replace_whitespace(sbuf, s, MIN(tmplen, l)); - l -= tmplen; - s += tmplen; - } + s += subst_len; + l -= subst_len; } out: diff --git a/src/veritysetup/veritysetup-generator.c b/src/veritysetup/veritysetup-generator.c index 519ac050f9..0bb0bd6e8f 100644 --- a/src/veritysetup/veritysetup-generator.c +++ b/src/veritysetup/veritysetup-generator.c @@ -76,10 +76,10 @@ static int create_device(void) { r = unit_name_from_path(u, ".device", &d); if (r < 0) - return log_error_errno(r, "Failed to to generate unit name: %m"); + return log_error_errno(r, "Failed to generate unit name: %m"); r = unit_name_from_path(v, ".device", &e); if (r < 0) - return log_error_errno(r, "Failed to to generate unit name: %m"); + return log_error_errno(r, "Failed to generate unit name: %m"); f = fopen(p, "wxe"); if (!f) |