diff options
Diffstat (limited to 'src')
431 files changed, 15013 insertions, 3752 deletions
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index ac0470b20d..1eb2ca0ccf 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -627,7 +627,7 @@ static int analyze_plot(sd_bus *bus) { "<!-- that render these files properly but much slower are ImageMagick, -->\n" "<!-- gimp, inkscape, etc. To display the files on your system, just -->\n" "<!-- point your browser to this file. -->\n\n" - "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", VERSION); + "<!-- This plot was generated by systemd-analyze version %-16.16s -->\n\n", PACKAGE_VERSION); /* style sheet */ svg("<defs>\n <style type=\"text/css\">\n <![CDATA[\n" diff --git a/src/analyze/meson.build b/src/analyze/meson.build new file mode 100644 index 0000000000..fcbd814233 --- /dev/null +++ b/src/analyze/meson.build @@ -0,0 +1,5 @@ +systemd_analyze_sources = files(''' + analyze.c + analyze-verify.c + analyze-verify.h +'''.split()) diff --git a/src/basic/af-to-name.awk b/src/basic/af-to-name.awk new file mode 100644 index 0000000000..18d0a89728 --- /dev/null +++ b/src/basic/af-to-name.awk @@ -0,0 +1,9 @@ +BEGIN{ + print "static const char* const af_names[] = { " +} +!/AF_FILE/ && !/AF_ROUTE/ && !/AF_LOCAL/ { + printf " [%s] = \"%s\",\n", $1, $1 +} +END{ + print "};" +} diff --git a/src/basic/architecture.c b/src/basic/architecture.c index 5a3dc08a4a..2518dd8112 100644 --- a/src/basic/architecture.c +++ b/src/basic/architecture.c @@ -132,6 +132,9 @@ int uname_architecture(void) { # elif __SIZEOF_POINTER__ == 8 { "riscv", ARCHITECTURE_RISCV64 }, # endif +#elif defined(__arc__) + { "arc", ARCHITECTURE_ARC }, + { "arceb", ARCHITECTURE_ARC_BE }, #else #error "Please register your architecture here!" #endif @@ -185,6 +188,8 @@ static const char *const architecture_table[_ARCHITECTURE_MAX] = { [ARCHITECTURE_NIOS2] = "nios2", [ARCHITECTURE_RISCV32] = "riscv32", [ARCHITECTURE_RISCV64] = "riscv64", + [ARCHITECTURE_ARC] = "arc", + [ARCHITECTURE_ARC_BE] = "arc-be", }; DEFINE_STRING_TABLE_LOOKUP(architecture, int); diff --git a/src/basic/architecture.h b/src/basic/architecture.h index 46883719d1..40a2ce6448 100644 --- a/src/basic/architecture.h +++ b/src/basic/architecture.h @@ -60,6 +60,8 @@ enum { ARCHITECTURE_NIOS2, ARCHITECTURE_RISCV32, ARCHITECTURE_RISCV64, + ARCHITECTURE_ARC, + ARCHITECTURE_ARC_BE, _ARCHITECTURE_MAX, _ARCHITECTURE_INVALID = -1 }; @@ -79,7 +81,11 @@ int uname_architecture(void); #if defined(__x86_64__) # define native_architecture() ARCHITECTURE_X86_64 -# define LIB_ARCH_TUPLE "x86_64-linux-gnu" +# if defined(__ILP32__) +# define LIB_ARCH_TUPLE "x86_64-linux-gnux32" +# else +# define LIB_ARCH_TUPLE "x86_64-linux-gnu" +# endif # define SECONDARY_ARCHITECTURE ARCHITECTURE_X86 #elif defined(__i386__) # define native_architecture() ARCHITECTURE_X86 @@ -97,7 +103,11 @@ int uname_architecture(void); #elif defined(__powerpc__) # if __BYTE_ORDER == __BIG_ENDIAN # define native_architecture() ARCHITECTURE_PPC -# define LIB_ARCH_TUPLE "powerpc-linux-gnu" +# if defined(__NO_FPRS__) +# define LIB_ARCH_TUPLE "powerpc-linux-gnuspe" +# else +# define LIB_ARCH_TUPLE "powerpc-linux-gnu" +# endif # else # define native_architecture() ARCHITECTURE_PPC_LE # error "Missing LIB_ARCH_TUPLE for PPCLE" @@ -189,7 +199,23 @@ int uname_architecture(void); # error "Missing LIB_ARCH_TUPLE for SH64" #elif defined(__sh__) # define native_architecture() ARCHITECTURE_SH -# define LIB_ARCH_TUPLE "sh4-linux-gnu" +# if defined(__SH1__) +# define LIB_ARCH_TUPLE "sh1-linux-gnu" +# elif defined(__SH2__) +# define LIB_ARCH_TUPLE "sh2-linux-gnu" +# elif defined(__SH2A__) +# define LIB_ARCH_TUPLE "sh2a-linux-gnu" +# elif defined(__SH2E__) +# define LIB_ARCH_TUPLE "sh2e-linux-gnu" +# elif defined(__SH3__) +# define LIB_ARCH_TUPLE "sh3-linux-gnu" +# elif defined(__SH3E__) +# define LIB_ARCH_TUPLE "sh3e-linux-gnu" +# elif defined(__SH4__) && !defined(__SH4A__) +# define LIB_ARCH_TUPLE "sh4-linux-gnu" +# elif defined(__SH4A__) +# define LIB_ARCH_TUPLE "sh4a-linux-gnu" +# endif #elif defined(__m68k__) # define native_architecture() ARCHITECTURE_M68K # define LIB_ARCH_TUPLE "m68k-linux-gnu" @@ -213,6 +239,14 @@ int uname_architecture(void); # else # error "Unrecognized riscv architecture variant" # endif +#elif defined(__arc__) +# if __BYTE_ORDER == __BIG_ENDIAN +# define native_architecture() ARCHITECTURE_ARC_BE +# define LIB_ARCH_TUPLE "arceb-linux" +# else +# define native_architecture() ARCHITECTURE_ARC +# define LIB_ARCH_TUPLE "arc-linux" +# endif #else # error "Please register your architecture here!" #endif diff --git a/src/basic/arphrd-to-name.awk b/src/basic/arphrd-to-name.awk new file mode 100644 index 0000000000..5a35673e2c --- /dev/null +++ b/src/basic/arphrd-to-name.awk @@ -0,0 +1,9 @@ +BEGIN{ + print "static const char* const arphrd_names[] = { " +} +!/CISCO/ { + printf " [ARPHRD_%s] = \"%s\",\n", $1, $1 +} +END{ + print "};" +} diff --git a/src/basic/blkid-util.h b/src/basic/blkid-util.h index 7aa75eb091..1b9cace040 100644 --- a/src/basic/blkid-util.h +++ b/src/basic/blkid-util.h @@ -20,7 +20,7 @@ ***/ #ifdef HAVE_BLKID -#include <blkid/blkid.h> +#include <blkid.h> #endif #include "util.h" diff --git a/src/basic/build.h b/src/basic/build.h index 91312bd2a3..3223915da6 100644 --- a/src/basic/build.h +++ b/src/basic/build.h @@ -127,6 +127,12 @@ #define _KMOD_FEATURE_ "-KMOD" #endif +#ifdef HAVE_LIBIDN2 +#define _IDN2_FEATURE_ "+IDN2" +#else +#define _IDN2_FEATURE_ "-IDN2" +#endif + #ifdef HAVE_LIBIDN #define _IDN_FEATURE_ "+IDN" #else @@ -154,5 +160,6 @@ _BLKID_FEATURE_ " " \ _ELFUTILS_FEATURE_ " " \ _KMOD_FEATURE_ " " \ + _IDN2_FEATURE_ " " \ _IDN_FEATURE_ " " \ _CGROUP_HIEARCHY_ diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c index 2323eb8555..204120ee0e 100644 --- a/src/basic/calendarspec.c +++ b/src/basic/calendarspec.c @@ -487,22 +487,33 @@ static int parse_weekdays(const char **p, CalendarSpec *c) { } } +static int parse_one_number(const char *p, const char **e, unsigned long *ret) { + char *ee = NULL; + unsigned long value; + + errno = 0; + value = strtoul(p, &ee, 10); + if (errno > 0) + return -errno; + if (ee == p) + return -EINVAL; + + *ret = value; + *e = ee; + return 0; +} + static int parse_component_decimal(const char **p, bool usec, int *res) { unsigned long value; const char *e = NULL; - char *ee = NULL; int r; if (!isdigit(**p)) return -EINVAL; - errno = 0; - value = strtoul(*p, &ee, 10); - if (errno > 0) - return -errno; - if (ee == *p) - return -EINVAL; - e = ee; + r = parse_one_number(*p, &e, &value); + if (r < 0) + return r; if (usec) { if (value * USEC_PER_SEC / USEC_PER_SEC != value) @@ -553,6 +564,47 @@ static int const_chain(int value, CalendarComponent **c) { return 0; } +static int calendarspec_from_time_t(CalendarSpec *c, time_t time) { + struct tm tm; + CalendarComponent *year = NULL, *month = NULL, *day = NULL, *hour = NULL, *minute = NULL, *us = NULL; + int r; + + assert_se(gmtime_r(&time, &tm)); + + r = const_chain(tm.tm_year + 1900, &year); + if (r < 0) + return r; + + r = const_chain(tm.tm_mon + 1, &month); + if (r < 0) + return r; + + r = const_chain(tm.tm_mday, &day); + if (r < 0) + return r; + + r = const_chain(tm.tm_hour, &hour); + if (r < 0) + return r; + + r = const_chain(tm.tm_min, &minute); + if (r < 0) + return r; + + r = const_chain(tm.tm_sec * USEC_PER_SEC, &us); + if (r < 0) + return r; + + c->utc = true; + c->year = year; + c->month = month; + c->day = day; + c->hour = hour; + c->minute = minute; + c->microsecond = us; + return 0; +} + static int prepend_component(const char **p, bool usec, CalendarComponent **c) { int r, start, stop = -1, repeat = 0; CalendarComponent *cc; @@ -657,6 +709,27 @@ static int parse_date(const char **p, CalendarSpec *c) { if (*t == 0) return 0; + /* @TIMESTAMP — UNIX time in seconds since the epoch */ + if (*t == '@') { + unsigned long value; + time_t time; + + r = parse_one_number(t + 1, &t, &value); + if (r < 0) + return r; + + time = value; + if ((unsigned long) time != value) + return -ERANGE; + + r = calendarspec_from_time_t(c, time); + if (r < 0) + return r; + + *p = t; + return 1; /* finito, don't parse H:M:S after that */ + } + r = parse_chain(&t, false, &first); if (r < 0) return r; @@ -832,7 +905,7 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { continue; e = endswith_no_case(p, tzname[j]); - if(!e) + if (!e) continue; if (e == p) continue; @@ -986,9 +1059,11 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { if (r < 0) goto fail; - r = parse_calendar_time(&p, c); - if (r < 0) - goto fail; + if (r == 0) { + r = parse_calendar_time(&p, c); + if (r < 0) + goto fail; + } if (*p != 0) { r = -EINVAL; diff --git a/src/basic/cap-to-name.awk b/src/basic/cap-to-name.awk new file mode 100644 index 0000000000..402a782024 --- /dev/null +++ b/src/basic/cap-to-name.awk @@ -0,0 +1,9 @@ +BEGIN{ + print "static const char* const capability_names[] = { " +} +{ + printf " [%s] = \"%s\",\n", $1, tolower($1) +} +END{ + print "};" +} diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index bda5c555ad..6344372610 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -55,6 +55,7 @@ #include "stdio-util.h" #include "string-table.h" #include "string-util.h" +#include "strv.h" #include "unit-name.h" #include "user-util.h" @@ -839,7 +840,7 @@ int cg_attach(const char *controller, const char *path, pid_t pid) { if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { r = cg_attach(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, pid); if (r < 0) - log_warning_errno(r, "Failed to attach %d to compat systemd cgroup %s: %m", pid, path); + log_warning_errno(r, "Failed to attach "PID_FMT" to compat systemd cgroup %s: %m", pid, path); } return 0; @@ -2211,6 +2212,60 @@ int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) return 0; } +int cg_mask_to_string(CGroupMask mask, char **ret) { + const char *controllers[_CGROUP_CONTROLLER_MAX + 1]; + CGroupController c; + int i = 0; + char *s; + + assert(ret); + + if (mask == 0) { + *ret = NULL; + return 0; + } + + for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { + + if (!(mask & CGROUP_CONTROLLER_TO_MASK(c))) + continue; + + controllers[i++] = cgroup_controller_to_string(c); + controllers[i] = NULL; + } + + s = strv_join((char **)controllers, NULL); + if (!s) + return -ENOMEM; + + *ret = s; + return 0; +} + +int cg_mask_from_string(const char *value, CGroupMask *mask) { + assert(mask); + assert(value); + + for (;;) { + _cleanup_free_ char *n = NULL; + CGroupController v; + int r; + + r = extract_first_word(&value, &n, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; + + v = cgroup_controller_from_string(n); + if (v < 0) + continue; + + *mask |= CGROUP_CONTROLLER_TO_MASK(v); + } + return 0; +} + int cg_mask_supported(CGroupMask *ret) { CGroupMask mask = 0; int r; @@ -2224,7 +2279,6 @@ int cg_mask_supported(CGroupMask *ret) { return r; if (r > 0) { _cleanup_free_ char *root = NULL, *controllers = NULL, *path = NULL; - const char *c; /* In the unified hierarchy we can read the supported * and accessible controllers from a the top-level @@ -2242,23 +2296,9 @@ int cg_mask_supported(CGroupMask *ret) { if (r < 0) return r; - c = controllers; - for (;;) { - _cleanup_free_ char *n = NULL; - CGroupController v; - - r = extract_first_word(&c, &n, NULL, 0); - if (r < 0) - return r; - if (r == 0) - break; - - v = cgroup_controller_from_string(n); - if (v < 0) - continue; - - mask |= CGROUP_CONTROLLER_TO_MASK(v); - } + r = cg_mask_from_string(controllers, &mask); + if (r < 0) + return r; /* Currently, we support the cpu, memory, io and pids * controller in the unified hierarchy, mask diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h index a522095d95..c16a33723c 100644 --- a/src/basic/cgroup-util.h +++ b/src/basic/cgroup-util.h @@ -235,6 +235,8 @@ int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p); int cg_mask_supported(CGroupMask *ret); +int cg_mask_from_string(const char *s, CGroupMask *ret); +int cg_mask_to_string(CGroupMask mask, char **ret); int cg_kernel_controllers(Set *controllers); diff --git a/src/basic/def.h b/src/basic/def.h index 200ea973c1..b1a3bc190b 100644 --- a/src/basic/def.h +++ b/src/basic/def.h @@ -67,10 +67,6 @@ .un.sun_path = "\0/org/freedesktop/plymouthd", \ } -#ifndef TTY_GID -#define TTY_GID 5 -#endif - #define NOTIFY_FD_MAX 768 #define NOTIFY_BUFFER_MAX PIPE_BUF diff --git a/src/basic/dirent-util.c b/src/basic/dirent-util.c index 6b9d26773e..5bf58bcdc3 100644 --- a/src/basic/dirent-util.c +++ b/src/basic/dirent-util.c @@ -75,3 +75,14 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) { return endswith(de->d_name, suffix); } + +struct dirent* readdir_no_dot(DIR *dirp) { + struct dirent* d; + + for (;;) { + d = readdir(dirp); + if (d && dot_or_dot_dot(d->d_name)) + continue; + return d; + } +} diff --git a/src/basic/dirent-util.h b/src/basic/dirent-util.h index b91d04908f..18b9db9b28 100644 --- a/src/basic/dirent-util.h +++ b/src/basic/dirent-util.h @@ -31,6 +31,8 @@ int dirent_ensure_type(DIR *d, struct dirent *de); bool dirent_is_file(const struct dirent *de) _pure_; bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pure_; +struct dirent* readdir_no_dot(DIR *dirp); + #define FOREACH_DIRENT(de, d, on_error) \ for (errno = 0, de = readdir(d);; errno = 0, de = readdir(d)) \ if (!de) { \ diff --git a/src/basic/env-util.c b/src/basic/env-util.c index 1ec574e8a0..56e7b6fd8c 100644 --- a/src/basic/env-util.c +++ b/src/basic/env-util.c @@ -779,7 +779,7 @@ int serialize_environment(FILE *f, char **environment) { if (!ce) return -ENOMEM; - fprintf(f, "env=%s\n", *e); + fprintf(f, "env=%s\n", ce); } /* caller should call ferror() */ @@ -788,7 +788,7 @@ int serialize_environment(FILE *f, char **environment) { } int deserialize_environment(char ***environment, const char *line) { - char *uce = NULL; + char *uce; int r; assert(line); @@ -799,8 +799,10 @@ int deserialize_environment(char ***environment, const char *line) { if (r < 0) return r; - if (!env_assignment_is_valid(uce)) + if (!env_assignment_is_valid(uce)) { + free(uce); return -EINVAL; + } return strv_env_replace(environment, uce); } diff --git a/src/basic/errno-to-name.awk b/src/basic/errno-to-name.awk new file mode 100644 index 0000000000..0878abacbd --- /dev/null +++ b/src/basic/errno-to-name.awk @@ -0,0 +1,9 @@ +BEGIN{ + print "static const char* const errno_names[] = { " +} +!/EDEADLOCK/ && !/EWOULDBLOCK/ && !/ENOTSUP/ { + printf " [%s] = \"%s\",\n", $1, $1 +} +END{ + print "};" +} diff --git a/src/basic/escape.c b/src/basic/escape.c index 4a1ec4505e..85e4b5282e 100644 --- a/src/basic/escape.c +++ b/src/basic/escape.c @@ -441,10 +441,16 @@ char *octescape(const char *s, size_t len) { } -static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad) { +static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad, bool escape_tab_nl) { assert(bad); for (; *s; s++) { + if (escape_tab_nl && IN_SET(*s, '\n', '\t')) { + *(t++) = '\\'; + *(t++) = *s == '\n' ? 'n' : 't'; + continue; + } + if (*s == '\\' || strchr(bad, *s)) *(t++) = '\\'; @@ -461,20 +467,21 @@ char *shell_escape(const char *s, const char *bad) { if (!r) return NULL; - t = strcpy_backslash_escaped(r, s, bad); + t = strcpy_backslash_escaped(r, s, bad, false); *t = 0; return r; } -char *shell_maybe_quote(const char *s) { +char* shell_maybe_quote(const char *s, EscapeStyle style) { const char *p; char *r, *t; assert(s); - /* Encloses a string in double quotes if necessary to make it - * OK as shell string. */ + /* Encloses a string in quotes if necessary to make it OK as a shell + * string. Note that we treat benign UTF-8 characters as needing + * escaping too, but that should be OK. */ for (p = s; *p; p++) if (*p <= ' ' || @@ -485,17 +492,30 @@ char *shell_maybe_quote(const char *s) { if (!*p) return strdup(s); - r = new(char, 1+strlen(s)*2+1+1); + r = new(char, (style == ESCAPE_POSIX) + 1 + strlen(s)*2 + 1 + 1); if (!r) return NULL; t = r; - *(t++) = '"'; + if (style == ESCAPE_BACKSLASH) + *(t++) = '"'; + else if (style == ESCAPE_POSIX) { + *(t++) = '$'; + *(t++) = '\''; + } else + assert_not_reached("Bad EscapeStyle"); + t = mempcpy(t, s, p - s); - t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE); + if (style == ESCAPE_BACKSLASH) + t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE, false); + else + t = strcpy_backslash_escaped(t, p, SHELL_NEED_ESCAPE_POSIX, true); - *(t++)= '"'; + if (style == ESCAPE_BACKSLASH) + *(t++) = '"'; + else + *(t++) = '\''; *t = 0; return r; diff --git a/src/basic/escape.h b/src/basic/escape.h index deaa4def28..6f5cc60bc8 100644 --- a/src/basic/escape.h +++ b/src/basic/escape.h @@ -31,13 +31,30 @@ /* What characters are special in the shell? */ /* must be escaped outside and inside double-quotes */ #define SHELL_NEED_ESCAPE "\"\\`$" -/* can be escaped or double-quoted */ -#define SHELL_NEED_QUOTES SHELL_NEED_ESCAPE GLOB_CHARS "'()<>|&;" + +/* Those that can be escaped or double-quoted. + * + * Stricly speaking, ! does not need to be escaped, except in interactive + * mode, but let's be extra nice to the user and quote ! in case this + * output is ever used in interactive mode. */ +#define SHELL_NEED_QUOTES SHELL_NEED_ESCAPE GLOB_CHARS "'()<>|&;!" + +/* Note that we assume control characters would need to be escaped too in + * addition to the "special" characters listed here, if they appear in the + * string. Current users disallow control characters. Also '"' shall not + * be escaped. + */ +#define SHELL_NEED_ESCAPE_POSIX "\\\'" typedef enum UnescapeFlags { UNESCAPE_RELAX = 1, } UnescapeFlags; +typedef enum EscapeStyle { + ESCAPE_BACKSLASH = 1, + ESCAPE_POSIX = 2, +} EscapeStyle; + char *cescape(const char *s); char *cescape_length(const char *s, size_t n); size_t cescape_char(char c, char *buf); @@ -51,4 +68,4 @@ char *xescape(const char *s, const char *bad); char *octescape(const char *s, size_t len); char *shell_escape(const char *s, const char *bad); -char *shell_maybe_quote(const char *s); +char* shell_maybe_quote(const char *s, EscapeStyle style); diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c index f8cac3e911..804f14c44c 100644 --- a/src/basic/extract-word.c +++ b/src/basic/extract-word.c @@ -241,7 +241,12 @@ int extract_first_word_and_warn( return log_syntax(unit, LOG_ERR, filename, line, r, "Unable to decode word \"%s\", ignoring: %m", rvalue); } -int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) { +/* We pass ExtractFlags as unsigned int (to avoid undefined behaviour when passing + * an object that undergoes default argument promotion as an argument to va_start). + * Let's make sure that ExtractFlags fits into an unsigned int. */ +assert_cc(sizeof(enum ExtractFlags) <= sizeof(unsigned)); + +int extract_many_words(const char **p, const char *separators, unsigned flags, ...) { va_list ap; char **l; int n = 0, i, c, r; diff --git a/src/basic/extract-word.h b/src/basic/extract-word.h index 21db5ef33f..04746c6d08 100644 --- a/src/basic/extract-word.h +++ b/src/basic/extract-word.h @@ -32,4 +32,4 @@ typedef enum ExtractFlags { int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags); int extract_first_word_and_warn(const char **p, char **ret, const char *separators, ExtractFlags flags, const char *unit, const char *filename, unsigned line, const char *rvalue); -int extract_many_words(const char **p, const char *separators, ExtractFlags flags, ...) _sentinel_; +int extract_many_words(const char **p, const char *separators, unsigned flags, ...) _sentinel_; diff --git a/src/basic/fileio-label.c b/src/basic/fileio-label.c index 66dbc0fe1e..ef51c49395 100644 --- a/src/basic/fileio-label.c +++ b/src/basic/fileio-label.c @@ -24,14 +24,14 @@ #include "fileio.h" #include "selinux-util.h" -int write_string_file_atomic_label(const char *fn, const char *line) { +int write_string_file_atomic_label_ts(const char *fn, const char *line, struct timespec *ts) { int r; r = mac_selinux_create_file_prepare(fn, S_IFREG); if (r < 0) return r; - r = write_string_file(fn, line, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); + r = write_string_file_ts(fn, line, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC, ts); mac_selinux_create_file_clear(); diff --git a/src/basic/fileio-label.h b/src/basic/fileio-label.h index fe7543013d..9854ea50b9 100644 --- a/src/basic/fileio-label.h +++ b/src/basic/fileio-label.h @@ -24,7 +24,10 @@ #include "fileio.h" -int write_string_file_atomic_label(const char *fn, const char *line); +int write_string_file_atomic_label_ts(const char *fn, const char *line, struct timespec *ts); +static inline int write_string_file_atomic_label(const char *fn, const char *line) { + return write_string_file_atomic_label_ts(fn, line, NULL); +} int write_env_file_label(const char *fname, char **l); int fopen_temporary_label(const char *target, const char *path, FILE **f, char **temp_path); diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 7c2c2b38f5..9a185e3e60 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -51,7 +51,7 @@ #define READ_FULL_BYTES_MAX (4U*1024U*1024U) -int write_string_stream(FILE *f, const char *line, bool enforce_newline) { +int write_string_stream_ts(FILE *f, const char *line, bool enforce_newline, struct timespec *ts) { assert(f); assert(line); @@ -60,6 +60,13 @@ int write_string_stream(FILE *f, const char *line, bool enforce_newline) { if (enforce_newline && !endswith(line, "\n")) fputc('\n', f); + if (ts) { + struct timespec twice[2] = {*ts, *ts}; + + if (futimens(fileno(f), twice) < 0) + return -errno; + } + return fflush_and_check(f); } @@ -89,7 +96,7 @@ static int write_string_file_atomic(const char *fn, const char *line, bool enfor return r; } -int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) { +int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, struct timespec *ts) { _cleanup_fclose_ FILE *f = NULL; int q, r; @@ -104,7 +111,8 @@ int write_string_file(const char *fn, const char *line, WriteStringFileFlags fla goto fail; return r; - } + } else + assert(ts == NULL); if (flags & WRITE_STRING_FILE_CREATE) { f = fopen(fn, "we"); @@ -131,7 +139,7 @@ int write_string_file(const char *fn, const char *line, WriteStringFileFlags fla } } - r = write_string_stream(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE)); + r = write_string_stream_ts(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE), ts); if (r < 0) goto fail; diff --git a/src/basic/fileio.h b/src/basic/fileio.h index e547614cc4..6098562265 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -35,8 +35,14 @@ typedef enum { WRITE_STRING_FILE_VERIFY_ON_FAILURE = 8, } WriteStringFileFlags; -int write_string_stream(FILE *f, const char *line, bool enforce_newline); -int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags); +int write_string_stream_ts(FILE *f, const char *line, bool enforce_newline, struct timespec *ts); +static inline int write_string_stream(FILE *f, const char *line, bool enforce_newline) { + return write_string_stream_ts(f, line, enforce_newline, NULL); +} +int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, struct timespec *ts); +static inline int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) { + return write_string_file_ts(fn, line, flags, NULL); +} int read_one_line_file(const char *fn, char **line); int read_full_file(const char *fn, char **contents, size_t *size); diff --git a/src/basic/format-util.h b/src/basic/format-util.h index 39a185f59b..ae42a8f89e 100644 --- a/src/basic/format-util.h +++ b/src/basic/format-util.h @@ -54,6 +54,12 @@ # error Unknown time_t size #endif +#if defined __x86_64__ && defined __ILP32__ +# define PRI_TIMEX PRIi64 +#else +# define PRI_TIMEX "li" +#endif + #if SIZEOF_RLIM_T == 8 # define RLIM_FMT "%" PRIu64 #elif SIZEOF_RLIM_T == 4 diff --git a/src/basic/generate-af-list.sh b/src/basic/generate-af-list.sh new file mode 100755 index 0000000000..8d9cdd1836 --- /dev/null +++ b/src/basic/generate-af-list.sh @@ -0,0 +1,5 @@ +#!/bin/sh -eu + +$1 -E -dM -include sys/socket.h - </dev/null | \ + grep -Ev 'AF_UNSPEC|AF_MAX' | \ + awk '/^#define[ \t]+AF_[^ \t]+[ \t]+PF_[^ \t]/ { print $2; }' diff --git a/src/basic/generate-arphrd-list.sh b/src/basic/generate-arphrd-list.sh new file mode 100755 index 0000000000..ee207fb38e --- /dev/null +++ b/src/basic/generate-arphrd-list.sh @@ -0,0 +1,5 @@ +#!/bin/sh -eu + +$1 -dM -include net/if_arp.h - </dev/null | \ + awk '/^#define[ \t]+ARPHRD_[^ \t]+[ \t]+[^ \t]/ { print $2; }' | \ + sed -e 's/ARPHRD_//' diff --git a/src/basic/generate-cap-list.sh b/src/basic/generate-cap-list.sh new file mode 100755 index 0000000000..1d4a562e7c --- /dev/null +++ b/src/basic/generate-cap-list.sh @@ -0,0 +1,5 @@ +#!/bin/sh -eu + +$1 -dM -include linux/capability.h -include "$2" -include "$3" - </dev/null | \ + awk '/^#define[ \t]+CAP_[A-Z_]+[ \t]+/ { print $2; }' | \ + grep -v CAP_LAST_CAP diff --git a/src/basic/generate-errno-list.sh b/src/basic/generate-errno-list.sh new file mode 100755 index 0000000000..e2bab8b320 --- /dev/null +++ b/src/basic/generate-errno-list.sh @@ -0,0 +1,4 @@ +#!/bin/sh -eu + +$1 -dM -include errno.h - </dev/null | \ + awk '/^#define[ \t]+E[^ _]+[ \t]+/ { print $2; }' diff --git a/src/basic/generate-gperfs.py b/src/basic/generate-gperfs.py new file mode 100755 index 0000000000..d4cc9aa45c --- /dev/null +++ b/src/basic/generate-gperfs.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 + +"""Generate %-from-name.gperf from %-list.txt +""" + +import sys + +name, prefix, input = sys.argv[1:] + +print("""\ +struct {}_name {{ const char* name; int id; }}; +%null-strings +%%""".format(name)) + +for line in open(input): + print("{0}, {1}{0}".format(line.rstrip(), prefix)) diff --git a/src/basic/glob-util.c b/src/basic/glob-util.c index 007198c269..f611c42e4c 100644 --- a/src/basic/glob-util.c +++ b/src/basic/glob-util.c @@ -17,54 +17,70 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <dirent.h> #include <errno.h> #include <glob.h> +#include <sys/types.h> +#include "dirent-util.h" #include "glob-util.h" #include "macro.h" +#include "path-util.h" #include "strv.h" -int glob_exists(const char *path) { - _cleanup_globfree_ glob_t g = {}; +int safe_glob(const char *path, int flags, glob_t *pglob) { int k; - assert(path); + /* We want to set GLOB_ALTDIRFUNC ourselves, don't allow it to be set. */ + assert(!(flags & GLOB_ALTDIRFUNC)); + + if (!pglob->gl_closedir) + pglob->gl_closedir = (void (*)(void *)) closedir; + if (!pglob->gl_readdir) + pglob->gl_readdir = (struct dirent *(*)(void *)) readdir_no_dot; + if (!pglob->gl_opendir) + pglob->gl_opendir = (void *(*)(const char *)) opendir; + if (!pglob->gl_lstat) + pglob->gl_lstat = lstat; + if (!pglob->gl_stat) + pglob->gl_stat = stat; errno = 0; - k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g); + k = glob(path, flags | GLOB_ALTDIRFUNC, NULL, pglob); if (k == GLOB_NOMATCH) - return 0; + return -ENOENT; if (k == GLOB_NOSPACE) return -ENOMEM; if (k != 0) return errno > 0 ? -errno : -EIO; + if (strv_isempty(pglob->gl_pathv)) + return -ENOENT; - return !strv_isempty(g.gl_pathv); + return 0; } -int glob_extend(char ***strv, const char *path) { +int glob_exists(const char *path) { _cleanup_globfree_ glob_t g = {}; int k; - char **p; - errno = 0; - k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g); + assert(path); - if (k == GLOB_NOMATCH) - return -ENOENT; - if (k == GLOB_NOSPACE) - return -ENOMEM; - if (k != 0) - return errno > 0 ? -errno : -EIO; - if (strv_isempty(g.gl_pathv)) - return -ENOENT; + k = safe_glob(path, GLOB_NOSORT|GLOB_BRACE, &g); + if (k == -ENOENT) + return false; + if (k < 0) + return k; + return true; +} + +int glob_extend(char ***strv, const char *path) { + _cleanup_globfree_ glob_t g = {}; + int k; - STRV_FOREACH(p, g.gl_pathv) { - k = strv_extend(strv, *p); - if (k < 0) - return k; - } + k = safe_glob(path, GLOB_NOSORT|GLOB_BRACE, &g); + if (k < 0) + return k; - return 0; + return strv_extend_strv(strv, g.gl_pathv, false); } diff --git a/src/basic/glob-util.h b/src/basic/glob-util.h index 5d8fb47a26..e1f6083afa 100644 --- a/src/basic/glob-util.h +++ b/src/basic/glob-util.h @@ -19,12 +19,16 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <glob.h> #include <stdbool.h> #include <string.h> #include "macro.h" #include "string-util.h" +/* Note: this function modifies pglob to set various functions. */ +int safe_glob(const char *path, int flags, glob_t *pglob); + int glob_exists(const char *path); int glob_extend(char ***strv, const char *path); diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c index 3927df2955..d52fdad3ac 100644 --- a/src/basic/in-addr-util.c +++ b/src/basic/in-addr-util.c @@ -464,3 +464,45 @@ int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen) return -EAFNOSUPPORT; } + +int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, uint8_t *ret_prefixlen) { + union in_addr_union buffer; + const char *e, *l; + uint8_t k; + int r; + + assert(p); + + if (!IN_SET(family, AF_INET, AF_INET6)) + return -EAFNOSUPPORT; + + e = strchr(p, '/'); + if (e) + l = strndupa(p, e - p); + else + l = p; + + r = in_addr_from_string(family, l, &buffer); + if (r < 0) + return r; + + k = FAMILY_ADDRESS_SIZE(family) * 8; + + if (e) { + uint8_t n; + + r = safe_atou8(e + 1, &n); + if (r < 0) + return r; + + if (n > k) + return -ERANGE; + + k = n; + } + + *ret_prefix = buffer; + *ret_prefixlen = k; + + return 0; +} diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h index 51a5aa67e4..14e27246b5 100644 --- a/src/basic/in-addr-util.h +++ b/src/basic/in-addr-util.h @@ -60,6 +60,7 @@ struct in_addr* in_addr_prefixlen_to_netmask(struct in_addr *addr, unsigned char int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen); int in_addr_default_subnet_mask(const struct in_addr *addr, struct in_addr *mask); int in_addr_mask(int family, union in_addr_union *addr, unsigned char prefixlen); +int in_addr_prefix_from_string(const char *p, int family, union in_addr_union *ret_prefix, uint8_t *ret_prefixlen); static inline size_t FAMILY_ADDRESS_SIZE(int family) { assert(family == AF_INET || family == AF_INET6); diff --git a/src/basic/journal-importer.c b/src/basic/journal-importer.c index 4c13e46a49..7d72effdea 100644 --- a/src/basic/journal-importer.c +++ b/src/basic/journal-importer.c @@ -24,6 +24,7 @@ #include "fd-util.h" #include "parse-util.h" #include "string-util.h" +#include "unaligned.h" enum { IMPORTER_STATE_LINE = 0, /* waiting to read, or reading line */ @@ -68,6 +69,7 @@ void journal_importer_cleanup(JournalImporter *imp) { safe_close(imp->fd); } + free(imp->name); free(imp->buf); iovw_free_contents(&imp->iovw); } @@ -203,7 +205,7 @@ static int get_data_size(JournalImporter *imp) { if (r <= 0) return r; - imp->data_size = le64toh( *(uint64_t *) data ); + imp->data_size = unaligned_read_le64(data); if (imp->data_size > DATA_SIZE_MAX) { log_error("Stream declares field with size %zu > DATA_SIZE_MAX = %u", imp->data_size, DATA_SIZE_MAX); @@ -314,7 +316,7 @@ int journal_importer_process_data(JournalImporter *imp) { return r; if (r == 0) { imp->state = IMPORTER_STATE_EOF; - return r; + return 0; } assert(n > 0); assert(line[n-1] == '\n'); diff --git a/src/basic/log.c b/src/basic/log.c index 36efc9ac7d..3fd53800a0 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -58,7 +58,8 @@ #define SNDBUF_SIZE (8*1024*1024) static LogTarget log_target = LOG_TARGET_CONSOLE; -static int log_max_level = LOG_INFO; +static int log_max_level[] = {LOG_INFO, LOG_INFO}; +assert_cc(ELEMENTSOF(log_max_level) == _LOG_REALM_MAX); static int log_facility = LOG_DAEMON; static int console_fd = STDERR_FILENO; @@ -231,6 +232,8 @@ fail: int log_open(void) { int r; + /* Do not call from library code. */ + /* If we don't use the console we close it here, to not get * killed by SAK. If we don't use syslog we close it here so * that we are not confused by somebody deleting the socket in @@ -305,6 +308,8 @@ void log_set_target(LogTarget target) { } void log_close(void) { + /* Do not call from library code. */ + log_close_journal(); log_close_syslog(); log_close_kmsg(); @@ -312,13 +317,16 @@ void log_close(void) { } void log_forget_fds(void) { + /* Do not call from library code. */ + console_fd = kmsg_fd = syslog_fd = journal_fd = -1; } -void log_set_max_level(int level) { +void log_set_max_level_realm(LogRealm realm, int level) { assert((level & LOG_PRIMASK) == level); + assert(realm < ELEMENTSOF(log_max_level)); - log_max_level = level; + log_max_level[realm] = level; } void log_set_facility(int facility) { @@ -553,7 +561,7 @@ static int write_to_journal( return 1; } -static int log_dispatch( +int log_dispatch_internal( int level, int error, const char *file, @@ -636,13 +644,14 @@ static int log_dispatch( } int log_dump_internal( - int level, - int error, - const char *file, - int line, - const char *func, - char *buffer) { + int level, + int error, + const char *file, + int line, + const char *func, + char *buffer) { + LogRealm realm = LOG_REALM_REMOVE_LEVEL(level); PROTECT_ERRNO; /* This modifies the buffer... */ @@ -650,13 +659,13 @@ int log_dump_internal( if (error < 0) error = -error; - if (_likely_(LOG_PRI(level) > log_max_level)) + if (_likely_(LOG_PRI(level) > log_max_level[realm])) return -error; - return log_dispatch(level, error, file, line, func, NULL, NULL, NULL, NULL, buffer); + return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, buffer); } -int log_internalv( +int log_internalv_realm( int level, int error, const char *file, @@ -665,13 +674,14 @@ int log_internalv( const char *format, va_list ap) { - PROTECT_ERRNO; + LogRealm realm = LOG_REALM_REMOVE_LEVEL(level); char buffer[LINE_MAX]; + PROTECT_ERRNO; if (error < 0) error = -error; - if (_likely_(LOG_PRI(level) > log_max_level)) + if (_likely_(LOG_PRI(level) > log_max_level[realm])) return -error; /* Make sure that %m maps to the specified error */ @@ -680,10 +690,10 @@ int log_internalv( vsnprintf(buffer, sizeof(buffer), format, ap); - return log_dispatch(level, error, file, line, func, NULL, NULL, NULL, NULL, buffer); + return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, buffer); } -int log_internal( +int log_internal_realm( int level, int error, const char *file, @@ -695,7 +705,7 @@ int log_internal( int r; va_start(ap, format); - r = log_internalv(level, error, file, line, func, format, ap); + r = log_internalv_realm(level, error, file, line, func, format, ap); va_end(ap); return r; @@ -716,12 +726,11 @@ int log_object_internalv( PROTECT_ERRNO; char *buffer, *b; - size_t l; if (error < 0) error = -error; - if (_likely_(LOG_PRI(level) > log_max_level)) + if (_likely_(LOG_PRI(level) > log_max_level[LOG_REALM_SYSTEMD])) return -error; /* Make sure that %m maps to the specified error */ @@ -733,18 +742,15 @@ int log_object_internalv( size_t n; n = strlen(object); - l = n + 2 + LINE_MAX; - - buffer = newa(char, l); + buffer = newa(char, n + 2 + LINE_MAX); b = stpcpy(stpcpy(buffer, object), ": "); - } else { - l = LINE_MAX; - b = buffer = newa(char, l); - } + } else + b = buffer = newa(char, LINE_MAX); - vsnprintf(b, l, format, ap); + vsnprintf(b, LINE_MAX, format, ap); - return log_dispatch(level, error, file, line, func, object_field, object, extra_field, extra, buffer); + return log_dispatch_internal(level, error, file, line, func, + object_field, object, extra_field, extra, buffer); } int log_object_internal( @@ -778,8 +784,9 @@ static void log_assert( const char *format) { static char buffer[LINE_MAX]; + LogRealm realm = LOG_REALM_REMOVE_LEVEL(level); - if (_likely_(LOG_PRI(level) > log_max_level)) + if (_likely_(LOG_PRI(level) > log_max_level[realm])) return; DISABLE_WARNING_FORMAT_NONLITERAL; @@ -788,26 +795,45 @@ static void log_assert( log_abort_msg = buffer; - log_dispatch(level, 0, file, line, func, NULL, NULL, NULL, NULL, buffer); + log_dispatch_internal(level, 0, file, line, func, NULL, NULL, NULL, NULL, buffer); } -noreturn void log_assert_failed(const char *text, const char *file, int line, const char *func) { - log_assert(LOG_CRIT, text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Aborting."); +noreturn void log_assert_failed_realm( + LogRealm realm, + const char *text, + const char *file, + int line, + const char *func) { + log_assert(LOG_REALM_PLUS_LEVEL(realm, LOG_CRIT), text, file, line, func, + "Assertion '%s' failed at %s:%u, function %s(). Aborting."); abort(); } -noreturn void log_assert_failed_unreachable(const char *text, const char *file, int line, const char *func) { - log_assert(LOG_CRIT, text, file, line, func, "Code should not be reached '%s' at %s:%u, function %s(). Aborting."); +noreturn void log_assert_failed_unreachable_realm( + LogRealm realm, + const char *text, + const char *file, + int line, + const char *func) { + log_assert(LOG_REALM_PLUS_LEVEL(realm, LOG_CRIT), text, file, line, func, + "Code should not be reached '%s' at %s:%u, function %s(). Aborting."); abort(); } -void log_assert_failed_return(const char *text, const char *file, int line, const char *func) { +void log_assert_failed_return_realm( + LogRealm realm, + const char *text, + const char *file, + int line, + const char *func) { PROTECT_ERRNO; - log_assert(LOG_DEBUG, text, file, line, func, "Assertion '%s' failed at %s:%u, function %s(). Ignoring."); + log_assert(LOG_REALM_PLUS_LEVEL(realm, LOG_DEBUG), text, file, line, func, + "Assertion '%s' failed at %s:%u, function %s(). Ignoring."); } -int log_oom_internal(const char *file, int line, const char *func) { - log_internal(LOG_ERR, ENOMEM, file, line, func, "Out of memory."); +int log_oom_internal(LogRealm realm, const char *file, int line, const char *func) { + log_internal_realm(LOG_REALM_PLUS_LEVEL(realm, LOG_ERR), + ENOMEM, file, line, func, "Out of memory."); return -ENOMEM; } @@ -867,13 +893,14 @@ int log_struct_internal( char buf[LINE_MAX]; bool found = false; + LogRealm realm = LOG_REALM_REMOVE_LEVEL(level); PROTECT_ERRNO; va_list ap; if (error < 0) error = -error; - if (_likely_(LOG_PRI(level) > log_max_level)) + if (_likely_(LOG_PRI(level) > log_max_level[realm])) return -error; if (log_target == LOG_TARGET_NULL) @@ -943,7 +970,7 @@ int log_struct_internal( if (!found) return -error; - return log_dispatch(level, error, file, line, func, NULL, NULL, NULL, NULL, buf + 8); + return log_dispatch_internal(level, error, file, line, func, NULL, NULL, NULL, NULL, buf + 8); } int log_set_target_from_string(const char *e) { @@ -957,14 +984,14 @@ int log_set_target_from_string(const char *e) { return 0; } -int log_set_max_level_from_string(const char *e) { +int log_set_max_level_from_string_realm(LogRealm realm, const char *e) { int t; t = log_level_from_string(e); if (t < 0) return -EINVAL; - log_set_max_level(t); + log_set_max_level_realm(realm, t); return 0; } @@ -1012,7 +1039,9 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat return 0; } -void log_parse_environment(void) { +void log_parse_environment_realm(LogRealm realm) { + /* Do not call from library code. */ + const char *e; if (get_ctty_devnr(0, NULL) < 0) @@ -1020,19 +1049,19 @@ void log_parse_environment(void) { user stuff. */ (void) proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX); - e = secure_getenv("SYSTEMD_LOG_TARGET"); + e = getenv("SYSTEMD_LOG_TARGET"); if (e && log_set_target_from_string(e) < 0) log_warning("Failed to parse log target '%s'. Ignoring.", e); - e = secure_getenv("SYSTEMD_LOG_LEVEL"); - if (e && log_set_max_level_from_string(e) < 0) + e = getenv("SYSTEMD_LOG_LEVEL"); + if (e && log_set_max_level_from_string_realm(realm, e) < 0) log_warning("Failed to parse log level '%s'. Ignoring.", e); - e = secure_getenv("SYSTEMD_LOG_COLOR"); + e = getenv("SYSTEMD_LOG_COLOR"); if (e && log_show_color_from_string(e) < 0) log_warning("Failed to parse bool '%s'. Ignoring.", e); - e = secure_getenv("SYSTEMD_LOG_LOCATION"); + e = getenv("SYSTEMD_LOG_LOCATION"); if (e && log_show_location_from_string(e) < 0) log_warning("Failed to parse bool '%s'. Ignoring.", e); } @@ -1041,8 +1070,8 @@ LogTarget log_get_target(void) { return log_target; } -int log_get_max_level(void) { - return log_max_level; +int log_get_max_level_realm(LogRealm realm) { + return log_max_level[realm]; } void log_show_color(bool b) { @@ -1146,7 +1175,7 @@ int log_syntax_internal( if (error < 0) error = -error; - if (_likely_(LOG_PRI(level) > log_max_level)) + if (_likely_(LOG_PRI(level) > log_max_level[LOG_REALM_SYSTEMD])) return -error; if (log_target == LOG_TARGET_NULL) @@ -1163,7 +1192,8 @@ int log_syntax_internal( unit_fmt = getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s"; return log_struct_internal( - level, error, + LOG_REALM_PLUS_LEVEL(LOG_REALM_SYSTEMD, level), + error, file, line, func, "MESSAGE_ID=" SD_MESSAGE_INVALID_CONFIGURATION_STR, "CONFIG_FILE=%s", config_file, diff --git a/src/basic/log.h b/src/basic/log.h index 72714e02e5..ff5d776b1d 100644 --- a/src/basic/log.h +++ b/src/basic/log.h @@ -31,6 +31,16 @@ #include "macro.h" +typedef enum LogRealm { + LOG_REALM_SYSTEMD, + LOG_REALM_UDEV, + _LOG_REALM_MAX, +} LogRealm; + +#ifndef LOG_REALM +# define LOG_REALM LOG_REALM_SYSTEMD +#endif + typedef enum LogTarget{ LOG_TARGET_CONSOLE, LOG_TARGET_CONSOLE_PREFIXED, @@ -44,14 +54,24 @@ typedef enum LogTarget{ LOG_TARGET_NULL, _LOG_TARGET_MAX, _LOG_TARGET_INVALID = -1 -} LogTarget; +} LogTarget; + +#define LOG_REALM_PLUS_LEVEL(realm, level) \ + ((realm) << 10 | (level)) +#define LOG_REALM_REMOVE_LEVEL(realm_level) \ + ((realm_level >> 10)) void log_set_target(LogTarget target); -void log_set_max_level(int level); +void log_set_max_level_realm(LogRealm realm, int level); +#define log_set_max_level(level) \ + log_set_max_level_realm(LOG_REALM, (level)) + void log_set_facility(int facility); int log_set_target_from_string(const char *e); -int log_set_max_level_from_string(const char *e); +int log_set_max_level_from_string_realm(LogRealm realm, const char *e); +#define log_set_max_level_from_string(e) \ + log_set_max_level_from_string_realm(LOG_REALM, (e)) void log_show_color(bool b); bool log_get_show_color(void) _pure_; @@ -62,7 +82,14 @@ int log_show_color_from_string(const char *e); int log_show_location_from_string(const char *e); LogTarget log_get_target(void) _pure_; -int log_get_max_level(void) _pure_; +int log_get_max_level_realm(LogRealm realm) _pure_; +#define log_get_max_level() \ + log_get_max_level_realm(LOG_REALM) + +/* Functions below that open and close logs or configure logging based on the + * environment should not be called from library code — this is always a job + * for the application itself. + */ int log_open(void); void log_close(void); @@ -73,17 +100,33 @@ void log_close_journal(void); void log_close_kmsg(void); void log_close_console(void); -void log_parse_environment(void); +void log_parse_environment_realm(LogRealm realm); +#define log_parse_environment() \ + log_parse_environment_realm(LOG_REALM) -int log_internal( +int log_dispatch_internal( + int level, + int error, + const char *file, + int line, + const char *func, + const char *object_field, + const char *object, + const char *extra, + const char *extra_field, + char *buffer); + +int log_internal_realm( int level, int error, const char *file, int line, const char *func, const char *format, ...) _printf_(6,7); +#define log_internal(level, ...) \ + log_internal_realm(LOG_REALM_PLUS_LEVEL(LOG_REALM, (level)), __VA_ARGS__) -int log_internalv( +int log_internalv_realm( int level, int error, const char *file, @@ -91,7 +134,10 @@ int log_internalv( const char *func, const char *format, va_list ap) _printf_(6,0); +#define log_internalv(level, ...) \ + log_internalv_realm(LOG_REALM_PLUS_LEVEL(LOG_REALM, (level)), __VA_ARGS__) +/* Realm is fixed to LOG_REALM_SYSTEMD for those */ int log_object_internal( int level, int error, @@ -115,7 +161,7 @@ int log_object_internalv( const char *extra_field, const char *extra, const char *format, - va_list ap) _printf_(9,0); + va_list ap) _printf_(10,0); int log_struct_internal( int level, @@ -126,6 +172,7 @@ int log_struct_internal( const char *format, ...) _printf_(6,0) _sentinel_; int log_oom_internal( + LogRealm realm, const char *file, int line, const char *func); @@ -137,7 +184,7 @@ int log_format_iovec( bool newline_separator, int error, const char *format, - va_list ap); + va_list ap) _printf_(6, 0); /* This modifies the buffer passed! */ int log_dump_internal( @@ -149,34 +196,50 @@ int log_dump_internal( char *buffer); /* Logging for various assertions */ -noreturn void log_assert_failed( +noreturn void log_assert_failed_realm( + LogRealm realm, const char *text, const char *file, int line, const char *func); +#define log_assert_failed(text, ...) \ + log_assert_failed_realm(LOG_REALM, (text), __VA_ARGS__) -noreturn void log_assert_failed_unreachable( +noreturn void log_assert_failed_unreachable_realm( + LogRealm realm, const char *text, const char *file, int line, const char *func); +#define log_assert_failed_unreachable(text, ...) \ + log_assert_failed_unreachable_realm(LOG_REALM, (text), __VA_ARGS__) -void log_assert_failed_return( +void log_assert_failed_return_realm( + LogRealm realm, const char *text, const char *file, int line, const char *func); +#define log_assert_failed_return(text, ...) \ + log_assert_failed_return_realm(LOG_REALM, (text), __VA_ARGS__) + +#define log_dispatch(level, error, buffer) \ + log_dispatch_internal(level, error, __FILE__, __LINE__, __func__, NULL, NULL, NULL, NULL, buffer) /* Logging with level */ -#define log_full_errno(level, error, ...) \ +#define log_full_errno_realm(realm, level, error, ...) \ ({ \ int _level = (level), _e = (error); \ - (log_get_max_level() >= LOG_PRI(_level)) \ - ? log_internal(_level, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \ + (log_get_max_level_realm((realm)) >= LOG_PRI(_level)) \ + ? log_internal_realm(LOG_REALM_PLUS_LEVEL((realm), _level), _e, \ + __FILE__, __LINE__, __func__, __VA_ARGS__) \ : -abs(_e); \ }) -#define log_full(level, ...) log_full_errno(level, 0, __VA_ARGS__) +#define log_full_errno(level, error, ...) \ + log_full_errno_realm(LOG_REALM, (level), (error), __VA_ARGS__) + +#define log_full(level, ...) log_full_errno((level), 0, __VA_ARGS__) /* Normal logging */ #define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__) @@ -201,13 +264,17 @@ void log_assert_failed_return( #endif /* Structured logging */ -#define log_struct(level, ...) log_struct_internal(level, 0, __FILE__, __LINE__, __func__, __VA_ARGS__) -#define log_struct_errno(level, error, ...) log_struct_internal(level, error, __FILE__, __LINE__, __func__, __VA_ARGS__) +#define log_struct_errno(level, error, ...) \ + log_struct_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ + error, __FILE__, __LINE__, __func__, __VA_ARGS__) +#define log_struct(level, ...) log_struct_errno(level, 0, __VA_ARGS__) /* This modifies the buffer passed! */ -#define log_dump(level, buffer) log_dump_internal(level, 0, __FILE__, __LINE__, __func__, buffer) +#define log_dump(level, buffer) \ + log_dump_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \ + 0, __FILE__, __LINE__, __func__, buffer) -#define log_oom() log_oom_internal(__FILE__, __LINE__, __func__) +#define log_oom() log_oom_internal(LOG_REALM, __FILE__, __LINE__, __func__) bool log_on_console(void) _pure_; diff --git a/src/basic/macro.h b/src/basic/macro.h index 6b2aeb933f..a51562db35 100644 --- a/src/basic/macro.h +++ b/src/basic/macro.h @@ -19,7 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <assert.h> #include <inttypes.h> #include <stdbool.h> #include <sys/param.h> diff --git a/src/basic/meson.build b/src/basic/meson.build new file mode 100644 index 0000000000..065f0ac4af --- /dev/null +++ b/src/basic/meson.build @@ -0,0 +1,281 @@ +basic_sources_plain = files(''' + af-list.c + af-list.h + alloc-util.c + alloc-util.h + architecture.c + architecture.h + arphrd-list.c + arphrd-list.h + async.c + async.h + audit-util.c + audit-util.h + barrier.c + barrier.h + bitmap.c + bitmap.h + blkid-util.h + btrfs-ctree.h + btrfs-util.c + btrfs-util.h + build.h + bus-label.c + bus-label.h + calendarspec.c + calendarspec.h + capability-util.c + capability-util.h + cap-list.c + cap-list.h + cgroup-util.c + cgroup-util.h + chattr-util.c + chattr-util.h + clock-util.c + clock-util.h + conf-files.c + conf-files.h + copy.c + copy.h + cpu-set-util.c + cpu-set-util.h + def.h + device-nodes.c + device-nodes.h + dirent-util.c + dirent-util.h + env-util.c + env-util.h + errno-list.c + errno-list.h + escape.c + escape.h + ether-addr-util.c + ether-addr-util.h + exec-util.c + exec-util.h + exit-status.c + exit-status.h + extract-word.c + extract-word.h + fd-util.c + fd-util.h + fileio.c + fileio.h + fileio-label.c + fileio-label.h + format-util.h + fs-util.c + fs-util.h + glob-util.c + glob-util.h + gunicode.c + gunicode.h + hash-funcs.c + hash-funcs.h + hashmap.c + hashmap.h + hexdecoct.c + hexdecoct.h + hostname-util.c + hostname-util.h + in-addr-util.c + in-addr-util.h + ioprio.h + io-util.c + io-util.h + journal-importer.c + journal-importer.h + khash.c + khash.h + label.c + label.h + list.h + locale-util.c + locale-util.h + lockfile-util.c + lockfile-util.h + log.c + log.h + login-util.c + login-util.h + macro.h + memfd-util.c + memfd-util.h + mempool.c + mempool.h + missing_syscall.h + mkdir.c + mkdir.h + mkdir-label.c + mount-util.c + mount-util.h + MurmurHash2.c + MurmurHash2.h + nss-util.h + ordered-set.c + ordered-set.h + parse-util.c + parse-util.h + path-util.c + path-util.h + prioq.c + prioq.h + proc-cmdline.c + proc-cmdline.h + process-util.c + process-util.h + random-util.c + random-util.h + ratelimit.c + ratelimit.h + raw-clone.h + refcnt.h + replace-var.c + replace-var.h + rlimit-util.c + rlimit-util.h + rm-rf.c + rm-rf.h + securebits.h + selinux-util.c + selinux-util.h + set.h + sigbus.c + sigbus.h + signal-util.c + signal-util.h + siphash24.c + siphash24.h + smack-util.c + smack-util.h + socket-label.c + socket-util.c + socket-util.h + sparse-endian.h + special.h + stat-util.c + stat-util.h + stdio-util.h + strbuf.c + strbuf.h + string-table.c + string-table.h + string-util.c + string-util.h + strv.c + strv.h + strxcpyx.c + strxcpyx.h + syslog-util.c + syslog-util.h + terminal-util.c + terminal-util.h + time-util.c + time-util.h + umask-util.h + unaligned.h + unit-name.c + unit-name.h + user-util.c + user-util.h + utf8.c + utf8.h + util.c + util.h + verbs.c + verbs.h + virt.c + virt.h + web-util.c + web-util.h + xattr-util.c + xattr-util.h + xml.c + xml.h +'''.split()) + +missing_h = files('missing.h') + +generate_gperfs = find_program('generate-gperfs.py') + +generate_af_list = find_program('generate-af-list.sh') +af_list_txt = custom_target( + 'af-list.txt', + output : 'af-list.txt', + command : [generate_af_list, cpp], + capture : true) + +generate_arphrd_list = find_program('generate-arphrd-list.sh') +arphrd_list_txt = custom_target( + 'arphrd-list.txt', + output : 'arphrd-list.txt', + command : [generate_arphrd_list, cpp], + capture : true) + +generate_cap_list = find_program('generate-cap-list.sh') +cap_list_txt = custom_target( + 'cap-list.txt', + output : 'cap-list.txt', + command : [generate_cap_list, cpp, config_h, missing_h], + capture : true) + +generate_errno_list = find_program('generate-errno-list.sh') +errno_list_txt = custom_target( + 'errno-list.txt', + output : 'errno-list.txt', + command : [generate_errno_list, cpp], + capture : true) + +generated_gperf_headers = [] +foreach item : [['af', af_list_txt, 'af', ''], + ['arphrd', arphrd_list_txt, 'arphrd', 'ARPHRD_'], + ['cap', cap_list_txt, 'capability', ''], + ['errno', errno_list_txt, 'errno', '']] + + fname = '@0@-from-name.gperf'.format(item[0]) + gperf_file = custom_target( + fname, + input : item[1], + output : fname, + command : [generate_gperfs, item[2], item[3], '@INPUT@'], + capture : true) + + fname = '@0@-from-name.h'.format(item[0]) + target1 = custom_target( + fname, + input : gperf_file, + output : fname, + command : [gperf, + '-L', 'ANSI-C', '-t', '--ignore-case', + '-N', 'lookup_@0@'.format(item[2]), + '-H', 'hash_@0@_name'.format(item[2]), + '-p', '-C', + '@INPUT@'], + capture : true) + + fname = '@0@-to-name.h'.format(item[0]) + awkscript = '@0@-to-name.awk'.format(item[0]) + target2 = custom_target( + fname, + input : [awkscript, item[1]], + output : fname, + command : [awk, '-f', '@INPUT0@', '@INPUT1@'], + capture : true) + + generated_gperf_headers += [target1, target2] +endforeach + +basic_sources = basic_sources_plain + [missing_h] + generated_gperf_headers + +libbasic = static_library( + 'basic', + basic_sources, + include_directories : includes, + dependencies : [threads, + libcap, + libblkid, + libselinux, + ], + install : false) diff --git a/src/basic/missing.h b/src/basic/missing.h index 480462357d..7830a4f415 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -23,6 +23,7 @@ #include <errno.h> #include <fcntl.h> +#include <inttypes.h> #include <linux/audit.h> #include <linux/capability.h> #include <linux/if_link.h> @@ -68,8 +69,6 @@ struct sockaddr_vm { }; #endif /* !HAVE_LINUX_VM_SOCKETS_H */ -#include "macro.h" - #ifndef RLIMIT_RTTIME #define RLIMIT_RTTIME 15 #endif @@ -571,6 +570,17 @@ struct btrfs_ioctl_quota_ctl_args { # define EVIOCREVOKE _IOW('E', 0x91, int) #endif +#ifndef EVIOCSMASK + +struct input_mask { + uint32_t type; + uint32_t codes_size; + uint64_t codes_ptr; +}; + +#define EVIOCSMASK _IOW('E', 0x93, struct input_mask) +#endif + #ifndef DRM_IOCTL_SET_MASTER # define DRM_IOCTL_SET_MASTER _IO('d', 0x1e) #endif @@ -726,7 +736,7 @@ struct btrfs_ioctl_quota_ctl_args { #define IFLA_VLAN_MAX (__IFLA_VLAN_MAX - 1) #endif -#if !HAVE_DECL_IFLA_VXLAN_REMCSUM_NOPARTIAL +#if !HAVE_DECL_IFLA_VXLAN_GPE #define IFLA_VXLAN_UNSPEC 0 #define IFLA_VXLAN_ID 1 #define IFLA_VXLAN_GROUP 2 @@ -752,11 +762,34 @@ struct btrfs_ioctl_quota_ctl_args { #define IFLA_VXLAN_REMCSUM_RX 22 #define IFLA_VXLAN_GBP 23 #define IFLA_VXLAN_REMCSUM_NOPARTIAL 24 -#define __IFLA_VXLAN_MAX 25 +#define IFLA_VXLAN_COLLECT_METADATA 25 +#define IFLA_VXLAN_LABEL 26 +#define IFLA_VXLAN_GPE 27 + +#define __IFLA_VXLAN_MAX 28 #define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1) #endif +#if !HAVE_DECL_IFLA_GENEVE_LABEL +#define IFLA_GENEVE_UNSPEC 0 +#define IFLA_GENEVE_ID 1 +#define IFLA_GENEVE_REMOTE 2 +#define IFLA_GENEVE_TTL 3 +#define IFLA_GENEVE_TOS 4 +#define IFLA_GENEVE_PORT 5 +#define IFLA_GENEVE_COLLECT_METADATA 6 +#define IFLA_GENEVE_REMOTE6 7 +#define IFLA_GENEVE_UDP_CSUM 8 +#define IFLA_GENEVE_UDP_ZERO_CSUM6_TX 9 +#define IFLA_GENEVE_UDP_ZERO_CSUM6_RX 10 +#define IFLA_GENEVE_LABEL 11 + +#define __IFLA_GENEVE_MAX 12 + +#define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1) +#endif + #if !HAVE_DECL_IFLA_IPTUN_ENCAP_DPORT #define IFLA_IPTUN_UNSPEC 0 #define IFLA_IPTUN_LINK 1 @@ -1040,6 +1073,15 @@ struct btrfs_ioctl_quota_ctl_args { #define INPUT_PROP_ACCELEROMETER 0x06 #endif +#ifndef BTN_DPAD_UP +#define BTN_DPAD_UP 0x220 +#define BTN_DPAD_RIGHT 0x223 +#endif + +#ifndef KEY_ALS_TOGGLE +#define KEY_ALS_TOGGLE 0x230 +#endif + #ifndef HAVE_KEY_SERIAL_T typedef int32_t key_serial_t; #endif diff --git a/src/basic/missing_syscall.h b/src/basic/missing_syscall.h index 9fc4156564..898116c7b3 100644 --- a/src/basic/missing_syscall.h +++ b/src/basic/missing_syscall.h @@ -52,6 +52,8 @@ static inline int pivot_root(const char *new_root, const char *put_old) { # endif # elif defined __i386__ # define __NR_memfd_create 356 +# elif defined __arc__ +# define __NR_memfd_create 279 # else # warning "__NR_memfd_create unknown for your architecture" # endif @@ -97,6 +99,8 @@ static inline int memfd_create(const char *name, unsigned int flags) { # if _MIPS_SIM == _MIPS_SIM_ABI64 # define __NR_getrandom 5313 # endif +# elif defined(__arc__) +# define __NR_getrandom 278 # else # warning "__NR_getrandom unknown for your architecture" # endif @@ -132,6 +136,8 @@ static inline pid_t gettid(void) { # define __NR_name_to_handle_at 370 # elif defined(__powerpc__) # define __NR_name_to_handle_at 345 +# elif defined(__arc__) +# define __NR_name_to_handle_at 264 # else # error "__NR_name_to_handle_at is not defined" # endif @@ -161,6 +167,8 @@ static inline int name_to_handle_at(int fd, const char *name, struct file_handle # define __NR_setns 308 # elif defined(__i386__) # define __NR_setns 346 +# elif defined(__arc__) +# define __NR_setns 268 # else # error "__NR_setns is not defined" # endif @@ -208,6 +216,12 @@ static inline pid_t raw_getpid(void) { # endif # elif defined __i386__ # define __NR_renameat2 353 +# elif defined __powerpc64__ +# define __NR_renameat2 357 +# elif defined __s390__ || defined __s390x__ +# define __NR_renameat2 347 +# elif defined __arc__ +# define __NR_renameat2 276 # else # warning "__NR_renameat2 unknown for your architecture" # endif @@ -283,6 +297,8 @@ static inline key_serial_t request_key(const char *type, const char *description # define __NR_copy_file_range 285 # elif defined __powerpc__ # define __NR_copy_file_range 379 +# elif defined __arc__ +# define __NR_copy_file_range 285 # else # warning "__NR_copy_file_range not defined for your architecture" # endif diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c index a8fd63fb45..7b9400b47c 100644 --- a/src/basic/mount-util.c +++ b/src/basic/mount-util.c @@ -317,11 +317,16 @@ static int get_mount_flags(const char *path, unsigned long *flags) { return 0; } -int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) { +/* Use this function only if do you have direct access to /proc/self/mountinfo + * and need the caller to open it for you. This is the case when /proc is + * masked or not mounted. Otherwise, use bind_remount_recursive. */ +int bind_remount_recursive_with_mountinfo(const char *prefix, bool ro, char **blacklist, FILE *proc_self_mountinfo) { _cleanup_set_free_free_ Set *done = NULL; _cleanup_free_ char *cleaned = NULL; int r; + assert(proc_self_mountinfo); + /* Recursively remount a directory (and all its submounts) read-only or read-write. If the directory is already * mounted, we reuse the mount and simply mark it MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write * operation). If it isn't we first make it one. Afterwards we apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to @@ -344,7 +349,6 @@ int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) { return -ENOMEM; for (;;) { - _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; _cleanup_set_free_free_ Set *todo = NULL; bool top_autofs = false; char *x; @@ -354,9 +358,7 @@ int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) { if (!todo) return -ENOMEM; - proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); - if (!proc_self_mountinfo) - return -errno; + rewind(proc_self_mountinfo); for (;;) { _cleanup_free_ char *path = NULL, *p = NULL, *type = NULL; @@ -495,6 +497,16 @@ int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) { } } +int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) { + _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; + + proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); + if (!proc_self_mountinfo) + return -errno; + + return bind_remount_recursive_with_mountinfo(prefix, ro, blacklist, proc_self_mountinfo); +} + int mount_move_root(const char *path) { assert(path); diff --git a/src/basic/mount-util.h b/src/basic/mount-util.h index 1615c94e9a..2e24a184c5 100644 --- a/src/basic/mount-util.h +++ b/src/basic/mount-util.h @@ -36,6 +36,7 @@ int repeat_unmount(const char *path, int flags); int umount_recursive(const char *target, int flags); int bind_remount_recursive(const char *prefix, bool ro, char **blacklist); +int bind_remount_recursive_with_mountinfo(const char *prefix, bool ro, char **blacklist, FILE *proc_self_mountinfo); int mount_move_root(const char *path); diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 6e58ced6f5..4532f222c8 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -23,7 +23,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <xlocale.h> #include "alloc-util.h" #include "extract-word.h" @@ -590,3 +589,18 @@ int parse_ip_port(const char *s, uint16_t *ret) { return 0; } + +int parse_dev(const char *s, dev_t *ret) { + unsigned x, y; + dev_t d; + + if (sscanf(s, "%u:%u", &x, &y) != 2) + return -EINVAL; + + d = makedev(x, y); + if ((unsigned) major(d) != x || (unsigned) minor(d) != y) + return -EINVAL; + + *ret = d; + return 0; +} diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h index 4d132f0de5..dc09782ca8 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -30,6 +30,7 @@ #define MODE_INVALID ((mode_t) -1) int parse_boolean(const char *v) _pure_; +int parse_dev(const char *s, dev_t *ret); int parse_pid(const char *s, pid_t* ret_pid); int parse_mode(const char *s, mode_t *ret); int parse_ifindex(const char *s, int *ret); diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 1313a52c9c..80fdda170f 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -442,8 +442,8 @@ bool path_equal(const char *a, const char *b) { return path_compare(a, b) == 0; } -bool path_equal_or_files_same(const char *a, const char *b) { - return path_equal(a, b) || files_same(a, b) > 0; +bool path_equal_or_files_same(const char *a, const char *b, int flags) { + return path_equal(a, b) || files_same(a, b, flags) > 0; } char* path_join(const char *root, const char *path, const char *rest) { diff --git a/src/basic/path-util.h b/src/basic/path-util.h index 35aef3adc8..26f165fc38 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -46,7 +46,7 @@ char* path_kill_slashes(char *path); char* path_startswith(const char *path, const char *prefix) _pure_; int path_compare(const char *a, const char *b) _pure_; bool path_equal(const char *a, const char *b) _pure_; -bool path_equal_or_files_same(const char *a, const char *b); +bool path_equal_or_files_same(const char *a, const char *b, int flags); char* path_join(const char *root, const char *path, const char *rest); static inline bool path_equal_ptr(const char *a, const char *b) { diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 0df3fed640..b80cacaa42 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -807,7 +807,7 @@ int pid_from_same_root_fs(pid_t pid) { root = procfs_file_alloca(pid, "root"); - return files_same(root, "/proc/1/root"); + return files_same(root, "/proc/1/root", 0); } bool is_main_thread(void) { @@ -905,6 +905,23 @@ int pid_compare_func(const void *a, const void *b) { return 0; } +int ioprio_parse_priority(const char *s, int *ret) { + int i, r; + + assert(s); + assert(ret); + + r = safe_atoi(s, &i); + if (r < 0) + return r; + + if (!ioprio_priority_is_valid(i)) + return -EINVAL; + + *ret = i; + return 0; +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/src/basic/process-util.h b/src/basic/process-util.h index d378901399..28d8d7499a 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -25,10 +25,11 @@ #include <stddef.h> #include <stdio.h> #include <string.h> -#include <sys/types.h> #include <sys/resource.h> +#include <sys/types.h> #include "format-util.h" +#include "ioprio.h" #include "macro.h" #define procfs_file_alloca(pid, field) \ @@ -108,3 +109,13 @@ int pid_compare_func(const void *a, const void *b); static inline bool nice_is_valid(int n) { return n >= PRIO_MIN && n < PRIO_MAX; } + +static inline bool ioprio_class_is_valid(int i) { + return IN_SET(i, IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE); +} + +static inline bool ioprio_priority_is_valid(int i) { + return i >= 0 && i < IOPRIO_BE_NR; +} + +int ioprio_parse_priority(const char *s, int *ret); diff --git a/src/basic/random-util.c b/src/basic/random-util.c index ad7b3eedf2..810eeab4d5 100644 --- a/src/basic/random-util.c +++ b/src/basic/random-util.c @@ -27,7 +27,13 @@ #include <stdint.h> #ifdef HAVE_SYS_AUXV_H -#include <sys/auxv.h> +# include <sys/auxv.h> +#endif + +#ifdef USE_SYS_RANDOM_H +# include <sys/random.h> +#else +# include <linux/random.h> #endif #include "fd-util.h" @@ -36,53 +42,59 @@ #include "random-util.h" #include "time-util.h" -int dev_urandom(void *p, size_t n) { +int acquire_random_bytes(void *p, size_t n, bool high_quality_required) { static int have_syscall = -1; _cleanup_close_ int fd = -1; + unsigned already_done = 0; int r; - /* Gathers some randomness from the kernel. This call will - * never block, and will always return some data from the - * kernel, regardless if the random pool is fully initialized - * or not. It thus makes no guarantee for the quality of the - * returned entropy, but is good enough for our usual usecases - * of seeding the hash functions for hashtable */ + /* Gathers some randomness from the kernel. This call will never block. If + * high_quality_required, it will always return some data from the kernel, + * regardless of whether the random pool is fully initialized or not. + * Otherwise, it will return success if at least some random bytes were + * successfully acquired, and an error if the kernel has no entropy whatsover + * for us. */ - /* Use the getrandom() syscall unless we know we don't have - * it, or when the requested size is too large for it. */ - if (have_syscall != 0 || (size_t) (int) n != n) { + /* Use the getrandom() syscall unless we know we don't have it. */ + if (have_syscall != 0) { r = getrandom(p, n, GRND_NONBLOCK); - if (r == (int) n) { + if (r > 0) { + have_syscall = true; + if ((size_t) r == n) + return 0; + if (!high_quality_required) { + /* Fill in the remaing bytes using pseudorandom values */ + pseudorandom_bytes((uint8_t*) p + r, n - r); + return 0; + } + + already_done = r; + } else if (errno == ENOSYS) + /* We lack the syscall, continue with reading from /dev/urandom. */ + have_syscall = false; + else if (errno == EAGAIN) { + /* The kernel has no entropy whatsoever. Let's remember to + * use the syscall the next time again though. + * + * If high_quality_required is false, return an error so that + * random_bytes() can produce some pseudorandom + * bytes. Otherwise, fall back to /dev/urandom, which we know + * is empty, but the kernel will produce some bytes for us on + * a best-effort basis. */ have_syscall = true; - return 0; - } - - if (r < 0) { - if (errno == ENOSYS) - /* we lack the syscall, continue with - * reading from /dev/urandom */ - have_syscall = false; - else if (errno == EAGAIN) - /* not enough entropy for now. Let's - * remember to use the syscall the - * next time, again, but also read - * from /dev/urandom for now, which - * doesn't care about the current - * amount of entropy. */ - have_syscall = true; - else - return -errno; + + if (!high_quality_required) + return -ENODATA; } else - /* too short read? */ - return -ENODATA; + return -errno; } fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); if (fd < 0) return errno == ENOENT ? -ENOSYS : -errno; - return loop_read_exact(fd, p, n, true); + return loop_read_exact(fd, (uint8_t*) p + already_done, n - already_done, true); } void initialize_srand(void) { @@ -96,12 +108,13 @@ void initialize_srand(void) { return; #ifdef HAVE_SYS_AUXV_H - /* The kernel provides us with 16 bytes of entropy in auxv, so let's try to make use of that to seed the - * pseudo-random generator. It's better than nothing... */ + /* The kernel provides us with 16 bytes of entropy in auxv, so let's + * try to make use of that to seed the pseudo-random generator. It's + * better than nothing... */ auxv = (void*) getauxval(AT_RANDOM); if (auxv) { - assert_cc(sizeof(x) < 16); + assert_cc(sizeof(x) <= 16); memcpy(&x, auxv, sizeof(x)); } else #endif @@ -115,19 +128,44 @@ void initialize_srand(void) { srand_called = true; } -void random_bytes(void *p, size_t n) { +/* INT_MAX gives us only 31 bits, so use 24 out of that. */ +#if RAND_MAX >= INT_MAX +# define RAND_STEP 3 +#else +/* SHORT_INT_MAX or lower gives at most 15 bits, we just just 8 out of that. */ +# define RAND_STEP 1 +#endif + +void pseudorandom_bytes(void *p, size_t n) { uint8_t *q; + + initialize_srand(); + + for (q = p; q < (uint8_t*) p + n; q += RAND_STEP) { + unsigned rr; + + rr = (unsigned) rand(); + +#if RAND_STEP >= 3 + if ((size_t) (q - (uint8_t*) p + 2) < n) + q[2] = rr >> 16; +#endif +#if RAND_STEP >= 2 + if ((size_t) (q - (uint8_t*) p + 1) < n) + q[1] = rr >> 8; +#endif + q[0] = rr; + } +} + +void random_bytes(void *p, size_t n) { int r; - r = dev_urandom(p, n); + r = acquire_random_bytes(p, n, false); if (r >= 0) return; - /* If some idiot made /dev/urandom unavailable to us, he'll - * get a PRNG instead. */ - - initialize_srand(); - - for (q = p; q < (uint8_t*) p + n; q ++) - *q = rand(); + /* If some idiot made /dev/urandom unavailable to us, or the + * kernel has no entropy, use a PRNG instead. */ + return pseudorandom_bytes(p, n); } diff --git a/src/basic/random-util.h b/src/basic/random-util.h index 3cee4c5014..804e225fc1 100644 --- a/src/basic/random-util.h +++ b/src/basic/random-util.h @@ -19,10 +19,12 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <stdbool.h> #include <stddef.h> #include <stdint.h> -int dev_urandom(void *p, size_t n); +int acquire_random_bytes(void *p, size_t n, bool high_quality_required); +void pseudorandom_bytes(void *p, size_t n); void random_bytes(void *p, size_t n); void initialize_srand(void); diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c index 08497af729..3f80ed263a 100644 --- a/src/basic/rm-rf.c +++ b/src/basic/rm-rf.c @@ -182,7 +182,7 @@ int rm_rf(const char *path, RemoveFlags flags) { /* We refuse to clean the root file system with this * call. This is extra paranoia to never cause a really * seriously broken system. */ - if (path_equal(path, "/")) { + if (path_equal_or_files_same(path, "/", AT_SYMLINK_NOFOLLOW)) { log_error("Attempted to remove entire root file system, and we can't allow that."); return -EPERM; } diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c index bc07654668..139e6e21e3 100644 --- a/src/basic/selinux-util.c +++ b/src/basic/selinux-util.c @@ -50,10 +50,10 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(context_t, context_free); static int cached_use = -1; static struct selabel_handle *label_hnd = NULL; -#define log_enforcing(...) log_full(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG, __VA_ARGS__) +#define log_enforcing(...) log_full_errno(security_getenforce() == 1 ? LOG_ERR : LOG_DEBUG, errno, __VA_ARGS__) #endif -bool mac_selinux_have(void) { +bool mac_selinux_use(void) { #ifdef HAVE_SELINUX if (cached_use < 0) cached_use = is_selinux_enabled() > 0; @@ -64,16 +64,6 @@ bool mac_selinux_have(void) { #endif } -bool mac_selinux_use(void) { - if (!mac_selinux_have()) - return false; - - /* Never try to configure SELinux features if we aren't - * root */ - - return getuid() == 0; -} - void mac_selinux_retest(void) { #ifdef HAVE_SELINUX cached_use = -1; @@ -205,7 +195,7 @@ int mac_selinux_get_create_label_from_exe(const char *exe, char **label) { assert(exe); assert(label); - if (!mac_selinux_have()) + if (!mac_selinux_use()) return -EOPNOTSUPP; r = getcon_raw(&mycon); @@ -231,7 +221,7 @@ int mac_selinux_get_our_label(char **label) { assert(label); #ifdef HAVE_SELINUX - if (!mac_selinux_have()) + if (!mac_selinux_use()) return -EOPNOTSUPP; r = getcon_raw(label); @@ -255,7 +245,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char * assert(exe); assert(label); - if (!mac_selinux_have()) + if (!mac_selinux_use()) return -EOPNOTSUPP; r = getcon_raw(&mycon); @@ -310,7 +300,7 @@ char* mac_selinux_free(char *label) { if (!label) return NULL; - if (!mac_selinux_have()) + if (!mac_selinux_use()) return NULL; diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h index ce6bc8e44c..5bf72364b4 100644 --- a/src/basic/selinux-util.h +++ b/src/basic/selinux-util.h @@ -26,7 +26,6 @@ #include "macro.h" bool mac_selinux_use(void); -bool mac_selinux_have(void); void mac_selinux_retest(void); int mac_selinux_init(void); diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index e5847dce00..016e64aa03 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -48,6 +48,12 @@ #include "utf8.h" #include "util.h" +#ifdef ENABLE_IDN +# define IDN_FLAGS (NI_IDN|NI_IDN_USE_STD3_ASCII_RULES) +#else +# define IDN_FLAGS 0 +#endif + int socket_address_parse(SocketAddress *a, const char *s) { char *e, *n; unsigned u; @@ -406,7 +412,7 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) { return false; if (a->sockaddr.un.sun_path[0]) { - if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path)) + if (!path_equal_or_files_same(a->sockaddr.un.sun_path, b->sockaddr.un.sun_path, 0)) return false; } else { if (a->size != b->size) @@ -723,8 +729,7 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) assert(_ret); - r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0, - NI_IDN|NI_IDN_USE_STD3_ASCII_RULES); + r = getnameinfo(&sa->sa, salen, host, sizeof(host), NULL, 0, IDN_FLAGS); if (r != 0) { int saved_errno = errno; diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 7e1914aa14..d87370e672 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -169,16 +169,16 @@ int path_is_os_tree(const char *path) { return 1; } -int files_same(const char *filea, const char *fileb) { +int files_same(const char *filea, const char *fileb, int flags) { struct stat a, b; assert(filea); assert(fileb); - if (stat(filea, &a) < 0) + if (fstatat(AT_FDCWD, filea, &a, flags) < 0) return -errno; - if (stat(fileb, &b) < 0) + if (fstatat(AT_FDCWD, fileb, &b, flags) < 0) return -errno; return a.st_dev == b.st_dev && diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h index 5d571efe18..cd204ac6cb 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -49,7 +49,7 @@ int null_or_empty_fd(int fd); int path_is_read_only_fs(const char *path); int path_is_os_tree(const char *path); -int files_same(const char *filea, const char *fileb); +int files_same(const char *filea, const char *fileb, int flags); /* The .f_type field of struct statfs is really weird defined on * different archs. Let's give its type a name. */ diff --git a/src/basic/strv.c b/src/basic/strv.c index 0eec868eed..c63f11c6ad 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -770,11 +770,7 @@ static int str_compare(const void *_a, const void *_b) { } char **strv_sort(char **l) { - - if (strv_isempty(l)) - return l; - - qsort(l, strv_length(l), sizeof(char*), str_compare); + qsort_safe(l, strv_length(l), sizeof(char*), str_compare); return l; } diff --git a/src/basic/strxcpyx.c b/src/basic/strxcpyx.c index aaf11d21f6..c6fbe79647 100644 --- a/src/basic/strxcpyx.c +++ b/src/basic/strxcpyx.c @@ -19,8 +19,13 @@ /* * Concatenates/copies strings. In any case, terminates in all cases - * with '\0' * and moves the @dest pointer forward to the added '\0'. - * Returns the * remaining size, and 0 if the string was truncated. + * with '\0' and moves the @dest pointer forward to the added '\0'. + * Returns the remaining size, and 0 if the string was truncated. + * + * Due to the intended usage, these helpers silently noop invocations + * having zero size. This is technically an exception to the above + * statement "terminates in all cases". It's unexpected for such calls to + * occur outside of a loop where this is the preferred behavior. */ #include <stdarg.h> @@ -32,6 +37,12 @@ size_t strpcpy(char **dest, size_t size, const char *src) { size_t len; + assert(dest); + assert(src); + + if (size == 0) + return 0; + len = strlen(src); if (len >= size) { if (size > 1) @@ -51,23 +62,30 @@ size_t strpcpyf(char **dest, size_t size, const char *src, ...) { va_list va; int i; + assert(dest); + assert(src); + + if (size == 0) + return 0; + va_start(va, src); i = vsnprintf(*dest, size, src, va); if (i < (int)size) { *dest += i; size -= i; } else { - *dest += size; size = 0; } va_end(va); - *dest[0] = '\0'; return size; } size_t strpcpyl(char **dest, size_t size, const char *src, ...) { va_list va; + assert(dest); + assert(src); + va_start(va, src); do { size = strpcpy(dest, size, src); @@ -80,6 +98,9 @@ size_t strpcpyl(char **dest, size_t size, const char *src, ...) { size_t strscpy(char *dest, size_t size, const char *src) { char *s; + assert(dest); + assert(src); + s = dest; return strpcpy(&s, size, src); } @@ -88,6 +109,9 @@ size_t strscpyl(char *dest, size_t size, const char *src, ...) { va_list va; char *s; + assert(dest); + assert(src); + va_start(va, src); s = dest; do { diff --git a/src/basic/time-util.c b/src/basic/time-util.c index a0db97c41a..68ba86f6a5 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -107,7 +107,7 @@ dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) { ts->realtime = u; delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u; - ts->monotonic = usec_sub(now(CLOCK_MONOTONIC), delta); + ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta); return ts; } @@ -124,8 +124,8 @@ triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) ts->realtime = u; delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u; - ts->monotonic = usec_sub(now(CLOCK_MONOTONIC), delta); - ts->boottime = clock_boottime_supported() ? usec_sub(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY; + ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta); + ts->boottime = clock_boottime_supported() ? usec_sub_signed(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY; return ts; } @@ -141,7 +141,7 @@ dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { ts->monotonic = u; delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u; - ts->realtime = usec_sub(now(CLOCK_REALTIME), delta); + ts->realtime = usec_sub_signed(now(CLOCK_REALTIME), delta); return ts; } @@ -156,8 +156,8 @@ dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, us dual_timestamp_get(ts); delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u; - ts->realtime = usec_sub(ts->realtime, delta); - ts->monotonic = usec_sub(ts->monotonic, delta); + ts->realtime = usec_sub_signed(ts->realtime, delta); + ts->monotonic = usec_sub_signed(ts->monotonic, delta); return ts; } @@ -241,7 +241,7 @@ 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; @@ -555,15 +555,29 @@ void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) { int dual_timestamp_deserialize(const char *value, dual_timestamp *t) { uint64_t a, b; + int r, pos; assert(value); assert(t); - if (sscanf(value, "%" PRIu64 "%" PRIu64, &a, &b) != 2) { - log_debug("Failed to parse dual timestamp value \"%s\": %m", value); + pos = strspn(value, WHITESPACE); + if (value[pos] == '-') + return -EINVAL; + pos += strspn(value + pos, DIGITS); + pos += strspn(value + pos, WHITESPACE); + if (value[pos] == '-') + return -EINVAL; + + r = sscanf(value, "%" PRIu64 "%" PRIu64 "%n", &a, &b, &pos); + if (r != 2) { + log_debug("Failed to parse dual timestamp value \"%s\".", value); return -EINVAL; } + if (value[pos] != '\0') + /* trailing garbage */ + return -EINVAL; + t->realtime = a; t->monotonic = b; @@ -850,10 +864,10 @@ finish: if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX) return -EINVAL; - if (ret > minus) + if (ret >= minus) ret -= minus; else - ret = 0; + return -EINVAL; *usec = ret; @@ -995,6 +1009,16 @@ int parse_sec(const char *t, usec_t *usec) { return parse_time(t, usec, USEC_PER_SEC); } +int parse_sec_fix_0(const char *t, usec_t *usec) { + t += strspn(t, WHITESPACE); + if (streq(t, "0")) { + *usec = USEC_INFINITY; + return 0; + } + + return parse_sec(t, usec); +} + int parse_nsec(const char *t, nsec_t *nsec) { static const struct { const char *suffix; @@ -1337,3 +1361,22 @@ unsigned long usec_to_jiffies(usec_t u) { return DIV_ROUND_UP(u , USEC_PER_SEC / hz); } + +usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) { + usec_t a, b; + + if (x == USEC_INFINITY) + return USEC_INFINITY; + if (map_clock_id(from) == map_clock_id(to)) + return x; + + a = now(from); + b = now(to); + + if (x > a) + /* x lies in the future */ + return usec_add(b, usec_sub_unsigned(x, a)); + else + /* x lies in the past */ + return usec_sub_unsigned(b, usec_sub_unsigned(a, x)); +} diff --git a/src/basic/time-util.h b/src/basic/time-util.h index 7463507f51..3b7f0e99c0 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -133,6 +133,7 @@ int timestamp_deserialize(const char *value, usec_t *timestamp); int parse_timestamp(const char *t, usec_t *usec); int parse_sec(const char *t, usec_t *usec); +int parse_sec_fix_0(const char *t, usec_t *usec); int parse_time(const char *t, usec_t *usec, usec_t default_unit); int parse_nsec(const char *t, nsec_t *nsec); @@ -145,6 +146,8 @@ bool clock_boottime_supported(void); bool clock_supported(clockid_t clock); clockid_t clock_boottime_or_monotonic(void); +usec_t usec_shift_clock(usec_t, clockid_t from, clockid_t to); + #define xstrftime(buf, fmt, tm) \ assert_message_se(strftime(buf, ELEMENTSOF(buf), fmt, tm) > 0, \ "xstrftime: " #buf "[] must be big enough") @@ -169,19 +172,23 @@ static inline usec_t usec_add(usec_t a, usec_t b) { return c; } -static inline usec_t usec_sub(usec_t timestamp, int64_t delta) { - if (delta < 0) - return usec_add(timestamp, (usec_t) (-delta)); +static inline usec_t usec_sub_unsigned(usec_t timestamp, usec_t delta) { if (timestamp == USEC_INFINITY) /* Make sure infinity doesn't degrade */ return USEC_INFINITY; - - if (timestamp < (usec_t) delta) + if (timestamp < delta) return 0; return timestamp - delta; } +static inline usec_t usec_sub_signed(usec_t timestamp, int64_t delta) { + if (delta < 0) + return usec_add(timestamp, (usec_t) (-delta)); + else + return usec_sub_unsigned(timestamp, (usec_t) 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. */ diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c index 0a6efa449a..920ca0d9f5 100644 --- a/src/basic/unit-name.c +++ b/src/basic/unit-name.c @@ -1047,3 +1047,12 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency); + +static const char* const notify_access_table[_NOTIFY_ACCESS_MAX] = { + [NOTIFY_NONE] = "none", + [NOTIFY_MAIN] = "main", + [NOTIFY_EXEC] = "exec", + [NOTIFY_ALL] = "all" +}; + +DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess); diff --git a/src/basic/unit-name.h b/src/basic/unit-name.h index 44eadf0347..0f164a6aa9 100644 --- a/src/basic/unit-name.h +++ b/src/basic/unit-name.h @@ -257,6 +257,15 @@ typedef enum UnitDependency { _UNIT_DEPENDENCY_INVALID = -1 } UnitDependency; +typedef enum NotifyAccess { + NOTIFY_NONE, + NOTIFY_ALL, + NOTIFY_MAIN, + NOTIFY_EXEC, + _NOTIFY_ACCESS_MAX, + _NOTIFY_ACCESS_INVALID = -1 +} NotifyAccess; + typedef enum UnitNameFlags { UNIT_NAME_PLAIN = 1, /* Allow foo.service */ UNIT_NAME_INSTANCE = 2, /* Allow foo@bar.service */ @@ -365,3 +374,6 @@ TimerState timer_state_from_string(const char *s) _pure_; const char *unit_dependency_to_string(UnitDependency i) _const_; UnitDependency unit_dependency_from_string(const char *s) _pure_; + +const char* notify_access_to_string(NotifyAccess i) _const_; +NotifyAccess notify_access_from_string(const char *s) _pure_; diff --git a/src/basic/util.c b/src/basic/util.c index 3dce0ea92e..b52a5db31b 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -541,7 +541,7 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0) return -ENOMEM; - r = files_same(userns_fd_path, "/proc/self/ns/user"); + r = files_same(userns_fd_path, "/proc/self/ns/user", 0); if (r < 0) return r; if (r) diff --git a/src/basic/virt.c b/src/basic/virt.c index ff4491d6d6..6011744523 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -574,7 +574,7 @@ int running_in_chroot(void) { if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0) return 0; - ret = files_same("/proc/1/root", "/"); + ret = files_same("/proc/1/root", "/", 0); if (ret < 0) return ret; diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 155bf278b2..233bc80292 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -18,8 +18,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <assert.h> -#include <blkid/blkid.h> +#include <blkid.h> #include <ctype.h> #include <dirent.h> #include <errno.h> @@ -74,17 +73,23 @@ static int verify_esp( struct statfs sfs; sd_id128_t uuid = SD_ID128_NULL; uint32_t part = 0; + bool quiet; int r; assert(p); + /* Non-root user can run only `bootctl status`, then if error occured in the following, it does not cause any issues. + * So, let's silence the error messages. */ + quiet = (geteuid() != 0); + if (statfs(p, &sfs) < 0) { /* If we are searching for the mount point, don't generate a log message if we can't find the path */ if (errno == ENOENT && searching) return -ENOENT; - return log_error_errno(errno, "Failed to check file system type of \"%s\": %m", p); + return log_full_errno(quiet && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno, + "Failed to check file system type of \"%s\": %m", p); } if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC)) { @@ -97,7 +102,8 @@ static int verify_esp( } if (stat(p, &st) < 0) - return log_error_errno(errno, "Failed to determine block device node of \"%s\": %m", p); + return log_full_errno(quiet && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno, + "Failed to determine block device node of \"%s\": %m", p); if (major(st.st_dev) == 0) { log_error("Block device node of %p is invalid.", p); @@ -107,7 +113,8 @@ static int verify_esp( t2 = strjoina(p, "/.."); r = stat(t2, &st2); if (r < 0) - return log_error_errno(errno, "Failed to determine block device node of parent of \"%s\": %m", p); + return log_full_errno(quiet && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno, + "Failed to determine block device node of parent of \"%s\": %m", p); if (st.st_dev == st2.st_dev) { log_error("Directory \"%s\" is not the root of the EFI System Partition (ESP) file system.", p); @@ -115,8 +122,8 @@ static int verify_esp( } /* In a container we don't have access to block devices, skip this part of the verification, we trust the - * container manager set everything up correctly on its own. */ - if (detect_container() > 0) + * container manager set everything up correctly on its own. Also skip the following verification for non-root user. */ + if (detect_container() > 0 || geteuid() != 0) goto finish; r = asprintf(&t, "/dev/block/%u:%u", major(st.st_dev), minor(st.st_dev)); @@ -340,7 +347,15 @@ static int status_binaries(const char *esp_path, sd_id128_t partition) { printf("Boot Loader Binaries:\n"); - printf(" ESP: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", SD_ID128_FORMAT_VAL(partition)); + if (!esp_path) { + printf(" ESP: Cannot find or access mount point of ESP.\n\n"); + return -ENOENT; + } + + printf(" ESP: %s", esp_path); + if (!sd_id128_is_null(partition)) + printf(" (/dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x)", SD_ID128_FORMAT_VAL(partition)); + printf("\n"); r = enumerate_binaries(esp_path, "EFI/systemd", NULL); if (r == 0) @@ -391,11 +406,6 @@ static int status_variables(void) { _cleanup_free_ uint16_t *options = NULL, *order = NULL; int i; - if (!is_efi_boot()) { - log_notice("Not booted with EFI, not showing EFI variables."); - return 0; - } - n_options = efi_get_boot_options(&options); if (n_options == -ENOENT) return log_error_errno(n_options, @@ -1027,15 +1037,9 @@ static int must_be_root(void) { static int verb_status(int argc, char *argv[], void *userdata) { sd_id128_t uuid = SD_ID128_NULL; - int r; + int r, r2; - r = must_be_root(); - if (r < 0) - return r; - - r = find_esp(NULL, NULL, NULL, &uuid); - if (r < 0) - return r; + r2 = find_esp(NULL, NULL, NULL, &uuid); if (is_efi_boot()) { _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL; @@ -1051,44 +1055,47 @@ static int verb_status(int argc, char *argv[], void *userdata) { r = efi_loader_get_device_part_uuid(&loader_part_uuid); if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to read EFI variable LoaderDevicePartUUID: %m"); + r2 = log_warning_errno(r, "Failed to read EFI variable LoaderDevicePartUUID: %m"); printf("System:\n"); printf(" Firmware: %s (%s)\n", strna(fw_type), strna(fw_info)); r = is_efi_secure_boot(); if (r < 0) - log_warning_errno(r, "Failed to query secure boot status: %m"); + r2 = log_warning_errno(r, "Failed to query secure boot status: %m"); else printf(" Secure Boot: %sd\n", enable_disable(r)); r = is_efi_secure_boot_setup_mode(); if (r < 0) - log_warning_errno(r, "Failed to query secure boot mode: %m"); + r2 = log_warning_errno(r, "Failed to query secure boot mode: %m"); else printf(" Setup Mode: %s\n", r ? "setup" : "user"); printf("\n"); - printf("Loader:\n"); + printf("Current Loader:\n"); printf(" Product: %s\n", strna(loader)); if (!sd_id128_is_null(loader_part_uuid)) - printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", + printf(" ESP: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", SD_ID128_FORMAT_VAL(loader_part_uuid)); else - printf(" Partition: n/a\n"); + printf(" ESP: n/a\n"); printf(" File: %s%s\n", special_glyph(TREE_RIGHT), strna(loader_path)); printf("\n"); } else - printf("System:\n Not booted with EFI\n"); + printf("System:\n Not booted with EFI\n\n"); r = status_binaries(arg_path, uuid); if (r < 0) - return r; + r2 = r; - if (arg_touch_variables) + if (is_efi_boot()) { r = status_variables(); + if (r < 0) + r2 = r; + } - return r; + return r2; } static int verb_install(int argc, char *argv[], void *userdata) { diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 681e783f2e..1e990b3825 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -20,16 +20,17 @@ #include "disk.h" #include "graphics.h" #include "linux.h" -#include "pefile.h" -#include "util.h" #include "measure.h" +#include "pe.h" +#include "shim.h" +#include "util.h" #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL #endif /* magic string to find in the binary image */ -static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-boot " VERSION " ####"; +static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-boot " PACKAGE_VERSION " ####"; static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; @@ -363,7 +364,7 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) { uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, EFI_LIGHTGRAY|EFI_BACKGROUND_BLACK); uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); - Print(L"systemd-boot version: " VERSION "\n"); + Print(L"systemd-boot version: " PACKAGE_VERSION "\n"); Print(L"architecture: " EFI_MACHINE_TYPE_NAME "\n"); Print(L"loaded image: %s\n", loaded_image_path); Print(L"UEFI specification: %d.%02d\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); @@ -383,6 +384,9 @@ static VOID print_status(Config *config, CHAR16 *loaded_image_path) { FreePool(b); } + if (shim_loaded()) + Print(L"Shim: present\n"); + if (efivar_get_raw(&global_guid, L"OsIndicationsSupported", &b, &size) == EFI_SUCCESS) { Print(L"OsIndicationsSupported: %d\n", (UINT64)*b); FreePool(b); @@ -781,7 +785,7 @@ static BOOLEAN menu_run(Config *config, ConfigEntry **chosen_entry, CHAR16 *load break; case KEYPRESS(0, 0, 'v'): - status = PoolPrint(L"systemd-boot " VERSION " (" EFI_MACHINE_TYPE_NAME "), UEFI Specification %d.%02d, Vendor %s %d.%02d", + status = PoolPrint(L"systemd-boot " PACKAGE_VERSION " (" EFI_MACHINE_TYPE_NAME "), UEFI Specification %d.%02d, Vendor %s %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff, ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); break; @@ -1539,7 +1543,7 @@ static VOID config_entry_add_linux( Config *config, EFI_LOADED_IMAGE *loaded_ima continue; /* look for .osrel and .cmdline sections in the .efi binary */ - err = pefile_locate_sections(linux_dir, f->FileName, sections, addrs, offs, szs); + err = pe_file_locate_sections(linux_dir, f->FileName, sections, addrs, offs, szs); if (EFI_ERROR(err)) continue; @@ -1718,7 +1722,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { InitializeLib(image, sys_table); init_usec = time_usec(); efivar_set_time_usec(L"LoaderTimeInitUSec", init_usec); - efivar_set(L"LoaderInfo", L"systemd-boot " VERSION, FALSE); + efivar_set(L"LoaderInfo", L"systemd-boot " PACKAGE_VERSION, FALSE); s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); efivar_set(L"LoaderFirmwareInfo", s, FALSE); FreePool(s); @@ -1745,6 +1749,14 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { return EFI_LOAD_ERROR; } + if (secure_boot_enabled() && shim_loaded()) { + err = security_policy_install(); + if (EFI_ERROR(err)) { + Print(L"Error installing security policy: %r ", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + return err; + } + } /* the filesystem path to this image, to prevent adding ourselves to the menu */ loaded_image_path = DevicePathToStr(loaded_image->FilePath); diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c index 4ac11a9bb0..b22d37b62d 100644 --- a/src/boot/efi/measure.c +++ b/src/boot/efi/measure.c @@ -199,7 +199,7 @@ static EFI_STATUS tpm1_measure_to_pcr_and_event_log(const EFI_TCG *tcg, UINT32 p event_number = 1; status = uefi_call_wrapper(tcg->HashLogExtendEvent, 7, - tcg, buffer, buffer_size, TCG_ALG_SHA, tcg_event, &event_number, &event_log_last); + (EFI_TCG *) tcg, buffer, buffer_size, TCG_ALG_SHA, tcg_event, &event_number, &event_log_last); if (EFI_ERROR(status)) return status; @@ -219,7 +219,7 @@ static EFI_STATUS tpm1_measure_to_pcr_and_event_log(const EFI_TCG *tcg, UINT32 p */ static EFI_STATUS trigger_tcg2_final_events_table(const EFI_TCG2 *tcg) { - return uefi_call_wrapper(tcg->GetEventLog, 5, tcg, + return uefi_call_wrapper(tcg->GetEventLog, 5, (EFI_TCG2 *) tcg, EFI_TCG2_EVENT_LOG_FORMAT_TCG_2, NULL, NULL, NULL); } @@ -254,7 +254,7 @@ static EFI_STATUS tpm2_measure_to_pcr_and_event_log(const EFI_TCG2 *tcg, UINT32 CopyMem((VOID *) tcg_event->Event, (VOID *) description, desc_len); - status = uefi_call_wrapper(tcg->HashLogExtendEvent, 5, tcg, 0, buffer, buffer_size, tcg_event); + status = uefi_call_wrapper(tcg->HashLogExtendEvent, 5, (EFI_TCG2 *) tcg, 0, buffer, buffer_size, tcg_event); uefi_call_wrapper(BS->FreePool, 1, tcg_event); diff --git a/src/boot/efi/measure.h b/src/boot/efi/measure.h index a2cfe817d0..43aa8a0058 100644 --- a/src/boot/efi/measure.h +++ b/src/boot/efi/measure.h @@ -13,9 +13,6 @@ #ifndef __SDBOOT_MEASURE_H #define __SDBOOT_MEASURE_H -#ifndef SD_TPM_PCR -#define SD_TPM_PCR 8 -#endif - EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description); + #endif diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build new file mode 100644 index 0000000000..5ef5b2d20b --- /dev/null +++ b/src/boot/efi/meson.build @@ -0,0 +1,205 @@ +efi_headers = files(''' + console.h + disk.h + graphics.h + linux.h + measure.h + pe.h + splash.h + util.h + shim.h +'''.split()) + +common_sources = ''' + disk.c + graphics.c + measure.c + pe.c + util.c +'''.split() + +systemd_boot_sources = ''' + boot.c + console.c + shim.c +'''.split() + +stub_sources = ''' + linux.c + splash.c + stub.c +'''.split() + +if conf.get('ENABLE_EFI', false) and get_option('gnu-efi') != 'false' + efi_cc = get_option('efi-cc') + efi_ld = get_option('efi-ld') + + efi_incdir = get_option('efi-includedir') + have_header = (gnu_efi_arch != '' and + cc.has_header('@0@/@1@/efibind.h'.format(efi_incdir, gnu_efi_arch))) + + if have_header and EFI_MACHINE_TYPE_NAME == '' + error('gnu-efi is available, but EFI_MACHINE_TYPE_NAME is unknown') + endif + + efi_libdir = get_option('efi-libdir') + if efi_libdir == '' + cmd = 'cd /usr/lib/$(@0@ -print-multi-os-directory) && pwd'.format(efi_cc) + ret = run_command('sh', '-c', cmd) + if ret.returncode() == 0 + efi_libdir = ret.stdout().strip() + endif + endif + + have_gnu_efi = have_header and efi_libdir != '' +else + have_gnu_efi = false +endif + +if get_option('gnu-efi') == 'true' and not have_gnu_efi + error('gnu-efi support requested, but headers were not found') +endif + +if have_gnu_efi + efi_conf = configuration_data() + efi_conf.set_quoted('PACKAGE_VERSION', meson.project_version()) + efi_conf.set_quoted('EFI_MACHINE_TYPE_NAME', EFI_MACHINE_TYPE_NAME) + efi_conf.set('SD_BOOT_LOG_TPM', get_option('tpm')) + efi_conf.set('SD_TPM_PCR', get_option('tpm-pcrindex')) + + efi_config_h = configure_file( + output : 'efi_config.h', + configuration : efi_conf) + + objcopy = find_program('objcopy') + + efi_ldsdir = get_option('efi-ldsdir') + arch_lds = 'elf_@0@_efi.lds'.format(gnu_efi_arch) + if efi_ldsdir == '' + efi_ldsdir = join_paths(efi_libdir, 'gnuefi') + cmd = run_command('test', '-f', join_paths(efi_ldsdir, arch_lds)) + if cmd.returncode() != 0 + efi_ldsdir = efi_libdir + cmd = run_command('test', '-f', join_paths(efi_ldsdir, arch_lds)) + if cmd.returncode() != 0 + error('Cannot find @0@'.format(arch_lds)) + endif + endif + endif + + message('efi-libdir: "@0@"'.format(efi_libdir)) + message('efi-ldsdir: "@0@"'.format(efi_ldsdir)) + message('efi-includedir: "@0@"'.format(efi_incdir)) + + compile_args = ['-Wall', + '-Wextra', + '-std=gnu90', + '-nostdinc', + '-ggdb', '-O0', + '-fpic', + '-fshort-wchar', + '-ffreestanding', + '-fno-strict-aliasing', + '-fno-stack-protector', + '-Wsign-compare', + '-Wno-missing-field-initializers', + '-isystem', efi_incdir, + '-isystem', join_paths(efi_incdir, gnu_efi_arch), + '-include', efi_config_h] + if efi_arch == 'x86_64' + compile_args += ['-mno-red-zone', + '-mno-sse', + '-mno-mmx', + '-DEFI_FUNCTION_WRAPPER', + '-DGNU_EFI_USE_MS_ABI'] + elif efi_arch == 'ia32' + compile_args += ['-mno-sse', + '-mno-mmx'] + endif + + efi_ldflags = ['-T', + join_paths(efi_ldsdir, arch_lds), + '-shared', + '-Bsymbolic', + '-nostdlib', + '-znocombreloc', + '-L', efi_libdir, + join_paths(efi_ldsdir, 'crt0-efi-@0@.o'.format(gnu_efi_arch))] + if efi_arch == 'aarch64' or efi_arch == 'arm' + # Aarch64 and ARM32 don't have an EFI capable objcopy. Use 'binary' + # instead, and add required symbols manually. + efi_ldflags += ['--defsym=EFI_SUBSYSTEM=0xa'] + efi_format = ['-O', 'binary'] + else + efi_format = ['--target=efi-app-@0@'.format(gnu_efi_arch)] + endif + + systemd_boot_objects = [] + stub_objects = [] + foreach file : common_sources + systemd_boot_sources + stub_sources + o_file = custom_target(file + '.o', + input : file, + output : file + '.o', + command : [efi_cc, '-c', '@INPUT@', '-o', '@OUTPUT@'] + + compile_args, + depend_files : efi_headers) + if (common_sources + systemd_boot_sources).contains(file) + systemd_boot_objects += [o_file] + endif + if (common_sources + stub_sources).contains(file) + stub_objects += [o_file] + endif + endforeach + + libgcc_file_name = run_command(efi_cc, '-print-libgcc-file-name').stdout().strip() + systemd_boot_efi_name = 'systemd-boot@0@.efi'.format(EFI_MACHINE_TYPE_NAME) + stub_efi_name = 'linux@0@.efi.stub'.format(EFI_MACHINE_TYPE_NAME) + no_undefined_symbols = find_program('no-undefined-symbols.sh') + + foreach tuple : [['systemd_boot.so', systemd_boot_efi_name, systemd_boot_objects], + ['stub.so', stub_efi_name, stub_objects]] + so = custom_target( + tuple[0], + input : tuple[2], + output : tuple[0], + command : [efi_ld, '-o', '@OUTPUT@'] + + efi_ldflags + tuple[2] + + ['-lefi', '-lgnuefi', libgcc_file_name]) + + test('no-undefined-symbols-' + tuple[0], + no_undefined_symbols, + args : [so]) + + stub = custom_target( + tuple[1], + input : so, + output : tuple[1], + command : [objcopy, + '-j', '.text', + '-j', '.sdata', + '-j', '.data', + '-j', '.dynamic', + '-j', '.dynsym', + '-j', '.rel', + '-j', '.rela', + '-j', '.reloc'] + + efi_format + + ['@INPUT@', '@OUTPUT@'], + install : true, + install_dir : bootlibdir) + + set_variable(tuple[0].underscorify(), so) + set_variable(tuple[0].underscorify() + '_stub', stub) + endforeach +endif + +############################################################ + +if have_gnu_efi + test_efi_disk_img = custom_target( + 'test-efi-disk.img', + input : [systemd_boot_so, stub_so_stub], + output : 'test-efi-disk.img', + command : [test_efi_create_disk_sh, '@OUTPUT@', + '@INPUT0@', '@INPUT1@', splash_bmp]) +endif diff --git a/src/boot/efi/no-undefined-symbols.sh b/src/boot/efi/no-undefined-symbols.sh new file mode 100755 index 0000000000..08b266c455 --- /dev/null +++ b/src/boot/efi/no-undefined-symbols.sh @@ -0,0 +1,6 @@ +#!/bin/sh -eu + +if nm -D -u "$1" | grep ' U '; then + echo "Undefined symbols detected!" + exit 1 +fi diff --git a/src/boot/efi/pefile.c b/src/boot/efi/pe.c index 77fff77b69..054e8edbc6 100644 --- a/src/boot/efi/pefile.c +++ b/src/boot/efi/pe.c @@ -15,7 +15,7 @@ #include <efi.h> #include <efilib.h> -#include "pefile.h" +#include "pe.h" #include "util.h" struct DosFileHeader { @@ -52,6 +52,11 @@ struct PeFileHeader { UINT16 Characteristics; } __attribute__((packed)); +struct PeHeader { + UINT8 Magic[4]; + struct PeFileHeader FileHeader; +} __attribute__((packed)); + struct PeSectionHeader { UINT8 Name[8]; UINT32 VirtualSize; @@ -65,15 +70,61 @@ struct PeSectionHeader { UINT32 Characteristics; } __attribute__((packed)); +EFI_STATUS pe_memory_locate_sections(CHAR8 *base, CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes) { + struct DosFileHeader *dos; + struct PeHeader *pe; + UINTN i; + UINTN offset; + + dos = (struct DosFileHeader *)base; + + if (CompareMem(dos->Magic, "MZ", 2) != 0) + return EFI_LOAD_ERROR; + + pe = (struct PeHeader *)&base[dos->ExeHeader]; + if (CompareMem(pe->Magic, "PE\0\0", 4) != 0) + return EFI_LOAD_ERROR; + + /* PE32+ Subsystem type */ + if (pe->FileHeader.Machine != PE_HEADER_MACHINE_X64 && + pe->FileHeader.Machine != PE_HEADER_MACHINE_I386) + return EFI_LOAD_ERROR; + + if (pe->FileHeader.NumberOfSections > 96) + return EFI_LOAD_ERROR; + + offset = dos->ExeHeader + sizeof(*pe) + pe->FileHeader.SizeOfOptionalHeader; + + for (i = 0; i < pe->FileHeader.NumberOfSections; i++) { + struct PeSectionHeader *sect; + UINTN j; + + sect = (struct PeSectionHeader *)&base[offset]; + for (j = 0; sections[j]; j++) { + if (CompareMem(sect->Name, sections[j], strlena(sections[j])) != 0) + continue; -EFI_STATUS pefile_locate_sections(EFI_FILE *dir, CHAR16 *path, CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes) { + if (addrs) + addrs[j] = (UINTN)sect->VirtualAddress; + if (offsets) + offsets[j] = (UINTN)sect->PointerToRawData; + if (sizes) + sizes[j] = (UINTN)sect->VirtualSize; + } + offset += sizeof(*sect); + } + + return EFI_SUCCESS; +} + +EFI_STATUS pe_file_locate_sections(EFI_FILE *dir, CHAR16 *path, CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes) { EFI_FILE_HANDLE handle; struct DosFileHeader dos; - uint8_t magic[4]; - struct PeFileHeader pe; + struct PeHeader pe; UINTN len; - UINTN i; + UINTN headerlen; EFI_STATUS err; + CHAR8 *header = NULL; err = uefi_call_wrapper(dir->Open, 5, dir, &handle, path, EFI_FILE_MODE_READ, 0ULL); if (EFI_ERROR(err)) @@ -89,30 +140,10 @@ EFI_STATUS pefile_locate_sections(EFI_FILE *dir, CHAR16 *path, CHAR8 **sections, goto out; } - if (CompareMem(dos.Magic, "MZ", 2) != 0) { - err = EFI_LOAD_ERROR; - goto out; - } - err = uefi_call_wrapper(handle->SetPosition, 2, handle, dos.ExeHeader); if (EFI_ERROR(err)) goto out; - /* PE header */ - len = sizeof(magic); - err = uefi_call_wrapper(handle->Read, 3, handle, &len, &magic); - if (EFI_ERROR(err)) - goto out; - if (len != sizeof(magic)) { - err = EFI_LOAD_ERROR; - goto out; - } - - if (CompareMem(magic, "PE\0\0", 2) != 0) { - err = EFI_LOAD_ERROR; - goto out; - } - len = sizeof(pe); err = uefi_call_wrapper(handle->Read, 3, handle, &len, &pe); if (EFI_ERROR(err)) @@ -122,49 +153,30 @@ EFI_STATUS pefile_locate_sections(EFI_FILE *dir, CHAR16 *path, CHAR8 **sections, goto out; } - /* PE32+ Subsystem type */ - if (pe.Machine != PE_HEADER_MACHINE_X64 && - pe.Machine != PE_HEADER_MACHINE_I386) { - err = EFI_LOAD_ERROR; + headerlen = sizeof(dos) + sizeof(pe) + pe.FileHeader.SizeOfOptionalHeader + pe.FileHeader.NumberOfSections * sizeof(struct PeSectionHeader); + header = AllocatePool(headerlen); + if (!header) { + err = EFI_OUT_OF_RESOURCES; goto out; } + len = headerlen; + err = uefi_call_wrapper(handle->SetPosition, 2, handle, 0); + if (EFI_ERROR(err)) + goto out; - if (pe.NumberOfSections > 96) { - err = EFI_LOAD_ERROR; + err = uefi_call_wrapper(handle->Read, 3, handle, &len, header); + if (EFI_ERROR(err)) { goto out; } - - /* the sections start directly after the headers */ - err = uefi_call_wrapper(handle->SetPosition, 2, handle, dos.ExeHeader + sizeof(magic) + sizeof(pe) + pe.SizeOfOptionalHeader); - if (EFI_ERROR(err)) + if (len != headerlen) { + err = EFI_LOAD_ERROR; goto out; - - for (i = 0; i < pe.NumberOfSections; i++) { - struct PeSectionHeader sect; - UINTN j; - - len = sizeof(sect); - err = uefi_call_wrapper(handle->Read, 3, handle, &len, §); - if (EFI_ERROR(err)) - goto out; - if (len != sizeof(sect)) { - err = EFI_LOAD_ERROR; - goto out; - } - for (j = 0; sections[j]; j++) { - if (CompareMem(sect.Name, sections[j], strlena(sections[j])) != 0) - continue; - - if (addrs) - addrs[j] = (UINTN)sect.VirtualAddress; - if (offsets) - offsets[j] = (UINTN)sect.PointerToRawData; - if (sizes) - sizes[j] = (UINTN)sect.VirtualSize; - } } + err = pe_memory_locate_sections(header, sections, addrs, offsets, sizes); out: + if (header) + FreePool(header); uefi_call_wrapper(handle->Close, 1, handle); return err; } diff --git a/src/boot/efi/pefile.h b/src/boot/efi/pe.h index 2e445ede17..fa8feea758 100644 --- a/src/boot/efi/pefile.h +++ b/src/boot/efi/pe.h @@ -15,6 +15,8 @@ #ifndef __SDBOOT_PEFILE_H #define __SDBOOT_PEFILE_H -EFI_STATUS pefile_locate_sections(EFI_FILE *dir, CHAR16 *path, - CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes); +EFI_STATUS pe_memory_locate_sections(CHAR8 *base, + CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes); +EFI_STATUS pe_file_locate_sections(EFI_FILE *dir, CHAR16 *path, + CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes); #endif diff --git a/src/boot/efi/shim.c b/src/boot/efi/shim.c new file mode 100644 index 0000000000..0f73be9549 --- /dev/null +++ b/src/boot/efi/shim.c @@ -0,0 +1,262 @@ +/* + * This program 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. + * + * This program 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. + * + * Port to systemd-boot + * Copyright 2017 Max Resch <resch.max@gmail.com> + * + * Security Policy Handling + * Copyright 2012 <James.Bottomley@HansenPartnership.com> + * https://github.com/mjg59/efitools + */ + +#include <efi.h> +#include <efilib.h> + +#include "util.h" +#include "shim.h" + +/* well known shim lock guid */ +#define SHIM_LOCK_GUID + +struct ShimLock { + EFI_STATUS __attribute__((sysv_abi)) (*shim_verify) (VOID *buffer, UINT32 size); + + /* context is actually a struct for the PE header, but it isn't needed so void is sufficient just do define the interface + * see shim.c/shim.h and PeHeader.h in the github shim repo */ + EFI_STATUS __attribute__((sysv_abi)) (*generate_hash) (VOID *data, UINT32 datasize, VOID *context, UINT8 *sha256hash, UINT8 *sha1hash); + + EFI_STATUS __attribute__((sysv_abi)) (*read_header) (VOID *data, UINT32 datasize, VOID *context); +}; + +static const EFI_GUID simple_fs_guid = SIMPLE_FILE_SYSTEM_PROTOCOL; +static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; + +static const EFI_GUID security_protocol_guid = { 0xa46423e3, 0x4617, 0x49f1, {0xb9, 0xff, 0xd1, 0xbf, 0xa9, 0x11, 0x58, 0x39 } }; +static const EFI_GUID security2_protocol_guid = { 0x94ab2f58, 0x1438, 0x4ef1, {0x91, 0x52, 0x18, 0x94, 0x1a, 0x3a, 0x0e, 0x68 } }; +static const EFI_GUID shim_lock_guid = { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} }; + +BOOLEAN shim_loaded(void) { + struct ShimLock *shim_lock; + + return uefi_call_wrapper(BS->LocateProtocol, 3, (EFI_GUID*) &shim_lock_guid, NULL, (VOID**) &shim_lock) == EFI_SUCCESS; +} + +static BOOLEAN shim_validate(VOID *data, UINT32 size) { + struct ShimLock *shim_lock; + + if (!data) + return FALSE; + + if (uefi_call_wrapper(BS->LocateProtocol, 3, (EFI_GUID*) &shim_lock_guid, NULL, (VOID**) &shim_lock) != EFI_SUCCESS) + return FALSE; + + if (!shim_lock) + return FALSE; + + if (shim_lock->shim_verify(data, size) == EFI_SUCCESS) + return TRUE; + + return FALSE; +} + +BOOLEAN secure_boot_enabled(void) { + CHAR8 *b; + UINTN size; + BOOLEAN result; + + if (efivar_get_raw(&global_guid, L"SecureBoot", &b, &size) == EFI_SUCCESS) { + result = *b > 0; + FreePool(b); + return result; + } + + return FALSE; +} + +/* + * See the UEFI Platform Initialization manual (Vol2: DXE) for this + */ +struct _EFI_SECURITY2_PROTOCOL; +struct _EFI_SECURITY_PROTOCOL; +struct _EFI_DEVICE_PATH_PROTOCOL; + +typedef struct _EFI_SECURITY2_PROTOCOL EFI_SECURITY2_PROTOCOL; +typedef struct _EFI_SECURITY_PROTOCOL EFI_SECURITY_PROTOCOL; +typedef struct _EFI_DEVICE_PATH_PROTOCOL EFI_DEVICE_PATH_PROTOCOL; + +typedef EFI_STATUS (EFIAPI *EFI_SECURITY_FILE_AUTHENTICATION_STATE) ( + const EFI_SECURITY_PROTOCOL *This, + UINT32 AuthenticationStatus, + const EFI_DEVICE_PATH_PROTOCOL *File +); + +typedef EFI_STATUS (EFIAPI *EFI_SECURITY2_FILE_AUTHENTICATION) ( + const EFI_SECURITY2_PROTOCOL *This, + const EFI_DEVICE_PATH_PROTOCOL *DevicePath, + VOID *FileBuffer, + UINTN FileSize, + BOOLEAN BootPolicy +); + +struct _EFI_SECURITY2_PROTOCOL { + EFI_SECURITY2_FILE_AUTHENTICATION FileAuthentication; +}; + +struct _EFI_SECURITY_PROTOCOL { + EFI_SECURITY_FILE_AUTHENTICATION_STATE FileAuthenticationState; +}; + +/* Handle to the original authenticator for security1 protocol */ +static EFI_SECURITY_FILE_AUTHENTICATION_STATE esfas = NULL; + +/* Handle to the original authenticator for security2 protocol */ +static EFI_SECURITY2_FILE_AUTHENTICATION es2fa = NULL; + +/* + * Perform shim/MOK and Secure Boot authentication on a binary that's already been + * loaded into memory. This function does the platform SB authentication first + * but preserves its return value in case of its failure, so that it can be + * returned in case of a shim/MOK authentication failure. This is done because + * the SB failure code seems to vary from one implementation to another, and I + * don't want to interfere with that at this time. + */ +static EFIAPI EFI_STATUS security2_policy_authentication (const EFI_SECURITY2_PROTOCOL *this, + const EFI_DEVICE_PATH_PROTOCOL *device_path, + VOID *file_buffer, UINTN file_size, BOOLEAN boot_policy) { + EFI_STATUS status; + + /* Chain original security policy */ + status = uefi_call_wrapper(es2fa, 5, this, device_path, file_buffer, file_size, boot_policy); + + /* if OK, don't bother with MOK check */ + if (status == EFI_SUCCESS) + return status; + + if (shim_validate(file_buffer, file_size)) + return EFI_SUCCESS; + + return status; +} + +/* + * Perform both shim/MOK and platform Secure Boot authentication. This function loads + * the file and performs shim/MOK authentication first simply to avoid double loads + * of Linux kernels, which are much more likely to be shim/MOK-signed than platform-signed, + * since kernels are big and can take several seconds to load on some computers and + * filesystems. This also has the effect of returning whatever the platform code is for + * authentication failure, be it EFI_ACCESS_DENIED, EFI_SECURITY_VIOLATION, or something + * else. (This seems to vary between implementations.) + */ +static EFIAPI EFI_STATUS security_policy_authentication (const EFI_SECURITY_PROTOCOL *this, UINT32 authentication_status, + const EFI_DEVICE_PATH_PROTOCOL *device_path_const) { + EFI_STATUS status; + EFI_DEVICE_PATH *dev_path; + EFI_HANDLE h; + EFI_FILE *root; + VOID *file_buffer = NULL; + UINTN file_size; + CHAR16 *dev_path_str; + + if (!device_path_const) + return EFI_INVALID_PARAMETER; + + dev_path = DuplicateDevicePath((EFI_DEVICE_PATH*) device_path_const); + + status = uefi_call_wrapper(BS->LocateDevicePath, 3, (EFI_GUID*) &simple_fs_guid, &dev_path, &h); + if (status != EFI_SUCCESS) { + FreePool(dev_path); + return status; + } + + /* No need to check return value, this already happend in efi_main() */ + root = LibOpenRoot(h); + dev_path_str = DevicePathToStr(dev_path); + FreePool(dev_path); + + file_size = file_read(root, dev_path_str, 0, 0, file_buffer); + FreePool(dev_path_str); + uefi_call_wrapper(root->Close, 1, root); + + if (shim_validate(file_buffer, file_size)) + status = EFI_SUCCESS; + + FreePool(file_buffer); + + /* Try using the platform's native policy.... */ + if (status != EFI_SUCCESS) + status = uefi_call_wrapper(esfas, 3, this, authentication_status, device_path_const); + + return status; +} + +EFI_STATUS security_policy_install(void) { + EFI_SECURITY_PROTOCOL *security_protocol; + EFI_SECURITY2_PROTOCOL *security2_protocol = NULL; + EFI_STATUS status; + + /* Already Installed */ + if (esfas) + return EFI_ALREADY_STARTED; + + /* + * Don't bother with status here. The call is allowed + * to fail, since SECURITY2 was introduced in PI 1.2.1 + * If it fails, use security2_protocol == NULL as indicator + */ + uefi_call_wrapper(BS->LocateProtocol, 3, (EFI_GUID*) &security2_protocol_guid, NULL, (VOID**) &security2_protocol); + + status = uefi_call_wrapper(BS->LocateProtocol, 3, (EFI_GUID*) &security_protocol_guid, NULL, (VOID**) &security_protocol); + /* This one is mandatory, so there's a serious problem */ + if (status != EFI_SUCCESS) + return status; + + if (!security2_protocol) { + es2fa = security2_protocol->FileAuthentication; + security2_protocol->FileAuthentication = security2_policy_authentication; + } + + esfas = security_protocol->FileAuthenticationState; + security_protocol->FileAuthenticationState = security_policy_authentication; + + return EFI_SUCCESS; +} + +EFI_STATUS security_policy_uninstall(void) { + EFI_STATUS status; + + if (esfas) { + EFI_SECURITY_PROTOCOL *security_protocol; + + status = uefi_call_wrapper(BS->LocateProtocol, 3, (EFI_GUID*) &security_protocol_guid, NULL, (VOID**) &security_protocol); + + if (status != EFI_SUCCESS) + return status; + + security_protocol->FileAuthenticationState = esfas; + esfas = NULL; + } else + /* nothing installed */ + return EFI_NOT_STARTED; + + if (es2fa) { + EFI_SECURITY2_PROTOCOL *security2_protocol; + + status = uefi_call_wrapper(BS->LocateProtocol, 3, (EFI_GUID*) &security2_protocol_guid, NULL, (VOID**) &security2_protocol); + + if (status != EFI_SUCCESS) + return status; + + security2_protocol->FileAuthentication = es2fa; + es2fa = NULL; + } + + return EFI_SUCCESS; +} diff --git a/src/boot/efi/shim.h b/src/boot/efi/shim.h new file mode 100644 index 0000000000..2dcf48dcd5 --- /dev/null +++ b/src/boot/efi/shim.h @@ -0,0 +1,31 @@ +/* + * This program 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. + * + * This program 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. + * + * Port to systemd-boot + * Copyright 2017 Max Resch <resch.max@gmail.com> + * + * Security Policy Handling + * Copyright 2012 <James.Bottomley@HansenPartnership.com> + * https://github.com/mjg59/efitools + */ + +#ifndef __SDBOOT_SHIM_H +#define __SDBOOT_SHIM_H + +BOOLEAN shim_loaded(void); + +BOOLEAN secure_boot_enabled(void); + +EFI_STATUS security_policy_install(void); + +EFI_STATUS security_policy_uninstall(void); + +#endif diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index b7d5d3cdae..bab5d46de9 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -17,20 +17,18 @@ #include "disk.h" #include "graphics.h" #include "linux.h" -#include "pefile.h" +#include "measure.h" +#include "pe.h" #include "splash.h" #include "util.h" -#include "measure.h" /* magic string to find in the binary image */ -static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-stub " VERSION " ####"; +static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-stub " PACKAGE_VERSION " ####"; static const EFI_GUID global_guid = EFI_GLOBAL_VARIABLE; EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { EFI_LOADED_IMAGE *loaded_image; - EFI_FILE *root_dir; - CHAR16 *loaded_image_path; CHAR8 *b; UINTN size; BOOLEAN secure = FALSE; @@ -59,22 +57,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { return err; } - root_dir = LibOpenRoot(loaded_image->DeviceHandle); - if (!root_dir) { - Print(L"Unable to open root directory: %r ", err); - uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); - return EFI_LOAD_ERROR; - } - - loaded_image_path = DevicePathToStr(loaded_image->FilePath); - if (efivar_get_raw(&global_guid, L"SecureBoot", &b, &size) == EFI_SUCCESS) { if (*b > 0) secure = TRUE; FreePool(b); } - - err = pefile_locate_sections(root_dir, loaded_image_path, sections, addrs, offs, szs); + err = pe_memory_locate_sections(loaded_image->ImageBase, sections, addrs, offs, szs); if (EFI_ERROR(err)) { Print(L"Unable to locate embedded .linux section: %r ", err); uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); diff --git a/src/libsystemd/sd-bus/busctl-introspect.c b/src/busctl/busctl-introspect.c index a05794941f..a05794941f 100644 --- a/src/libsystemd/sd-bus/busctl-introspect.c +++ b/src/busctl/busctl-introspect.c diff --git a/src/libsystemd/sd-bus/busctl-introspect.h b/src/busctl/busctl-introspect.h index d922e352db..d922e352db 100644 --- a/src/libsystemd/sd-bus/busctl-introspect.h +++ b/src/busctl/busctl-introspect.h diff --git a/src/libsystemd/sd-bus/busctl.c b/src/busctl/busctl.c index 9dd3828364..b38d6c7267 100644 --- a/src/libsystemd/sd-bus/busctl.c +++ b/src/busctl/busctl.c @@ -1083,6 +1083,8 @@ static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FIL _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; char **i; uint32_t flags = 0; + const char *unique_name; + bool is_monitor = false; int r; /* upgrade connection; it's not used for anything else after this call */ @@ -1140,6 +1142,10 @@ static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FIL return r; } + r = sd_bus_get_unique_name(bus, &unique_name); + if (r < 0) + return log_error_errno(r, "Failed to get unique name: %m"); + log_info("Monitoring bus message stream."); for (;;) { @@ -1149,6 +1155,23 @@ static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FIL if (r < 0) return log_error_errno(r, "Failed to process bus: %m"); + if (!is_monitor) { + const char *name; + + /* wait until we lose our unique name */ + if (sd_bus_message_is_signal(m, "org.freedesktop.DBus", "NameLost") <= 0) + continue; + + r = sd_bus_message_read(m, "s", &name); + if (r < 0) + return log_error_errno(r, "Failed to read lost name: %m"); + + if (streq(name, unique_name)) + is_monitor = true; + + continue; + } + if (m) { dump(m, stdout); fflush(stdout); diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index a1c0f48c89..7ebb02fa8c 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -75,6 +75,7 @@ static usec_t arg_delay = 1*USEC_PER_SEC; static char* arg_machine = NULL; static char* arg_root = NULL; static bool arg_recursive = true; +static bool arg_recursive_unset = false; static enum { COUNT_PIDS, @@ -118,7 +119,7 @@ static const char *maybe_format_bytes(char *buf, size_t l, bool is_valid, uint64 if (!is_valid) return "-"; if (arg_raw) { - snprintf(buf, l, "%jd", t); + snprintf(buf, l, "%" PRIu64, t); return buf; } return format_bytes(buf, l, t); @@ -732,7 +733,6 @@ static int parse_argv(int argc, char *argv[]) { {} }; - bool recursive_unset = false; int c, r; assert(argc >= 1); @@ -852,7 +852,7 @@ static int parse_argv(int argc, char *argv[]) { } arg_recursive = r; - recursive_unset = r == 0; + arg_recursive_unset = r == 0; break; case 'M': @@ -873,11 +873,6 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } - if (recursive_unset && arg_count == COUNT_PIDS) { - log_error("Non-recursive counting is only supported when counting processes, not tasks. Use -P or -k."); - return -EINVAL; - } - return 1; } @@ -902,6 +897,10 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + r = cg_mask_supported(&mask); if (r < 0) { log_error_errno(r, "Failed to determine supported controllers: %m"); @@ -910,9 +909,10 @@ int main(int argc, char *argv[]) { arg_count = (mask & CGROUP_MASK_PIDS) ? COUNT_PIDS : COUNT_USERSPACE_PROCESSES; - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; + if (arg_recursive_unset && arg_count == COUNT_PIDS) { + log_error("Non-recursive counting is only supported when counting processes, not tasks. Use -P or -k."); + return -EINVAL; + } r = show_cgroup_get_path_and_warn(arg_machine, arg_root, &root); if (r < 0) { diff --git a/src/core/audit-fd.c b/src/core/audit-fd.c index 76afe3fe15..a91906b626 100644 --- a/src/core/audit-fd.c +++ b/src/core/audit-fd.c @@ -27,6 +27,7 @@ #include <libaudit.h> #include <stdbool.h> +#include "capability-util.h" #include "fd-util.h" #include "log.h" #include "util.h" @@ -37,6 +38,13 @@ static int audit_fd; int get_audit_fd(void) { if (!initialized) { + if (have_effective_cap(CAP_AUDIT_WRITE) == 0) { + audit_fd = -EPERM; + initialized = true; + + return audit_fd; + } + audit_fd = audit_open(); if (audit_fd < 0) { diff --git a/src/core/automount.c b/src/core/automount.c index 99e8047620..0f72854ceb 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -415,8 +415,11 @@ static int autofs_set_timeout(int dev_autofs_fd, int ioctl_fd, usec_t usec) { init_autofs_dev_ioctl(¶m); param.ioctlfd = ioctl_fd; - /* Convert to seconds, rounding up. */ - param.timeout.timeout = (usec + USEC_PER_SEC - 1) / USEC_PER_SEC; + if (usec == USEC_INFINITY) + param.timeout.timeout = 0; + else + /* Convert to seconds, rounding up. */ + param.timeout.timeout = (usec + USEC_PER_SEC - 1) / USEC_PER_SEC; if (ioctl(dev_autofs_fd, AUTOFS_DEV_IOCTL_TIMEOUT, ¶m) < 0) return -errno; @@ -471,10 +474,10 @@ static int automount_send_ready(Automount *a, Set *tokens, int status) { while ((token = PTR_TO_UINT(set_steal_first(tokens)))) { int k; - /* Autofs fun fact II: + /* Autofs fun fact: * - * if you pass a positive status code here, the kernel will - * freeze! Yay! */ + * if you pass a positive status code here, kernels + * prior to 4.12 will freeze! Yay! */ k = autofs_send_ready(UNIT(a)->manager->dev_autofs_fd, ioctl_fd, @@ -616,12 +619,6 @@ static void automount_enter_waiting(Automount *a) { if (r < 0) goto fail; - /* Autofs fun fact: - * - * Unless we close the ioctl fd here, for some weird reason - * the direct mount will not receive events from the - * kernel. */ - r = sd_event_add_io(UNIT(a)->manager->event, &a->pipe_event_source, p[0], EPOLLIN, automount_dispatch_io, a); if (r < 0) goto fail; @@ -742,8 +739,9 @@ static void automount_stop_expire(Automount *a) { (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF); } -static void automount_enter_runnning(Automount *a) { +static void automount_enter_running(Automount *a) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + Unit *trigger; struct stat st; int r; @@ -772,22 +770,24 @@ static void automount_enter_runnning(Automount *a) { goto fail; } - if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id) + /* The mount unit may have been explicitly started before we got the + * autofs request. Ack it to unblock anything waiting on the mount point. */ + if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id) { log_unit_info(UNIT(a), "Automount point already active?"); - else { - Unit *trigger; + automount_send_ready(a, a->tokens, 0); + return; + } - trigger = UNIT_TRIGGER(UNIT(a)); - if (!trigger) { - log_unit_error(UNIT(a), "Unit to trigger vanished."); - goto fail; - } + trigger = UNIT_TRIGGER(UNIT(a)); + if (!trigger) { + log_unit_error(UNIT(a), "Unit to trigger vanished."); + goto fail; + } - r = manager_add_job(UNIT(a)->manager, JOB_START, trigger, JOB_REPLACE, &error, NULL); - if (r < 0) { - log_unit_warning(UNIT(a), "Failed to queue mount startup job: %s", bus_error_message(&error, r)); - goto fail; - } + r = manager_add_job(UNIT(a)->manager, JOB_START, trigger, JOB_REPLACE, &error, NULL); + if (r < 0) { + log_unit_warning(UNIT(a), "Failed to queue mount startup job: %s", bus_error_message(&error, r)); + goto fail; } automount_set_state(a, AUTOMOUNT_RUNNING); @@ -970,7 +970,6 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; union autofs_v5_packet_union packet; Automount *a = AUTOMOUNT(userdata); - struct stat st; Unit *trigger; int r; @@ -1012,7 +1011,7 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo goto fail; } - automount_enter_runnning(a); + automount_enter_running(a); break; case autofs_ptype_expire_direct: @@ -1032,18 +1031,6 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo goto fail; } - /* Before we do anything, let's see if somebody is playing games with us? */ - if (lstat(a->where, &st) < 0) { - log_unit_warning_errno(UNIT(a), errno, "Failed to stat automount point: %m"); - goto fail; - } - - if (!S_ISDIR(st.st_mode) || st.st_dev == a->dev_id) { - log_unit_info(UNIT(a), "Automount point already unmounted?"); - automount_send_ready(a, a->expire_tokens, 0); - break; - } - trigger = UNIT_TRIGGER(UNIT(a)); if (!trigger) { log_unit_error(UNIT(a), "Unit to trigger vanished."); diff --git a/src/core/busname.c b/src/core/busname.c index 88b758eecb..955f6f88d8 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -764,7 +764,7 @@ static int busname_peek_message(BusName *n) { struct kdbus_item *d; struct kdbus_msg *k; size_t start, ps, sz, delta; - void *p = NULL; + void *p = MAP_FAILED; pid_t pid = 0; int r; @@ -825,7 +825,7 @@ static int busname_peek_message(BusName *n) { r = 0; finish: - if (p) + if (p != MAP_FAILED) (void) munmap(p, sz); cmd_free.offset = cmd_recv.msg.offset; diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 774b832a63..71f307fb6c 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -398,8 +398,7 @@ static int whitelist_major(const char *path, const char *name, char type, const return 0; fail: - log_warning_errno(errno, "Failed to read /proc/devices: %m"); - return -errno; + return log_warning_errno(errno, "Failed to read /proc/devices: %m"); } static bool cgroup_context_has_cpu_weight(CGroupContext *c) { diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c index c4067a95bf..12d3ca076b 100644 --- a/src/core/dbus-cgroup.c +++ b/src/core/dbus-cgroup.c @@ -407,7 +407,15 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->cpu_quota_per_sec_usec = u64; unit_invalidate_cgroup(u, CGROUP_MASK_CPU); - unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%0.f%%", (double) (c->cpu_quota_per_sec_usec / 10000)); + if (c->cpu_quota_per_sec_usec == USEC_INFINITY) + unit_write_drop_in_private_format(u, mode, "CPUQuota", + "CPUQuota="); + else + /* config_parse_cpu_quota() requires an integer, so + * truncating division is used on purpose here. */ + unit_write_drop_in_private_format(u, mode, "CPUQuota", + "CPUQuota=%0.f%%", + (double) (c->cpu_quota_per_sec_usec / 10000)); } return 1; diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 7df4cab3f6..c041a7d94c 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -45,6 +45,7 @@ #endif #include "strv.h" #include "syslog-util.h" +#include "unit-printf.h" #include "user-util.h" #include "utf8.h" @@ -159,21 +160,50 @@ static int property_get_ioprio( ExecContext *c = userdata; - int32_t n; assert(bus); assert(reply); assert(c); - if (c->ioprio_set) - n = c->ioprio; - else { - n = ioprio_get(IOPRIO_WHO_PROCESS, 0); - if (n < 0) - n = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 4); - } + return sd_bus_message_append(reply, "i", exec_context_get_effective_ioprio(c)); +} - return sd_bus_message_append(reply, "i", n); +static int property_get_ioprio_class( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + + ExecContext *c = userdata; + + assert(bus); + assert(reply); + assert(c); + + return sd_bus_message_append(reply, "i", IOPRIO_PRIO_CLASS(exec_context_get_effective_ioprio(c))); +} + +static int property_get_ioprio_priority( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + + ExecContext *c = userdata; + + assert(bus); + assert(reply); + assert(c); + + return sd_bus_message_append(reply, "i", IOPRIO_PRIO_DATA(exec_context_get_effective_ioprio(c))); } static int property_get_cpu_sched_policy( @@ -710,7 +740,7 @@ static int property_get_bind_paths( c->bind_mounts[i].source, c->bind_mounts[i].destination, c->bind_mounts[i].ignore_enoent, - c->bind_mounts[i].recursive ? MS_REC : 0); + c->bind_mounts[i].recursive ? (uint64_t) MS_REC : (uint64_t) 0); if (r < 0) return r; } @@ -761,7 +791,8 @@ const sd_bus_vtable bus_exec_vtable[] = { 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), + SD_BUS_PROPERTY("IOSchedulingClass", "i", property_get_ioprio_class, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("IOSchedulingPriority", "i", property_get_ioprio_priority, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("CPUSchedulingPolicy", "i", property_get_cpu_sched_policy, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("CPUSchedulingPriority", "i", property_get_cpu_sched_priority, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("CPUAffinity", "ay", property_get_cpu_affinity, 0, SD_BUS_VTABLE_PROPERTY_CONST), @@ -783,7 +814,6 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("SyslogLevelPrefix", "b", bus_property_get_bool, offsetof(ExecContext, syslog_level_prefix), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SyslogLevel", "i", property_get_syslog_level, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SyslogFacility", "i", property_get_syslog_facility, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("Capabilities", "s", property_get_empty_string, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("SecureBits", "i", bus_property_get_int, offsetof(ExecContext, secure_bits), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("CapabilityBoundingSet", "t", property_get_capability_bounding_set, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("AmbientCapabilities", "t", property_get_ambient_capabilities, 0, SD_BUS_VTABLE_PROPERTY_CONST), @@ -793,9 +823,6 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(ExecContext, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SupplementaryGroups", "as", NULL, offsetof(ExecContext, supplementary_groups), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PAMName", "s", NULL, offsetof(ExecContext, pam_name), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ReadWriteDirectories", "as", NULL, offsetof(ExecContext, read_write_paths), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("ReadOnlyDirectories", "as", NULL, offsetof(ExecContext, read_only_paths), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), - SD_BUS_PROPERTY("InaccessibleDirectories", "as", NULL, offsetof(ExecContext, inaccessible_paths), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("ReadWritePaths", "as", NULL, offsetof(ExecContext, read_write_paths), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ReadOnlyPaths", "as", NULL, offsetof(ExecContext, read_only_paths), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("InaccessiblePaths", "as", NULL, offsetof(ExecContext, inaccessible_paths), SD_BUS_VTABLE_PROPERTY_CONST), @@ -830,6 +857,14 @@ const sd_bus_vtable bus_exec_vtable[] = { 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), + + /* Obsolete/redundant properties: */ + SD_BUS_PROPERTY("Capabilities", "s", property_get_empty_string, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("ReadWriteDirectories", "as", NULL, offsetof(ExecContext, read_write_paths), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("ReadOnlyDirectories", "as", NULL, offsetof(ExecContext, read_only_paths), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("InaccessibleDirectories", "as", NULL, offsetof(ExecContext, inaccessible_paths), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("IOScheduling", "i", property_get_ioprio, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_VTABLE_END }; @@ -1001,7 +1036,7 @@ int bus_exec_context_set_transient_property( return 1; } else if (streq(name, "SyslogLevel")) { - int level; + int32_t level; r = sd_bus_message_read(message, "i", &level); if (r < 0) @@ -1017,7 +1052,7 @@ int bus_exec_context_set_transient_property( return 1; } else if (streq(name, "SyslogFacility")) { - int facility; + int32_t facility; r = sd_bus_message_read(message, "i", &facility); if (r < 0) @@ -1033,7 +1068,7 @@ int bus_exec_context_set_transient_property( return 1; } else if (streq(name, "Nice")) { - int n; + int32_t n; r = sd_bus_message_read(message, "i", &n); if (r < 0) @@ -1049,6 +1084,50 @@ int bus_exec_context_set_transient_property( return 1; + } else if (streq(name, "IOSchedulingClass")) { + int32_t q; + + r = sd_bus_message_read(message, "i", &q); + if (r < 0) + return r; + + if (!ioprio_class_is_valid(q)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid IO scheduling class: %i", q); + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *s = NULL; + + r = ioprio_class_to_string_alloc(q, &s); + if (r < 0) + return r; + + c->ioprio = IOPRIO_PRIO_VALUE(q, IOPRIO_PRIO_DATA(c->ioprio)); + c->ioprio_set = true; + + unit_write_drop_in_private_format(u, mode, name, "IOSchedulingClass=%s", s); + } + + return 1; + + } else if (streq(name, "IOSchedulingPriority")) { + int32_t p; + + r = sd_bus_message_read(message, "i", &p); + if (r < 0) + return r; + + if (!ioprio_priority_is_valid(p)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid IO scheduling priority: %i", p); + + if (mode != UNIT_CHECK) { + c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_PRIO_CLASS(c->ioprio), p); + c->ioprio_set = true; + + unit_write_drop_in_private_format(u, mode, name, "IOSchedulingPriority=%i", p); + } + + return 1; + } else if (STR_IN_SET(name, "TTYPath", "RootDirectory", "RootImage")) { const char *s; @@ -1317,7 +1396,7 @@ int bus_exec_context_set_transient_property( } else if (streq(name, "Environment")) { - _cleanup_strv_free_ char **l = NULL; + _cleanup_strv_free_ char **l = NULL, **q = NULL; r = sd_bus_message_read_strv(message, &l); if (r < 0) @@ -1326,22 +1405,27 @@ int bus_exec_context_set_transient_property( if (!strv_env_is_valid(l)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment block."); - if (mode != UNIT_CHECK) { - _cleanup_free_ char *joined = NULL; - char **e; + r = unit_full_printf_strv(u, l, &q); + if (r < 0) + return r; - if (strv_length(l) == 0) { + if (mode != UNIT_CHECK) { + if (strv_length(q) == 0) { c->environment = strv_free(c->environment); unit_write_drop_in_private_format(u, mode, name, "Environment="); } else { - e = strv_env_merge(2, c->environment, l); + _cleanup_free_ char *joined = NULL; + char **e; + + e = strv_env_merge(2, c->environment, q); if (!e) return -ENOMEM; strv_free(c->environment); c->environment = e; - joined = strv_join_quoted(c->environment); + /* We write just the new settings out to file, with unresolved specifiers */ + joined = strv_join_quoted(q); if (!joined) return -ENOMEM; diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index 85b67318ed..6458ee5c12 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -199,6 +199,39 @@ static int bus_service_set_transient_property( return 1; + } else if (streq(name, "FileDescriptorStoreMax")) { + uint32_t u; + + r = sd_bus_message_read(message, "u", &u); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + s->n_fd_store_max = (unsigned) u; + unit_write_drop_in_private_format(UNIT(s), mode, name, "FileDescriptorStoreMax=%" PRIu32, u); + } + + return 1; + + } else if (streq(name, "NotifyAccess")) { + const char *t; + NotifyAccess k; + + r = sd_bus_message_read(message, "s", &t); + if (r < 0) + return r; + + k = notify_access_from_string(t); + if (k < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid notify access setting %s", t); + + if (mode != UNIT_CHECK) { + s->notify_access = k; + unit_write_drop_in_private_format(UNIT(s), mode, name, "NotifyAccess=%s", notify_access_to_string(s->notify_access)); + } + + return 1; + } else if (streq(name, "ExecStart")) { unsigned n = 0; diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c index efbb0e8915..c98282a5d4 100644 --- a/src/core/dbus-timer.c +++ b/src/core/dbus-timer.c @@ -144,28 +144,14 @@ static int property_get_next_elapse_monotonic( sd_bus_error *error) { Timer *t = userdata; - usec_t x; assert(bus); assert(reply); assert(t); - if (t->next_elapse_monotonic_or_boottime <= 0) - x = 0; - else if (t->wake_system) { - usec_t a, b; - - a = now(CLOCK_MONOTONIC); - b = now(clock_boottime_or_monotonic()); - - if (t->next_elapse_monotonic_or_boottime + a > b) - x = t->next_elapse_monotonic_or_boottime + a - b; - else - x = 0; - } else - x = t->next_elapse_monotonic_or_boottime; - - return sd_bus_message_append(reply, "t", x); + return sd_bus_message_append(reply, "t", + (uint64_t) usec_shift_clock(t->next_elapse_monotonic_or_boottime, + TIMER_MONOTONIC_CLOCK(t), CLOCK_MONOTONIC)); } const sd_bus_vtable bus_timer_vtable[] = { diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index f15bb2196c..b0645ce294 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -748,6 +748,7 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("IgnoreOnIsolate", "b", bus_property_get_bool, offsetof(Unit, ignore_on_isolate), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("NeedDaemonReload", "b", property_get_need_daemon_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("JobTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_timeout), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("JobRunningTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_running_timeout), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("JobTimeoutAction", "s", property_get_emergency_action, offsetof(Unit, job_timeout_action), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("JobTimeoutRebootArgument", "s", NULL, offsetof(Unit, job_timeout_reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ConditionResult", "b", bus_property_get_bool, offsetof(Unit, condition_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), diff --git a/src/core/dbus.c b/src/core/dbus.c index 065f2d81d6..cfc045d282 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -753,13 +753,13 @@ int manager_sync_bus_names(Manager *m, sd_bus *bus) { /* If it is, determine its current owner */ r = sd_bus_get_name_creds(bus, name, SD_BUS_CREDS_UNIQUE_NAME, &creds); if (r < 0) { - log_error_errno(r, "Failed to get bus name owner %s: %m", name); + log_full_errno(r == -ENXIO ? LOG_DEBUG : LOG_ERR, r, "Failed to get bus name owner %s: %m", name); continue; } r = sd_bus_creds_get_unique_name(creds, &unique); if (r < 0) { - log_error_errno(r, "Failed to get unique name for %s: %m", name); + log_full_errno(r == -ENXIO ? LOG_DEBUG : LOG_ERR, r, "Failed to get unique name for %s: %m", name); continue; } diff --git a/src/core/device.c b/src/core/device.c index 0e67c96552..77601c5520 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -112,7 +112,7 @@ static void device_init(Unit *u) { * indefinitely for plugged in devices, something which cannot * happen for the other units since their operations time out * anyway. */ - u->job_timeout = u->manager->default_timeout_start_usec; + u->job_running_timeout = u->manager->default_timeout_start_usec; u->ignore_on_isolate = true; } @@ -501,12 +501,16 @@ static void device_update_found_one(Device *d, bool add, DeviceFound found, bool * now referenced by the kernel, then we assume the * kernel knows it now, and udev might soon too. */ device_set_state(d, DEVICE_TENTATIVE); - else + else { /* If nobody sees the device, or if the device was * previously seen by udev and now is only referenced * from the kernel, then we consider the device is * gone, the kernel just hasn't noticed it yet. */ + device_set_state(d, DEVICE_DEAD); + device_unset_sysfs(d); + } + } static int device_update_found_by_sysfs(Manager *m, const char *sysfs, bool add, DeviceFound found, bool now) { diff --git a/src/core/execute.c b/src/core/execute.c index d7798387c5..d72e5bf08c 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -157,22 +157,26 @@ static int shift_fds(int fds[], unsigned n_fds) { return 0; } -static int flags_fds(const int fds[], unsigned n_fds, bool nonblock) { - unsigned i; +static int flags_fds(const int fds[], unsigned n_storage_fds, unsigned n_socket_fds, bool nonblock) { + unsigned i, n_fds; int r; + n_fds = n_storage_fds + n_socket_fds; if (n_fds <= 0) return 0; assert(fds); - /* Drops/Sets O_NONBLOCK and FD_CLOEXEC from the file flags */ + /* Drops/Sets O_NONBLOCK and FD_CLOEXEC from the file flags. + * O_NONBLOCK only applies to socket activation though. */ for (i = 0; i < n_fds; i++) { - r = fd_nonblock(fds[i], nonblock); - if (r < 0) - return r; + if (i < n_socket_fds) { + r = fd_nonblock(fds[i], nonblock); + if (r < 0) + return r; + } /* We unconditionally drop FD_CLOEXEC from the fds, * since after all we want to pass these fds to our @@ -1670,7 +1674,7 @@ static bool exec_needs_mount_namespace( context->protect_control_groups) return true; - if (context->mount_apivfs) + if (context->mount_apivfs && (context->root_image || context->root_directory)) return true; return false; @@ -2241,7 +2245,9 @@ static int exec_child( char **argv, int socket_fd, int named_iofds[3], - int *fds, unsigned n_fds, + int *fds, + unsigned n_storage_fds, + unsigned n_socket_fds, char **files_env, int user_lookup_fd, int *exit_status, @@ -2258,6 +2264,7 @@ static int exec_child( uid_t uid = UID_INVALID; gid_t gid = GID_INVALID; int i, r, ngids = 0; + unsigned n_fds; assert(unit); assert(command); @@ -2298,6 +2305,7 @@ static int exec_child( log_forget_fds(); + n_fds = n_storage_fds + n_socket_fds; r = close_remaining_fds(params, runtime, dcreds, user_lookup_fd, socket_fd, fds, n_fds); if (r < 0) { *exit_status = EXIT_FDS; @@ -2669,7 +2677,7 @@ static int exec_child( if (r >= 0) r = shift_fds(fds, n_fds); if (r >= 0) - r = flags_fds(fds, n_fds, context->non_blocking); + r = flags_fds(fds, n_storage_fds, n_socket_fds, context->non_blocking); if (r < 0) { *exit_status = EXIT_FDS; return r; @@ -2887,9 +2895,9 @@ static int exec_child( if (line) { log_open(); log_struct(LOG_DEBUG, - LOG_UNIT_ID(unit), "EXECUTABLE=%s", command->path, LOG_UNIT_MESSAGE(unit, "Executing: %s", line), + LOG_UNIT_ID(unit), NULL); log_close(); } @@ -2909,7 +2917,8 @@ int exec_spawn(Unit *unit, pid_t *ret) { _cleanup_strv_free_ char **files_env = NULL; - int *fds = NULL; unsigned n_fds = 0; + int *fds = NULL; + unsigned n_storage_fds = 0, n_socket_fds = 0; _cleanup_free_ char *line = NULL; int socket_fd, r; int named_iofds[3] = { -1, -1, -1 }; @@ -2921,22 +2930,28 @@ int exec_spawn(Unit *unit, assert(context); assert(ret); assert(params); - assert(params->fds || params->n_fds <= 0); + assert(params->fds || (params->n_storage_fds + params->n_socket_fds <= 0)); if (context->std_input == EXEC_INPUT_SOCKET || context->std_output == EXEC_OUTPUT_SOCKET || context->std_error == EXEC_OUTPUT_SOCKET) { - if (params->n_fds != 1) { + if (params->n_socket_fds > 1) { log_unit_error(unit, "Got more than one socket."); return -EINVAL; } + if (params->n_socket_fds == 0) { + log_unit_error(unit, "Got no socket."); + return -EINVAL; + } + socket_fd = params->fds[0]; } else { socket_fd = -1; fds = params->fds; - n_fds = params->n_fds; + n_storage_fds = params->n_storage_fds; + n_socket_fds = params->n_socket_fds; } r = exec_context_named_iofds(unit, context, params, named_iofds); @@ -2953,9 +2968,9 @@ int exec_spawn(Unit *unit, return log_oom(); log_struct(LOG_DEBUG, - LOG_UNIT_ID(unit), LOG_UNIT_MESSAGE(unit, "About to execute: %s", line), "EXECUTABLE=%s", command->path, + LOG_UNIT_ID(unit), NULL); pid = fork(); if (pid < 0) @@ -2974,7 +2989,9 @@ int exec_spawn(Unit *unit, argv, socket_fd, named_iofds, - fds, n_fds, + fds, + n_storage_fds, + n_socket_fds, files_env, unit->manager->user_lookup_fds[1], &exit_status, @@ -2989,6 +3006,14 @@ int exec_spawn(Unit *unit, error_message), "EXECUTABLE=%s", command->path, NULL); + else if (r == -ENOENT && command->ignore) + log_struct_errno(LOG_INFO, r, + "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR, + LOG_UNIT_ID(unit), + LOG_UNIT_MESSAGE(unit, "Skipped spawning %s: %m", + command->path), + "EXECUTABLE=%s", command->path, + NULL); else log_struct_errno(LOG_ERR, r, "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR, @@ -3175,6 +3200,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]; + unsigned n_fds; assert(c); assert(p); @@ -3186,7 +3212,9 @@ int exec_context_named_iofds(Unit *unit, const ExecContext *c, const ExecParamet for (i = 0; i < 3; i++) stdio_fdname[i] = exec_context_fdname(c, i); - for (i = 0; i < p->n_fds && targets > 0; i++) + n_fds = p->n_storage_fds + p->n_socket_fds; + + for (i = 0; i < n_fds && targets > 0; i++) if (named_iofds[STDIN_FILENO] < 0 && c->std_input == EXEC_INPUT_NAMED_FD && stdio_fdname[STDIN_FILENO] && @@ -3224,10 +3252,10 @@ int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l) { STRV_FOREACH(i, c->environment_files) { char *fn; int k; + unsigned n; bool ignore = false; char **p; _cleanup_globfree_ glob_t pglob = {}; - int count, n; fn = *i; @@ -3245,23 +3273,19 @@ int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l) { } /* Filename supports globbing, take all matching files */ - errno = 0; - if (glob(fn, 0, NULL, &pglob) != 0) { + k = safe_glob(fn, 0, &pglob); + if (k < 0) { if (ignore) continue; strv_free(r); - return errno > 0 ? -errno : -EINVAL; + return k; } - count = pglob.gl_pathc; - if (count == 0) { - if (ignore) - continue; - strv_free(r); - return -EINVAL; - } - for (n = 0; n < count; n++) { + /* When we don't match anything, -ENOENT should be returned */ + assert(pglob.gl_pathc > 0); + + for (n = 0; n < pglob.gl_pathc; n++) { k = load_env_file(NULL, pglob.gl_pathv[n], NULL, &p); if (k < 0) { if (ignore) @@ -3677,6 +3701,21 @@ bool exec_context_maintains_privileges(ExecContext *c) { return false; } +int exec_context_get_effective_ioprio(ExecContext *c) { + int p; + + assert(c); + + if (c->ioprio_set) + return c->ioprio; + + p = ioprio_get(IOPRIO_WHO_PROCESS, 0); + if (p < 0) + return IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 4); + + return p; +} + void exec_status_start(ExecStatus *s, pid_t pid) { assert(s); diff --git a/src/core/execute.h b/src/core/execute.h index 9f2b6fd39e..9f07aa4aef 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -246,7 +246,8 @@ struct ExecParameters { int *fds; char **fd_names; - unsigned n_fds; + unsigned n_storage_fds; + unsigned n_socket_fds; ExecFlags flags; bool selinux_context_net:1; @@ -306,6 +307,8 @@ const char* exec_context_fdname(const ExecContext *c, int fd_index); bool exec_context_may_touch_console(ExecContext *c); bool exec_context_maintains_privileges(ExecContext *c); +int exec_context_get_effective_ioprio(ExecContext *c); + void exec_status_start(ExecStatus *s, pid_t pid); void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status); void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix); diff --git a/src/core/ima-setup.c b/src/core/ima-setup.c index 94ae429f46..7b5c98a57c 100644 --- a/src/core/ima-setup.c +++ b/src/core/ima-setup.c @@ -49,6 +49,11 @@ int ima_setup(void) { return 0; } + if (access(IMA_POLICY_PATH, F_OK) < 0) { + log_debug("No IMA custom policy file "IMA_POLICY_PATH", ignoring."); + return 0; + } + imafd = open(IMA_SECFS_POLICY, O_WRONLY|O_CLOEXEC); if (imafd < 0) { log_error_errno(errno, "Failed to open the IMA kernel interface "IMA_SECFS_POLICY", ignoring: %m"); @@ -62,8 +67,7 @@ int ima_setup(void) { /* fall back to copying the policy line-by-line */ input = fopen(IMA_POLICY_PATH, "re"); if (!input) { - log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno, - "Failed to open the IMA custom policy file "IMA_POLICY_PATH", ignoring: %m"); + log_warning_errno(errno, "Failed to open the IMA custom policy file "IMA_POLICY_PATH", ignoring: %m"); return 0; } diff --git a/src/core/job.c b/src/core/job.c index e2349830a8..8e2039d321 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -576,6 +576,7 @@ int job_run_and_invalidate(Job *j) { if (!job_is_runnable(j)) return -EAGAIN; + job_start_timer(j, true); job_set_state(j, JOB_RUNNING); job_add_to_dbus_queue(j); @@ -696,20 +697,20 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR return NULL; } -static void job_print_status_message(Unit *u, JobType t, JobResult result) { - static const struct { - const char *color, *word; - } statuses[_JOB_RESULT_MAX] = { - [JOB_DONE] = { ANSI_GREEN, " OK " }, - [JOB_TIMEOUT] = { ANSI_HIGHLIGHT_RED, " TIME " }, - [JOB_FAILED] = { ANSI_HIGHLIGHT_RED, "FAILED" }, - [JOB_DEPENDENCY] = { ANSI_HIGHLIGHT_YELLOW, "DEPEND" }, - [JOB_SKIPPED] = { ANSI_HIGHLIGHT, " INFO " }, - [JOB_ASSERT] = { ANSI_HIGHLIGHT_YELLOW, "ASSERT" }, - [JOB_UNSUPPORTED] = { ANSI_HIGHLIGHT_YELLOW, "UNSUPP" }, - [JOB_COLLECTED] = { ANSI_HIGHLIGHT, " INFO " }, - }; +static const struct { + const char *color, *word; +} job_print_status_messages [_JOB_RESULT_MAX] = { + [JOB_DONE] = { ANSI_GREEN, " OK " }, + [JOB_TIMEOUT] = { ANSI_HIGHLIGHT_RED, " TIME " }, + [JOB_FAILED] = { ANSI_HIGHLIGHT_RED, "FAILED" }, + [JOB_DEPENDENCY] = { ANSI_HIGHLIGHT_YELLOW, "DEPEND" }, + [JOB_SKIPPED] = { ANSI_HIGHLIGHT, " INFO " }, + [JOB_ASSERT] = { ANSI_HIGHLIGHT_YELLOW, "ASSERT" }, + [JOB_UNSUPPORTED] = { ANSI_HIGHLIGHT_YELLOW, "UNSUPP" }, + /* JOB_COLLECTED */ +}; +static void job_print_status_message(Unit *u, JobType t, JobResult result) { const char *format; const char *status; @@ -721,14 +722,19 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { if (t == JOB_RELOAD) return; + if (!job_print_status_messages[result].word) + return; + format = job_get_status_message_format(u, t, result); if (!format) return; if (log_get_show_color()) - status = strjoina(statuses[result].color, statuses[result].word, ANSI_NORMAL); + status = strjoina(job_print_status_messages[result].color, + job_print_status_messages[result].word, + ANSI_NORMAL); else - status = statuses[result].word; + status = job_print_status_messages[result].word; if (result != JOB_DONE) manager_flip_auto_status(u->manager, true); @@ -740,7 +746,7 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { if (t == JOB_START && result == JOB_FAILED) { _cleanup_free_ char *quoted; - quoted = shell_maybe_quote(u->id); + quoted = shell_maybe_quote(u->id, ESCAPE_BACKSLASH); manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, "See 'systemctl status %s' for details.", strna(quoted)); } } @@ -765,10 +771,9 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) { assert(t >= 0); assert(t < _JOB_TYPE_MAX); - /* Skip this if it goes to the console. since we already print - * to the console anyway... */ - - if (log_on_console()) + /* Skip printing if output goes to the console, and job_print_status_message() + will actually print something to the console. */ + if (log_on_console() && job_print_status_messages[result].word) return; format = job_get_status_message_format(u, t, result); @@ -800,18 +805,18 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) { default: log_struct(job_result_log_level[result], - LOG_UNIT_ID(u), LOG_MESSAGE("%s", buf), "RESULT=%s", job_result_to_string(result), + LOG_UNIT_ID(u), NULL); return; } log_struct(job_result_log_level[result], - mid, - LOG_UNIT_ID(u), LOG_MESSAGE("%s", buf), "RESULT=%s", job_result_to_string(result), + LOG_UNIT_ID(u), + mid, NULL); } @@ -949,22 +954,46 @@ static int job_dispatch_timer(sd_event_source *s, uint64_t monotonic, void *user return 0; } -int job_start_timer(Job *j) { +int job_start_timer(Job *j, bool job_running) { int r; + usec_t timeout_time, old_timeout_time; - if (j->timer_event_source) - return 0; + if (job_running) { + j->begin_running_usec = now(CLOCK_MONOTONIC); + + if (j->unit->job_running_timeout == USEC_INFINITY) + return 0; - j->begin_usec = now(CLOCK_MONOTONIC); + timeout_time = usec_add(j->begin_running_usec, j->unit->job_running_timeout); - if (j->unit->job_timeout == USEC_INFINITY) - return 0; + if (j->timer_event_source) { + /* Update only if JobRunningTimeoutSec= results in earlier timeout */ + r = sd_event_source_get_time(j->timer_event_source, &old_timeout_time); + if (r < 0) + return r; + + if (old_timeout_time <= timeout_time) + return 0; + + return sd_event_source_set_time(j->timer_event_source, timeout_time); + } + } else { + if (j->timer_event_source) + return 0; + + j->begin_usec = now(CLOCK_MONOTONIC); + + if (j->unit->job_timeout == USEC_INFINITY) + return 0; + + timeout_time = usec_add(j->begin_usec, j->unit->job_timeout); + } r = sd_event_add_time( j->manager->event, &j->timer_event_source, CLOCK_MONOTONIC, - usec_add(j->begin_usec, j->unit->job_timeout), 0, + timeout_time, 0, job_dispatch_timer, j); if (r < 0) return r; @@ -1027,6 +1056,8 @@ int job_serialize(Job *j, FILE *f) { if (j->begin_usec > 0) fprintf(f, "job-begin="USEC_FMT"\n", j->begin_usec); + if (j->begin_running_usec > 0) + fprintf(f, "job-begin-running="USEC_FMT"\n", j->begin_running_usec); bus_track_serialize(j->bus_track, f, "subscribed"); @@ -1124,6 +1155,14 @@ int job_deserialize(Job *j, FILE *f) { else j->begin_usec = ull; + } else if (streq(l, "job-begin-running")) { + unsigned long long ull; + + if (sscanf(v, "%llu", &ull) != 1) + log_debug("Failed to parse job-begin-running value %s", v); + else + j->begin_running_usec = ull; + } else if (streq(l, "subscribed")) { if (strv_extend(&j->deserialized_clients, v) < 0) @@ -1134,6 +1173,7 @@ int job_deserialize(Job *j, FILE *f) { int job_coldplug(Job *j) { int r; + usec_t timeout_time = USEC_INFINITY; assert(j); @@ -1147,7 +1187,18 @@ int job_coldplug(Job *j) { /* Maybe due to new dependencies we don't actually need this job anymore? */ job_add_to_gc_queue(j); - if (j->begin_usec == 0 || j->unit->job_timeout == USEC_INFINITY) + /* Create timer only when job began or began running and the respective timeout is finite. + * Follow logic of job_start_timer() if both timeouts are finite */ + if (j->begin_usec == 0) + return 0; + + if (j->unit->job_timeout != USEC_INFINITY) + timeout_time = usec_add(j->begin_usec, j->unit->job_timeout); + + if (j->begin_running_usec > 0 && j->unit->job_running_timeout != USEC_INFINITY) + timeout_time = MIN(timeout_time, usec_add(j->begin_running_usec, j->unit->job_running_timeout)); + + if (timeout_time == USEC_INFINITY) return 0; j->timer_event_source = sd_event_source_unref(j->timer_event_source); @@ -1156,7 +1207,7 @@ int job_coldplug(Job *j) { j->manager->event, &j->timer_event_source, CLOCK_MONOTONIC, - usec_add(j->begin_usec, j->unit->job_timeout), 0, + timeout_time, 0, job_dispatch_timer, j); if (r < 0) log_debug_errno(r, "Failed to restart timeout for job: %m"); @@ -1263,9 +1314,8 @@ bool job_check_gc(Job *j) { return true; } - /* If we are going down, but something else is orederd After= us, then it needs to wait for us */ - if (IN_SET(j->type, JOB_STOP, JOB_RESTART)) { - + /* If we are going down, but something else is ordered After= us, then it needs to wait for us */ + if (IN_SET(j->type, JOB_STOP, JOB_RESTART)) SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i) { if (!other->job) continue; @@ -1275,7 +1325,6 @@ bool job_check_gc(Job *j) { return true; } - } /* The logic above is kinda the inverse of the job_is_runnable() logic. Specifically, if the job "we" is * ordered before the job "other": diff --git a/src/core/job.h b/src/core/job.h index bea743f462..b17001889e 100644 --- a/src/core/job.h +++ b/src/core/job.h @@ -150,6 +150,7 @@ struct Job { sd_event_source *timer_event_source; usec_t begin_usec; + usec_t begin_running_usec; /* * This tracks where to send signals, and also which clients @@ -220,7 +221,7 @@ int job_type_merge_and_collapse(JobType *a, JobType b, Unit *u); void job_add_to_run_queue(Job *j); void job_add_to_dbus_queue(Job *j); -int job_start_timer(Job *j); +int job_start_timer(Job *j, bool job_running); int job_run_and_invalidate(Job *j); int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool already); diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c index ff3636149a..00f09ce60a 100644 --- a/src/core/load-dropin.c +++ b/src/core/load-dropin.c @@ -29,25 +29,27 @@ #include "unit-name.h" #include "unit.h" -static bool unit_name_compatible(const char *a, const char *b) { +static int unit_name_compatible(const char *a, const char *b) { _cleanup_free_ char *prefix = NULL; int r; /* the straightforward case: the symlink name matches the target */ if (streq(a, b)) - return true; + return 1; r = unit_name_template(a, &prefix); - if (r < 0) { - log_oom(); - return true; - } + if (r == -EINVAL) + /* not a template */ + return 0; + if (r < 0) + /* oom, or some other failure. Just skip the warning. */ + return r; /* an instance name points to a target that is just the template name */ if (streq(prefix, b)) - return true; + return 1; - return false; + return 0; } static int process_deps(Unit *u, UnitDependency dependency, const char *dir_suffix) { @@ -104,7 +106,12 @@ static int process_deps(Unit *u, UnitDependency dependency, const char *dir_suff /* 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))) + r = unit_name_compatible(entry, basename(target)); + if (r < 0) { + log_unit_warning_errno(u, r, "Can't check if names %s and %s are compatible, ignoring: %m", entry, basename(target)); + continue; + } + if (r == 0) log_unit_warning(u, "%s dependency dropin %s target %s has different name", unit_dependency_to_string(dependency), *p, target); diff --git a/src/core/load-fragment-gperf-nulstr.awk b/src/core/load-fragment-gperf-nulstr.awk new file mode 100644 index 0000000000..b52438abe3 --- /dev/null +++ b/src/core/load-fragment-gperf-nulstr.awk @@ -0,0 +1,14 @@ +BEGIN{ + keywords=0 ; FS="," ; + print "extern const char load_fragment_gperf_nulstr[];" ; + print "const char load_fragment_gperf_nulstr[] =" +} +keyword==1 { + print "\"" $$1 "\\0\"" +} +/%%/ { + keyword=1 +} +END { + print ";" +} diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index cb9e6fea27..5b5a86250e 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -18,8 +18,8 @@ struct ConfigPerfItem; 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.RootDirectory, config_parse_unit_path_printf, true, offsetof($1, exec_context.root_directory) +$1.RootImage, config_parse_unit_path_printf, true, 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) @@ -35,7 +35,7 @@ $1.UMask, config_parse_mode, 0, $1.Environment, config_parse_environ, 0, offsetof($1, exec_context.environment) $1.EnvironmentFile, config_parse_unit_env_file, 0, offsetof($1, exec_context.environment_files) $1.PassEnvironment, config_parse_pass_environ, 0, offsetof($1, exec_context.pass_environment) -$1.DynamicUser, config_parse_bool, 0, offsetof($1, exec_context.dynamic_user) +$1.DynamicUser, config_parse_bool, true, offsetof($1, exec_context.dynamic_user) $1.StandardInput, config_parse_exec_input, 0, offsetof($1, exec_context) $1.StandardOutput, config_parse_exec_output, 0, offsetof($1, exec_context) $1.StandardError, config_parse_exec_output, 0, offsetof($1, exec_context) @@ -194,6 +194,7 @@ Unit.OnFailureIsolate, config_parse_job_mode_isolate, 0, Unit.IgnoreOnIsolate, config_parse_bool, 0, offsetof(Unit, ignore_on_isolate) Unit.IgnoreOnSnapshot, config_parse_warn_compat, DISABLED_LEGACY, 0 Unit.JobTimeoutSec, config_parse_sec_fix_0, 0, offsetof(Unit, job_timeout) +Unit.JobRunningTimeoutSec, config_parse_sec, 0, offsetof(Unit, job_running_timeout) Unit.JobTimeoutAction, config_parse_emergency_action, 0, offsetof(Unit, job_timeout_action) Unit.JobTimeoutRebootArgument, config_parse_unit_string_printf, 0, offsetof(Unit, job_timeout_reboot_arg) Unit.StartLimitIntervalSec, config_parse_sec, 0, offsetof(Unit, start_limit.interval) @@ -220,6 +221,8 @@ Unit.ConditionSecurity, config_parse_unit_condition_string, CONDITION_S Unit.ConditionCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, offsetof(Unit, conditions) Unit.ConditionHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, conditions) Unit.ConditionACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, conditions) +Unit.ConditionUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, conditions) +Unit.ConditionGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, conditions) Unit.ConditionNull, config_parse_unit_condition_null, 0, offsetof(Unit, conditions) Unit.AssertPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, asserts) Unit.AssertPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, asserts) @@ -239,6 +242,8 @@ Unit.AssertSecurity, config_parse_unit_condition_string, CONDITION_S Unit.AssertCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, offsetof(Unit, asserts) Unit.AssertHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, asserts) Unit.AssertACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, asserts) +Unit.AssertUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, asserts) +Unit.AssertGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, asserts) Unit.AssertNull, config_parse_unit_condition_null, 0, offsetof(Unit, asserts) m4_dnl Service.PIDFile, config_parse_unit_path_printf, 0, offsetof(Service, pid_file) @@ -298,7 +303,7 @@ Socket.ExecStartPre, config_parse_exec, SOCKET_EXEC Socket.ExecStartPost, config_parse_exec, SOCKET_EXEC_START_POST, offsetof(Socket, exec_command) Socket.ExecStopPre, config_parse_exec, SOCKET_EXEC_STOP_PRE, offsetof(Socket, exec_command) Socket.ExecStopPost, config_parse_exec, SOCKET_EXEC_STOP_POST, offsetof(Socket, exec_command) -Socket.TimeoutSec, config_parse_sec, 0, offsetof(Socket, timeout_usec) +Socket.TimeoutSec, config_parse_sec_fix_0, 0, offsetof(Socket, timeout_usec) Socket.SocketUser, config_parse_user_group, 0, offsetof(Socket, user) Socket.SocketGroup, config_parse_user_group, 0, offsetof(Socket, group) Socket.SocketMode, config_parse_mode, 0, offsetof(Socket, socket_mode) @@ -362,7 +367,7 @@ Mount.What, config_parse_unit_string_printf, 0, Mount.Where, config_parse_path, 0, offsetof(Mount, where) Mount.Options, config_parse_unit_string_printf, 0, offsetof(Mount, parameters_fragment.options) Mount.Type, config_parse_string, 0, offsetof(Mount, parameters_fragment.fstype) -Mount.TimeoutSec, config_parse_sec, 0, offsetof(Mount, timeout_usec) +Mount.TimeoutSec, config_parse_sec_fix_0, 0, offsetof(Mount, timeout_usec) Mount.DirectoryMode, config_parse_mode, 0, offsetof(Mount, directory_mode) Mount.SloppyOptions, config_parse_bool, 0, offsetof(Mount, sloppy_options) Mount.LazyUnmount, config_parse_bool, 0, offsetof(Mount, lazy_unmount) @@ -373,12 +378,12 @@ KILL_CONTEXT_CONFIG_ITEMS(Mount)m4_dnl m4_dnl Automount.Where, config_parse_path, 0, offsetof(Automount, where) Automount.DirectoryMode, config_parse_mode, 0, offsetof(Automount, directory_mode) -Automount.TimeoutIdleSec, config_parse_sec, 0, offsetof(Automount, timeout_idle_usec) +Automount.TimeoutIdleSec, config_parse_sec_fix_0, 0, offsetof(Automount, timeout_idle_usec) m4_dnl Swap.What, config_parse_path, 0, offsetof(Swap, parameters_fragment.what) Swap.Priority, config_parse_int, 0, offsetof(Swap, parameters_fragment.priority) Swap.Options, config_parse_unit_string_printf, 0, offsetof(Swap, parameters_fragment.options) -Swap.TimeoutSec, config_parse_sec, 0, offsetof(Swap, timeout_usec) +Swap.TimeoutSec, config_parse_sec_fix_0, 0, offsetof(Swap, timeout_usec) EXEC_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl CGROUP_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl KILL_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 5b7471c0d0..9d5c39b3dd 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -242,6 +242,7 @@ int config_parse_unit_path_printf( _cleanup_free_ char *k = NULL; Unit *u = userdata; int r; + bool fatal = ltype; assert(filename); assert(lvalue); @@ -250,8 +251,10 @@ int config_parse_unit_path_printf( r = unit_full_printf(u, rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue); - return 0; + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to resolve unit specifiers on %s%s: %m", + fatal ? "" : ", ignoring", rvalue); + return fatal ? -ENOEXEC : 0; } return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata); @@ -392,7 +395,9 @@ int config_parse_socket_listen(const char *unit, r = socket_address_parse_and_warn(&p->address, k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address value, ignoring: %s", rvalue); + if (r != -EAFNOSUPPORT) + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address value, ignoring: %s", rvalue); + return 0; } @@ -634,26 +639,36 @@ int config_parse_exec( r = unit_full_printf(u, f, &path); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", f); - return 0; + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to resolve unit specifiers on %s%s: %m", + f, ignore ? ", ignoring" : ""); + return ignore ? 0 : -ENOEXEC; } if (isempty(path)) { /* First word is either "-" or "@" with no command. */ - log_syntax(unit, LOG_ERR, filename, line, 0, "Empty path in command line, ignoring: \"%s\"", rvalue); - return 0; + log_syntax(unit, LOG_ERR, filename, line, 0, + "Empty path in command line%s: \"%s\"", + ignore ? ", ignoring" : "", rvalue); + return ignore ? 0 : -ENOEXEC; } if (!string_is_safe(path)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path contains special characters, ignoring: %s", rvalue); - return 0; + log_syntax(unit, LOG_ERR, filename, line, 0, + "Executable path contains special characters%s: %s", + ignore ? ", ignoring" : "", rvalue); + return ignore ? 0 : -ENOEXEC; } if (!path_is_absolute(path)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path is not absolute, ignoring: %s", rvalue); - return 0; + log_syntax(unit, LOG_ERR, filename, line, 0, + "Executable path is not absolute%s: %s", + ignore ? ", ignoring" : "", rvalue); + return ignore ? 0 : -ENOEXEC; } if (endswith(path, "/")) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path specifies a directory, ignoring: %s", rvalue); - return 0; + log_syntax(unit, LOG_ERR, filename, line, 0, + "Executable path specifies a directory%s: %s", + ignore ? ", ignoring" : "", rvalue); + return ignore ? 0 : -ENOEXEC; } if (!separate_argv0) { @@ -706,12 +721,14 @@ int config_parse_exec( if (r == 0) break; if (r < 0) - return 0; + return ignore ? 0 : -ENOEXEC; r = unit_full_printf(u, word, &resolved); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to resolve unit specifiers on %s, ignoring: %m", word); - return 0; + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to resolve unit specifiers on %s%s: %m", + word, ignore ? ", ignoring" : ""); + return ignore ? 0 : -ENOEXEC; } if (!GREEDY_REALLOC(n, nbufsize, nlen + 2)) @@ -722,8 +739,10 @@ int config_parse_exec( } if (!n || !n[0]) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Empty executable name or zeroeth argument, ignoring: %s", rvalue); - return 0; + log_syntax(unit, LOG_ERR, filename, line, 0, + "Empty executable name or zeroeth argument%s: %s", + ignore ? ", ignoring" : "", rvalue); + return ignore ? 0 : -ENOEXEC; } nce = new0(ExecCommand, 1); @@ -938,8 +957,8 @@ int config_parse_exec_io_priority(const char *unit, assert(rvalue); assert(data); - r = safe_atoi(rvalue, &i); - if (r < 0 || i < 0 || i >= IOPRIO_BE_NR) { + r = ioprio_parse_priority(rvalue, &i); + if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IO priority, ignoring: %s", rvalue); return 0; } @@ -1330,8 +1349,10 @@ int config_parse_exec_selinux_context( r = unit_full_printf(u, rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); - return 0; + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to resolve specifiers%s: %m", + ignore ? ", ignoring" : ""); + return ignore ? 0 : -ENOEXEC; } free(c->selinux_context); @@ -1378,8 +1399,10 @@ int config_parse_exec_apparmor_profile( r = unit_full_printf(u, rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); - return 0; + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to resolve specifiers%s: %m", + ignore ? ", ignoring" : ""); + return ignore ? 0 : -ENOEXEC; } free(c->apparmor_profile); @@ -1426,8 +1449,10 @@ int config_parse_exec_smack_process_label( r = unit_full_printf(u, rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %m"); - return 0; + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to resolve specifiers%s: %m", + ignore ? ", ignoring" : ""); + return ignore ? 0 : -ENOEXEC; } free(c->smack_process_label); @@ -1645,19 +1670,19 @@ int config_parse_socket_service( r = unit_name_printf(UNIT(s), rvalue, &p); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers, ignoring: %s", rvalue); - return 0; + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve specifiers: %s", rvalue); + return -ENOEXEC; } if (!endswith(p, ".service")) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service, ignoring: %s", rvalue); - return 0; + log_syntax(unit, LOG_ERR, filename, line, 0, "Unit must be of type service: %s", rvalue); + return -ENOEXEC; } r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r)); - return 0; + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s: %s", rvalue, bus_error_message(&error, r)); + return -ENOEXEC; } unit_ref_set(&s->service, x); @@ -1868,15 +1893,12 @@ int config_parse_sec_fix_0( * compatibility with older versions of systemd where 0 instead of infinity was used as indicator to turn off a * timeout. */ - r = parse_sec(rvalue, usec); + r = parse_sec_fix_0(rvalue, usec); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse %s= parameter, ignoring: %s", lvalue, rvalue); return 0; } - if (*usec <= 0) - *usec = USEC_INFINITY; - return 0; } @@ -1908,13 +1930,13 @@ int config_parse_user_group( r = unit_full_printf(u, rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", rvalue); - return 0; + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", rvalue); + return -ENOEXEC; } if (!valid_user_group_name_or_id(k)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID, ignoring: %s", k); - return 0; + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k); + return -ENOEXEC; } n = k; @@ -1972,19 +1994,19 @@ int config_parse_user_group_strv( if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); - break; + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax: %s", rvalue); + return -ENOEXEC; } r = unit_full_printf(u, word, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s, ignoring: %m", word); - continue; + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in %s: %m", word); + return -ENOEXEC; } if (!valid_user_group_name_or_id(k)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID, ignoring: %s", k); - continue; + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid user/group name or numeric ID: %s", k); + return -ENOEXEC; } r = strv_push(users, k); @@ -2143,25 +2165,28 @@ int config_parse_working_directory( r = unit_full_printf(u, rvalue, &k); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in working directory path '%s', ignoring: %m", rvalue); - return 0; + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to resolve unit specifiers in working directory path '%s'%s: %m", + rvalue, missing_ok ? ", ignoring" : ""); + return missing_ok ? 0 : -ENOEXEC; } path_kill_slashes(k); if (!utf8_is_valid(k)) { log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue); - return 0; + return missing_ok ? 0 : -ENOEXEC; } if (!path_is_absolute(k)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Working directory path '%s' is not absolute, ignoring.", rvalue); - return 0; + log_syntax(unit, LOG_ERR, filename, line, 0, + "Working directory path '%s' is not absolute%s.", + rvalue, missing_ok ? ", ignoring" : ""); + return missing_ok ? 0 : -ENOEXEC; } - free_and_replace(c->working_directory, k); - c->working_directory_home = false; + free_and_replace(c->working_directory, k); } c->working_directory_missing_ok = missing_ok; @@ -3907,6 +3932,7 @@ int config_parse_bind_paths( void *userdata) { ExecContext *c = data; + Unit *u = userdata; const char *p; int r; @@ -3926,6 +3952,7 @@ int config_parse_bind_paths( p = rvalue; for (;;) { _cleanup_free_ char *source = NULL, *destination = NULL; + _cleanup_free_ char *sresolved = NULL, *dresolved = NULL; char *s = NULL, *d = NULL; bool rbind = true, ignore_enoent = false; @@ -3939,7 +3966,14 @@ int config_parse_bind_paths( return 0; } - s = source; + r = unit_full_printf(u, source, &sresolved); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to resolved specifiers in \"%s\", ignoring: %m", source); + return 0; + } + + s = sresolved; if (s[0] == '-') { ignore_enoent = true; s++; @@ -3970,16 +4004,23 @@ int config_parse_bind_paths( return 0; } - if (!utf8_is_valid(destination)) { - log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, destination); + r = unit_full_printf(u, destination, &dresolved); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to resolved specifiers in \"%s\", ignoring: %m", destination); + return 0; + } + + if (!utf8_is_valid(dresolved)) { + log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, dresolved); return 0; } - if (!path_is_absolute(destination)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute destination path, ignoring: %s", destination); + if (!path_is_absolute(dresolved)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute destination path, ignoring: %s", dresolved); return 0; } - d = path_kill_slashes(destination); + d = path_kill_slashes(dresolved); /* Optionally, there's also a short option string specified */ if (p && p[-1] == ':') { @@ -4441,8 +4482,11 @@ int unit_load_fragment(Unit *u) { return r; r = load_from_path(u, k); - if (r < 0) + if (r < 0) { + if (r == -ENOEXEC) + log_unit_notice(u, "Unit configuration has fatal error, unit will not be started."); return r; + } if (u->load_state == UNIT_STUB) { SET_FOREACH(t, u->names, i) { diff --git a/src/core/loopback-setup.c b/src/core/loopback-setup.c index 04062a7910..c167305ca9 100644 --- a/src/core/loopback-setup.c +++ b/src/core/loopback-setup.c @@ -26,10 +26,36 @@ #include "missing.h" #include "netlink-util.h" -static int start_loopback(sd_netlink *rtnl) { +#define LOOPBACK_SETUP_TIMEOUT_USEC (5 * USEC_PER_SEC) + +struct state { + unsigned n_messages; + int rcode; + const char *title; +}; + +static int generic_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + struct state *s = userdata; + + assert(s); + assert(s->n_messages > 0); + s->n_messages--; + + errno = 0; + log_debug_errno(sd_netlink_message_get_errno(m), "Failed to %s: %m", s->title); + + s->rcode = sd_netlink_message_get_errno(m); + + return 0; +} + +static int start_loopback(sd_netlink *rtnl, struct state *s) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; + assert(rtnl); + assert(s); + r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, LOOPBACK_IFINDEX); if (r < 0) return r; @@ -38,10 +64,81 @@ static int start_loopback(sd_netlink *rtnl) { if (r < 0) return r; - r = sd_netlink_call(rtnl, req, 0, NULL); + r = sd_netlink_call_async(rtnl, req, generic_handler, s, LOOPBACK_SETUP_TIMEOUT_USEC, NULL); + if (r < 0) + return r; + + s->n_messages ++; + return 0; +} + +static int add_ipv4_address(sd_netlink *rtnl, struct state *s) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(rtnl); + assert(s); + + r = sd_rtnl_message_new_addr(rtnl, &req, RTM_NEWADDR, LOOPBACK_IFINDEX, AF_INET); + if (r < 0) + return r; + + r = sd_rtnl_message_addr_set_prefixlen(req, 8); + if (r < 0) + return r; + + r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT); + if (r < 0) + return r; + + r = sd_rtnl_message_addr_set_scope(req, RT_SCOPE_HOST); + if (r < 0) + return r; + + r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &(struct in_addr) { .s_addr = htobe32(INADDR_LOOPBACK) } ); + if (r < 0) + return r; + + r = sd_netlink_call_async(rtnl, req, generic_handler, s, USEC_INFINITY, NULL); if (r < 0) return r; + s->n_messages ++; + return 0; +} + +static int add_ipv6_address(sd_netlink *rtnl, struct state *s) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(rtnl); + assert(s); + + r = sd_rtnl_message_new_addr(rtnl, &req, RTM_NEWADDR, LOOPBACK_IFINDEX, AF_INET6); + if (r < 0) + return r; + + r = sd_rtnl_message_addr_set_prefixlen(req, 128); + if (r < 0) + return r; + + r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT); + if (r < 0) + return r; + + r = sd_rtnl_message_addr_set_scope(req, RT_SCOPE_HOST); + if (r < 0) + return r; + + r = sd_netlink_message_append_in6_addr(req, IFA_LOCAL, &in6addr_loopback); + if (r < 0) + return r; + + r = sd_netlink_call_async(rtnl, req, generic_handler, s, USEC_INFINITY, NULL); + if (r < 0) + return r; + + s->n_messages ++; return 0; } @@ -54,7 +151,7 @@ static bool check_loopback(sd_netlink *rtnl) { if (r < 0) return false; - r = sd_netlink_call(rtnl, req, 0, &reply); + r = sd_netlink_call(rtnl, req, USEC_INFINITY, &reply); if (r < 0) return false; @@ -67,23 +164,52 @@ static bool check_loopback(sd_netlink *rtnl) { int loopback_setup(void) { _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + struct state state_4 = { .title = "add address 127.0.0.1 to loopback interface" }, + state_6 = { .title = "add address ::1 to loopback interface"}, + state_up = { .title = "bring loopback interface up" }; int r; r = sd_netlink_open(&rtnl); if (r < 0) - return r; + return log_error_errno(r, "Failed to open netlink: %m"); + + /* Note that we add the IP addresses here explicitly even though the kernel does that too implicitly when + * setting up the loopback device. The reason we do this here a second time (and possibly race against the + * kernel) is that we want to synchronously wait until the IP addresses are set up correctly, see + * + * https://github.com/systemd/systemd/issues/5641 */ - r = start_loopback(rtnl); - if (r < 0) { + r = add_ipv4_address(rtnl, &state_4); + if (r < 0) + return log_error_errno(r, "Failed to enqueue IPv4 loopback address add request: %m"); + + r = add_ipv6_address(rtnl, &state_6); + if (r < 0) + return log_error_errno(r, "Failed to enqueue IPv6 loopback address add request: %m"); + + r = start_loopback(rtnl, &state_up); + if (r < 0) + return log_error_errno(r, "Failed to enqueue loopback interface start request: %m"); + + while (state_4.n_messages + state_6.n_messages + state_up.n_messages > 0) { + r = sd_netlink_wait(rtnl, LOOPBACK_SETUP_TIMEOUT_USEC); + if (r < 0) + return log_error_errno(r, "Failed to wait for netlink event: %m"); + + r = sd_netlink_process(rtnl, NULL); + if (r < 0) + return log_warning_errno(r, "Failed to process netlink event: %m"); + } - /* If we lack the permissions to configure the - * loopback device, but we find it to be already - * configured, let's exit cleanly, in order to - * supported unprivileged containers. */ - if (r == -EPERM && check_loopback(rtnl)) + /* Note that we don't really care whether the addresses could be added or not */ + if (state_up.rcode != 0) { + /* If we lack the permissions to configure the loopback device, + * but we find it to be already configured, let's exit cleanly, + * in order to supported unprivileged containers. */ + if (state_up.rcode == -EPERM && check_loopback(rtnl)) return 0; - return log_warning_errno(r, "Failed to configure loopback device: %m"); + return log_warning_errno(state_up.rcode, "Failed to configure loopback device: %m"); } return 0; diff --git a/src/core/main.c b/src/core/main.c index bcf9ea5f25..88e2c92504 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -536,7 +536,7 @@ static int config_parse_cpu_affinity2( return ncpus; if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0) - log_warning("Failed to set CPU affinity: %m"); + log_warning_errno(errno, "Failed to set CPU affinity: %m"); return 0; } @@ -1162,6 +1162,8 @@ static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching static int bump_rlimit_nofile(struct rlimit *saved_rlimit) { struct rlimit nl; int r; + int min_max; + _cleanup_free_ char *nr_open = NULL; assert(saved_rlimit); @@ -1182,8 +1184,16 @@ static int bump_rlimit_nofile(struct rlimit *saved_rlimit) { arg_default_rlimit[RLIMIT_NOFILE] = rl; } + /* Get current RLIMIT_NOFILE maximum compiled into the kernel. */ + r = read_one_line_file("/proc/sys/fs/nr_open", &nr_open); + if (r == 0) + r = safe_atoi(nr_open, &min_max); + /* If we fail, fallback to the hard-coded kernel limit of 1024 * 1024. */ + if (r < 0) + min_max = 1024 * 1024; + /* Bump up the resource limit for ourselves substantially */ - nl.rlim_cur = nl.rlim_max = 64*1024; + nl.rlim_cur = nl.rlim_max = min_max; r = setrlimit_closest(RLIMIT_NOFILE, &nl); if (r < 0) return log_warning_errno(r, "Setting RLIMIT_NOFILE failed, ignoring: %m"); @@ -1770,7 +1780,7 @@ int main(int argc, char *argv[]) { if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0) log_error_errno(errno, "Failed to adjust timer slack: %m"); - if (!cap_test_all(arg_capability_bounding_set)) { + if (arg_system && !cap_test_all(arg_capability_bounding_set)) { r = capability_bounding_set_drop_usermode(arg_capability_bounding_set); if (r < 0) { log_emergency_errno(r, "Failed to drop capability bounding set of usermode helpers: %m"); diff --git a/src/core/manager.c b/src/core/manager.c index cff38e28de..283720750f 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -2319,7 +2319,10 @@ int manager_load_unit_from_dbus_path(Manager *m, const char *s, sd_bus_error *e, return sd_bus_error_setf(e, BUS_ERROR_NO_UNIT_FOR_INVOCATION_ID, "No unit with the specified invocation ID " SD_ID128_FORMAT_STR " known.", SD_ID128_FORMAT_VAL(invocation_id)); } - /* If this didn't work, we use the suffix as unit name. */ + /* If this didn't work, we check if this is a unit name */ + if (!unit_name_is_valid(n, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) + return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is neither a valid invocation ID nor unit name.", n); + r = manager_load_unit(m, n, NULL, e, &u); if (r < 0) return r; @@ -2585,8 +2588,8 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { m->n_reloading++; for (;;) { - char line[LINE_MAX], *l; - const char *val; + char line[LINE_MAX]; + const char *val, *l; if (!fgets(line, sizeof(line), f)) { if (feof(f)) @@ -2607,7 +2610,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { uint32_t id; if (safe_atou32(val, &id) < 0) - log_debug("Failed to parse current job id value %s", val); + log_notice("Failed to parse current job id value %s", val); else m->current_job_id = MAX(m->current_job_id, id); @@ -2615,7 +2618,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { uint32_t n; if (safe_atou32(val, &n) < 0) - log_debug("Failed to parse installed jobs counter %s", val); + log_notice("Failed to parse installed jobs counter %s", val); else m->n_installed_jobs += n; @@ -2623,7 +2626,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { uint32_t n; if (safe_atou32(val, &n) < 0) - log_debug("Failed to parse failed jobs counter %s", val); + log_notice("Failed to parse failed jobs counter %s", val); else m->n_failed_jobs += n; @@ -2632,7 +2635,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { b = parse_boolean(val); if (b < 0) - log_debug("Failed to parse taint /usr flag %s", val); + log_notice("Failed to parse taint /usr flag %s", val); else m->taint_usr = m->taint_usr || b; @@ -2662,14 +2665,16 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { dual_timestamp_deserialize(val, &m->units_load_finish_timestamp); else if (startswith(l, "env=")) { r = deserialize_environment(&m->environment, l); + if (r == -ENOMEM) + goto finish; if (r < 0) - return r; + log_notice_errno(r, "Failed to parse environment entry: \"%s\": %m", l); } else if ((val = startswith(l, "notify-fd="))) { int fd; if (safe_atoi(val, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) - log_debug("Failed to parse notify fd: %s", val); + log_notice("Failed to parse notify fd: \"%s\"", val); else { m->notify_event_source = sd_event_source_unref(m->notify_event_source); safe_close(m->notify_fd); @@ -2692,7 +2697,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { int fd; if (safe_atoi(val, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) - log_debug("Failed to parse cgroups agent fd: %s", val); + log_notice("Failed to parse cgroups agent fd: %s", val); else { m->cgroups_agent_event_source = sd_event_source_unref(m->cgroups_agent_event_source); safe_close(m->cgroups_agent_fd); @@ -2703,7 +2708,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { int fd0, fd1; if (sscanf(val, "%i %i", &fd0, &fd1) != 2 || fd0 < 0 || fd1 < 0 || fd0 == fd1 || !fdset_contains(fds, fd0) || !fdset_contains(fds, fd1)) - log_debug("Failed to parse user lookup fd: %s", val); + log_notice("Failed to parse user lookup fd: %s", val); else { m->user_lookup_event_source = sd_event_source_unref(m->user_lookup_event_source); safe_close_pair(m->user_lookup_fds); @@ -2723,7 +2728,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { log_oom(); } else if (!startswith(l, "kdbus-fd=")) /* ignore this one */ - log_debug("Unknown serialization item '%s'", l); + log_notice("Unknown serialization item '%s'", l); } for (;;) { diff --git a/src/core/meson.build b/src/core/meson.build new file mode 100644 index 0000000000..fb8f9dc36b --- /dev/null +++ b/src/core/meson.build @@ -0,0 +1,235 @@ +libcore_la_sources = ''' + unit.c + unit.h + unit-printf.c + unit-printf.h + job.c + job.h + manager.c + manager.h + transaction.c + transaction.h + load-fragment.c + load-fragment.h + service.c + service.h + socket.c + socket.h + busname.c + busname.h + bus-policy.c + bus-policy.h + target.c + target.h + device.c + device.h + mount.c + mount.h + automount.c + automount.h + swap.c + swap.h + timer.c + timer.h + path.c + path.h + slice.c + slice.h + scope.c + scope.h + load-dropin.c + load-dropin.h + execute.c + execute.h + dynamic-user.c + dynamic-user.h + kill.c + kill.h + dbus.c + dbus.h + dbus-manager.c + dbus-manager.h + dbus-unit.c + dbus-unit.h + dbus-job.c + dbus-job.h + dbus-service.c + dbus-service.h + dbus-socket.c + dbus-socket.h + dbus-busname.c + dbus-busname.h + dbus-target.c + dbus-target.h + dbus-device.c + dbus-device.h + dbus-mount.c + dbus-mount.h + dbus-automount.c + dbus-automount.h + dbus-swap.c + dbus-swap.h + dbus-timer.c + dbus-timer.h + dbus-path.c + dbus-path.h + dbus-slice.c + dbus-slice.h + dbus-scope.c + dbus-scope.h + dbus-execute.c + dbus-execute.h + dbus-kill.c + dbus-kill.h + dbus-cgroup.c + dbus-cgroup.h + cgroup.c + cgroup.h + selinux-access.c + selinux-access.h + selinux-setup.c + selinux-setup.h + smack-setup.c + smack-setup.h + ima-setup.c + ima-setup.h + locale-setup.h + locale-setup.c + hostname-setup.c + hostname-setup.h + machine-id-setup.c + machine-id-setup.h + mount-setup.c + mount-setup.h + kmod-setup.c + kmod-setup.h + loopback-setup.h + loopback-setup.c + namespace.c + namespace.h + killall.h + killall.c + audit-fd.c + audit-fd.h + show-status.c + show-status.h + emergency-action.c + emergency-action.h +'''.split() + +load_fragment_gperf_gperf = custom_target( + 'load-fragment-gperf.gperf', + input : 'load-fragment-gperf.gperf.m4', + output: 'load-fragment-gperf.gperf', + command : [m4, '-P'] + m4_defines + ['@INPUT@'], + capture : true) + +load_fragment_gperf_c = custom_target( + 'load-fragment-gperf.c', + input : load_fragment_gperf_gperf, + output : 'load-fragment-gperf.c', + command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@']) + +awkscript = 'load-fragment-gperf-nulstr.awk' +load_fragment_gperf_nulstr_c = custom_target( + 'load-fragment-gperf-nulstr.c', + input : [awkscript, load_fragment_gperf_gperf], + output : 'load-fragment-gperf-nulstr.c', + command : [awk, '-f', '@INPUT0@', '@INPUT1@'], + capture : true) + +libcore = static_library( + 'core', + libcore_la_sources, + load_fragment_gperf_c, + load_fragment_gperf_nulstr_c, + include_directories : includes, + link_with : [libshared_static], + dependencies : [threads, + libseccomp, + libpam, + libaudit, + libkmod, + libapparmor, + libmount]) + +systemd_sources = files('main.c') + +systemd_shutdown_sources = files(''' + shutdown.c + umount.c + umount.h + mount-setup.c + mount-setup.h + killall.c + killall.h +'''.split()) + +in_files = [['macros.systemd', rpmmacrosdir], + ['triggers.systemd', ''], + ['systemd.pc', pkgconfigdatadir]] + +foreach item : in_files + file = item[0] + dir = item[1] + + # If 'no', disable generation completely. + # If '', generate, but do not install. + if dir != 'no' + gen = configure_file( + input : file + '.in', + output : file, + configuration : substs) + if dir != '' + install_data(gen, + install_dir : dir) + endif + endif +endforeach + +install_data('org.freedesktop.systemd1.conf', + install_dir : dbuspolicydir) +install_data('org.freedesktop.systemd1.service', + install_dir : dbussystemservicedir) + +policy_in = configure_file( + input : 'org.freedesktop.systemd1.policy.in.in', + output : 'org.freedesktop.systemd1.policy.in', + configuration : substs) + +custom_target( + 'org.freedesktop.systemd1.policy', + input : policy_in, + output : 'org.freedesktop.systemd1.policy', + command : intltool_command, + install : install_polkit, + install_dir : polkitpolicydir) + +# TODO: this might work with meson from git, see +# https://github.com/mesonbuild/meson/issues/1441#issuecomment-283585493 +# +# i18n.merge_file( +# 'org.freedesktop.systemd1.policy', +# po_dir : po_dir, +# input : policy_in, +# output : 'org.freedesktop.systemd1.policy', +# install : install_polkit, +# install_dir : polkitpolicydir) + +install_data('system.conf', + 'user.conf', + install_dir : pkgsysconfdir) + +meson.add_install_script('sh', '-c', mkdir_p.format(systemshutdowndir)) +meson.add_install_script('sh', '-c', mkdir_p.format(systemsleepdir)) +meson.add_install_script('sh', '-c', mkdir_p.format(systemgeneratordir)) +meson.add_install_script('sh', '-c', mkdir_p.format(usergeneratordir)) + +meson.add_install_script('sh', '-c', + mkdir_p.format(join_paths(pkgsysconfdir, 'system/multi-user.target.wants'))) +meson.add_install_script('sh', '-c', + mkdir_p.format(join_paths(pkgsysconfdir, 'system/getty.target.wants'))) +meson.add_install_script('sh', '-c', + mkdir_p.format(join_paths(pkgsysconfdir, 'user'))) +meson.add_install_script('sh', '-c', + mkdir_p.format(join_paths(sysconfdir, 'xdg/systemd'))) diff --git a/src/core/mount.c b/src/core/mount.c index ca0c4b0d5e..214364d87d 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -886,7 +886,7 @@ static void mount_enter_unmounting(Mount *m) { m->control_command_id = MOUNT_EXEC_UNMOUNT; m->control_command = m->exec_command + MOUNT_EXEC_UNMOUNT; - r = exec_command_set(m->control_command, UMOUNT_PATH, m->where, NULL); + r = exec_command_set(m->control_command, UMOUNT_PATH, m->where, "-c", NULL); if (r >= 0 && m->lazy_unmount) r = exec_command_append(m->control_command, "-l", NULL); if (r >= 0 && m->force_unmount) diff --git a/src/core/namespace.c b/src/core/namespace.c index 4f29217bc4..05175e9552 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -27,6 +27,7 @@ #include <linux/fs.h> #include "alloc-util.h" +#include "base-filesystem.h" #include "dev-setup.h" #include "fd-util.h" #include "fs-util.h" @@ -792,13 +793,14 @@ static int apply_mount( return 0; } -static int make_read_only(MountEntry *m, char **blacklist) { +static int make_read_only(MountEntry *m, char **blacklist, FILE *proc_self_mountinfo) { int r = 0; assert(m); + assert(proc_self_mountinfo); if (mount_entry_read_only(m)) - r = bind_remount_recursive(mount_entry_path(m), true, blacklist); + r = bind_remount_recursive_with_mountinfo(mount_entry_path(m), true, blacklist, proc_self_mountinfo); 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; @@ -815,18 +817,24 @@ static int make_read_only(MountEntry *m, char **blacklist) { return r; } -static bool namespace_info_mount_apivfs(const NameSpaceInfo *ns_info) { +static bool namespace_info_mount_apivfs(const char *root_directory, 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... */ + /* + * ProtectControlGroups= and ProtectKernelTunables= imply MountAPIVFS=, + * since to protect the API VFS mounts, they need to be around in the + * first place... and RootDirectory= or RootImage= need to be set. + */ - return ns_info->mount_apivfs || - ns_info->protect_control_groups || - ns_info->protect_kernel_tunables; + /* root_directory should point to a mount point */ + return root_directory && + (ns_info->mount_apivfs || + ns_info->protect_control_groups || + ns_info->protect_kernel_tunables); } static unsigned namespace_calculate_mounts( + const char* root_directory, const NameSpaceInfo *ns_info, char** read_write_paths, char** read_only_paths, @@ -863,7 +871,7 @@ static unsigned namespace_calculate_mounts( (ns_info->protect_control_groups ? 1 : 0) + (ns_info->protect_kernel_modules ? ELEMENTSOF(protect_kernel_modules_table) : 0) + protect_home_cnt + protect_system_cnt + - (namespace_info_mount_apivfs(ns_info) ? ELEMENTSOF(apivfs_table) : 0); + (namespace_info_mount_apivfs(root_directory, ns_info) ? ELEMENTSOF(apivfs_table) : 0); } int setup_namespace( @@ -931,6 +939,7 @@ int setup_namespace( } n_mounts = namespace_calculate_mounts( + root_directory, ns_info, read_write_paths, read_only_paths, @@ -1009,7 +1018,7 @@ int setup_namespace( if (r < 0) goto finish; - if (namespace_info_mount_apivfs(ns_info)) { + if (namespace_info_mount_apivfs(root_directory, ns_info)) { r = append_static_mounts(&m, apivfs_table, ELEMENTSOF(apivfs_table), ns_info->ignore_protect_paths); if (r < 0) goto finish; @@ -1044,6 +1053,10 @@ int setup_namespace( } } + /* Try to set up the new root directory before mounting anything there */ + if (root_directory) + (void) base_filesystem_create(root_directory, UID_INVALID, GID_INVALID); + if (root_image) { r = dissected_image_mount(dissected_image, root_directory, dissect_image_flags); if (r < 0) @@ -1070,9 +1083,18 @@ int setup_namespace( } if (n_mounts > 0) { + _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; char **blacklist; unsigned j; + /* Open /proc/self/mountinfo now as it may become unavailable if we mount anything on top of /proc. + * For example, this is the case with the option: 'InaccessiblePaths=/proc' */ + proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); + if (!proc_self_mountinfo) { + r = -errno; + goto finish; + } + /* First round, add in all special mounts we need */ for (m = mounts; m < mounts + n_mounts; ++m) { r = apply_mount(root_directory, m, tmp_dir, var_tmp_dir); @@ -1088,7 +1110,7 @@ int setup_namespace( /* Second round, flip the ro bits if necessary. */ for (m = mounts; m < mounts + n_mounts; ++m) { - r = make_read_only(m, blacklist); + r = make_read_only(m, blacklist, proc_self_mountinfo); if (r < 0) goto finish; } diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c index 2b96a9551b..0f8a2d68e2 100644 --- a/src/core/selinux-access.c +++ b/src/core/selinux-access.c @@ -135,7 +135,12 @@ _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) { fmt2 = strjoina("selinux: ", fmt); va_start(ap, fmt); - log_internalv(LOG_AUTH | callback_type_to_priority(type), 0, __FILE__, __LINE__, __FUNCTION__, fmt2, ap); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + log_internalv(LOG_AUTH | callback_type_to_priority(type), + 0, __FILE__, __LINE__, __FUNCTION__, + fmt2, ap); +#pragma GCC diagnostic pop va_end(ap); return 0; diff --git a/src/core/service.c b/src/core/service.c index 74054887b9..4c577db8d7 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -45,6 +45,7 @@ #include "service.h" #include "signal-util.h" #include "special.h" +#include "stdio-util.h" #include "string-table.h" #include "string-util.h" #include "strv.h" @@ -412,13 +413,12 @@ static int service_add_fd_store(Service *s, int fd, const char *name) { } r = sd_event_add_io(UNIT(s)->manager->event, &fs->event_source, fd, 0, on_fd_store_io, fs); - if (r < 0) { + if (r < 0 && r != -EPERM) { /* EPERM indicates fds that aren't pollable, which is OK */ free(fs->fdname); free(fs); return r; - } - - (void) sd_event_source_set_description(fs->event_source, "service-fd-store"); + } else if (r >= 0) + (void) sd_event_source_set_description(fs->event_source, "service-fd-store"); LIST_PREPEND(fd_store, s->fd_store, fs); s->n_fd_store++; @@ -1067,14 +1067,21 @@ static int service_coldplug(Unit *u) { return 0; } -static int service_collect_fds(Service *s, int **fds, char ***fd_names) { +static int service_collect_fds(Service *s, + int **fds, + char ***fd_names, + unsigned *n_storage_fds, + unsigned *n_socket_fds) { + _cleanup_strv_free_ char **rfd_names = NULL; _cleanup_free_ int *rfds = NULL; - int rn_fds = 0, r; + unsigned rn_socket_fds = 0, rn_storage_fds = 0; + int r; assert(s); assert(fds); assert(fd_names); + assert(n_socket_fds); if (s->socket_fd >= 0) { @@ -1089,7 +1096,7 @@ static int service_collect_fds(Service *s, int **fds, char ***fd_names) { if (!rfd_names) return -ENOMEM; - rn_fds = 1; + rn_socket_fds = 1; } else { Iterator i; Unit *u; @@ -1115,20 +1122,20 @@ static int service_collect_fds(Service *s, int **fds, char ***fd_names) { if (!rfds) { rfds = cfds; - rn_fds = cn_fds; + rn_socket_fds = cn_fds; cfds = NULL; } else { int *t; - t = realloc(rfds, (rn_fds + cn_fds) * sizeof(int)); + t = realloc(rfds, (rn_socket_fds + cn_fds) * sizeof(int)); if (!t) return -ENOMEM; - memcpy(t + rn_fds, cfds, cn_fds * sizeof(int)); + memcpy(t + rn_socket_fds, cfds, cn_fds * sizeof(int)); rfds = t; - rn_fds += cn_fds; + rn_socket_fds += cn_fds; } r = strv_extend_n(&rfd_names, socket_fdname(sock), cn_fds); @@ -1139,40 +1146,45 @@ static int service_collect_fds(Service *s, int **fds, char ***fd_names) { if (s->n_fd_store > 0) { ServiceFDStore *fs; + unsigned n_fds; char **nl; int *t; - t = realloc(rfds, (rn_fds + s->n_fd_store) * sizeof(int)); + t = realloc(rfds, (rn_socket_fds + s->n_fd_store) * sizeof(int)); if (!t) return -ENOMEM; rfds = t; - nl = realloc(rfd_names, (rn_fds + s->n_fd_store + 1) * sizeof(char*)); + nl = realloc(rfd_names, (rn_socket_fds + s->n_fd_store + 1) * sizeof(char*)); if (!nl) return -ENOMEM; rfd_names = nl; + n_fds = rn_socket_fds; LIST_FOREACH(fd_store, fs, s->fd_store) { - rfds[rn_fds] = fs->fd; - rfd_names[rn_fds] = strdup(strempty(fs->fdname)); - if (!rfd_names[rn_fds]) + rfds[n_fds] = fs->fd; + rfd_names[n_fds] = strdup(strempty(fs->fdname)); + if (!rfd_names[n_fds]) return -ENOMEM; - rn_fds++; + rn_storage_fds++; + n_fds++; } - rfd_names[rn_fds] = NULL; + rfd_names[n_fds] = NULL; } *fds = rfds; *fd_names = rfd_names; + *n_socket_fds = rn_socket_fds; + *n_storage_fds = rn_storage_fds; rfds = NULL; rfd_names = NULL; - return rn_fds; + return 0; } static bool service_exec_needs_notify_socket(Service *s, ExecFlags flags) { @@ -1203,7 +1215,7 @@ static int service_spawn( _cleanup_strv_free_ char **final_env = NULL, **our_env = NULL, **fd_names = NULL; _cleanup_free_ int *fds = NULL; - unsigned n_fds = 0, n_env = 0; + unsigned n_storage_fds = 0, n_socket_fds = 0, n_env = 0; const char *path; pid_t pid; @@ -1247,12 +1259,11 @@ static int service_spawn( s->exec_context.std_output == EXEC_OUTPUT_SOCKET || s->exec_context.std_error == EXEC_OUTPUT_SOCKET) { - r = service_collect_fds(s, &fds, &fd_names); + r = service_collect_fds(s, &fds, &fd_names, &n_storage_fds, &n_socket_fds); if (r < 0) return r; - n_fds = r; - log_unit_debug(UNIT(s), "Passing %i fds to service", n_fds); + log_unit_debug(UNIT(s), "Passing %i fds to service", n_storage_fds + n_socket_fds); } r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), timeout)); @@ -1346,7 +1357,8 @@ static int service_spawn( exec_params.environment = final_env; exec_params.fds = fds; exec_params.fd_names = fd_names; - exec_params.n_fds = n_fds; + exec_params.n_storage_fds = n_storage_fds; + exec_params.n_socket_fds = n_socket_fds; exec_params.confirm_spawn = manager_get_confirm_spawn(UNIT(s)->manager); exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; exec_params.cgroup_path = path; @@ -2140,6 +2152,79 @@ _pure_ static bool service_can_reload(Unit *u) { return !!s->exec_command[SERVICE_EXEC_RELOAD]; } +static unsigned service_exec_command_index(Unit *u, ServiceExecCommand id, ExecCommand *current) { + Service *s = SERVICE(u); + unsigned idx = 0; + ExecCommand *first, *c; + + assert(s); + + first = s->exec_command[id]; + + /* Figure out where we are in the list by walking back to the beginning */ + for (c = current; c != first; c = c->command_prev) + idx++; + + return idx; +} + +static int service_serialize_exec_command(Unit *u, FILE *f, ExecCommand *command) { + Service *s = SERVICE(u); + ServiceExecCommand id; + unsigned idx; + const char *type; + char **arg; + _cleanup_free_ char *args = NULL, *p = NULL; + size_t allocated = 0, length = 0; + + assert(s); + assert(f); + + if (!command) + return 0; + + if (command == s->control_command) { + type = "control"; + id = s->control_command_id; + } else { + type = "main"; + id = SERVICE_EXEC_START; + } + + idx = service_exec_command_index(u, id, command); + + STRV_FOREACH(arg, command->argv) { + size_t n; + _cleanup_free_ char *e = NULL; + + e = xescape(*arg, WHITESPACE); + if (!e) + return -ENOMEM; + + n = strlen(e); + if (!GREEDY_REALLOC(args, allocated, length + 1 + n + 1)) + return -ENOMEM; + + if (length > 0) + args[length++] = ' '; + + memcpy(args + length, e, n); + length += n; + } + + if (!GREEDY_REALLOC(args, allocated, length + 1)) + return -ENOMEM; + args[length++] = 0; + + p = xescape(command->path, WHITESPACE); + if (!p) + return -ENOMEM; + + fprintf(f, "%s-command=%s %u %s %s\n", type, service_exec_command_to_string(id), idx, p, args); + + return 0; +} + static int service_serialize(Unit *u, FILE *f, FDSet *fds) { Service *s = SERVICE(u); ServiceFDStore *fs; @@ -2167,11 +2252,8 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { if (r < 0) return r; - /* FIXME: There's a minor uncleanliness here: if there are - * multiple commands attached here, we will start from the - * first one again */ - if (s->control_command_id >= 0) - unit_serialize_item(u, f, "control-command", service_exec_command_to_string(s->control_command_id)); + service_serialize_exec_command(u, f, s->control_command); + service_serialize_exec_command(u, f, s->main_command); r = unit_serialize_item_fd(u, f, fds, "stdin-fd", s->stdin_fd); if (r < 0) @@ -2227,6 +2309,106 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { return 0; } +static int service_deserialize_exec_command(Unit *u, const char *key, const char *value) { + Service *s = SERVICE(u); + int r; + unsigned idx = 0, i; + bool control, found = false; + ServiceExecCommand id = _SERVICE_EXEC_COMMAND_INVALID; + ExecCommand *command = NULL; + _cleanup_free_ char *path = NULL; + _cleanup_strv_free_ char **argv = NULL; + + enum ExecCommandState { + STATE_EXEC_COMMAND_TYPE, + STATE_EXEC_COMMAND_INDEX, + STATE_EXEC_COMMAND_PATH, + STATE_EXEC_COMMAND_ARGS, + _STATE_EXEC_COMMAND_MAX, + _STATE_EXEC_COMMAND_INVALID = -1, + } state; + + assert(s); + assert(key); + assert(value); + + control = streq(key, "control-command"); + + state = STATE_EXEC_COMMAND_TYPE; + + for (;;) { + _cleanup_free_ char *arg = NULL; + + r = extract_first_word(&value, &arg, NULL, EXTRACT_CUNESCAPE); + if (r == 0) + break; + else if (r < 0) + return r; + + switch (state) { + case STATE_EXEC_COMMAND_TYPE: + id = service_exec_command_from_string(arg); + if (id < 0) + return -EINVAL; + + state = STATE_EXEC_COMMAND_INDEX; + break; + case STATE_EXEC_COMMAND_INDEX: + r = safe_atou(arg, &idx); + if (r < 0) + return -EINVAL; + + state = STATE_EXEC_COMMAND_PATH; + break; + case STATE_EXEC_COMMAND_PATH: + path = arg; + arg = NULL; + state = STATE_EXEC_COMMAND_ARGS; + + if (!path_is_absolute(path)) + return -EINVAL; + break; + case STATE_EXEC_COMMAND_ARGS: + r = strv_extend(&argv, arg); + if (r < 0) + return -ENOMEM; + break; + default: + assert_not_reached("Unknown error at deserialization of exec command"); + break; + } + } + + if (state != STATE_EXEC_COMMAND_ARGS) + return -EINVAL; + + /* Let's check whether exec command on given offset matches data that we just deserialized */ + for (command = s->exec_command[id], i = 0; command; command = command->command_next, i++) { + if (i != idx) + continue; + + found = strv_equal(argv, command->argv) && streq(command->path, path); + break; + } + + if (!found) { + /* Command at the index we serialized is different, let's look for command that exactly + * matches but is on different index. If there is no such command we will not resume execution. */ + for (command = s->exec_command[id]; command; command = command->command_next) + if (strv_equal(command->argv, argv) && streq(command->path, path)) + break; + } + + if (command && control) + s->control_command = command; + else if (command) + s->main_command = command; + else + log_unit_warning(u, "Current command vanished from the unit file, execution of the command list won't be resumed."); + + return 0; +} + static int service_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { Service *s = SERVICE(u); int r; @@ -2309,16 +2491,6 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, s->status_text = t; } - } else if (streq(key, "control-command")) { - ServiceExecCommand id; - - id = service_exec_command_from_string(value); - if (id < 0) - log_unit_debug(u, "Failed to parse exec-command value: %s", value); - else { - s->control_command_id = id; - s->control_command = s->exec_command[id]; - } } else if (streq(key, "accept-socket")) { Unit *socket; @@ -2437,6 +2609,10 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, s->watchdog_override_enable = true; s->watchdog_override_usec = watchdog_override_usec; } + } else if (STR_IN_SET(key, "main-command", "control-command")) { + r = service_deserialize_exec_command(u, key, value); + if (r < 0) + log_unit_debug_errno(u, r, "Failed to parse serialized command \"%s\": %m", value); } else log_unit_debug(u, "Unknown serialization key: %s", key); @@ -2693,7 +2869,6 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { log_struct(f == SERVICE_SUCCESS ? LOG_DEBUG : (code == CLD_EXITED ? LOG_NOTICE : LOG_WARNING), - LOG_UNIT_ID(u), LOG_UNIT_MESSAGE(u, "Main process exited, code=%s, status=%i/%s", sigchld_code_to_string(code), status, strna(code == CLD_EXITED @@ -2701,6 +2876,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { : signal_to_string(status))), "EXIT_CODE=%s", sigchld_code_to_string(code), "EXIT_STATUS=%i", status, + LOG_UNIT_ID(u), NULL); if (s->result == SERVICE_SUCCESS) @@ -3407,15 +3583,6 @@ static const char* const service_exec_command_table[_SERVICE_EXEC_COMMAND_MAX] = DEFINE_STRING_TABLE_LOOKUP(service_exec_command, ServiceExecCommand); -static const char* const notify_access_table[_NOTIFY_ACCESS_MAX] = { - [NOTIFY_NONE] = "none", - [NOTIFY_MAIN] = "main", - [NOTIFY_EXEC] = "exec", - [NOTIFY_ALL] = "all" -}; - -DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess); - static const char* const notify_state_table[_NOTIFY_STATE_MAX] = { [NOTIFY_UNKNOWN] = "unknown", [NOTIFY_READY] = "ready", diff --git a/src/core/service.h b/src/core/service.h index ff9cfaeb88..f4ba604f69 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -61,15 +61,6 @@ typedef enum ServiceExecCommand { _SERVICE_EXEC_COMMAND_INVALID = -1 } ServiceExecCommand; -typedef enum NotifyAccess { - NOTIFY_NONE, - NOTIFY_ALL, - NOTIFY_MAIN, - NOTIFY_EXEC, - _NOTIFY_ACCESS_MAX, - _NOTIFY_ACCESS_INVALID = -1 -} NotifyAccess; - typedef enum NotifyState { NOTIFY_UNKNOWN, NOTIFY_READY, @@ -218,9 +209,6 @@ ServiceType service_type_from_string(const char *s) _pure_; const char* service_exec_command_to_string(ServiceExecCommand i) _const_; ServiceExecCommand service_exec_command_from_string(const char *s) _pure_; -const char* notify_access_to_string(NotifyAccess i) _const_; -NotifyAccess notify_access_from_string(const char *s) _pure_; - const char* notify_state_to_string(NotifyState i) _const_; NotifyState notify_state_from_string(const char *s) _pure_; diff --git a/src/core/shutdown.c b/src/core/shutdown.c index a2309b7726..a7d5e57936 100644 --- a/src/core/shutdown.c +++ b/src/core/shutdown.c @@ -403,7 +403,7 @@ int main(int argc, char *argv[]) { _cleanup_free_ char *param = NULL; r = read_one_line_file("/run/systemd/reboot-param", ¶m); - if (r < 0) + if (r < 0 && r != -ENOENT) log_warning_errno(r, "Failed to read reboot parameter file: %m"); if (!isempty(param)) { diff --git a/src/core/smack-setup.c b/src/core/smack-setup.c index 5a6d11cfa1..adf2293142 100644 --- a/src/core/smack-setup.c +++ b/src/core/smack-setup.c @@ -264,6 +264,54 @@ static int write_netlabel_rules(const char* srcdir) { return r; } +static int write_onlycap_list(void) { + _cleanup_close_ int onlycap_fd = -1; + _cleanup_free_ char *list = NULL; + _cleanup_fclose_ FILE *f = NULL; + size_t len = 0, allocated = 0; + char buf[LINE_MAX]; + int r; + + f = fopen("/etc/smack/onlycap", "re"); + if (!f) { + if (errno != ENOENT) + log_warning_errno(errno, "Failed to read '/etc/smack/onlycap'"); + return errno == ENOENT ? ENOENT : -errno; + } + + FOREACH_LINE(buf, f, return -errno) { + size_t l; + + if (isempty(truncate_nl(buf)) || strchr(COMMENTS, *buf)) + continue; + + l = strlen(buf); + if (!GREEDY_REALLOC(list, allocated, len + l + 1)) + return log_oom(); + + stpcpy(list + len, buf)[0] = ' '; + len += l + 1; + } + + if (!len) + return 0; + + list[len - 1] = 0; + + onlycap_fd = open("/sys/fs/smackfs/onlycap", O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); + if (onlycap_fd < 0) { + if (errno != ENOENT) + log_warning_errno(errno, "Failed to open '/sys/fs/smackfs/onlycap'"); + return -errno; /* negative error */ + } + + r = write(onlycap_fd, list, len); + if (r < 0) + return log_error_errno(errno, "Failed to write onlycap list(%s) to '/sys/fs/smackfs/onlycap'", list); + + return 0; +} + #endif int mac_smack_setup(bool *loaded_policy) { @@ -338,6 +386,22 @@ int mac_smack_setup(bool *loaded_policy) { break; } + r = write_onlycap_list(); + switch(r) { + case -ENOENT: + log_debug("Smack is not enabled in the kernel."); + break; + case ENOENT: + log_debug("Smack onlycap list file '/etc/smack/onlycap' not found"); + break; + case 0: + log_info("Successfully wrote Smack onlycap list."); + break; + default: + log_emergency_errno(r, "Failed to write Smack onlycap list."); + return r; + } + *loaded_policy = true; #endif diff --git a/src/core/socket.c b/src/core/socket.c index c4da227e09..8750643d92 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -2528,7 +2528,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, else LIST_FOREACH(port, p, s->ports) if (p->type == SOCKET_FIFO && - path_equal_or_files_same(p->path, value+skip)) { + path_equal_or_files_same(p->path, value+skip, 0)) { socket_port_take_fd(p, fds, fd); break; } @@ -2542,7 +2542,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, else LIST_FOREACH(port, p, s->ports) if (p->type == SOCKET_SPECIAL && - path_equal_or_files_same(p->path, value+skip)) { + path_equal_or_files_same(p->path, value+skip, 0)) { socket_port_take_fd(p, fds, fd); break; } @@ -2596,7 +2596,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, else LIST_FOREACH(port, p, s->ports) if (p->type == SOCKET_USB_FUNCTION && - path_equal_or_files_same(p->path, value+skip)) { + path_equal_or_files_same(p->path, value+skip, 0)) { socket_port_take_fd(p, fds, fd); break; } diff --git a/src/core/swap.c b/src/core/swap.c index e9468e105c..4c3a74ce00 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -487,13 +487,14 @@ static void swap_set_state(Swap *s, SwapState state) { old_state = s->state; s->state = state; - if (state != SWAP_ACTIVATING && - state != SWAP_ACTIVATING_SIGTERM && - state != SWAP_ACTIVATING_SIGKILL && - state != SWAP_ACTIVATING_DONE && - state != SWAP_DEACTIVATING && - state != SWAP_DEACTIVATING_SIGTERM && - state != SWAP_DEACTIVATING_SIGKILL) { + if (!IN_SET(state, + SWAP_ACTIVATING, + SWAP_ACTIVATING_SIGTERM, + SWAP_ACTIVATING_SIGKILL, + SWAP_ACTIVATING_DONE, + SWAP_DEACTIVATING, + SWAP_DEACTIVATING_SIGTERM, + SWAP_DEACTIVATING_SIGKILL)) { s->timer_event_source = sd_event_source_unref(s->timer_event_source); swap_unwatch_control_pid(s); s->control_command = NULL; @@ -695,20 +696,19 @@ static void swap_enter_active(Swap *s, SwapResult f) { static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) { int r; + KillOperation kop; assert(s); if (s->result == SWAP_SUCCESS) s->result = f; - r = unit_kill_context( - UNIT(s), - &s->kill_context, - (state != SWAP_ACTIVATING_SIGTERM && state != SWAP_DEACTIVATING_SIGTERM) ? - KILL_KILL : KILL_TERMINATE, - -1, - s->control_pid, - false); + if (IN_SET(state, SWAP_ACTIVATING_SIGTERM, SWAP_DEACTIVATING_SIGTERM)) + kop = KILL_TERMINATE; + else + kop = KILL_KILL; + + r = unit_kill_context(UNIT(s), &s->kill_context, kop, -1, s->control_pid, false); if (r < 0) goto fail; @@ -829,17 +829,18 @@ static int swap_start(Unit *u) { /* We cannot fulfill this request right now, try again later * please! */ - if (s->state == SWAP_DEACTIVATING || - s->state == SWAP_DEACTIVATING_SIGTERM || - s->state == SWAP_DEACTIVATING_SIGKILL || - s->state == SWAP_ACTIVATING_SIGTERM || - s->state == SWAP_ACTIVATING_SIGKILL) + if (IN_SET(s->state, + SWAP_DEACTIVATING, + SWAP_DEACTIVATING_SIGTERM, + SWAP_DEACTIVATING_SIGKILL, + SWAP_ACTIVATING_SIGTERM, + SWAP_ACTIVATING_SIGKILL)) return -EAGAIN; if (s->state == SWAP_ACTIVATING) return 0; - assert(s->state == SWAP_DEAD || s->state == SWAP_FAILED); + assert(IN_SET(s->state, SWAP_DEAD, SWAP_FAILED)); if (detect_container() > 0) return -EPERM; @@ -873,16 +874,15 @@ static int swap_stop(Unit *u) { assert(s); - if (s->state == SWAP_DEACTIVATING || - s->state == SWAP_DEACTIVATING_SIGTERM || - s->state == SWAP_DEACTIVATING_SIGKILL || - s->state == SWAP_ACTIVATING_SIGTERM || - s->state == SWAP_ACTIVATING_SIGKILL) + if (IN_SET(s->state, + SWAP_DEACTIVATING, + SWAP_DEACTIVATING_SIGTERM, + SWAP_DEACTIVATING_SIGKILL, + SWAP_ACTIVATING_SIGTERM, + SWAP_ACTIVATING_SIGKILL)) return 0; - assert(s->state == SWAP_ACTIVATING || - s->state == SWAP_ACTIVATING_DONE || - s->state == SWAP_ACTIVE); + assert(IN_SET(s->state, SWAP_ACTIVATING, SWAP_ACTIVATING_DONE, SWAP_ACTIVE)); if (detect_container() > 0) return -EPERM; @@ -1340,7 +1340,7 @@ int swap_process_device_new(Manager *m, struct udev_device *dev) { struct udev_list_entry *item = NULL, *first = NULL; _cleanup_free_ char *e = NULL; const char *dn; - Swap *s; + Unit *u; int r = 0; assert(m); @@ -1354,9 +1354,9 @@ int swap_process_device_new(Manager *m, struct udev_device *dev) { if (r < 0) return r; - s = hashmap_get(m->units, e); - if (s) - r = swap_set_devnode(s, dn); + u = manager_get_unit(m, e); + if (u) + r = swap_set_devnode(SWAP(u), dn); first = udev_device_get_devlinks_list_entry(dev); udev_list_entry_foreach(item, first) { @@ -1367,9 +1367,9 @@ int swap_process_device_new(Manager *m, struct udev_device *dev) { if (q < 0) return q; - s = hashmap_get(m->units, n); - if (s) { - q = swap_set_devnode(s, dn); + u = manager_get_unit(m, n); + if (u) { + q = swap_set_devnode(SWAP(u), dn); if (q < 0) r = q; } diff --git a/src/core/target.c b/src/core/target.c index ff0d764fb5..2a58dd394d 100644 --- a/src/core/target.c +++ b/src/core/target.c @@ -63,6 +63,9 @@ static int target_add_default_dependencies(Target *t) { assert(t); + if (!UNIT(t)->default_dependencies) + return 0; + /* Imply ordering for requirement dependencies on target * units. Note that when the user created a contradicting * ordering manually we won't add anything in here to make @@ -93,7 +96,7 @@ static int target_load(Unit *u) { return r; /* This is a new unit? Then let's add in some extras */ - if (u->load_state == UNIT_LOADED && u->default_dependencies) { + if (u->load_state == UNIT_LOADED) { r = target_add_default_dependencies(t); if (r < 0) return r; diff --git a/src/core/timer.c b/src/core/timer.c index af67b7591a..701949fd60 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -316,21 +316,6 @@ static void timer_enter_elapsed(Timer *t, bool leave_around) { timer_enter_dead(t, TIMER_SUCCESS); } -static usec_t monotonic_to_boottime(usec_t t) { - usec_t a, b; - - if (t <= 0) - return 0; - - a = now(clock_boottime_or_monotonic()); - b = now(CLOCK_MONOTONIC); - - if (t + a > b) - return t + a - b; - else - return 0; -} - static void add_random(Timer *t, usec_t *v) { char s[FORMAT_TIMESPAN_MAX]; usec_t add; @@ -355,9 +340,9 @@ static void add_random(Timer *t, usec_t *v) { static void timer_enter_waiting(Timer *t, bool initial) { bool found_monotonic = false, found_realtime = false; - usec_t ts_realtime, ts_monotonic; - usec_t base = 0; bool leave_around = false; + triple_timestamp ts; + usec_t base = 0; TimerValue *v; Unit *trigger; int r; @@ -371,11 +356,7 @@ static void timer_enter_waiting(Timer *t, bool initial) { return; } - /* If we shall wake the system we use the boottime clock - * rather than the monotonic clock. */ - - ts_realtime = now(CLOCK_REALTIME); - ts_monotonic = now(t->wake_system ? clock_boottime_or_monotonic() : CLOCK_MONOTONIC); + triple_timestamp_get(&ts); t->next_elapse_monotonic_or_boottime = t->next_elapse_realtime = 0; LIST_FOREACH(value, v, t->values) { @@ -391,7 +372,7 @@ static void timer_enter_waiting(Timer *t, bool initial) { * to that. If we don't just start from * now. */ - b = t->last_trigger.realtime > 0 ? t->last_trigger.realtime : ts_realtime; + b = t->last_trigger.realtime > 0 ? t->last_trigger.realtime : ts.realtime; r = calendar_spec_next_usec(v->calendar_spec, b, &v->next_elapse); if (r < 0) @@ -405,13 +386,14 @@ static void timer_enter_waiting(Timer *t, bool initial) { found_realtime = true; } else { + switch (v->base) { case TIMER_ACTIVE: if (state_translation_table[t->state] == UNIT_ACTIVE) base = UNIT(t)->inactive_exit_timestamp.monotonic; else - base = ts_monotonic; + base = ts.monotonic; break; case TIMER_BOOT: @@ -456,12 +438,11 @@ static void timer_enter_waiting(Timer *t, bool initial) { assert_not_reached("Unknown timer base"); } - if (t->wake_system) - base = monotonic_to_boottime(base); - - v->next_elapse = base + v->value; + v->next_elapse = usec_add(usec_shift_clock(base, CLOCK_MONOTONIC, TIMER_MONOTONIC_CLOCK(t)), v->value); - if (!initial && v->next_elapse < ts_monotonic && IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) { + if (!initial && + v->next_elapse < triple_timestamp_by_clock(&ts, TIMER_MONOTONIC_CLOCK(t)) && + IN_SET(v->base, TIMER_ACTIVE, TIMER_BOOT, TIMER_STARTUP)) { /* This is a one time trigger, disable it now */ v->disabled = true; continue; @@ -488,7 +469,7 @@ static void timer_enter_waiting(Timer *t, bool initial) { add_random(t, &t->next_elapse_monotonic_or_boottime); - left = t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0; + left = usec_sub_unsigned(t->next_elapse_monotonic_or_boottime, triple_timestamp_by_clock(&ts, TIMER_MONOTONIC_CLOCK(t))); log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), left, 0)); if (t->monotonic_event_source) { diff --git a/src/core/timer.h b/src/core/timer.h index 9c4b64f898..546c60d750 100644 --- a/src/core/timer.h +++ b/src/core/timer.h @@ -78,6 +78,8 @@ struct Timer { char *stamp_path; }; +#define TIMER_MONOTONIC_CLOCK(t) ((t)->wake_system && clock_boottime_supported() ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC) + void timer_free_values(Timer *t); extern const UnitVTable timer_vtable; diff --git a/src/core/transaction.c b/src/core/transaction.c index b6d1062414..a2dfd8ae90 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -632,7 +632,7 @@ static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) { job_add_to_run_queue(j); job_add_to_dbus_queue(j); - job_start_timer(j); + job_start_timer(j, false); job_shutdown_magic(j); } diff --git a/src/core/umount.c b/src/core/umount.c index 2f4b12bdb9..591dac71f0 100644 --- a/src/core/umount.c +++ b/src/core/umount.c @@ -19,7 +19,6 @@ #include <errno.h> #include <fcntl.h> -#include <linux/dm-ioctl.h> #include <linux/loop.h> #include <string.h> #include <sys/mount.h> @@ -31,6 +30,7 @@ #include "escape.h" #include "fd-util.h" #include "fstab-util.h" +#include "linux-3.13/dm-ioctl.h" #include "list.h" #include "mount-setup.h" #include "path-util.h" @@ -369,6 +369,14 @@ static int delete_dm(dev_t devnum) { return 0; } +static bool nonunmountable_path(const char *path) { + return path_equal(path, "/") +#ifndef HAVE_SPLIT_USR + || path_equal(path, "/usr") +#endif + || path_startswith(path, "/run/initramfs"); +} + static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) { MountPoint *m, *n; int n_failed = 0; @@ -404,21 +412,21 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e * somehwere else via a bind mount. If we * explicitly remount the super block of that * alias read-only we hence should be - * relatively safe regarding keeping the fs we - * can otherwise not see dirty. */ + * relatively safe regarding keeping dirty an fs + * we cannot otherwise see. */ log_info("Remounting '%s' read-only with options '%s'.", m->path, options); - (void) mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, options); + if (mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, options) < 0) { + if (log_error) + log_notice_errno(errno, "Failed to remount '%s' read-only: %m", m->path); + if (nonunmountable_path(m->path)) + n_failed++; + } } /* Skip / and /usr since we cannot unmount that * anyway, since we are running from it. They have * already been remounted ro. */ - if (path_equal(m->path, "/") -#ifndef HAVE_SPLIT_USR - || path_equal(m->path, "/usr") -#endif - || path_startswith(m->path, "/run/initramfs") - ) + if (nonunmountable_path(m->path)) continue; /* Trying to umount. We don't force here since we rely @@ -430,8 +438,9 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e *changed = true; mount_point_free(head, m); - } else if (log_error) { - log_warning_errno(errno, "Could not unmount %s: %m", m->path); + } else { + if (log_error) + log_warning_errno(errno, "Could not unmount %s: %m", m->path); n_failed++; } } @@ -555,8 +564,6 @@ int umount_all(bool *changed) { /* umount one more time with logging enabled */ r = mount_points_list_umount(&mp_list_head, &umount_changed, true); - if (r <= 0) - goto end; end: mount_points_list_free(&mp_list_head); diff --git a/src/core/unit.c b/src/core/unit.c index f76b6c30a8..b28eeb2262 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -99,6 +99,7 @@ Unit *unit_new(Manager *m, size_t size) { u->on_failure_job_mode = JOB_REPLACE; u->cgroup_inotify_wd = -1; u->job_timeout = USEC_INFINITY; + u->job_running_timeout = USEC_INFINITY; u->ref_uid = UID_INVALID; u->ref_gid = GID_INVALID; u->cpu_usage_last = NSEC_INFINITY; @@ -955,9 +956,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s\tPerpetual: %s\n" "%s\tSlice: %s\n" "%s\tCGroup: %s\n" - "%s\tCGroup realized: %s\n" - "%s\tCGroup mask: 0x%x\n" - "%s\tCGroup members mask: 0x%x\n", + "%s\tCGroup realized: %s\n", prefix, u->id, prefix, unit_description(u), prefix, strna(u->instance), @@ -974,9 +973,18 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { prefix, yes_no(u->perpetual), prefix, strna(unit_slice_name(u)), prefix, strna(u->cgroup_path), - prefix, yes_no(u->cgroup_realized), - prefix, u->cgroup_realized_mask, - prefix, u->cgroup_members_mask); + prefix, yes_no(u->cgroup_realized)); + + if (u->cgroup_realized_mask != 0) { + _cleanup_free_ char *s = NULL; + (void) cg_mask_to_string(u->cgroup_realized_mask, &s); + fprintf(f, "%s\tCGroup mask: %s\n", prefix, strnull(s)); + } + if (u->cgroup_members_mask != 0) { + _cleanup_free_ char *s = NULL; + (void) cg_mask_to_string(u->cgroup_members_mask, &s); + fprintf(f, "%s\tCGroup members mask: %s\n", prefix, strnull(s)); + } SET_FOREACH(t, u->names, i) fprintf(f, "%s\tName: %s\n", prefix, t); @@ -1336,6 +1344,9 @@ int unit_load(Unit *u) { goto fail; } + if (u->job_running_timeout != USEC_INFINITY && u->job_running_timeout > u->job_timeout) + log_unit_warning(u, "JobRunningTimeoutSec= is greater than JobTimeoutSec=, it has no effect."); + unit_update_cgroup_members_masks(u); } @@ -1497,9 +1508,9 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { * possible, which means we should avoid the low-level unit * name. */ log_struct(LOG_INFO, - mid, - LOG_UNIT_ID(u), LOG_MESSAGE("%s", buf), + LOG_UNIT_ID(u), + mid, NULL); } @@ -1821,6 +1832,10 @@ static void unit_check_binds_to(Unit *u) { if (other->job) continue; + if (!other->coldplugged) + /* We might yet create a job for the other unit… */ + continue; + if (!UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) continue; @@ -2627,6 +2642,9 @@ static int signal_name_owner_changed(sd_bus_message *message, void *userdata, sd return 0; } + old_owner = isempty(old_owner) ? NULL : old_owner; + new_owner = isempty(new_owner) ? NULL : new_owner; + if (UNIT_VTABLE(u)->bus_name_owner_change) UNIT_VTABLE(u)->bus_name_owner_change(u, name, old_owner, new_owner); @@ -2693,6 +2711,25 @@ bool unit_can_serialize(Unit *u) { return UNIT_VTABLE(u)->serialize && UNIT_VTABLE(u)->deserialize_item; } +static int unit_serialize_cgroup_mask(FILE *f, const char *key, CGroupMask mask) { + _cleanup_free_ char *s = NULL; + int r = 0; + + assert(f); + assert(key); + + if (mask != 0) { + r = cg_mask_to_string(mask, &s); + if (r >= 0) { + fputs(key, f); + fputc('=', f); + fputs(s, f); + fputc('\n', f); + } + } + return r; +} + int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { int r; @@ -2740,6 +2777,8 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { if (u->cgroup_path) unit_serialize_item(u, f, "cgroup", u->cgroup_path); unit_serialize_item(u, f, "cgroup-realized", yes_no(u->cgroup_realized)); + (void) unit_serialize_cgroup_mask(f, "cgroup-realized-mask", u->cgroup_realized_mask); + (void) unit_serialize_cgroup_mask(f, "cgroup-enabled-mask", u->cgroup_enabled_mask); if (uid_is_valid(u->ref_uid)) unit_serialize_item_format(u, f, "ref-uid", UID_FMT, u->ref_uid); @@ -2997,6 +3036,20 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { continue; + } else if (streq(l, "cgroup-realized-mask")) { + + r = cg_mask_from_string(v, &u->cgroup_realized_mask); + if (r < 0) + log_unit_debug(u, "Failed to parse cgroup-realized-mask %s, ignoring.", v); + continue; + + } else if (streq(l, "cgroup-enabled-mask")) { + + r = cg_mask_from_string(v, &u->cgroup_enabled_mask); + if (r < 0) + log_unit_debug(u, "Failed to parse cgroup-enabled-mask %s, ignoring.", v); + continue; + } else if (streq(l, "ref-uid")) { uid_t uid; diff --git a/src/core/unit.h b/src/core/unit.h index 8052c234fd..cf21b37e22 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -114,6 +114,7 @@ struct Unit { /* Job timeout and action to take */ usec_t job_timeout; + usec_t job_running_timeout; EmergencyAction job_timeout_action; char *job_timeout_reboot_arg; diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 4c4f36aea0..a2c62e55a5 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -144,10 +144,10 @@ static int parse_config(void) { }; return config_parse_many_nulstr(PKGSYSCONFDIR "/coredump.conf", - CONF_PATHS_NULSTR("systemd/coredump.conf.d"), - "Coredump\0", - config_item_table_lookup, items, - false, NULL); + CONF_PATHS_NULSTR("systemd/coredump.conf.d"), + "Coredump\0", + config_item_table_lookup, items, + false, NULL); } static inline uint64_t storage_size_max(void) { @@ -800,12 +800,11 @@ log: if (journald_crash) { /* We cannot log to the journal, so just print the MESSAGE. * The target was set previously to something safe. */ - log_struct(LOG_ERR, core_message, NULL); + log_dispatch(LOG_ERR, 0, core_message); return 0; } - if (core_message) - IOVEC_SET_STRING(iovec[n_iovec++], core_message); + IOVEC_SET_STRING(iovec[n_iovec++], core_message); if (truncated) IOVEC_SET_STRING(iovec[n_iovec++], "COREDUMP_TRUNCATED=1"); @@ -1326,7 +1325,8 @@ static int process_backtrace(int argc, char *argv[]) { log_error_errno(r, "Failed to parse journal entry on stdin: %m"); goto finish; } - if (r == 1) + if (r == 1 || /* complete entry */ + journal_importer_eof(&importer)) /* end of data */ break; } diff --git a/src/coredump/meson.build b/src/coredump/meson.build new file mode 100644 index 0000000000..8f7d898b62 --- /dev/null +++ b/src/coredump/meson.build @@ -0,0 +1,24 @@ +systemd_coredump_sources = files(''' + coredump.c + coredump-vacuum.c + coredump-vacuum.h +'''.split()) + +if conf.get('HAVE_ELFUTILS', false) + systemd_coredump_sources += files(['stacktrace.c', + 'stacktrace.h']) +endif + +coredumpctl_sources = files('coredumpctl.c') + +install_data('coredump.conf', + install_dir : pkgsysconfdir) + +tests += [ + [['src/coredump/test-coredump-vacuum.c', + 'src/coredump/coredump-vacuum.c', + 'src/coredump/coredump-vacuum.h'], + [], + [], + 'ENABLE_COREDUMP', 'manual'], +] diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 91c653312a..3b4c086162 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -190,7 +190,7 @@ static int parse_one_option(const char *option) { arg_type = CRYPT_PLAIN; else if ((val = startswith(option, "timeout="))) { - r = parse_sec(val, &arg_timeout); + r = parse_sec_fix_0(val, &arg_timeout); if (r < 0) { log_error_errno(r, "Failed to parse %s, ignoring: %m", option); return 0; diff --git a/src/delta/delta.c b/src/delta/delta.c index 9a44b15da7..b0689ffff7 100644 --- a/src/delta/delta.c +++ b/src/delta/delta.c @@ -205,7 +205,12 @@ static int found_override(const char *top, const char *bottom) { return k; } -static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *toppath, const char *drop) { +static int enumerate_dir_d( + OrderedHashmap *top, + OrderedHashmap *bottom, + OrderedHashmap *drops, + const char *toppath, const char *drop) { + _cleanup_free_ char *unit = NULL; _cleanup_free_ char *path = NULL; _cleanup_strv_free_ char **list = NULL; @@ -234,8 +239,10 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const if (r < 0) return log_error_errno(r, "Failed to enumerate %s: %m", path); + strv_sort(list); + STRV_FOREACH(file, list) { - Hashmap *h; + OrderedHashmap *h; int k; char *p; char *d; @@ -249,7 +256,7 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const d = p + strlen(toppath) + 1; log_debug("Adding at top: %s %s %s", d, special_glyph(ARROW), p); - k = hashmap_put(top, d, p); + k = ordered_hashmap_put(top, d, p); if (k >= 0) { p = strdup(p); if (!p) @@ -261,19 +268,19 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const } log_debug("Adding at bottom: %s %s %s", d, special_glyph(ARROW), p); - free(hashmap_remove(bottom, d)); - k = hashmap_put(bottom, d, p); + free(ordered_hashmap_remove(bottom, d)); + k = ordered_hashmap_put(bottom, d, p); if (k < 0) { free(p); return k; } - h = hashmap_get(drops, unit); + h = ordered_hashmap_get(drops, unit); if (!h) { - h = hashmap_new(&string_hash_ops); + h = ordered_hashmap_new(&string_hash_ops); if (!h) return -ENOMEM; - hashmap_put(drops, unit, h); + ordered_hashmap_put(drops, unit, h); unit = strdup(unit); if (!unit) return -ENOMEM; @@ -285,7 +292,7 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const log_debug("Adding to drops: %s %s %s %s %s", unit, special_glyph(ARROW), basename(p), special_glyph(ARROW), p); - k = hashmap_put(h, basename(p), p); + k = ordered_hashmap_put(h, basename(p), p); if (k < 0) { free(p); if (k != -EEXIST) @@ -295,9 +302,18 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const return 0; } -static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const char *path, bool dropins) { - _cleanup_closedir_ DIR *d; +static int enumerate_dir( + OrderedHashmap *top, + OrderedHashmap *bottom, + OrderedHashmap *drops, + const char *path, bool dropins) { + + _cleanup_closedir_ DIR *d = NULL; struct dirent *de; + _cleanup_strv_free_ char **files = NULL, **dirs = NULL; + size_t n_files = 0, allocated_files = 0, n_dirs = 0, allocated_dirs = 0; + char **t; + int r; assert(top); assert(bottom); @@ -315,40 +331,63 @@ static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const ch } FOREACH_DIRENT_ALL(de, d, return -errno) { - int k; - char *p; - dirent_ensure_type(d, de); - if (dropins && de->d_type == DT_DIR && endswith(de->d_name, ".d")) - enumerate_dir_d(top, bottom, drops, path, de->d_name); + if (dropins && de->d_type == DT_DIR && endswith(de->d_name, ".d")) { + if (!GREEDY_REALLOC0(dirs, allocated_dirs, n_dirs + 2)) + return -ENOMEM; + + dirs[n_dirs] = strdup(de->d_name); + if (!dirs[n_dirs]) + return -ENOMEM; + n_dirs ++; + } if (!dirent_is_file(de)) continue; - p = strjoin(path, "/", de->d_name); + if (!GREEDY_REALLOC0(files, allocated_files, n_files + 2)) + return -ENOMEM; + + files[n_files] = strdup(de->d_name); + if (!files[n_files]) + return -ENOMEM; + n_files ++; + } + + strv_sort(dirs); + strv_sort(files); + + STRV_FOREACH(t, dirs) { + r = enumerate_dir_d(top, bottom, drops, path, *t); + if (r < 0) + return r; + } + + STRV_FOREACH(t, files) { + _cleanup_free_ char *p = NULL; + + p = strjoin(path, "/", *t); if (!p) return -ENOMEM; log_debug("Adding at top: %s %s %s", basename(p), special_glyph(ARROW), p); - k = hashmap_put(top, basename(p), p); - if (k >= 0) { + r = ordered_hashmap_put(top, basename(p), p); + if (r >= 0) { p = strdup(p); if (!p) return -ENOMEM; - } else if (k != -EEXIST) { - free(p); - return k; - } + } else if (r != -EEXIST) + return r; log_debug("Adding at bottom: %s %s %s", basename(p), special_glyph(ARROW), p); - free(hashmap_remove(bottom, basename(p))); - k = hashmap_put(bottom, basename(p), p); - if (k < 0) { - free(p); - return k; - } + free(ordered_hashmap_remove(bottom, basename(p))); + r = ordered_hashmap_put(bottom, basename(p), p); + if (r < 0) + return r; + p = NULL; } + return 0; } @@ -370,8 +409,8 @@ static int should_skip_prefix(const char* p) { static int process_suffix(const char *suffix, const char *onlyprefix) { const char *p; char *f; - Hashmap *top, *bottom, *drops; - Hashmap *h; + OrderedHashmap *top, *bottom, *drops; + OrderedHashmap *h; char *key; int r = 0, k; Iterator i, j; @@ -384,9 +423,9 @@ static int process_suffix(const char *suffix, const char *onlyprefix) { dropins = nulstr_contains(have_dropins, suffix); - top = hashmap_new(&string_hash_ops); - bottom = hashmap_new(&string_hash_ops); - drops = hashmap_new(&string_hash_ops); + top = ordered_hashmap_new(&string_hash_ops); + bottom = ordered_hashmap_new(&string_hash_ops); + drops = ordered_hashmap_new(&string_hash_ops); if (!top || !bottom || !drops) { r = -ENOMEM; goto finish; @@ -415,10 +454,10 @@ static int process_suffix(const char *suffix, const char *onlyprefix) { r = k; } - HASHMAP_FOREACH_KEY(f, key, top, i) { + ORDERED_HASHMAP_FOREACH_KEY(f, key, top, i) { char *o; - o = hashmap_get(bottom, key); + o = ordered_hashmap_get(bottom, key); assert(o); if (!onlyprefix || startswith(o, onlyprefix)) { @@ -433,23 +472,23 @@ static int process_suffix(const char *suffix, const char *onlyprefix) { } } - h = hashmap_get(drops, key); + h = ordered_hashmap_get(drops, key); if (h) - HASHMAP_FOREACH(o, h, j) + ORDERED_HASHMAP_FOREACH(o, h, j) if (!onlyprefix || startswith(o, onlyprefix)) n_found += notify_override_extended(f, o); } finish: - hashmap_free_free(top); - hashmap_free_free(bottom); + ordered_hashmap_free_free(top); + ordered_hashmap_free_free(bottom); - HASHMAP_FOREACH_KEY(h, key, drops, i) { - hashmap_free_free(hashmap_remove(drops, key)); - hashmap_remove(drops, key); + ORDERED_HASHMAP_FOREACH_KEY(h, key, drops, i) { + ordered_hashmap_free_free(ordered_hashmap_remove(drops, key)); + ordered_hashmap_remove(drops, key); free(key); } - hashmap_free(drops); + ordered_hashmap_free(drops); return r < 0 ? r : n_found; } diff --git a/src/environment-d-generator/environment-d-generator.c b/src/environment-d-generator/environment-d-generator.c index 2d4c4235e4..9c72502373 100644 --- a/src/environment-d-generator/environment-d-generator.c +++ b/src/environment-d-generator/environment-d-generator.c @@ -78,7 +78,7 @@ static int load_and_print(void) { t = strchr(*i, '='); assert(t); - q = shell_maybe_quote(t + 1); + q = shell_maybe_quote(t + 1, ESCAPE_BACKSLASH); if (!q) return log_oom(); diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index bc16290c72..b3578d3e16 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -572,7 +572,7 @@ static int process_root_password(void) { if (!arg_root_password) return 0; - r = dev_urandom(raw, 16); + r = acquire_random_bytes(raw, 16, true); if (r < 0) return log_error_errno(r, "Failed to get salt: %m"); diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 2677a3fb32..7f23b9fd74 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -25,6 +25,7 @@ #include "alloc-util.h" #include "fd-util.h" +#include "fs-util.h" #include "fileio.h" #include "fstab-util.h" #include "generator.h" @@ -176,7 +177,7 @@ static bool mount_in_initrd(struct mntent *me) { } static int write_timeout(FILE *f, const char *where, const char *opts, - const char *filter, const char *variable) { + const char *filter, const char *variable) { _cleanup_free_ char *timeout = NULL; char timespan[FORMAT_TIMESPAN_MAX]; usec_t u; @@ -188,7 +189,7 @@ static int write_timeout(FILE *f, const char *where, const char *opts, if (r == 0) return 0; - r = parse_sec(timeout, &u); + r = parse_sec_fix_0(timeout, &u); if (r < 0) { log_warning("Failed to parse timeout for %s, ignoring: %s", where, timeout); return 0; @@ -290,6 +291,7 @@ static int add_mount( const char *dest, const char *what, const char *where, + const char *original_where, const char *fstype, const char *opts, int passno, @@ -358,7 +360,21 @@ static int add_mount( "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n", source); - if (!noauto && !nofail && !automount) + if (STRPTR_IN_SET(fstype, "nfs", "nfs4") && !automount && + fstab_test_yes_no_option(opts, "bg\0" "fg\0")) { + /* The default retry timeout that mount.nfs uses for 'bg' mounts + * is 10000 minutes, where as it uses 2 minutes for 'fg' mounts. + * As we are making 'bg' mounts look like an 'fg' mount to + * mount.nfs (so systemd can manage the job-control aspects of 'bg'), + * we need to explicitly preserve that default, and also ensure + * the systemd mount-timeout doesn't interfere. + * By placing these options first, they can be over-ridden by + * settings in /etc/fstab. */ + opts = strjoina("x-systemd.mount-timeout=infinity,retry=10000,", opts, ",fg"); + nofail = true; + } + + if (!nofail && !automount) fprintf(f, "Before=%s\n", post); if (!automount && opts) { @@ -382,11 +398,10 @@ static int add_mount( return r; } - fprintf(f, - "\n" - "[Mount]\n" - "Where=%s\n", - where); + fprintf(f, "\n[Mount]\n"); + if (original_where) + fprintf(f, "# Canonicalized from %s\n", original_where); + fprintf(f, "Where=%s\n", where); r = write_what(f, what); if (r < 0) @@ -399,6 +414,10 @@ static int add_mount( if (r < 0) return r; + r = generator_write_device_deps(dest, what, where, opts); + if (r < 0) + return r; + r = write_mount_timeout(f, where, opts); if (r < 0) return r; @@ -502,7 +521,7 @@ static int parse_fstab(bool initrd) { } while ((me = getmntent(f))) { - _cleanup_free_ char *where = NULL, *what = NULL; + _cleanup_free_ char *where = NULL, *what = NULL, *canonical_where = NULL; bool noauto, nofail; int k; @@ -522,8 +541,28 @@ static int parse_fstab(bool initrd) { if (!where) return log_oom(); - if (is_path(where)) + if (is_path(where)) { path_kill_slashes(where); + /* Follow symlinks here; see 5261ba901845c084de5a8fd06500ed09bfb0bd80 which makes sense for + * mount units, but causes problems since it historically worked to have symlinks in e.g. + * /etc/fstab. So we canonicalize here. Note that we use CHASE_NONEXISTENT to handle the case + * where a symlink refers to another mount target; this works assuming the sub-mountpoint + * target is the final directory. + */ + r = chase_symlinks(where, initrd ? "/sysroot" : NULL, + CHASE_PREFIX_ROOT | CHASE_NONEXISTENT, + &canonical_where); + if (r < 0) + /* In this case for now we continue on as if it wasn't a symlink */ + log_warning_errno(r, "Failed to read symlink target for %s: %m", where); + else { + if (streq(canonical_where, where)) + canonical_where = mfree(canonical_where); + else + log_debug("Canonicalized what=%s where=%s to %s", + what, where, canonical_where); + } + } noauto = fstab_test_yes_no_option(me->mnt_opts, "noauto\0" "auto\0"); nofail = fstab_test_yes_no_option(me->mnt_opts, "nofail\0" "fail\0"); @@ -549,7 +588,8 @@ static int parse_fstab(bool initrd) { k = add_mount(arg_dest, what, - where, + canonical_where ?: where, + canonical_where ? where: NULL, me->mnt_type, me->mnt_opts, me->mnt_passno, @@ -612,6 +652,7 @@ static int add_sysroot_mount(void) { return add_mount(arg_dest, what, "/sysroot", + NULL, arg_root_fstype, opts, is_device_path(what) ? 1 : 0, /* passno */ @@ -666,6 +707,7 @@ static int add_sysroot_usr_mount(void) { return add_mount(arg_dest, what, "/sysroot/usr", + NULL, arg_usr_fstype, opts, is_device_path(what) ? 1 : 0, /* passno */ @@ -706,6 +748,7 @@ static int add_volatile_var(void) { return add_mount(arg_dest_late, "tmpfs", "/var", + NULL, "tmpfs", "mode=0755", 0, diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index 80f676e477..a072242430 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -17,7 +17,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <blkid/blkid.h> +#include <blkid.h> #include <stdlib.h> #include <sys/statfs.h> #include <unistd.h> @@ -305,6 +305,15 @@ static int add_swap(const char *path) { assert(path); + /* Disable the swap auto logic if at least one swap is defined in /etc/fstab, see #6192. */ + r = fstab_has_fstype("swap"); + if (r < 0) + return log_error_errno(r, "Failed to parse fstab: %m"); + if (r > 0) { + log_debug("swap specified in fstab, ignoring."); + return 0; + } + log_debug("Adding swap: %s", path); r = unit_name_from_path(path, ".swap", &name); @@ -435,7 +444,10 @@ static int add_esp(DissectedPartition *p) { esp = access("/efi/", F_OK) >= 0 ? "/efi" : "/boot"; /* We create an .automount which is not overridden by the .mount from the fstab generator. */ - if (fstab_is_mount_point(esp)) { + r = fstab_is_mount_point(esp); + if (r < 0) + return log_error_errno(r, "Failed to parse fstab: %m"); + if (r > 0) { log_debug("%s specified in fstab, ignoring.", esp); return 0; } diff --git a/src/hostname/meson.build b/src/hostname/meson.build new file mode 100644 index 0000000000..d58caa6787 --- /dev/null +++ b/src/hostname/meson.build @@ -0,0 +1,14 @@ +if conf.get('ENABLE_HOSTNAMED', false) + install_data('org.freedesktop.hostname1.conf', + install_dir : dbuspolicydir) + install_data('org.freedesktop.hostname1.service', + install_dir : dbussystemservicedir) + + custom_target( + 'org.freedesktop.hostname1.policy', + input : 'org.freedesktop.hostname1.policy.in', + output : 'org.freedesktop.hostname1.policy', + command : intltool_command, + install : install_polkit, + install_dir : polkitpolicydir) +endif diff --git a/src/hwdb/hwdb.c b/src/hwdb/hwdb.c index a23b614791..793398ca68 100644 --- a/src/hwdb/hwdb.c +++ b/src/hwdb/hwdb.c @@ -31,6 +31,7 @@ #include "hwdb-util.h" #include "label.h" #include "mkdir.h" +#include "path-util.h" #include "selinux-util.h" #include "strbuf.h" #include "string-util.h" @@ -390,7 +391,7 @@ static int trie_store(struct trie *trie, const char *filename) { int64_t size; struct trie_header_f h = { .signature = HWDB_SIG, - .tool_version = htole64(atoi(VERSION)), + .tool_version = htole64(atoi(PACKAGE_VERSION)), .header_size = htole64(sizeof(struct trie_header_f)), .node_size = htole64(sizeof(struct trie_node_f)), .child_entry_size = htole64(sizeof(struct trie_child_entry_f)), @@ -670,7 +671,7 @@ static int hwdb_update(int argc, char *argv[], void *userdata) { log_debug("strings dedup'ed: %8zu bytes (%8zu)", trie->strings->dedup_len, trie->strings->dedup_count); - hwdb_bin = strjoin(arg_root, "/", arg_hwdb_bin_dir, "/hwdb.bin"); + hwdb_bin = path_join(arg_root, arg_hwdb_bin_dir, "hwdb.bin"); if (!hwdb_bin) return -ENOMEM; diff --git a/src/import/import-raw.c b/src/import/import-raw.c index 808eae38f8..55cf8e8edd 100644 --- a/src/import/import-raw.c +++ b/src/import/import-raw.c @@ -355,7 +355,7 @@ static int raw_import_process(RawImport *i) { } if (l == 0) { if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) { - log_error("Premature end of file: %m"); + log_error("Premature end of file."); r = -EIO; goto finish; } @@ -369,7 +369,7 @@ static int raw_import_process(RawImport *i) { if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) { r = import_uncompress_detect(&i->compress, i->buffer, i->buffer_size); if (r < 0) { - log_error("Failed to detect file compression: %m"); + log_error_errno(r, "Failed to detect file compression: %m"); goto finish; } if (r == 0) /* Need more data */ diff --git a/src/import/import-tar.c b/src/import/import-tar.c index 1c229ec82f..ba140bccbd 100644 --- a/src/import/import-tar.c +++ b/src/import/import-tar.c @@ -284,7 +284,7 @@ static int tar_import_process(TarImport *i) { } if (l == 0) { if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) { - log_error("Premature end of file: %m"); + log_error("Premature end of file."); r = -EIO; goto finish; } @@ -298,7 +298,7 @@ static int tar_import_process(TarImport *i) { if (i->compress.type == IMPORT_COMPRESS_UNKNOWN) { r = import_uncompress_detect(&i->compress, i->buffer, i->buffer_size); if (r < 0) { - log_error("Failed to detect file compression: %m"); + log_error_errno(r, "Failed to detect file compression: %m"); goto finish; } if (r == 0) /* Need more data */ diff --git a/src/import/meson.build b/src/import/meson.build new file mode 100644 index 0000000000..3fd58cc2c8 --- /dev/null +++ b/src/import/meson.build @@ -0,0 +1,77 @@ +systemd_importd_sources = files(''' + importd.c +'''.split()) + +systemd_pull_sources = files(''' + pull.c + pull-raw.c + pull-raw.h + pull-tar.c + pull-tar.h + pull-job.c + pull-job.h + pull-common.c + pull-common.h + import-common.c + import-common.h + import-compress.c + import-compress.h + curl-util.c + curl-util.h + qcow2-util.c + qcow2-util.h +'''.split()) + +systemd_import_sources = files(''' + import.c + import-raw.c + import-raw.h + import-tar.c + import-tar.h + import-common.c + import-common.h + import-compress.c + import-compress.h + qcow2-util.c + qcow2-util.h +'''.split()) + +systemd_export_sources = files(''' + export.c + export-tar.c + export-tar.h + export-raw.c + export-raw.h + import-common.c + import-common.h + import-compress.c + import-compress.h +'''.split()) + +if conf.get('ENABLE_IMPORTD', false) + install_data('org.freedesktop.import1.conf', + install_dir : dbuspolicydir) + install_data('org.freedesktop.import1.service', + install_dir : dbussystemservicedir) + + custom_target( + 'org.freedesktop.import1.policy', + input : 'org.freedesktop.import1.policy.in', + output : 'org.freedesktop.import1.policy', + command : intltool_command, + install : install_polkit, + install_dir : polkitpolicydir) + + install_data('import-pubring.gpg', + install_dir : rootlibexecdir) + # TODO: shouldn't this be in pkgdatadir? +endif + +tests += [ + [['src/import/test-qcow2.c', + 'src/import/qcow2-util.c', + 'src/import/qcow2-util.h'], + [libshared], + [libz], + 'HAVE_ZLIB', 'manual'], +] diff --git a/src/import/pull-common.c b/src/import/pull-common.c index 62a9195cc4..78840dd882 100644 --- a/src/import/pull-common.c +++ b/src/import/pull-common.c @@ -275,6 +275,7 @@ int pull_make_verification_jobs( _cleanup_(pull_job_unrefp) PullJob *checksum_job = NULL, *signature_job = NULL; int r; + const char *chksums = NULL; assert(ret_checksum_job); assert(ret_signature_job); @@ -284,10 +285,16 @@ int pull_make_verification_jobs( assert(glue); if (verify != IMPORT_VERIFY_NO) { - _cleanup_free_ char *checksum_url = NULL; + _cleanup_free_ char *checksum_url = NULL, *fn = NULL; - /* Queue job for the SHA256SUMS file for the image */ - r = import_url_change_last_component(url, "SHA256SUMS", &checksum_url); + /* Queue jobs for the checksum file for the image. */ + r = import_url_last_component(url, &fn); + if (r < 0) + return r; + + chksums = strjoina(fn, ".sha256"); + + r = import_url_change_last_component(url, chksums, &checksum_url); if (r < 0) return r; @@ -362,6 +369,15 @@ static int verify_one(PullJob *checksum_job, PullJob *job) { line, strlen(line)); + if (!p) { + line = strjoina(job->checksum, " ", fn, "\n"); + + p = memmem(checksum_job->payload, + checksum_job->payload_size, + line, + strlen(line)); + } + if (!p || (p != (char*) checksum_job->payload && p[-1] != '\n')) { log_error("DOWNLOAD INVALID: Checksum of %s file did not checkout, file has been tampered with.", fn); return -EBADMSG; @@ -378,7 +394,6 @@ int pull_verify(PullJob *main_job, PullJob *signature_job) { _cleanup_close_pair_ int gpg_pipe[2] = { -1, -1 }; - _cleanup_free_ char *fn = NULL; _cleanup_close_ int sig_file = -1; char sig_file_path[] = "/tmp/sigXXXXXX", gpg_home[] = "/tmp/gpghomeXXXXXX"; _cleanup_(sigkill_waitp) pid_t pid = 0; @@ -416,6 +431,9 @@ int pull_verify(PullJob *main_job, if (!signature_job) return 0; + if (checksum_job->style == VERIFICATION_PER_FILE) + signature_job = checksum_job; + assert(signature_job->state == PULL_JOB_DONE); if (!signature_job->payload || signature_job->payload_size <= 0) { @@ -507,9 +525,11 @@ int pull_verify(PullJob *main_job, cmd[k++] = "--keyring=" VENDOR_KEYRING_PATH; cmd[k++] = "--verify"; - cmd[k++] = sig_file_path; - cmd[k++] = "-"; - cmd[k++] = NULL; + if (checksum_job->style == VERIFICATION_PER_DIRECTORY) { + cmd[k++] = sig_file_path; + cmd[k++] = "-"; + cmd[k++] = NULL; + } stdio_unset_cloexec(); diff --git a/src/import/pull-job.c b/src/import/pull-job.c index e550df2c57..320c21305a 100644 --- a/src/import/pull-job.c +++ b/src/import/pull-job.c @@ -22,9 +22,11 @@ #include "alloc-util.h" #include "fd-util.h" #include "hexdecoct.h" +#include "import-util.h" #include "io-util.h" #include "machine-pool.h" #include "parse-util.h" +#include "pull-common.h" #include "pull-job.h" #include "string-util.h" #include "strv.h" @@ -73,6 +75,31 @@ static void pull_job_finish(PullJob *j, int ret) { j->on_finished(j); } +static int pull_job_restart(PullJob *j) { + int r; + char *chksum_url = NULL; + + r = import_url_change_last_component(j->url, "SHA256SUMS", &chksum_url); + if (r < 0) + return r; + + free(j->url); + j->url = chksum_url; + j->state = PULL_JOB_INIT; + j->payload = mfree(j->payload); + j->payload_size = 0; + j->payload_allocated = 0; + j->written_compressed = 0; + j->written_uncompressed = 0; + j->written_since_last_grow = 0; + + r = pull_job_begin(j); + if (r < 0) + return r; + + return 0; +} + void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { PullJob *j = NULL; CURLcode code; @@ -102,6 +129,26 @@ void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { r = 0; goto finish; } else if (status >= 300) { + if (status == 404 && j->style == VERIFICATION_PER_FILE) { + + /* retry pull job with SHA256SUMS file */ + r = pull_job_restart(j); + if (r < 0) + goto finish; + + code = curl_easy_getinfo(j->curl, CURLINFO_RESPONSE_CODE, &status); + if (code != CURLE_OK) { + log_error("Failed to retrieve response code: %s", curl_easy_strerror(code)); + r = -EIO; + goto finish; + } + + if (status == 0) { + j->style = VERIFICATION_PER_DIRECTORY; + return; + } + } + log_error("HTTP request to %s failed with code %li.", j->url, status); r = -EIO; goto finish; @@ -527,7 +574,8 @@ int pull_job_new(PullJob **ret, const char *url, CurlGlue *glue, void *userdata) j->glue = glue; j->content_length = (uint64_t) -1; j->start_usec = now(CLOCK_MONOTONIC); - j->compressed_max = j->uncompressed_max = 8LLU * 1024LLU * 1024LLU * 1024LLU; /* 8GB */ + j->compressed_max = j->uncompressed_max = 64LLU * 1024LLU * 1024LLU * 1024LLU; /* 64GB safety limit */ + j->style = VERIFICATION_STYLE_UNSET; j->url = strdup(url); if (!j->url) diff --git a/src/import/pull-job.h b/src/import/pull-job.h index 3a152a50e3..412b66cf22 100644 --- a/src/import/pull-job.h +++ b/src/import/pull-job.h @@ -42,6 +42,12 @@ typedef enum PullJobState { _PULL_JOB_STATE_INVALID = -1, } PullJobState; +typedef enum VerificationStyle { + VERIFICATION_STYLE_UNSET, + VERIFICATION_PER_FILE, /* SuSE-style ".sha256" files with inline signature */ + VERIFICATION_PER_DIRECTORY, /* Ubuntu-style SHA256SUM files with detach SHA256SUM.gpg signatures */ +} VerificationStyle; + #define PULL_JOB_IS_COMPLETE(j) (IN_SET((j)->state, PULL_JOB_DONE, PULL_JOB_FAILED)) struct PullJob { @@ -94,6 +100,8 @@ struct PullJob { bool grow_machine_directory; uint64_t written_since_last_grow; + + VerificationStyle style; }; int pull_job_new(PullJob **job, const char *url, CurlGlue *glue, void *userdata); diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c index 60a769e944..b45ac814a9 100644 --- a/src/import/pull-raw.c +++ b/src/import/pull-raw.c @@ -444,7 +444,7 @@ static int raw_pull_rename_auxiliary_file( assert(suffix); assert(path); - /* Regenerate final name for this auxiliary file, we might know the etag of the raw file now, and we shoud + /* Regenerate final name for this auxiliary file, we might know the etag of the file now, and we should * incorporate it in the file name if we can */ *path = mfree(*path); r = raw_pull_determine_path(i, suffix, path); @@ -478,11 +478,9 @@ static void raw_pull_job_on_finished(PullJob *j) { } else if (j == i->settings_job) { if (j->error != 0) log_info_errno(j->error, "Settings file could not be retrieved, proceeding without."); - } else if (j->error != 0) { + } else if (j->error != 0 && j != i->signature_job) { if (j == i->checksum_job) log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)"); - else if (j == i->signature_job) - log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)"); else log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)"); @@ -500,6 +498,13 @@ static void raw_pull_job_on_finished(PullJob *j) { if (!raw_pull_is_done(i)) return; + if (i->signature_job && i->checksum_job->style == VERIFICATION_PER_DIRECTORY && i->signature_job->error != 0) { + log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)"); + + r = i->signature_job->error; + goto finish; + } + if (i->roothash_job) i->roothash_job->disk_fd = safe_close(i->roothash_job->disk_fd); if (i->settings_job) @@ -533,7 +538,7 @@ static void raw_pull_job_on_finished(PullJob *j) { r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path); if (r < 0) { - log_error_errno(r, "Failed to move RAW file into place: %m"); + log_error_errno(r, "Failed to rename raw file to %s: %m", i->final_path); goto finish; } @@ -575,7 +580,6 @@ static int raw_pull_job_on_open_disk_generic( const char *extra, char **temp_path) { - _cleanup_free_ char *p = NULL; int r; assert(i); @@ -744,6 +748,7 @@ int raw_pull_start( if (i->checksum_job) { i->checksum_job->on_progress = raw_pull_job_on_progress; + i->checksum_job->style = VERIFICATION_PER_FILE; r = pull_job_begin(i->checksum_job); if (r < 0) diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index 91833d6174..12211a6fc6 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -114,6 +114,7 @@ TarPull* tar_pull_unref(TarPull *i) { free(i->settings_path); free(i->image_root); free(i->local); + return mfree(i); } @@ -298,11 +299,9 @@ static void tar_pull_job_on_finished(PullJob *j) { if (j == i->settings_job) { if (j->error != 0) log_info_errno(j->error, "Settings file could not be retrieved, proceeding without."); - } else if (j->error != 0) { + } else if (j->error != 0 && j != i->signature_job) { if (j == i->checksum_job) log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)"); - else if (j == i->signature_job) - log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)"); else log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)"); @@ -317,6 +316,13 @@ static void tar_pull_job_on_finished(PullJob *j) { if (!tar_pull_is_done(i)) return; + if (i->signature_job && i->checksum_job->style == VERIFICATION_PER_DIRECTORY && i->signature_job->error != 0) { + log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)"); + + r = i->signature_job->error; + goto finish; + } + i->tar_job->disk_fd = safe_close(i->tar_job->disk_fd); if (i->settings_job) i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd); @@ -353,7 +359,7 @@ static void tar_pull_job_on_finished(PullJob *j) { r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path); if (r < 0) { - log_error_errno(r, "Failed to rename to final image name: %m"); + log_error_errno(r, "Failed to rename to final image name to %s: %m", i->final_path); goto finish; } @@ -362,13 +368,14 @@ static void tar_pull_job_on_finished(PullJob *j) { if (i->settings_job && i->settings_job->error == 0) { - assert(i->settings_temp_path); - assert(i->settings_path); - - /* Also move the settings file into place, if it exist. Note that we do so only if we also + /* Also move the settings file into place, if it exists. Note that we do so only if we also * moved the tar file in place, to keep things strictly in sync. */ + assert(i->settings_temp_path); + /* Regenerate final name for this auxiliary file, we might know the etag of the file now, and + * we should incorporate it in the file name if we can */ i->settings_path = mfree(i->settings_path); + r = tar_pull_determine_path(i, ".nspawn", &i->settings_path); if (r < 0) goto finish; @@ -379,7 +386,7 @@ static void tar_pull_job_on_finished(PullJob *j) { r = rename_noreplace(AT_FDCWD, i->settings_temp_path, AT_FDCWD, i->settings_path); if (r < 0) { - log_error_errno(r, "Failed to rename settings file: %m"); + log_error_errno(r, "Failed to rename settings file to %s: %m", i->settings_path); goto finish; } @@ -547,6 +554,7 @@ int tar_pull_start( if (i->checksum_job) { i->checksum_job->on_progress = tar_pull_job_on_progress; + i->checksum_job->style = VERIFICATION_PER_FILE; r = pull_job_begin(i->checksum_job); if (r < 0) diff --git a/src/journal-remote/journal-remote-parse.c b/src/journal-remote/journal-remote-parse.c index 79afe6604c..d61d1c18f6 100644 --- a/src/journal-remote/journal-remote-parse.c +++ b/src/journal-remote/journal-remote-parse.c @@ -41,7 +41,7 @@ void source_free(RemoteSource *source) { /** * Initialize zero-filled source with given values. On success, takes - * ownerhship of fd and writer, otherwise does not touch them. + * ownership of fd, name, and writer, otherwise does not touch them. */ RemoteSource* source_new(int fd, bool passive_fd, char *name, Writer *writer) { diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c index 202a5a3f97..36c1a32dcd 100644 --- a/src/journal-remote/journal-remote.c +++ b/src/journal-remote/journal-remote.c @@ -529,7 +529,7 @@ static int process_http_upload( log_warning("Failed to process data for connection %p", connection); if (r == -E2BIG) return mhd_respondf(connection, - r, MHD_HTTP_REQUEST_ENTITY_TOO_LARGE, + r, MHD_HTTP_PAYLOAD_TOO_LARGE, "Entry is too large, maximum is " STRINGIFY(DATA_SIZE_MAX) " bytes."); else return mhd_respondf(connection, @@ -1200,9 +1200,9 @@ static int parse_config(void) { {}}; return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-remote.conf", - CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"), - "Remote\0", config_item_table_lookup, items, - false, NULL); + CONF_PATHS_NULSTR("systemd/journal-remote.conf.d"), + "Remote\0", config_item_table_lookup, items, + false, NULL); } static void help(void) { diff --git a/src/journal-remote/journal-upload-journal.c b/src/journal-remote/journal-upload-journal.c index 8ce8e1895e..3a36e46ae0 100644 --- a/src/journal-remote/journal-upload-journal.c +++ b/src/journal-remote/journal-upload-journal.c @@ -251,7 +251,7 @@ static inline void check_update_watchdog(Uploader *u) { return; after = now(CLOCK_MONOTONIC); - elapsed_time = usec_sub(after, u->watchdog_timestamp); + elapsed_time = usec_sub_unsigned(after, u->watchdog_timestamp); if (elapsed_time > u->watchdog_usec / 2) { log_debug("Update watchdog timer"); sd_notify(false, "WATCHDOG=1"); diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c index 371b6acc64..e0858dda7b 100644 --- a/src/journal-remote/journal-upload.c +++ b/src/journal-remote/journal-upload.c @@ -541,9 +541,9 @@ static int parse_config(void) { {}}; return config_parse_many_nulstr(PKGSYSCONFDIR "/journal-upload.conf", - CONF_PATHS_NULSTR("systemd/journal-upload.conf.d"), - "Upload\0", config_item_table_lookup, items, - false, NULL); + CONF_PATHS_NULSTR("systemd/journal-upload.conf.d"), + "Upload\0", config_item_table_lookup, items, + false, NULL); } static void help(void) { diff --git a/src/journal-remote/log-generator.py b/src/journal-remote/log-generator.py index 7b434b334e..c2f945bb47 100755 --- a/src/journal-remote/log-generator.py +++ b/src/journal-remote/log-generator.py @@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 import sys import argparse diff --git a/src/journal-remote/meson.build b/src/journal-remote/meson.build new file mode 100644 index 0000000000..d266b34e65 --- /dev/null +++ b/src/journal-remote/meson.build @@ -0,0 +1,49 @@ +systemd_journal_upload_sources = files(''' + journal-upload.h + journal-upload.c + journal-upload-journal.c +'''.split()) + +systemd_journal_remote_sources = files(''' + journal-remote-parse.h + journal-remote-parse.c + journal-remote-write.h + journal-remote-write.c + journal-remote.h + journal-remote.c + microhttpd-util.h + microhttpd-util.c +'''.split()) + +systemd_journal_gatewayd_sources = files(''' + journal-gatewayd.c + microhttpd-util.h + microhttpd-util.c +'''.split()) + +if conf.get('ENABLE_REMOTE', false) and conf.get('HAVE_LIBCURL', false) + journal_upload_conf = configure_file( + input : 'journal-upload.conf.in', + output : 'journal-upload.conf', + configuration : substs) + install_data(journal_upload_conf, + install_dir : pkgsysconfdir) +endif + +if conf.get('ENABLE_REMOTE', false) and conf.get('HAVE_MICROHTTPD', false) + journal_remote_conf = configure_file( + input : 'journal-remote.conf.in', + output : 'journal-remote.conf', + configuration : substs) + install_data(journal_remote_conf, + install_dir : pkgsysconfdir) + + install_data('browse.html', + install_dir : join_paths(pkgdatadir, 'gatewayd')) + + meson.add_install_script('sh', '-c', + mkdir_p.format('/var/log/journal/remote')) + meson.add_install_script('sh', '-c', + 'chown 0:0 $DESTDIR/var/log/journal/remote && + chmod 755 $DESTDIR/var/log/journal/remote || :') +endif diff --git a/src/journal-remote/microhttpd-util.c b/src/journal-remote/microhttpd-util.c index cae10203c6..f5d2d7967a 100644 --- a/src/journal-remote/microhttpd-util.c +++ b/src/journal-remote/microhttpd-util.c @@ -103,7 +103,10 @@ int mhd_respondf(struct MHD_Connection *connection, errno = -error; fmt = strjoina(format, "\n"); va_start(ap, format); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" r = vasprintf(&m, fmt, ap); +#pragma GCC diagnostic pop va_end(ap); if (r < 0) diff --git a/src/journal-remote/microhttpd-util.h b/src/journal-remote/microhttpd-util.h index 49def4f630..7f88c2cb7d 100644 --- a/src/journal-remote/microhttpd-util.h +++ b/src/journal-remote/microhttpd-util.h @@ -31,14 +31,21 @@ # define MHD_HTTP_NOT_ACCEPTABLE MHD_HTTP_METHOD_NOT_ACCEPTABLE #endif +/* Renamed in µhttpd 0.9.51 */ +#ifndef MHD_USE_PIPE_FOR_SHUTDOWN +# define MHD_USE_ITC MHD_USE_PIPE_FOR_SHUTDOWN +#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 +/* Both the old and new names are defines, check for the new one. */ + +/* Renamed in µhttpd 0.9.53 */ +#ifndef MHD_HTTP_PAYLOAD_TOO_LARGE +# define MHD_HTTP_PAYLOAD_TOO_LARGE MHD_HTTP_REQUEST_ENTITY_TOO_LARGE #endif #if MHD_VERSION < 0x00094203 diff --git a/src/journal/audit_type-to-name.awk b/src/journal/audit_type-to-name.awk new file mode 100644 index 0000000000..44fc702eb3 --- /dev/null +++ b/src/journal/audit_type-to-name.awk @@ -0,0 +1,9 @@ +BEGIN{ + print "const char *audit_type_to_string(int type) {\n\tswitch(type) {" +} +{ + printf " case AUDIT_%s: return \"%s\";\n", $1, $1 +} +END{ + print " default: return NULL;\n\t}\n}\n" +} diff --git a/src/journal/fsprg.c b/src/journal/fsprg.c index 612b10f3a9..e7c22880be 100644 --- a/src/journal/fsprg.c +++ b/src/journal/fsprg.c @@ -40,6 +40,9 @@ #define RND_GEN_Q 0x02 #define RND_GEN_X 0x03 +#pragma GCC diagnostic ignored "-Wpointer-arith" +/* TODO: remove void* arithmetic and this work-around */ + /******************************************************************************/ static void mpi_export(void *buf, size_t buflen, const gcry_mpi_t x) { diff --git a/src/journal/generate-audit_type-list.sh b/src/journal/generate-audit_type-list.sh new file mode 100755 index 0000000000..18cbe0599c --- /dev/null +++ b/src/journal/generate-audit_type-list.sh @@ -0,0 +1,14 @@ +#!/bin/sh -eu + +cpp="$1" +shift + +includes="" +for i in "$@"; do + includes="$includes -include $i" +done + +$cpp -dM $includes - </dev/null | \ + grep -vE 'AUDIT_.*(FIRST|LAST)_' | \ + sed -r -n 's/^#define\s+AUDIT_(\w+)\s+([0-9]{4})\s*$$/\1\t\2/p' | \ + sort -k2 diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index a6ccb679a8..4ff38de2e6 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -162,7 +162,7 @@ static int journal_file_set_offline_thread_join(JournalFile *f) { f->offline_state = OFFLINE_JOINED; - if (mmap_cache_got_sigbus(f->mmap, f->fd)) + if (mmap_cache_got_sigbus(f->mmap, f->cache_fd)) return -EIO; return 0; @@ -300,7 +300,7 @@ static int journal_file_set_online(JournalFile *f) { } } - if (mmap_cache_got_sigbus(f->mmap, f->fd)) + if (mmap_cache_got_sigbus(f->mmap, f->cache_fd)) return -EIO; switch (f->header->state) { @@ -356,8 +356,8 @@ JournalFile* journal_file_close(JournalFile *f) { journal_file_set_offline(f, true); - if (f->mmap && f->fd >= 0) - mmap_cache_close_fd(f->mmap, f->fd); + if (f->mmap && f->cache_fd) + mmap_cache_free_fd(f->mmap, f->cache_fd); if (f->fd >= 0 && f->defrag_on_close) { @@ -546,6 +546,8 @@ static bool warn_wrong_flags(const JournalFile *f, bool compatible) { } static int journal_file_verify_header(JournalFile *f) { + uint64_t arena_size, header_size; + assert(f); assert(f->header); @@ -564,17 +566,21 @@ static int journal_file_verify_header(JournalFile *f) { if (f->header->state >= _STATE_MAX) return -EBADMSG; + header_size = le64toh(f->header->header_size); + /* The first addition was n_data, so check that we are at least this large */ - if (le64toh(f->header->header_size) < HEADER_SIZE_MIN) + if (header_size < HEADER_SIZE_MIN) return -EBADMSG; if (JOURNAL_HEADER_SEALED(f->header) && !JOURNAL_HEADER_CONTAINS(f->header, n_entry_arrays)) return -EBADMSG; - if ((le64toh(f->header->header_size) + le64toh(f->header->arena_size)) > (uint64_t) f->last_stat.st_size) + arena_size = le64toh(f->header->arena_size); + + if (UINT64_MAX - header_size < arena_size || header_size + arena_size > (uint64_t) f->last_stat.st_size) return -ENODATA; - if (le64toh(f->header->tail_object_offset) > (le64toh(f->header->header_size) + le64toh(f->header->arena_size))) + if (le64toh(f->header->tail_object_offset) > header_size + arena_size) return -ENODATA; if (!VALID64(le64toh(f->header->data_hash_table_offset)) || @@ -607,6 +613,9 @@ static int journal_file_verify_header(JournalFile *f) { return -EBUSY; } + if (f->header->field_hash_table_size == 0 || f->header->data_hash_table_size == 0) + return -EBADMSG; + /* Don't permit appending to files from the future. Because otherwise the realtime timestamps wouldn't * be strictly ordered in the entries in the file anymore, and we can't have that since it breaks * bisection. */ @@ -651,7 +660,7 @@ static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size) * for sure, since we always call posix_fallocate() * ourselves */ - if (mmap_cache_got_sigbus(f->mmap, f->fd)) + if (mmap_cache_got_sigbus(f->mmap, f->cache_fd)) return -EIO; old_size = @@ -740,7 +749,7 @@ static int journal_file_move_to(JournalFile *f, ObjectType type, bool keep_alway return -EADDRNOTAVAIL; } - return mmap_cache_get(f->mmap, f->fd, f->prot, type_to_context(type), keep_always, offset, size, &f->last_stat, ret); + return mmap_cache_get(f->mmap, f->cache_fd, f->prot, type_to_context(type), keep_always, offset, size, &f->last_stat, ret); } static uint64_t minimum_header_size(Object *o) { @@ -1848,7 +1857,7 @@ int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const st * it is very likely just an effect of a nullified replacement * mapping page */ - if (mmap_cache_got_sigbus(f->mmap, f->fd)) + if (mmap_cache_got_sigbus(f->mmap, f->cache_fd)) r = -EIO; if (f->post_change_timer) @@ -3135,6 +3144,12 @@ int journal_file_open( f->close_fd = true; } + f->cache_fd = mmap_cache_add_fd(f->mmap, f->fd); + if (!f->cache_fd) { + r = -ENOMEM; + goto fail; + } + r = journal_file_fstat(f); if (r < 0) goto fail; @@ -3181,7 +3196,7 @@ int journal_file_open( goto fail; } - r = mmap_cache_get(f->mmap, f->fd, f->prot, CONTEXT_HEADER, true, 0, PAGE_ALIGN(sizeof(Header)), &f->last_stat, &h); + r = mmap_cache_get(f->mmap, f->cache_fd, f->prot, CONTEXT_HEADER, true, 0, PAGE_ALIGN(sizeof(Header)), &f->last_stat, &h); if (r < 0) goto fail; @@ -3238,7 +3253,7 @@ int journal_file_open( #endif } - if (mmap_cache_got_sigbus(f->mmap, f->fd)) { + if (mmap_cache_got_sigbus(f->mmap, f->cache_fd)) { r = -EIO; goto fail; } @@ -3260,7 +3275,7 @@ int journal_file_open( return 0; fail: - if (f->fd >= 0 && mmap_cache_got_sigbus(f->mmap, f->fd)) + if (f->cache_fd && mmap_cache_got_sigbus(f->mmap, f->cache_fd)) r = -EIO; (void) journal_file_close(f); @@ -3473,7 +3488,7 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6 r = journal_file_append_entry_internal(to, &ts, xor_hash, items, n, seqnum, ret, offset); - if (mmap_cache_got_sigbus(to->mmap, to->fd)) + if (mmap_cache_got_sigbus(to->mmap, to->cache_fd)) return -EIO; return r; diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index 564e1a8179..df457c9a81 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -75,6 +75,7 @@ typedef enum OfflineState { typedef struct JournalFile { int fd; + MMapFileDescriptor *cache_fd; mode_t mode; diff --git a/src/journal/journal-qrcode.c b/src/journal/journal-qrcode.c index e38730d65c..5ee10498d1 100644 --- a/src/journal/journal-qrcode.c +++ b/src/journal/journal-qrcode.c @@ -17,7 +17,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <assert.h> #include <errno.h> #include <qrencode.h> #include <stdbool.h> @@ -25,6 +24,7 @@ #include <stdlib.h> #include "journal-qrcode.h" +#include "macro.h" #define WHITE_ON_BLACK "\033[40;37;1m" #define NORMAL "\033[0m" diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 9e4d8a28a5..9feb5b5ae6 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -377,12 +377,12 @@ static int write_uint64(int fd, uint64_t p) { return 0; } -static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) { +static int contains_uint64(MMapCache *m, MMapFileDescriptor *f, uint64_t n, uint64_t p) { uint64_t a, b; int r; assert(m); - assert(fd >= 0); + assert(f); /* Bisection ... */ @@ -392,7 +392,7 @@ static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) { c = (a + b) / 2; - r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, false, c * sizeof(uint64_t), sizeof(uint64_t), NULL, (void **) &z); + r = mmap_cache_get(m, f, PROT_READ|PROT_WRITE, 0, false, c * sizeof(uint64_t), sizeof(uint64_t), NULL, (void **) &z); if (r < 0) return r; @@ -413,7 +413,7 @@ static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) { static int entry_points_to_data( JournalFile *f, - int entry_fd, + MMapFileDescriptor *cache_entry_fd, uint64_t n_entries, uint64_t entry_p, uint64_t data_p) { @@ -424,9 +424,9 @@ static int entry_points_to_data( bool found = false; assert(f); - assert(entry_fd >= 0); + assert(cache_entry_fd); - if (!contains_uint64(f->mmap, entry_fd, n_entries, entry_p)) { + if (!contains_uint64(f->mmap, cache_entry_fd, n_entries, entry_p)) { error(data_p, "Data object references invalid entry at "OFSfmt, entry_p); return -EBADMSG; } @@ -500,16 +500,16 @@ static int entry_points_to_data( static int verify_data( JournalFile *f, Object *o, uint64_t p, - int entry_fd, uint64_t n_entries, - int entry_array_fd, uint64_t n_entry_arrays) { + MMapFileDescriptor *cache_entry_fd, uint64_t n_entries, + MMapFileDescriptor *cache_entry_array_fd, uint64_t n_entry_arrays) { uint64_t i, n, a, last, q; int r; assert(f); assert(o); - assert(entry_fd >= 0); - assert(entry_array_fd >= 0); + assert(cache_entry_fd); + assert(cache_entry_array_fd); n = le64toh(o->data.n_entries); a = le64toh(o->data.entry_array_offset); @@ -527,7 +527,7 @@ static int verify_data( assert(o->data.entry_offset); last = q = le64toh(o->data.entry_offset); - r = entry_points_to_data(f, entry_fd, n_entries, q, p); + r = entry_points_to_data(f, cache_entry_fd, n_entries, q, p); if (r < 0) return r; @@ -540,7 +540,7 @@ static int verify_data( return -EBADMSG; } - if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) { + if (!contains_uint64(f->mmap, cache_entry_array_fd, n_entry_arrays, a)) { error(p, "Invalid array offset "OFSfmt, a); return -EBADMSG; } @@ -565,7 +565,7 @@ static int verify_data( } last = q; - r = entry_points_to_data(f, entry_fd, n_entries, q, p); + r = entry_points_to_data(f, cache_entry_fd, n_entries, q, p); if (r < 0) return r; @@ -583,9 +583,9 @@ static int verify_data( static int verify_hash_table( JournalFile *f, - int data_fd, uint64_t n_data, - int entry_fd, uint64_t n_entries, - int entry_array_fd, uint64_t n_entry_arrays, + MMapFileDescriptor *cache_data_fd, uint64_t n_data, + MMapFileDescriptor *cache_entry_fd, uint64_t n_entries, + MMapFileDescriptor *cache_entry_array_fd, uint64_t n_entry_arrays, usec_t *last_usec, bool show_progress) { @@ -593,9 +593,9 @@ static int verify_hash_table( int r; assert(f); - assert(data_fd >= 0); - assert(entry_fd >= 0); - assert(entry_array_fd >= 0); + assert(cache_data_fd); + assert(cache_entry_fd); + assert(cache_entry_array_fd); assert(last_usec); n = le64toh(f->header->data_hash_table_size) / sizeof(HashItem); @@ -617,7 +617,7 @@ static int verify_hash_table( Object *o; uint64_t next; - if (!contains_uint64(f->mmap, data_fd, n_data, p)) { + if (!contains_uint64(f->mmap, cache_data_fd, n_data, p)) { error(p, "Invalid data object at hash entry %"PRIu64" of %"PRIu64, i, n); return -EBADMSG; } @@ -637,7 +637,7 @@ static int verify_hash_table( return -EBADMSG; } - r = verify_data(f, o, p, entry_fd, n_entries, entry_array_fd, n_entry_arrays); + r = verify_data(f, o, p, cache_entry_fd, n_entries, cache_entry_array_fd, n_entry_arrays); if (r < 0) return r; @@ -689,14 +689,14 @@ static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) static int verify_entry( JournalFile *f, Object *o, uint64_t p, - int data_fd, uint64_t n_data) { + MMapFileDescriptor *cache_data_fd, uint64_t n_data) { uint64_t i, n; int r; assert(f); assert(o); - assert(data_fd >= 0); + assert(cache_data_fd); n = journal_file_entry_n_items(o); for (i = 0; i < n; i++) { @@ -706,7 +706,7 @@ static int verify_entry( q = le64toh(o->entry.items[i].object_offset); h = le64toh(o->entry.items[i].hash); - if (!contains_uint64(f->mmap, data_fd, n_data, q)) { + if (!contains_uint64(f->mmap, cache_data_fd, n_data, q)) { error(p, "Invalid data object of entry"); return -EBADMSG; } @@ -734,9 +734,9 @@ static int verify_entry( static int verify_entry_array( JournalFile *f, - int data_fd, uint64_t n_data, - int entry_fd, uint64_t n_entries, - int entry_array_fd, uint64_t n_entry_arrays, + MMapFileDescriptor *cache_data_fd, uint64_t n_data, + MMapFileDescriptor *cache_entry_fd, uint64_t n_entries, + MMapFileDescriptor *cache_entry_array_fd, uint64_t n_entry_arrays, usec_t *last_usec, bool show_progress) { @@ -744,9 +744,9 @@ static int verify_entry_array( int r; assert(f); - assert(data_fd >= 0); - assert(entry_fd >= 0); - assert(entry_array_fd >= 0); + assert(cache_data_fd); + assert(cache_entry_fd); + assert(cache_entry_array_fd); assert(last_usec); n = le64toh(f->header->n_entries); @@ -763,7 +763,7 @@ static int verify_entry_array( return -EBADMSG; } - if (!contains_uint64(f->mmap, entry_array_fd, n_entry_arrays, a)) { + if (!contains_uint64(f->mmap, cache_entry_array_fd, n_entry_arrays, a)) { error(a, "Invalid array %"PRIu64" of %"PRIu64, i, n); return -EBADMSG; } @@ -789,7 +789,7 @@ static int verify_entry_array( } last = p; - if (!contains_uint64(f->mmap, entry_fd, n_entries, p)) { + if (!contains_uint64(f->mmap, cache_entry_fd, n_entries, p)) { error(a, "Invalid array entry at %"PRIu64" of %"PRIu64, i, n); return -EBADMSG; } @@ -798,7 +798,7 @@ static int verify_entry_array( if (r < 0) return r; - r = verify_entry(f, o, p, data_fd, n_data); + r = verify_entry(f, o, p, cache_data_fd, n_data); if (r < 0) return r; @@ -829,6 +829,7 @@ int journal_file_verify( uint64_t n_weird = 0, n_objects = 0, n_entries = 0, n_data = 0, n_fields = 0, n_data_hash_tables = 0, n_field_hash_tables = 0, n_entry_arrays = 0, n_tags = 0; usec_t last_usec = 0; int data_fd = -1, entry_fd = -1, entry_array_fd = -1; + MMapFileDescriptor *cache_data_fd = NULL, *cache_entry_fd = NULL, *cache_entry_array_fd = NULL; unsigned i; bool found_last = false; const char *tmp_dir = NULL; @@ -876,6 +877,24 @@ int journal_file_verify( goto fail; } + cache_data_fd = mmap_cache_add_fd(f->mmap, data_fd); + if (!cache_data_fd) { + r = log_oom(); + goto fail; + } + + cache_entry_fd = mmap_cache_add_fd(f->mmap, entry_fd); + if (!cache_entry_fd) { + r = log_oom(); + goto fail; + } + + cache_entry_array_fd = mmap_cache_add_fd(f->mmap, entry_array_fd); + if (!cache_entry_array_fd) { + r = log_oom(); + goto fail; + } + if (le32toh(f->header->compatible_flags) & ~HEADER_COMPATIBLE_SUPPORTED) { log_error("Cannot verify file with unknown extensions."); r = -EOPNOTSUPP; @@ -1247,18 +1266,18 @@ int journal_file_verify( * referenced is consistent. */ r = verify_entry_array(f, - data_fd, n_data, - entry_fd, n_entries, - entry_array_fd, n_entry_arrays, + cache_data_fd, n_data, + cache_entry_fd, n_entries, + cache_entry_array_fd, n_entry_arrays, &last_usec, show_progress); if (r < 0) goto fail; r = verify_hash_table(f, - data_fd, n_data, - entry_fd, n_entries, - entry_array_fd, n_entry_arrays, + cache_data_fd, n_data, + cache_entry_fd, n_entries, + cache_entry_array_fd, n_entry_arrays, &last_usec, show_progress); if (r < 0) @@ -1267,9 +1286,9 @@ int journal_file_verify( if (show_progress) flush_progress(); - mmap_cache_close_fd(f->mmap, data_fd); - mmap_cache_close_fd(f->mmap, entry_fd); - mmap_cache_close_fd(f->mmap, entry_array_fd); + mmap_cache_free_fd(f->mmap, cache_data_fd); + mmap_cache_free_fd(f->mmap, cache_entry_fd); + mmap_cache_free_fd(f->mmap, cache_entry_array_fd); safe_close(data_fd); safe_close(entry_fd); @@ -1294,20 +1313,23 @@ fail: (unsigned long long) f->last_stat.st_size, 100 * p / f->last_stat.st_size); - if (data_fd >= 0) { - mmap_cache_close_fd(f->mmap, data_fd); + if (data_fd >= 0) safe_close(data_fd); - } - if (entry_fd >= 0) { - mmap_cache_close_fd(f->mmap, entry_fd); + if (entry_fd >= 0) safe_close(entry_fd); - } - if (entry_array_fd >= 0) { - mmap_cache_close_fd(f->mmap, entry_array_fd); + if (entry_array_fd >= 0) safe_close(entry_array_fd); - } + + if (cache_data_fd) + mmap_cache_free_fd(f->mmap, cache_data_fd); + + if (cache_entry_fd) + mmap_cache_free_fd(f->mmap, cache_entry_fd); + + if (cache_entry_array_fd) + mmap_cache_free_fd(f->mmap, cache_entry_array_fd); return r; } diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index ad11fb314d..2313c8c678 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -299,8 +299,9 @@ static void help(void) { " --no-tail Show all lines, even in follow mode\n" " -r --reverse Show the newest entries first\n" " -o --output=STRING Change journal output mode (short, short-precise,\n" - " short-iso, short-full, short-monotonic, short-unix,\n" - " verbose, export, json, json-pretty, json-sse, cat)\n" + " short-iso, short-iso-precise, short-full,\n" + " short-monotonic, short-unix, verbose, export,\n" + " json, json-pretty, json-sse, cat)\n" " --utc Express time in Coordinated Universal Time (UTC)\n" " -x --catalog Add message explanations where available\n" " --no-full Ellipsize fields\n" @@ -2364,20 +2365,13 @@ int main(int argc, char *argv[]) { log_error_errno(r, "Failed to iterate through journal: %m"); goto finish; } - if (r == 0) { - if (arg_follow) - need_seek = true; - else { - if (!arg_quiet) - printf("-- No entries --\n"); - goto finish; - } - } + if (r == 0) + need_seek = true; if (!arg_follow) pager_open(arg_no_pager, arg_pager_end); - if (!arg_quiet) { + if (!arg_quiet && (arg_lines != 0 || arg_follow)) { usec_t start, end; char start_buf[FORMAT_TIMESTAMP_MAX], end_buf[FORMAT_TIMESTAMP_MAX]; @@ -2473,6 +2467,9 @@ int main(int argc, char *argv[]) { } if (!arg_follow) { + if (n_shown == 0 && !arg_quiet) + printf("-- No entries --\n"); + if (arg_show_cursor) { _cleanup_free_ char *cursor = NULL; @@ -2486,6 +2483,7 @@ int main(int argc, char *argv[]) { break; } + fflush(stdout); r = sd_journal_wait(j, (uint64_t) -1); if (r < 0) { log_error_errno(r, "Couldn't wait for journal event: %m"); @@ -2496,6 +2494,7 @@ int main(int argc, char *argv[]) { } finish: + fflush(stdout); pager_close(); strv_free(arg_file); diff --git a/src/journal/journald-console.c b/src/journal/journald-console.c index 5126c2160e..5fbcdb43c2 100644 --- a/src/journal/journald-console.c +++ b/src/journal/journald-console.c @@ -72,9 +72,9 @@ void server_forward_console( /* First: timestamp */ if (prefix_timestamp()) { assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); - xsprintf(tbuf, "[%5"PRI_TIME".%06ld] ", + xsprintf(tbuf, "[%5"PRI_TIME".%06"PRI_NSEC"] ", ts.tv_sec, - ts.tv_nsec / 1000); + (nsec_t)ts.tv_nsec / 1000); IOVEC_SET_STRING(iovec[n++], tbuf); } diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c index 3c03b83754..abd06b1adc 100644 --- a/src/journal/journald-native.c +++ b/src/journal/journald-native.c @@ -40,6 +40,7 @@ #include "selinux-util.h" #include "socket-util.h" #include "string-util.h" +#include "unaligned.h" bool valid_user_field(const char *p, size_t l, bool allow_protected) { const char *a; @@ -80,60 +81,110 @@ static bool allow_object_pid(const struct ucred *ucred) { return ucred && ucred->uid == 0; } -void server_process_native_message( +static void server_process_entry_meta( + const char *p, size_t l, + const struct ucred *ucred, + int *priority, + char **identifier, + char **message, + pid_t *object_pid) { + + /* We need to determine the priority of this entry for the rate limiting logic */ + + if (l == 10 && + startswith(p, "PRIORITY=") && + p[9] >= '0' && p[9] <= '9') + *priority = (*priority & LOG_FACMASK) | (p[9] - '0'); + + else if (l == 17 && + startswith(p, "SYSLOG_FACILITY=") && + p[16] >= '0' && p[16] <= '9') + *priority = (*priority & LOG_PRIMASK) | ((p[16] - '0') << 3); + + else if (l == 18 && + startswith(p, "SYSLOG_FACILITY=") && + p[16] >= '0' && p[16] <= '9' && + p[17] >= '0' && p[17] <= '9') + *priority = (*priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3); + + else if (l >= 19 && + startswith(p, "SYSLOG_IDENTIFIER=")) { + char *t; + + t = strndup(p + 18, l - 18); + if (t) { + free(*identifier); + *identifier = t; + } + + } else if (l >= 8 && + startswith(p, "MESSAGE=")) { + char *t; + + t = strndup(p + 8, l - 8); + if (t) { + free(*message); + *message = t; + } + + } else if (l > strlen("OBJECT_PID=") && + l < strlen("OBJECT_PID=") + DECIMAL_STR_MAX(pid_t) && + startswith(p, "OBJECT_PID=") && + allow_object_pid(ucred)) { + char buf[DECIMAL_STR_MAX(pid_t)]; + memcpy(buf, p + strlen("OBJECT_PID="), l - strlen("OBJECT_PID=")); + buf[l-strlen("OBJECT_PID=")] = '\0'; + + (void) parse_pid(buf, object_pid); + } +} + +static int server_process_entry( Server *s, - const void *buffer, size_t buffer_size, + const void *buffer, size_t *remaining, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len) { + /* Process a single entry from a native message. + * Returns 0 if nothing special happened and the message processing should continue, + * and a negative or positive value otherwise. + * + * Note that *remaining is altered on both success and failure. */ + struct iovec *iovec = NULL; unsigned n = 0, j, tn = (unsigned) -1; const char *p; - size_t remaining, m = 0, entry_size = 0; + size_t m = 0, entry_size = 0; int priority = LOG_INFO; char *identifier = NULL, *message = NULL; pid_t object_pid = 0; - - assert(s); - assert(buffer || buffer_size == 0); + int r = 0; p = buffer; - remaining = buffer_size; - while (remaining > 0) { + while (*remaining > 0) { const char *e, *q; - e = memchr(p, '\n', remaining); + e = memchr(p, '\n', *remaining); if (!e) { /* Trailing noise, let's ignore it, and flush what we collected */ log_debug("Received message with trailing noise, ignoring."); + r = 1; /* finish processing of the message */ break; } if (e == p) { /* Entry separator */ - - if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */ - log_debug("Entry is too big with %u properties and %zu bytes, ignoring.", n, entry_size); - continue; - } - - server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid); - n = 0; - priority = LOG_INFO; - entry_size = 0; - - p++; - remaining--; - continue; + *remaining -= 1; + break; } if (*p == '.' || *p == '#') { /* Ignore control commands for now, and * comments too. */ - remaining -= (e - p) + 1; + *remaining -= (e - p) + 1; p = e + 1; continue; } @@ -142,7 +193,7 @@ void server_process_native_message( /* n existing properties, 1 new, +1 for _TRANSPORT */ if (!GREEDY_REALLOC(iovec, m, n + 2 + N_IOVEC_META_FIELDS + N_IOVEC_OBJECT_FIELDS)) { - log_oom(); + r = log_oom(); break; } @@ -155,87 +206,40 @@ void server_process_native_message( /* If the field name starts with an * underscore, skip the variable, - * since that indidates a trusted + * since that indicates a trusted * field */ iovec[n].iov_base = (char*) p; iovec[n].iov_len = l; - entry_size += iovec[n].iov_len; + entry_size += l; n++; - /* We need to determine the priority - * of this entry for the rate limiting - * logic */ - if (l == 10 && - startswith(p, "PRIORITY=") && - p[9] >= '0' && p[9] <= '9') - priority = (priority & LOG_FACMASK) | (p[9] - '0'); - - else if (l == 17 && - startswith(p, "SYSLOG_FACILITY=") && - p[16] >= '0' && p[16] <= '9') - priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3); - - else if (l == 18 && - startswith(p, "SYSLOG_FACILITY=") && - p[16] >= '0' && p[16] <= '9' && - p[17] >= '0' && p[17] <= '9') - priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3); - - else if (l >= 19 && - startswith(p, "SYSLOG_IDENTIFIER=")) { - char *t; - - t = strndup(p + 18, l - 18); - if (t) { - free(identifier); - identifier = t; - } - - } else if (l >= 8 && - startswith(p, "MESSAGE=")) { - char *t; - - t = strndup(p + 8, l - 8); - if (t) { - free(message); - message = t; - } - - } else if (l > strlen("OBJECT_PID=") && - l < strlen("OBJECT_PID=") + DECIMAL_STR_MAX(pid_t) && - startswith(p, "OBJECT_PID=") && - allow_object_pid(ucred)) { - char buf[DECIMAL_STR_MAX(pid_t)]; - memcpy(buf, p + strlen("OBJECT_PID="), l - strlen("OBJECT_PID=")); - buf[l-strlen("OBJECT_PID=")] = '\0'; - - /* ignore error */ - parse_pid(buf, &object_pid); - } + server_process_entry_meta(p, l, ucred, + &priority, + &identifier, + &message, + &object_pid); } - remaining -= (e - p) + 1; + *remaining -= (e - p) + 1; p = e + 1; continue; } else { - le64_t l_le; uint64_t l; char *k; - if (remaining < e - p + 1 + sizeof(uint64_t) + 1) { + if (*remaining < e - p + 1 + sizeof(uint64_t) + 1) { log_debug("Failed to parse message, ignoring."); break; } - memcpy(&l_le, e + 1, sizeof(uint64_t)); - l = le64toh(l_le); + l = unaligned_read_le64(e + 1); if (l > DATA_SIZE_MAX) { log_debug("Received binary data block of %"PRIu64" bytes is too large, ignoring.", l); break; } - if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 || + if ((uint64_t) *remaining < e - p + 1 + sizeof(uint64_t) + l + 1 || e[1+sizeof(uint64_t)+l] != '\n') { log_debug("Failed to parse message, ignoring."); break; @@ -256,16 +260,24 @@ void server_process_native_message( iovec[n].iov_len = (e - p) + 1 + l; entry_size += iovec[n].iov_len; n++; + + server_process_entry_meta(k, (e - p) + 1 + l, ucred, + &priority, + &identifier, + &message, + &object_pid); } else free(k); - remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1; + *remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1; p = e + 1 + sizeof(uint64_t) + l + 1; } } - if (n <= 0) + if (n <= 0) { + r = 1; goto finish; + } tn = n++; IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal"); @@ -279,7 +291,7 @@ void server_process_native_message( if (message) { if (s->forward_to_syslog) - server_forward_syslog(s, priority, identifier, message, ucred, tv); + server_forward_syslog(s, syslog_fixup_facility(priority), identifier, message, ucred, tv); if (s->forward_to_kmsg) server_forward_kmsg(s, priority, identifier, message, ucred); @@ -299,13 +311,35 @@ finish: continue; if (iovec[j].iov_base < buffer || - (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size) + (const char*) iovec[j].iov_base >= p + *remaining) free(iovec[j].iov_base); } free(iovec); free(identifier); free(message); + + return r; +} + +void server_process_native_message( + Server *s, + const void *buffer, size_t buffer_size, + const struct ucred *ucred, + const struct timeval *tv, + const char *label, size_t label_len) { + + int r; + size_t remaining = buffer_size; + + assert(s); + assert(buffer || buffer_size == 0); + + do { + r = server_process_entry(s, + (const uint8_t*) buffer + (buffer_size - remaining), &remaining, + ucred, tv, label, label_len); + } while (r == 0); } void server_process_native_file( @@ -479,7 +513,7 @@ int server_open_native_socket(Server*s) { return log_error_errno(errno, "SO_PASSCRED failed: %m"); #ifdef HAVE_SELINUX - if (mac_selinux_have()) { + if (mac_selinux_use()) { r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)); if (r < 0) log_warning_errno(errno, "SO_PASSSEC failed: %m"); diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 6466e46ccc..05a1254d4a 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -918,7 +918,7 @@ static void dispatch_message_real( } #ifdef HAVE_SELINUX - if (mac_selinux_have()) { + if (mac_selinux_use()) { if (label) { x = alloca(strlen("_SELINUX_CONTEXT=") + label_len + 1); @@ -1637,10 +1637,10 @@ static int server_parse_config_file(Server *s) { assert(s); return config_parse_many_nulstr(PKGSYSCONFDIR "/journald.conf", - CONF_PATHS_NULSTR("systemd/journald.conf.d"), - "Journal\0", - config_item_perf_lookup, journald_gperf_lookup, - false, s); + CONF_PATHS_NULSTR("systemd/journald.conf.d"), + "Journal\0", + config_item_perf_lookup, journald_gperf_lookup, + false, s); } static int server_dispatch_sync(sd_event_source *es, usec_t t, void *userdata) { @@ -2177,6 +2177,8 @@ void server_done(Server *s) { free(s->tty_path); free(s->cgroup_root); free(s->hostname_field); + free(s->runtime_storage.path); + free(s->system_storage.path); if (s->mmap) mmap_cache_unref(s->mmap); diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h index 75ac114d24..203460c50a 100644 --- a/src/journal/journald-server.h +++ b/src/journal/journald-server.h @@ -61,7 +61,7 @@ typedef struct JournalStorageSpace { typedef struct JournalStorage { const char *name; - const char *path; + char *path; JournalMetrics metrics; JournalStorageSpace space; diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index bc092f3c12..77551dc14b 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -494,7 +494,7 @@ static int stdout_stream_install(Server *s, int fd, StdoutStream **ret) { if (r < 0) return log_error_errno(r, "Failed to determine peer credentials: %m"); - if (mac_selinux_have()) { + if (mac_selinux_use()) { r = getpeersec(fd, &stream->label); if (r < 0 && r != -EOPNOTSUPP) (void) log_warning_errno(r, "Failed to determine peer security context: %m"); diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c index 474369039a..8e034c8fa9 100644 --- a/src/journal/journald-syslog.c +++ b/src/journal/journald-syslog.c @@ -410,7 +410,7 @@ int server_open_syslog_socket(Server *s) { return log_error_errno(errno, "SO_PASSCRED failed: %m"); #ifdef HAVE_SELINUX - if (mac_selinux_have()) { + if (mac_selinux_use()) { r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)); if (r < 0) log_warning_errno(errno, "SO_PASSSEC failed: %m"); diff --git a/src/journal/meson.build b/src/journal/meson.build new file mode 100644 index 0000000000..582f83afb9 --- /dev/null +++ b/src/journal/meson.build @@ -0,0 +1,122 @@ +journal_internal_sources = files(''' + audit-type.c + audit-type.h + catalog.c + catalog.h + compress.c + compress.h + journal-def.h + journal-file.c + journal-file.h + journal-send.c + journal-vacuum.c + journal-vacuum.h + journal-verify.c + journal-verify.h + lookup3.c + lookup3.h + mmap-cache.c + mmap-cache.h + sd-journal.c +'''.split()) + +if conf.get('HAVE_GCRYPT', false) + journal_internal_sources += files(''' + journal-authenticate.c + journal-authenticate.h + fsprg.c + fsprg.h + '''.split()) + + journal_internal_sources += gcrypt_util_sources +endif + +############################################################ + +audit_type_includes = [config_h, + missing_h, + 'linux/audit.h'] +if conf.get('HAVE_AUDIT', false) + audit_type_includes += 'libaudit.h' +endif + +generate_audit_type_list = find_program('generate-audit_type-list.sh') +audit_type_list_txt = custom_target( + 'audit_type-list.txt', + output : 'audit_type-list.txt', + command : [generate_audit_type_list, cpp] + audit_type_includes, + capture : true) + +audit_type_to_name = custom_target( + 'audit_type-to-name.h', + input : ['audit_type-to-name.awk', audit_type_list_txt], + output : 'audit_type-to-name.h', + command : [awk, '-f', '@INPUT0@', '@INPUT1@'], + capture : true) + +journal_internal_sources += [audit_type_to_name] + +############################################################ + +libjournal_core_sources = files(''' + journald-kmsg.c + journald-kmsg.h + journald-syslog.c + journald-syslog.h + journald-stream.c + journald-stream.h + journald-server.c + journald-server.h + journald-console.c + journald-console.h + journald-wall.c + journald-wall.h + journald-native.c + journald-native.h + journald-audit.c + journald-audit.h + journald-rate-limit.c + journald-rate-limit.h + journal-internal.h +'''.split()) + +systemd_journald_sources = files(''' + journald.c + journald-server.h +'''.split()) + +journald_gperf_c = custom_target( + 'journald-gperf.c', + input : 'journald-gperf.gperf', + output : 'journald-gperf.c', + command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@']) + +systemd_cat_sources = files('cat.c') + +journalctl_sources = files('journalctl.c') + +if conf.get('HAVE_QRENCODE', false) + journalctl_sources += files('journal-qrcode.c', + 'journal-qrcode.h') +endif + +install_data('journald.conf', + install_dir : pkgsysconfdir) + +meson.add_install_script( + 'sh', '-c', + mkdir_p.format('/var/log/journal')) +meson.add_install_script( + 'sh', '-c', + 'chown 0:0 $DESTDIR/var/log/journal && + chmod 755 $DESTDIR/var/log/journal || :') +if get_option('adm-group') + meson.add_install_script( + 'sh', '-c', + 'setfacl -nm g:adm:rx,d:g:adm:rx $DESTDIR/var/log/journal || :') +endif +if get_option('wheel-group') + meson.add_install_script( + 'sh', '-c', + 'setfacl -nm g:wheel:rx,d:g:wheel:rx $DESTDIR/var/log/journal || :') +endif diff --git a/src/journal/mmap-cache.c b/src/journal/mmap-cache.c index d91247b524..5dfda73c56 100644 --- a/src/journal/mmap-cache.c +++ b/src/journal/mmap-cache.c @@ -33,7 +33,6 @@ typedef struct Window Window; typedef struct Context Context; -typedef struct FileDescriptor FileDescriptor; struct Window { MMapCache *cache; @@ -47,7 +46,7 @@ struct Window { uint64_t offset; size_t size; - FileDescriptor *fd; + MMapFileDescriptor *fd; LIST_FIELDS(Window, by_fd); LIST_FIELDS(Window, unused); @@ -63,7 +62,7 @@ struct Context { LIST_FIELDS(Context, by_window); }; -struct FileDescriptor { +struct MMapFileDescriptor { MMapCache *cache; int fd; bool sigbus; @@ -158,24 +157,24 @@ static void window_free(Window *w) { free(w); } -_pure_ static bool window_matches(Window *w, int fd, int prot, uint64_t offset, size_t size) { +_pure_ static bool window_matches(Window *w, MMapFileDescriptor *f, int prot, uint64_t offset, size_t size) { assert(w); - assert(fd >= 0); + assert(f); assert(size > 0); return w->fd && - fd == w->fd->fd && + f->fd == w->fd->fd && prot == w->prot && offset >= w->offset && offset + size <= w->offset + w->size; } -static Window *window_add(MMapCache *m, FileDescriptor *fd, int prot, bool keep_always, uint64_t offset, size_t size, void *ptr) { +static Window *window_add(MMapCache *m, MMapFileDescriptor *f, int prot, bool keep_always, uint64_t offset, size_t size, void *ptr) { Window *w; assert(m); - assert(fd); + assert(f); if (!m->last_unused || m->n_windows <= WINDOWS_MIN) { @@ -193,14 +192,14 @@ static Window *window_add(MMapCache *m, FileDescriptor *fd, int prot, bool keep_ } w->cache = m; - w->fd = fd; + w->fd = f; w->prot = prot; w->keep_always = keep_always; w->offset = offset; w->size = size; w->ptr = ptr; - LIST_PREPEND(by_fd, fd->windows, w); + LIST_PREPEND(by_fd, f->windows, w); return w; } @@ -290,49 +289,7 @@ static void context_free(Context *c) { free(c); } -static void fd_free(FileDescriptor *f) { - assert(f); - - while (f->windows) - window_free(f->windows); - - if (f->cache) - assert_se(hashmap_remove(f->cache->fds, FD_TO_PTR(f->fd))); - - free(f); -} - -static FileDescriptor* fd_add(MMapCache *m, int fd) { - FileDescriptor *f; - int r; - - assert(m); - assert(fd >= 0); - - f = hashmap_get(m->fds, FD_TO_PTR(fd)); - if (f) - return f; - - r = hashmap_ensure_allocated(&m->fds, NULL); - if (r < 0) - return NULL; - - f = new0(FileDescriptor, 1); - if (!f) - return NULL; - - f->cache = m; - f->fd = fd; - - r = hashmap_put(m->fds, FD_TO_PTR(fd), f); - if (r < 0) - return mfree(f); - - return f; -} - static void mmap_cache_free(MMapCache *m) { - FileDescriptor *f; int i; assert(m); @@ -341,9 +298,6 @@ static void mmap_cache_free(MMapCache *m) { if (m->contexts[i]) context_free(m->contexts[i]); - while ((f = hashmap_first(m->fds))) - fd_free(f); - hashmap_free(m->fds); while (m->unused) @@ -378,7 +332,7 @@ static int make_room(MMapCache *m) { static int try_context( MMapCache *m, - int fd, + MMapFileDescriptor *f, int prot, unsigned context, bool keep_always, @@ -390,7 +344,7 @@ static int try_context( assert(m); assert(m->n_ref > 0); - assert(fd >= 0); + assert(f); assert(size > 0); assert(ret); @@ -403,7 +357,7 @@ static int try_context( if (!c->window) return 0; - if (!window_matches(c->window, fd, prot, offset, size)) { + if (!window_matches(c->window, f, prot, offset, size)) { /* Drop the reference to the window, since it's unnecessary now */ context_detach_window(c); @@ -421,7 +375,7 @@ static int try_context( static int find_mmap( MMapCache *m, - int fd, + MMapFileDescriptor *f, int prot, unsigned context, bool keep_always, @@ -429,26 +383,19 @@ static int find_mmap( size_t size, void **ret) { - FileDescriptor *f; Window *w; Context *c; assert(m); assert(m->n_ref > 0); - assert(fd >= 0); + assert(f); assert(size > 0); - f = hashmap_get(m->fds, FD_TO_PTR(fd)); - if (!f) - return 0; - - assert(f->fd == fd); - if (f->sigbus) return -EIO; LIST_FOREACH(by_fd, w, f->windows) - if (window_matches(w, fd, prot, offset, size)) + if (window_matches(w, f, prot, offset, size)) break; if (!w) @@ -465,17 +412,17 @@ static int find_mmap( return 1; } -static int mmap_try_harder(MMapCache *m, void *addr, int fd, int prot, int flags, uint64_t offset, size_t size, void **res) { +static int mmap_try_harder(MMapCache *m, void *addr, MMapFileDescriptor *f, int prot, int flags, uint64_t offset, size_t size, void **res) { void *ptr; assert(m); - assert(fd >= 0); + assert(f); assert(res); for (;;) { int r; - ptr = mmap(addr, size, prot, flags, fd, offset); + ptr = mmap(addr, size, prot, flags, f->fd, offset); if (ptr != MAP_FAILED) break; if (errno != ENOMEM) @@ -494,7 +441,7 @@ static int mmap_try_harder(MMapCache *m, void *addr, int fd, int prot, int flags static int add_mmap( MMapCache *m, - int fd, + MMapFileDescriptor *f, int prot, unsigned context, bool keep_always, @@ -505,14 +452,13 @@ static int add_mmap( uint64_t woffset, wsize; Context *c; - FileDescriptor *f; Window *w; void *d; int r; assert(m); assert(m->n_ref > 0); - assert(fd >= 0); + assert(f); assert(size > 0); assert(ret); @@ -545,7 +491,7 @@ static int add_mmap( wsize = PAGE_ALIGN(st->st_size - woffset); } - r = mmap_try_harder(m, NULL, fd, prot, MAP_SHARED, woffset, wsize, &d); + r = mmap_try_harder(m, NULL, f, prot, MAP_SHARED, woffset, wsize, &d); if (r < 0) return r; @@ -553,10 +499,6 @@ static int add_mmap( if (!c) goto outofmem; - f = fd_add(m, fd); - if (!f) - goto outofmem; - w = window_add(m, f, prot, keep_always, woffset, wsize, d); if (!w) goto outofmem; @@ -575,7 +517,7 @@ outofmem: int mmap_cache_get( MMapCache *m, - int fd, + MMapFileDescriptor *f, int prot, unsigned context, bool keep_always, @@ -588,20 +530,20 @@ int mmap_cache_get( assert(m); assert(m->n_ref > 0); - assert(fd >= 0); + assert(f); assert(size > 0); assert(ret); assert(context < MMAP_CACHE_MAX_CONTEXTS); /* Check whether the current context is the right one already */ - r = try_context(m, fd, prot, context, keep_always, offset, size, ret); + r = try_context(m, f, prot, context, keep_always, offset, size, ret); if (r != 0) { m->n_hit++; return r; } /* Search for a matching mmap */ - r = find_mmap(m, fd, prot, context, keep_always, offset, size, ret); + r = find_mmap(m, f, prot, context, keep_always, offset, size, ret); if (r != 0) { m->n_hit++; return r; @@ -610,7 +552,7 @@ int mmap_cache_get( m->n_missed++; /* Create a new mmap */ - return add_mmap(m, fd, prot, context, keep_always, offset, size, st, ret); + return add_mmap(m, f, prot, context, keep_always, offset, size, st, ret); } unsigned mmap_cache_get_hit(MMapCache *m) { @@ -627,7 +569,7 @@ unsigned mmap_cache_get_missed(MMapCache *m) { static void mmap_cache_process_sigbus(MMapCache *m) { bool found = false; - FileDescriptor *f; + MMapFileDescriptor *f; Iterator i; int r; @@ -688,36 +630,59 @@ static void mmap_cache_process_sigbus(MMapCache *m) { } } -bool mmap_cache_got_sigbus(MMapCache *m, int fd) { - FileDescriptor *f; - +bool mmap_cache_got_sigbus(MMapCache *m, MMapFileDescriptor *f) { assert(m); - assert(fd >= 0); + assert(f); mmap_cache_process_sigbus(m); - f = hashmap_get(m->fds, FD_TO_PTR(fd)); - if (!f) - return false; - return f->sigbus; } -void mmap_cache_close_fd(MMapCache *m, int fd) { - FileDescriptor *f; +MMapFileDescriptor* mmap_cache_add_fd(MMapCache *m, int fd) { + MMapFileDescriptor *f; + int r; assert(m); assert(fd >= 0); + f = hashmap_get(m->fds, FD_TO_PTR(fd)); + if (f) + return f; + + r = hashmap_ensure_allocated(&m->fds, NULL); + if (r < 0) + return NULL; + + f = new0(MMapFileDescriptor, 1); + if (!f) + return NULL; + + f->cache = m; + f->fd = fd; + + r = hashmap_put(m->fds, FD_TO_PTR(fd), f); + if (r < 0) + return mfree(f); + + return f; +} + +void mmap_cache_free_fd(MMapCache *m, MMapFileDescriptor *f) { + assert(m); + assert(f); + /* Make sure that any queued SIGBUS are first dispatched, so * that we don't end up with a SIGBUS entry we cannot relate * to any existing memory map */ mmap_cache_process_sigbus(m); - f = hashmap_get(m->fds, FD_TO_PTR(fd)); - if (!f) - return; + while (f->windows) + window_free(f->windows); + + if (f->cache) + assert_se(hashmap_remove(f->cache->fds, FD_TO_PTR(f->fd))); - fd_free(f); + free(f); } diff --git a/src/journal/mmap-cache.h b/src/journal/mmap-cache.h index 199d944647..7b33218563 100644 --- a/src/journal/mmap-cache.h +++ b/src/journal/mmap-cache.h @@ -26,6 +26,7 @@ #define MMAP_CACHE_MAX_CONTEXTS 9 typedef struct MMapCache MMapCache; +typedef struct MMapFileDescriptor MMapFileDescriptor; MMapCache* mmap_cache_new(void); MMapCache* mmap_cache_ref(MMapCache *m); @@ -33,7 +34,7 @@ MMapCache* mmap_cache_unref(MMapCache *m); int mmap_cache_get( MMapCache *m, - int fd, + MMapFileDescriptor *f, int prot, unsigned context, bool keep_always, @@ -41,9 +42,10 @@ int mmap_cache_get( size_t size, struct stat *st, void **ret); -void mmap_cache_close_fd(MMapCache *m, int fd); +MMapFileDescriptor * mmap_cache_add_fd(MMapCache *m, int fd); +void mmap_cache_free_fd(MMapCache *m, MMapFileDescriptor *f); unsigned mmap_cache_get_hit(MMapCache *m); unsigned mmap_cache_get_missed(MMapCache *m); -bool mmap_cache_got_sigbus(MMapCache *m, int fd); +bool mmap_cache_got_sigbus(MMapCache *m, MMapFileDescriptor *f); diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 71967a0f33..cd56470a33 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -882,8 +882,11 @@ static int real_journal_next_skip(sd_journal *j, direction_t direction, uint64_t if (skip == 0) { /* If this is not a discrete skip, then at least * resolve the current location */ - if (j->current_location.type != LOCATION_DISCRETE) - return real_journal_next(j, direction); + if (j->current_location.type != LOCATION_DISCRETE) { + r = real_journal_next(j, direction); + if (r < 0) + return r; + } return 0; } @@ -2424,6 +2427,7 @@ _public_ int sd_journal_process(sd_journal *j) { assert_return(!journal_pid_changed(j), -ECHILD); j->last_process_usec = now(CLOCK_MONOTONIC); + j->last_invalidate_counter = j->current_invalidate_counter; for (;;) { union inotify_event_buffer buffer; diff --git a/src/journal/test-compress-benchmark.c b/src/journal/test-compress-benchmark.c index 6f6d71435d..4fb93ded73 100644 --- a/src/journal/test-compress-benchmark.c +++ b/src/journal/test-compress-benchmark.c @@ -30,6 +30,8 @@ typedef int (compress_t)(const void *src, uint64_t src_size, void *dst, typedef int (decompress_t)(const void *src, uint64_t src_size, void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max); +#if defined(HAVE_XZ) || defined(HAVE_LZ4) + static usec_t arg_duration = 2 * USEC_PER_SEC; static size_t arg_start; @@ -151,8 +153,10 @@ static void test_compress_decompress(const char* label, const char* type, 100 - compressed * 100. / total, skipped); } +#endif int main(int argc, char *argv[]) { +#if defined(HAVE_XZ) || defined(HAVE_LZ4) const char *i; log_set_max_level(LOG_INFO); @@ -177,4 +181,7 @@ int main(int argc, char *argv[]) { #endif } return 0; +#else + return EXIT_TEST_SKIP; +#endif } diff --git a/src/journal/test-compress.c b/src/journal/test-compress.c index 44a2cf5217..92108a84b3 100644 --- a/src/journal/test-compress.c +++ b/src/journal/test-compress.c @@ -54,6 +54,7 @@ typedef int (decompress_sw_t)(const void *src, uint64_t src_size, typedef int (compress_stream_t)(int fdf, int fdt, uint64_t max_bytes); typedef int (decompress_stream_t)(int fdf, int fdt, uint64_t max_size); +#if defined(HAVE_XZ) || defined(HAVE_LZ4) static void test_compress_decompress(int compression, compress_blob_t compress, decompress_blob_t decompress, @@ -203,6 +204,7 @@ static void test_compress_stream(int compression, assert_se(unlink(pattern) == 0); assert_se(unlink(pattern2) == 0); } +#endif #ifdef HAVE_LZ4 static void test_lz4_decompress_partial(void) { @@ -247,6 +249,7 @@ static void test_lz4_decompress_partial(void) { #endif int main(int argc, char *argv[]) { +#if defined(HAVE_XZ) || defined(HAVE_LZ4) const char text[] = "text\0foofoofoofoo AAAA aaaaaaaaa ghost busters barbarbar FFF" "foofoofoofoo AAAA aaaaaaaaa ghost busters barbarbar FFF"; @@ -312,4 +315,7 @@ int main(int argc, char *argv[]) { #endif return 0; +#else + return EXIT_TEST_SKIP; +#endif } diff --git a/src/journal/test-mmap-cache.c b/src/journal/test-mmap-cache.c index 0ad49aeb5f..c51b069f8b 100644 --- a/src/journal/test-mmap-cache.c +++ b/src/journal/test-mmap-cache.c @@ -29,6 +29,7 @@ #include "util.h" int main(int argc, char *argv[]) { + MMapFileDescriptor *fx; int x, y, z, r; char px[] = "/tmp/testmmapXXXXXXX", py[] = "/tmp/testmmapYXXXXXX", pz[] = "/tmp/testmmapZXXXXXX"; MMapCache *m; @@ -40,6 +41,8 @@ int main(int argc, char *argv[]) { assert_se(x >= 0); unlink(px); + assert_se(fx = mmap_cache_add_fd(m, x)); + y = mkostemp_safe(py); assert_se(y >= 0); unlink(py); @@ -48,27 +51,28 @@ int main(int argc, char *argv[]) { assert_se(z >= 0); unlink(pz); - r = mmap_cache_get(m, x, PROT_READ, 0, false, 1, 2, NULL, &p); + r = mmap_cache_get(m, fx, PROT_READ, 0, false, 1, 2, NULL, &p); assert_se(r >= 0); - r = mmap_cache_get(m, x, PROT_READ, 0, false, 2, 2, NULL, &q); + r = mmap_cache_get(m, fx, PROT_READ, 0, false, 2, 2, NULL, &q); assert_se(r >= 0); assert_se((uint8_t*) p + 1 == (uint8_t*) q); - r = mmap_cache_get(m, x, PROT_READ, 1, false, 3, 2, NULL, &q); + r = mmap_cache_get(m, fx, PROT_READ, 1, false, 3, 2, NULL, &q); assert_se(r >= 0); assert_se((uint8_t*) p + 2 == (uint8_t*) q); - r = mmap_cache_get(m, x, PROT_READ, 0, false, 16ULL*1024ULL*1024ULL, 2, NULL, &p); + r = mmap_cache_get(m, fx, PROT_READ, 0, false, 16ULL*1024ULL*1024ULL, 2, NULL, &p); assert_se(r >= 0); - r = mmap_cache_get(m, x, PROT_READ, 1, false, 16ULL*1024ULL*1024ULL+1, 2, NULL, &q); + r = mmap_cache_get(m, fx, PROT_READ, 1, false, 16ULL*1024ULL*1024ULL+1, 2, NULL, &q); assert_se(r >= 0); assert_se((uint8_t*) p + 1 == (uint8_t*) q); + mmap_cache_free_fd(m, fx); mmap_cache_unref(m); safe_close(x); diff --git a/src/kernel-install/50-depmod.install b/src/kernel-install/50-depmod.install index 68c24bed7a..56925c8a5d 100644 --- a/src/kernel-install/50-depmod.install +++ b/src/kernel-install/50-depmod.install @@ -2,7 +2,15 @@ # -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- # ex: ts=8 sw=4 sts=4 et filetype=sh -[[ $1 == "add" ]] || exit 0 [[ $2 ]] || exit 1 -exec depmod -a "$2" +case "$1" in + add) + exec depmod -a "$2" + ;; + remove) + exec rm -f /lib/modules/"$2"/modules.{alias{,.bin},builtin.bin,dep{,.bin},devname,softdep,symbols{,.bin}} + ;; + *) + exit 0 +esac diff --git a/src/kernel-install/90-loaderentry.install b/src/kernel-install/90-loaderentry.install index a0bca05c9a..305ea8f5c9 100644 --- a/src/kernel-install/90-loaderentry.install +++ b/src/kernel-install/90-loaderentry.install @@ -7,13 +7,11 @@ KERNEL_VERSION="$2" BOOT_DIR_ABS="$3" KERNEL_IMAGE="$4" -if [[ -f /etc/machine-id ]]; then - read MACHINE_ID < /etc/machine-id +if ! [[ $KERNEL_INSTALL_MACHINE_ID ]]; then + exit 0 fi -if ! [[ $MACHINE_ID ]]; then - exit 1 -fi +MACHINE_ID=$KERNEL_INSTALL_MACHINE_ID BOOT_DIR="/$MACHINE_ID/$KERNEL_VERSION" BOOT_ROOT=${BOOT_DIR_ABS%$BOOT_DIR} diff --git a/src/kernel-install/kernel-install b/src/kernel-install/kernel-install index c7d9f4eea9..66bc4a6e8e 100644 --- a/src/kernel-install/kernel-install +++ b/src/kernel-install/kernel-install @@ -77,18 +77,15 @@ if [[ -f /etc/machine-id ]]; then read MACHINE_ID < /etc/machine-id fi -if ! [[ $MACHINE_ID ]]; then - echo "Could not determine your machine ID from /etc/machine-id." >&2 - echo "Please run 'systemd-machine-id-setup' as root. See man:machine-id(5)" >&2 - exit 1 -fi - if [[ ! $COMMAND ]] || [[ ! $KERNEL_VERSION ]]; then echo "Not enough arguments" >&2 exit 1 fi -if [[ -d /efi/loader/entries ]] || [[ -d /efi/$MACHINE_ID ]]; then +if ! [[ $MACHINE_ID ]]; then + BOOT_DIR_ABS=$(mktemp -d /tmp/kernel-install.XXXXX) || exit 1 + trap "rm -rf '$BOOT_DIR_ABS'" EXIT INT QUIT PIPE +elif [[ -d /efi/loader/entries ]] || [[ -d /efi/$MACHINE_ID ]]; then BOOT_DIR_ABS="/efi/$MACHINE_ID/$KERNEL_VERSION" elif [[ -d /boot/loader/entries ]] || [[ -d /boot/$MACHINE_ID ]]; then BOOT_DIR_ABS="/boot/$MACHINE_ID/$KERNEL_VERSION" @@ -102,6 +99,8 @@ else BOOT_DIR_ABS="/boot/$MACHINE_ID/$KERNEL_VERSION" fi +export KERNEL_INSTALL_MACHINE_ID=$MACHINE_ID + ret=0 readarray -t PLUGINS <<<"$( @@ -127,11 +126,20 @@ case $COMMAND in "$f" add "$KERNEL_VERSION" "$BOOT_DIR_ABS" "$KERNEL_IMAGE" x=$? if [[ $x == $SKIP_REMAINING ]]; then - exit 0 + ret=0 + break fi ((ret+=$x)) fi done + + if ! [[ $MACHINE_ID ]] && ! rmdir "$BOOT_DIR_ABS"; then + echo "Warning: In kernel-install plugins, requiring BOOT_DIR_ABS to be preset is deprecated." >&2 + echo " All plugins should not put anything in BOOT_DIR_ABS if the environment" >&2 + echo " variable KERNEL_INSTALL_MACHINE_ID is empty." >&2 + rm -rf "$BOOT_DIR_ABS" + ((ret+=$?)) + fi ;; remove) @@ -140,7 +148,8 @@ case $COMMAND in "$f" remove "$KERNEL_VERSION" "$BOOT_DIR_ABS" x=$? if [[ $x == $SKIP_REMAINING ]]; then - exit 0 + ret=0 + break fi ((ret+=$x)) fi diff --git a/src/kernel-install/meson.build b/src/kernel-install/meson.build new file mode 100644 index 0000000000..ede3323bab --- /dev/null +++ b/src/kernel-install/meson.build @@ -0,0 +1,11 @@ +install_data('kernel-install', + install_mode : 'rwxr-xr-x', + install_dir : bindir) + +install_data('50-depmod.install', + '90-loaderentry.install', + install_mode : 'rwxr-xr-x', + install_dir : kernelinstalldir) + +meson.add_install_script('sh', '-c', + mkdir_p.format(join_paths(sysconfdir, 'kernel/install.d'))) diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h index 82cae2300a..7847ce0709 100644 --- a/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/libsystemd-network/dhcp-lease-internal.h @@ -75,6 +75,7 @@ struct sd_dhcp_lease { uint16_t mtu; /* 0 if unset */ char *domainname; + char **search_domains; char *hostname; char *root_path; @@ -92,6 +93,7 @@ struct sd_dhcp_lease { int dhcp_lease_new(sd_dhcp_lease **ret); int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata); +int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***domains); int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len); int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease); diff --git a/src/libsystemd-network/icmp6-util.c b/src/libsystemd-network/icmp6-util.c index c2e4b0e9e3..7fbebd6f27 100644 --- a/src/libsystemd-network/icmp6-util.c +++ b/src/libsystemd-network/icmp6-util.c @@ -32,6 +32,7 @@ #include "fd-util.h" #include "icmp6-util.h" #include "socket-util.h" +#include "in-addr-util.h" #define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \ { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ @@ -41,12 +42,9 @@ { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } } -int icmp6_bind_router_solicitation(int index) { - struct icmp6_filter filter = { }; - struct ipv6_mreq mreq = { - .ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT, - .ipv6mr_interface = index, - }; +static int icmp6_bind_router_message(const struct icmp6_filter *filter, + const struct ipv6_mreq *mreq) { + int index = mreq->ipv6mr_interface; _cleanup_close_ int s = -1; char ifname[IF_NAMESIZE] = ""; static const int zero = 0, one = 1, hops = 255; @@ -56,9 +54,11 @@ int icmp6_bind_router_solicitation(int index) { if (s < 0) return -errno; - ICMP6_FILTER_SETBLOCKALL(&filter); - ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter); - r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)); + r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, filter, sizeof(*filter)); + if (r < 0) + return -errno; + + r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq, sizeof(*mreq)); if (r < 0) return -errno; @@ -78,7 +78,7 @@ int icmp6_bind_router_solicitation(int index) { if (r < 0) return -errno; - r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + r = setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, sizeof(hops)); if (r < 0) return -errno; @@ -102,6 +102,32 @@ int icmp6_bind_router_solicitation(int index) { return r; } +int icmp6_bind_router_solicitation(int index) { + struct icmp6_filter filter = {}; + struct ipv6_mreq mreq = { + .ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT, + .ipv6mr_interface = index, + }; + + ICMP6_FILTER_SETBLOCKALL(&filter); + ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter); + + return icmp6_bind_router_message(&filter, &mreq); +} + +int icmp6_bind_router_advertisement(int index) { + struct icmp6_filter filter = {}; + struct ipv6_mreq mreq = { + .ipv6mr_multiaddr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT, + .ipv6mr_interface = index, + }; + + ICMP6_FILTER_SETBLOCKALL(&filter); + ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter); + + return icmp6_bind_router_message(&filter, &mreq); +} + int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) { struct sockaddr_in6 dst = { .sin6_family = AF_INET6, @@ -139,3 +165,74 @@ int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) { return 0; } + +int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *dst, + triple_timestamp *timestamp) { + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(int)) + /* ttl */ + CMSG_SPACE(sizeof(struct timeval))]; + } control = {}; + struct iovec iov = {}; + union sockaddr_union sa = {}; + struct msghdr msg = { + .msg_name = &sa.sa, + .msg_namelen = sizeof(sa), + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = &control, + .msg_controllen = sizeof(control), + }; + struct cmsghdr *cmsg; + ssize_t len; + + iov.iov_base = buffer; + iov.iov_len = size; + + len = recvmsg(fd, &msg, MSG_DONTWAIT); + if (len < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + return -errno; + } + + if ((size_t) len != size) + return -EINVAL; + + if (msg.msg_namelen == sizeof(struct sockaddr_in6) && + sa.in6.sin6_family == AF_INET6) { + + *dst = sa.in6.sin6_addr; + if (in_addr_is_link_local(AF_INET6, (union in_addr_union*) dst) <= 0) + return -EADDRNOTAVAIL; + + } else if (msg.msg_namelen > 0) + return -EPFNOSUPPORT; + + /* namelen == 0 only happens when running the test-suite over a socketpair */ + + assert(!(msg.msg_flags & MSG_CTRUNC)); + assert(!(msg.msg_flags & MSG_TRUNC)); + + CMSG_FOREACH(cmsg, &msg) { + if (cmsg->cmsg_level == SOL_IPV6 && + cmsg->cmsg_type == IPV6_HOPLIMIT && + cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { + int hops = *(int*) CMSG_DATA(cmsg); + + if (hops != 255) + return -EMULTIHOP; + } + + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SO_TIMESTAMP && + cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) + triple_timestamp_from_realtime(timestamp, timeval_load((struct timeval*) CMSG_DATA(cmsg))); + } + + if (!triple_timestamp_is_set(timestamp)) + triple_timestamp_get(timestamp); + + return 0; +} diff --git a/src/libsystemd-network/icmp6-util.h b/src/libsystemd-network/icmp6-util.h index 2b4dbc76ce..16b8be8298 100644 --- a/src/libsystemd-network/icmp6-util.h +++ b/src/libsystemd-network/icmp6-util.h @@ -21,5 +21,18 @@ #include <net/ethernet.h> +#include "time-util.h" + +#define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \ + { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } } } + +#define IN6ADDR_ALL_NODES_MULTICAST_INIT \ + { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } } + int icmp6_bind_router_solicitation(int index); +int icmp6_bind_router_advertisement(int index); int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr); +int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *dst, + triple_timestamp *timestamp); diff --git a/src/libsystemd-network/lldp-neighbor.c b/src/libsystemd-network/lldp-neighbor.c index 53e29377b3..f1fecba6fa 100644 --- a/src/libsystemd-network/lldp-neighbor.c +++ b/src/libsystemd-network/lldp-neighbor.c @@ -249,10 +249,9 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) { log_lldp("End marker TLV not zero-sized, ignoring datagram."); return -EBADMSG; } - if (left != 0) { - log_lldp("Trailing garbage in datagram, ignoring datagram."); - return -EBADMSG; - } + + /* Note that after processing the SD_LLDP_TYPE_END left could still be > 0 + * as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */ goto end_marker; diff --git a/src/libsystemd-network/meson.build b/src/libsystemd-network/meson.build new file mode 100644 index 0000000000..78c74560b8 --- /dev/null +++ b/src/libsystemd-network/meson.build @@ -0,0 +1,48 @@ +sources = files(''' + sd-dhcp-client.c + sd-dhcp-server.c + dhcp-network.c + dhcp-option.c + dhcp-packet.c + dhcp-internal.h + dhcp-server-internal.h + dhcp-protocol.h + dhcp-lease-internal.h + sd-dhcp-lease.c + sd-ipv4ll.c + sd-ipv4acd.c + arp-util.h + arp-util.c + network-internal.c + sd-ndisc.c + ndisc-internal.h + ndisc-router.h + ndisc-router.c + sd-radv.c + radv-internal.h + icmp6-util.h + icmp6-util.c + sd-dhcp6-client.c + dhcp6-internal.h + dhcp6-protocol.h + dhcp6-network.c + dhcp6-option.c + dhcp6-lease-internal.h + sd-dhcp6-lease.c + dhcp-identifier.h + dhcp-identifier.c + lldp-internal.h + lldp-network.h + lldp-network.c + lldp-neighbor.h + lldp-neighbor.c + sd-lldp.c +'''.split()) + +network_internal_h = files('network-internal.h') + +libsystemd_network = static_library( + 'systemd-network', + sources, + network_internal_h, + include_directories : includes) diff --git a/src/libsystemd-network/ndisc-internal.h b/src/libsystemd-network/ndisc-internal.h index 60e183ff8c..82b896dba3 100644 --- a/src/libsystemd-network/ndisc-internal.h +++ b/src/libsystemd-network/ndisc-internal.h @@ -23,6 +23,10 @@ #include "sd-ndisc.h" +#define NDISC_ROUTER_SOLICITATION_INTERVAL (4U * USEC_PER_SEC) +#define NDISC_MAX_ROUTER_SOLICITATION_INTERVAL (3600U * USEC_PER_SEC) +#define NDISC_MAX_ROUTER_SOLICITATIONS 3U + struct sd_ndisc { unsigned n_ref; @@ -38,8 +42,9 @@ struct sd_ndisc { sd_event_source *recv_event_source; sd_event_source *timeout_event_source; + sd_event_source *timeout_no_ra; - unsigned nd_sent; + usec_t retransmit_time; sd_ndisc_callback_t callback; void *userdata; diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index 092a1eabb0..337241a815 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -349,6 +349,45 @@ int config_parse_iaid(const char *unit, return 0; } +int config_parse_bridge_port_priority( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + uint16_t i; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = safe_atou16(rvalue, &i); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse bridge port priority, ignoring: %s", rvalue); + return 0; + } + + if (i > LINK_BRIDGE_PORT_PRIORITY_MAX) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Bridge port priority is larger than maximum %u, ignoring: %s", LINK_BRIDGE_PORT_PRIORITY_MAX, rvalue); + return 0; + } + + *((uint16_t *)data) = i; + + return 0; +} + + void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size) { unsigned i; diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h index 5bcd577167..4666f174e9 100644 --- a/src/libsystemd-network/network-internal.h +++ b/src/libsystemd-network/network-internal.h @@ -26,6 +26,9 @@ #include "condition.h" #include "udev.h" +#define LINK_BRIDGE_PORT_PRIORITY_INVALID 128 +#define LINK_BRIDGE_PORT_PRIORITY_MAX 63 + bool net_match_config(const struct ether_addr *match_mac, char * const *match_path, char * const *match_driver, @@ -62,6 +65,10 @@ int config_parse_iaid(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_bridge_port_priority(const char *unit, const char *filename, unsigned line, + const char *section, unsigned section_line, const char *lvalue, + int ltype, const char *rvalue, void *data, void *userdata); + int net_get_unique_predictable_data(struct udev_device *device, uint64_t *result); const char *net_get_name(struct udev_device *device); diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h new file mode 100644 index 0000000000..b21d4e54cb --- /dev/null +++ b/src/libsystemd-network/radv-internal.h @@ -0,0 +1,88 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2017 Intel Corporation. All rights reserved. + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "sd-radv.h" + +#include "log.h" +#include "list.h" +#include "sparse-endian.h" + +#define SD_RADV_DEFAULT_MIN_TIMEOUT_USEC (200*USEC_PER_SEC) +#define SD_RADV_DEFAULT_MAX_TIMEOUT_USEC (600*USEC_PER_SEC) +assert_cc(SD_RADV_DEFAULT_MIN_TIMEOUT_USEC <= SD_RADV_DEFAULT_MAX_TIMEOUT_USEC) + +#define SD_RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC (16*USEC_PER_SEC) +#define SD_RADV_MAX_INITIAL_RTR_ADVERTISEMENTS 3 +#define SD_RADV_MAX_FINAL_RTR_ADVERTISEMENTS 3 +#define SD_RADV_MIN_DELAY_BETWEEN_RAS 3 +#define SD_RADV_MAX_RA_DELAY_TIME_USEC (500*USEC_PER_MSEC) + +enum RAdvState { + SD_RADV_STATE_IDLE = 0, + SD_RADV_STATE_ADVERTISING = 1, +}; +typedef enum RAdvState RAdvState; + +struct sd_radv { + unsigned n_ref; + RAdvState state; + + int ifindex; + + sd_event *event; + int event_priority; + + struct ether_addr mac_addr; + uint8_t hop_limit; + uint8_t flags; + uint32_t mtu; + uint16_t lifetime; + + int fd; + unsigned ra_sent; + sd_event_source *recv_event_source; + sd_event_source *timeout_event_source; + + unsigned n_prefixes; + LIST_HEAD(sd_radv_prefix, prefixes); +}; + +struct sd_radv_prefix { + unsigned n_ref; + + struct { + uint8_t type; + uint8_t length; + uint8_t prefixlen; + uint8_t flags; + be32_t valid_lifetime; + be32_t preferred_lifetime; + uint32_t reserved; + struct in6_addr in6_addr; + } _packed_ opt; + + LIST_FIELDS(struct sd_radv_prefix, prefix); +}; + +#define log_radv_full(level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "RADV: " fmt, ##__VA_ARGS__) +#define log_radv_errno(error, fmt, ...) log_radv_full(LOG_DEBUG, error, fmt, ##__VA_ARGS__) +#define log_radv_warning_errno(error, fmt, ...) log_radv_full(LOG_WARNING, error, fmt, ##__VA_ARGS__) +#define log_radv(fmt, ...) log_radv_errno(0, fmt, ##__VA_ARGS__) diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 7c0317640f..e20d339bd5 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -1655,7 +1655,8 @@ static int client_receive_message_udp( if (errno == EAGAIN || errno == EINTR) return 0; - return log_dhcp_client_errno(client, errno, "Could not receive message from UDP socket: %m"); + return log_dhcp_client_errno(client, errno, + "Could not receive message from UDP socket: %m"); } if ((size_t) len < sizeof(DHCPMessage)) { log_dhcp_client(client, "Too small to be a DHCP message: ignoring"); @@ -1748,9 +1749,8 @@ static int client_receive_message_raw( if (errno == EAGAIN || errno == EINTR) return 0; - log_dhcp_client(client, "Could not receive message from raw socket: %m"); - - return -errno; + return log_dhcp_client_errno(client, errno, + "Could not receive message from raw socket: %m"); } else if ((size_t)len < sizeof(DHCPPacket)) return 0; diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index 7fed55c5fc..1661874a55 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -231,6 +231,21 @@ int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes) { return (int) lease->static_route_size; } +int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) { + unsigned r; + + assert_return(lease, -EINVAL); + assert_return(domains, -EINVAL); + + r = strv_length(lease->search_domains); + if (r > 0) { + *domains = lease->search_domains; + return (int) r; + } + + return -ENODATA; +} + int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) { assert_return(lease, -EINVAL); assert_return(data, -EINVAL); @@ -282,6 +297,7 @@ sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) { free(lease->static_route); free(lease->client_id); free(lease->vendor_specific); + strv_free(lease->search_domains); return mfree(lease); } @@ -594,6 +610,11 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void r = lease_parse_u16(option, len, &lease->mtu, 68); if (r < 0) log_debug_errno(r, "Failed to parse MTU, ignoring: %m"); + if (lease->mtu < DHCP_DEFAULT_MIN_SIZE) { + log_debug("MTU value of %" PRIu16 " too small. Using default MTU value of %d instead.", lease->mtu, DHCP_DEFAULT_MIN_SIZE); + lease->mtu = DHCP_DEFAULT_MIN_SIZE; + } + break; case SD_DHCP_OPTION_DOMAIN_NAME: @@ -605,6 +626,12 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void break; + case SD_DHCP_OPTION_DOMAIN_SEARCH_LIST: + r = dhcp_lease_parse_search_domains(option, len, &lease->search_domains); + if (r < 0) + log_debug_errno(r, "Failed to parse Domain Search List, ignoring: %m"); + break; + case SD_DHCP_OPTION_HOST_NAME: r = lease_parse_domain(option, len, &lease->hostname); if (r < 0) { @@ -696,6 +723,96 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void return 0; } +/* Parses compressed domain names. */ +int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***domains) { + _cleanup_strv_free_ char **names = NULL; + size_t pos = 0, cnt = 0; + int r; + + assert(domains); + assert_return(option && len > 0, -ENODATA); + + while (pos < len) { + _cleanup_free_ char *name = NULL; + size_t n = 0, allocated = 0; + size_t jump_barrier = pos, next_chunk = 0; + bool first = true; + + for (;;) { + uint8_t c; + c = option[pos++]; + + if (c == 0) { + /* End of name */ + break; + } else if (c <= 63) { + const char *label; + + /* Literal label */ + label = (const char*) (option + pos); + pos += c; + if (pos >= len) + return -EBADMSG; + + if (!GREEDY_REALLOC(name, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) + return -ENOMEM; + + if (first) + first = false; + else + name[n++] = '.'; + + r = dns_label_escape(label, c, name + n, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + return r; + + n += r; + } else if ((c & 0xc0) == 0xc0) { + /* Pointer */ + + uint8_t d; + uint16_t ptr; + + if (pos >= len) + return -EBADMSG; + + d = option[pos++]; + ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d; + + /* Jumps are limited to a "prior occurrence" (RFC-1035 4.1.4) */ + if (ptr >= jump_barrier) + return -EBADMSG; + jump_barrier = ptr; + + /* Save current location so we don't end up re-parsing what's parsed so far. */ + if (next_chunk == 0) + next_chunk = pos; + + pos = ptr; + } else + return -EBADMSG; + } + + if (!GREEDY_REALLOC(name, allocated, n + 1)) + return -ENOMEM; + name[n] = 0; + + r = strv_extend(&names, name); + if (r < 0) + return r; + + cnt++; + + if (next_chunk != 0) + pos = next_chunk; + } + + *domains = names; + names = NULL; + + return cnt; +} + int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len) { struct sd_dhcp_raw_option *cur, *option; @@ -751,6 +868,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { const char *string; uint16_t mtu; _cleanup_free_ sd_dhcp_route **routes = NULL; + char **search_domains = NULL; uint32_t t1, t2, lifetime; int r; @@ -824,6 +942,13 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { if (r >= 0) fprintf(f, "DOMAINNAME=%s\n", string); + r = sd_dhcp_lease_get_search_domains(lease, &search_domains); + if (r > 0) { + fputs("DOMAIN_SEARCH_LIST=", f); + fputstrv(f, search_domains, NULL, NULL); + fputs("\n", f); + } + r = sd_dhcp_lease_get_hostname(lease, &string); if (r >= 0) fprintf(f, "HOSTNAME=%s\n", string); @@ -905,6 +1030,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { *ntp = NULL, *mtu = NULL, *routes = NULL, + *domains = NULL, *client_id_hex = NULL, *vendor_specific_hex = NULL, *lifetime = NULL, @@ -933,6 +1059,7 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { "MTU", &mtu, "DOMAINNAME", &lease->domainname, "HOSTNAME", &lease->hostname, + "DOMAIN_SEARCH_LIST", &domains, "ROOT_PATH", &lease->root_path, "ROUTES", &routes, "CLIENTID", &client_id_hex, @@ -1038,6 +1165,18 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { log_debug_errno(r, "Failed to parse MTU %s, ignoring: %m", mtu); } + if (domains) { + _cleanup_strv_free_ char **a = NULL; + a = strv_split(domains, " "); + if (!a) + return -ENOMEM; + + if (!strv_isempty(a)) { + lease->search_domains = a; + a = NULL; + } + } + if (routes) { r = deserialize_dhcp_routes( &lease->static_route, diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index 2890681561..5a59c377f8 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -34,6 +34,14 @@ #define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR #define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12) +static void dhcp_lease_free(DHCPLease *lease) { + if (!lease) + return; + + free(lease->client_id.data); + free(lease); +} + /* configures the server's address and subnet, and optionally the pool's size and offset into the subnet * the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address * moreover, the server's own address may be in the pool, and is in that case reserved in order not to @@ -47,7 +55,6 @@ int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *addres assert_return(address, -EINVAL); assert_return(address->s_addr != INADDR_ANY, -EINVAL); assert_return(prefixlen <= 32, -ERANGE); - assert_return(server->address == INADDR_ANY, -EBUSY); assert_se(in_addr_prefixlen_to_netmask(&netmask_addr, prefixlen)); netmask = netmask_addr.s_addr; @@ -78,19 +85,28 @@ int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *addres else size = size_max; - server->bound_leases = new0(DHCPLease*, size); - if (!server->bound_leases) - return -ENOMEM; + if (server->address != address->s_addr || server->netmask != netmask || server->pool_size != size || server->pool_offset != offset) { + DHCPLease *lease; + + free(server->bound_leases); + server->bound_leases = new0(DHCPLease*, size); + if (!server->bound_leases) + return -ENOMEM; + + server->pool_offset = offset; + server->pool_size = size; - server->pool_offset = offset; - server->pool_size = size; + server->address = address->s_addr; + server->netmask = netmask; + server->subnet = address->s_addr & netmask; - server->address = address->s_addr; - server->netmask = netmask; - server->subnet = address->s_addr & netmask; + if (server_off >= offset && server_off - offset < size) + server->bound_leases[server_off - offset] = &server->invalid_lease; - if (server_off >= offset && server_off - offset < size) - server->bound_leases[server_off - offset] = &server->invalid_lease; + /* Drop any leases associated with the old address range */ + while ((lease = hashmap_steal_first(server->leases_by_client_id))) + dhcp_lease_free(lease); + } return 0; } @@ -143,14 +159,6 @@ static const struct hash_ops client_id_hash_ops = { .compare = client_id_compare_func }; -static void dhcp_lease_free(DHCPLease *lease) { - if (!lease) - return; - - free(lease->client_id.data); - free(lease); -} - sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) { DHCPLease *lease; @@ -197,7 +205,11 @@ int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) { server->address = htobe32(INADDR_ANY); server->netmask = htobe32(INADDR_ANY); server->ifindex = ifindex; + server->leases_by_client_id = hashmap_new(&client_id_hash_ops); + if (!server->leases_by_client_id) + return -ENOMEM; + server->default_lease_time = DIV_ROUND_UP(DHCP_DEFAULT_LEASE_TIME_USEC, USEC_PER_SEC); server->max_lease_time = DIV_ROUND_UP(DHCP_MAX_LEASE_TIME_USEC, USEC_PER_SEC); @@ -857,6 +869,8 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, if (!existing_lease) { lease = new0(DHCPLease, 1); + if (!lease) + return -ENOMEM; lease->address = address; lease->client_id.data = memdup(req->client_id.data, req->client_id.length); diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c index 13209261f9..88a90e593b 100644 --- a/src/libsystemd-network/sd-ipv4ll.c +++ b/src/libsystemd-network/sd-ipv4ll.c @@ -248,6 +248,12 @@ static int ipv4ll_pick_address(sd_ipv4ll *ll) { return sd_ipv4ll_set_address(ll, &(struct in_addr) { addr }); } +int sd_ipv4ll_restart(sd_ipv4ll *ll) { + ll->address = 0; + + return sd_ipv4ll_start(ll); +} + #define MAC_HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2) int sd_ipv4ll_start(sd_ipv4ll *ll) { diff --git a/src/libsystemd-network/sd-lldp.c b/src/libsystemd-network/sd-lldp.c index 0702241506..39ddb2461a 100644 --- a/src/libsystemd-network/sd-lldp.c +++ b/src/libsystemd-network/sd-lldp.c @@ -19,6 +19,7 @@ ***/ #include <arpa/inet.h> +#include <linux/sockios.h> #include "sd-lldp.h" diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c index 1d3be9b862..4bf558b12b 100644 --- a/src/libsystemd-network/sd-ndisc.c +++ b/src/libsystemd-network/sd-ndisc.c @@ -28,12 +28,12 @@ #include "in-addr-util.h" #include "ndisc-internal.h" #include "ndisc-router.h" +#include "random-util.h" #include "socket-util.h" #include "string-util.h" #include "util.h" -#define NDISC_ROUTER_SOLICITATION_INTERVAL (4U * USEC_PER_SEC) -#define NDISC_MAX_ROUTER_SOLICITATIONS 3U +#define NDISC_TIMEOUT_NO_RA_USEC (NDISC_ROUTER_SOLICITATION_INTERVAL * NDISC_MAX_ROUTER_SOLICITATIONS) static void ndisc_callback(sd_ndisc *ndisc, sd_ndisc_event event, sd_ndisc_router *rt) { assert(ndisc); @@ -129,6 +129,8 @@ static int ndisc_reset(sd_ndisc *nd) { assert(nd); nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source); + nd->timeout_no_ra = sd_event_source_unref(nd->timeout_no_ra); + nd->retransmit_time = 0; nd->recv_event_source = sd_event_source_unref(nd->recv_event_source); nd->fd = safe_close(nd->fd); @@ -221,23 +223,9 @@ static int ndisc_handle_datagram(sd_ndisc *nd, sd_ndisc_router *rt) { static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) { _cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL; sd_ndisc *nd = userdata; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int)) + /* ttl */ - CMSG_SPACE(sizeof(struct timeval))]; - } control = {}; - struct iovec iov = {}; - union sockaddr_union sa = {}; - struct msghdr msg = { - .msg_name = &sa.sa, - .msg_namelen = sizeof(sa), - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = &control, - .msg_controllen = sizeof(control), - }; - struct cmsghdr *cmsg; - ssize_t len, buflen; + ssize_t buflen; + int r; + _cleanup_free_ char *addr = NULL; assert(s); assert(nd); @@ -251,110 +239,90 @@ static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userda if (!rt) return -ENOMEM; - iov.iov_base = NDISC_ROUTER_RAW(rt); - iov.iov_len = rt->raw_size; - - len = recvmsg(fd, &msg, MSG_DONTWAIT); - if (len < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - - return log_ndisc_errno(errno, "Could not receive message from ICMPv6 socket: %m"); - } - - if ((size_t) len != rt->raw_size) { - log_ndisc("Packet size mismatch."); - return -EINVAL; - } - - if (msg.msg_namelen == sizeof(struct sockaddr_in6) && - sa.in6.sin6_family == AF_INET6) { - - if (in_addr_is_link_local(AF_INET6, (union in_addr_union*) &sa.in6.sin6_addr) <= 0) { - _cleanup_free_ char *addr = NULL; - - (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &sa.in6.sin6_addr, &addr); - log_ndisc("Received RA from non-link-local address %s. Ignoring.", strna(addr)); - return 0; - } - - rt->address = sa.in6.sin6_addr; - - } else if (msg.msg_namelen > 0) { - log_ndisc("Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t) msg.msg_namelen); - return -EINVAL; - } - - /* namelen == 0 only happens when running the test-suite over a socketpair */ - - assert(!(msg.msg_flags & MSG_CTRUNC)); - assert(!(msg.msg_flags & MSG_TRUNC)); - - CMSG_FOREACH(cmsg, &msg) { - if (cmsg->cmsg_level == SOL_IPV6 && - cmsg->cmsg_type == IPV6_HOPLIMIT && - cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { - int hops = *(int*) CMSG_DATA(cmsg); - - if (hops != 255) { - log_ndisc("Received RA with invalid hop limit %d. Ignoring.", hops); - return 0; - } + r = icmp6_receive(fd, NDISC_ROUTER_RAW(rt), rt->raw_size, &rt->address, + &rt->timestamp); + if (r < 0) { + switch (r) { + case -EADDRNOTAVAIL: + (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &rt->address, &addr); + log_ndisc("Received RA from non-link-local address %s. Ignoring", addr); + break; + + case -EMULTIHOP: + log_ndisc("Received RA with invalid hop limit. Ignoring."); + break; + + case -EPFNOSUPPORT: + log_ndisc("Received invalid source address from ICMPv6 socket."); + break; } - if (cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SO_TIMESTAMP && - cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) - triple_timestamp_from_realtime(&rt->timestamp, timeval_load((struct timeval*) CMSG_DATA(cmsg))); + return 0; } - if (!triple_timestamp_is_set(&rt->timestamp)) - triple_timestamp_get(&rt->timestamp); - nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source); return ndisc_handle_datagram(nd, rt); } +static usec_t ndisc_timeout_compute_random(usec_t val) { + /* compute a time that is random within ±10% of the given value */ + return val - val / 10 + + (random_u64() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC; +} + static int ndisc_timeout(sd_event_source *s, uint64_t usec, void *userdata) { sd_ndisc *nd = userdata; - usec_t time_now, next_timeout; + usec_t time_now; int r; + char time_string[FORMAT_TIMESPAN_MAX]; assert(s); assert(nd); assert(nd->event); - if (nd->nd_sent >= NDISC_MAX_ROUTER_SOLICITATIONS) { - nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source); - ndisc_callback(nd, SD_NDISC_EVENT_TIMEOUT, NULL); - return 0; + assert_se(sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now) >= 0); + + nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source); + + if (!nd->retransmit_time) + nd->retransmit_time = ndisc_timeout_compute_random(NDISC_ROUTER_SOLICITATION_INTERVAL); + else { + if (nd->retransmit_time > NDISC_MAX_ROUTER_SOLICITATION_INTERVAL / 2) + nd->retransmit_time = ndisc_timeout_compute_random(NDISC_MAX_ROUTER_SOLICITATION_INTERVAL); + else + nd->retransmit_time += ndisc_timeout_compute_random(nd->retransmit_time); } - r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr); - if (r < 0) { - log_ndisc_errno(r, "Error sending Router Solicitation: %m"); + r = sd_event_add_time(nd->event, &nd->timeout_event_source, + clock_boottime_or_monotonic(), + time_now + nd->retransmit_time, + 10 * USEC_PER_MSEC, ndisc_timeout, nd); + if (r < 0) goto fail; - } - log_ndisc("Sent Router Solicitation"); - nd->nd_sent++; + r = sd_event_source_set_priority(nd->timeout_event_source, nd->event_priority); + if (r < 0) + goto fail; - assert_se(sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now) >= 0); - next_timeout = time_now + NDISC_ROUTER_SOLICITATION_INTERVAL; + (void) sd_event_source_set_description(nd->timeout_event_source, "ndisc-timeout-no-ra"); - r = sd_event_source_set_time(nd->timeout_event_source, next_timeout); + r = sd_event_source_set_enabled(nd->timeout_event_source, SD_EVENT_ONESHOT); if (r < 0) { - log_ndisc_errno(r, "Error updating timer: %m"); + log_ndisc_errno(r, "Error reenabling timer: %m"); goto fail; } - r = sd_event_source_set_enabled(nd->timeout_event_source, SD_EVENT_ONESHOT); + r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr); if (r < 0) { - log_ndisc_errno(r, "Error reenabling timer: %m"); + log_ndisc_errno(r, "Error sending Router Solicitation: %m"); goto fail; } + log_ndisc("Sent Router Solicitation, next solicitation in %s", + format_timespan(time_string, FORMAT_TIMESPAN_MAX, + nd->retransmit_time, USEC_PER_SEC)); + return 0; fail: @@ -362,6 +330,20 @@ fail: return 0; } +static int ndisc_timeout_no_ra(sd_event_source *s, uint64_t usec, void *userdata) { + sd_ndisc *nd = userdata; + + assert(s); + assert(nd); + + log_ndisc("No RA received before link confirmation timeout"); + + nd->timeout_no_ra = sd_event_source_unref(nd->timeout_no_ra); + ndisc_callback(nd, SD_NDISC_EVENT_TIMEOUT, NULL); + + return 0; +} + _public_ int sd_ndisc_stop(sd_ndisc *nd) { assert_return(nd, -EINVAL); @@ -376,6 +358,7 @@ _public_ int sd_ndisc_stop(sd_ndisc *nd) { _public_ int sd_ndisc_start(sd_ndisc *nd) { int r; + usec_t time_now; assert_return(nd, -EINVAL); assert_return(nd->event, -EINVAL); @@ -387,6 +370,10 @@ _public_ int sd_ndisc_start(sd_ndisc *nd) { assert(!nd->recv_event_source); assert(!nd->timeout_event_source); + r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now); + if (r < 0) + goto fail; + nd->fd = icmp6_bind_router_solicitation(nd->ifindex); if (nd->fd < 0) return nd->fd; @@ -411,6 +398,19 @@ _public_ int sd_ndisc_start(sd_ndisc *nd) { (void) sd_event_source_set_description(nd->timeout_event_source, "ndisc-timeout"); + r = sd_event_add_time(nd->event, &nd->timeout_no_ra, + clock_boottime_or_monotonic(), + time_now + NDISC_TIMEOUT_NO_RA_USEC, + 10 * USEC_PER_MSEC, ndisc_timeout_no_ra, nd); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(nd->timeout_no_ra, nd->event_priority); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(nd->timeout_no_ra, "ndisc-timeout-no-ra"); + log_ndisc("Started IPv6 Router Solicitation client"); return 1; diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c new file mode 100644 index 0000000000..f23275a80c --- /dev/null +++ b/src/libsystemd-network/sd-radv.c @@ -0,0 +1,653 @@ +/*** + This file is part of systemd. + + Copyright (C) 2017 Intel Corporation. All rights reserved. + + 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/icmp6.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <linux/in6.h> + +#include "sd-radv.h" + +#include "macro.h" +#include "alloc-util.h" +#include "fd-util.h" +#include "icmp6-util.h" +#include "in-addr-util.h" +#include "radv-internal.h" +#include "socket-util.h" +#include "string-util.h" +#include "util.h" +#include "random-util.h" + +_public_ int sd_radv_new(sd_radv **ret) { + _cleanup_(sd_radv_unrefp) sd_radv *ra = NULL; + + assert_return(ret, -EINVAL); + + ra = new0(sd_radv, 1); + if (!ra) + return -ENOMEM; + + ra->n_ref = 1; + ra->fd = -1; + + LIST_HEAD_INIT(ra->prefixes); + + *ret = ra; + ra = NULL; + + return 0; +} + +_public_ int sd_radv_attach_event(sd_radv *ra, sd_event *event, int64_t priority) { + int r; + + assert_return(ra, -EINVAL); + assert_return(!ra->event, -EBUSY); + + if (event) + ra->event = sd_event_ref(event); + else { + r = sd_event_default(&ra->event); + if (r < 0) + return 0; + } + + ra->event_priority = priority; + + return 0; +} + +_public_ int sd_radv_detach_event(sd_radv *ra) { + + assert_return(ra, -EINVAL); + + ra->event = sd_event_unref(ra->event); + return 0; +} + +_public_ sd_event *sd_radv_get_event(sd_radv *ra) { + assert_return(ra, NULL); + + return ra->event; +} + +static void radv_reset(sd_radv *ra) { + + ra->timeout_event_source = + sd_event_source_unref(ra->timeout_event_source); + + ra->recv_event_source = + sd_event_source_unref(ra->recv_event_source); + + ra->ra_sent = 0; +} + +_public_ sd_radv *sd_radv_ref(sd_radv *ra) { + if (!ra) + return NULL; + + assert(ra->n_ref > 0); + ra->n_ref++; + + return ra; +} + +_public_ sd_radv *sd_radv_unref(sd_radv *ra) { + if (!ra) + return NULL; + + assert(ra->n_ref > 0); + ra->n_ref--; + + if (ra->n_ref > 0) + return NULL; + + while (ra->prefixes) { + sd_radv_prefix *p = ra->prefixes; + + LIST_REMOVE(prefix, ra->prefixes, p); + sd_radv_prefix_unref(p); + } + + radv_reset(ra); + + sd_radv_detach_event(ra); + return mfree(ra); +} + +static int radv_send(sd_radv *ra, const struct in6_addr *dst, + const uint32_t router_lifetime) { + static const struct ether_addr mac_zero = {}; + sd_radv_prefix *p; + struct sockaddr_in6 dst_addr = { + .sin6_family = AF_INET6, + .sin6_addr = IN6ADDR_ALL_NODES_MULTICAST_INIT, + }; + struct nd_router_advert adv = {}; + struct { + struct nd_opt_hdr opthdr; + struct ether_addr slladdr; + } _packed_ opt_mac = { + .opthdr = { + .nd_opt_type = ND_OPT_SOURCE_LINKADDR, + .nd_opt_len = (sizeof(struct nd_opt_hdr) + + sizeof(struct ether_addr) - 1) /8 + 1, + }, + }; + struct nd_opt_mtu opt_mtu = { + .nd_opt_mtu_type = ND_OPT_MTU, + .nd_opt_mtu_len = 1, + }; + /* Reserve iov space for RA header, linkaddr, MTU + N prefixes */ + struct iovec iov[3 + ra->n_prefixes]; + struct msghdr msg = { + .msg_name = &dst_addr, + .msg_namelen = sizeof(dst_addr), + .msg_iov = iov, + }; + + if (dst && !in_addr_is_null(AF_INET6, (union in_addr_union*) dst)) + dst_addr.sin6_addr = *dst; + + adv.nd_ra_type = ND_ROUTER_ADVERT; + adv.nd_ra_curhoplimit = ra->hop_limit; + adv.nd_ra_flags_reserved = ra->flags; + adv.nd_ra_router_lifetime = htobe16(router_lifetime); + iov[msg.msg_iovlen].iov_base = &adv; + iov[msg.msg_iovlen].iov_len = sizeof(adv); + msg.msg_iovlen++; + + /* MAC address is optional, either because the link does not use L2 + addresses or load sharing is desired. See RFC 4861, Section 4.2 */ + if (memcmp(&mac_zero, &ra->mac_addr, sizeof(mac_zero))) { + opt_mac.slladdr = ra->mac_addr; + iov[msg.msg_iovlen].iov_base = &opt_mac; + iov[msg.msg_iovlen].iov_len = sizeof(opt_mac); + msg.msg_iovlen++; + } + + if (ra->mtu) { + opt_mtu.nd_opt_mtu_mtu = htobe32(ra->mtu); + iov[msg.msg_iovlen].iov_base = &opt_mtu; + iov[msg.msg_iovlen].iov_len = sizeof(opt_mtu); + msg.msg_iovlen++; + } + + LIST_FOREACH(prefix, p, ra->prefixes) { + iov[msg.msg_iovlen].iov_base = &p->opt; + iov[msg.msg_iovlen].iov_len = sizeof(p->opt); + msg.msg_iovlen++; + } + + if (sendmsg(ra->fd, &msg, 0) < 0) + return -errno; + + return 0; +} + +static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + sd_radv *ra = userdata; + _cleanup_free_ char *addr = NULL; + struct in6_addr src; + triple_timestamp timestamp; + int r; + ssize_t buflen; + _cleanup_free_ char *buf = NULL; + + assert(s); + assert(ra); + assert(ra->event); + + buflen = next_datagram_size_fd(fd); + + if ((unsigned) buflen < sizeof(struct nd_router_solicit)) + return log_radv("Too short packet received"); + + buf = new0(char, buflen); + if (!buf) + return 0; + + r = icmp6_receive(fd, buf, buflen, &src, ×tamp); + if (r < 0) { + switch (r) { + case -EADDRNOTAVAIL: + (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &src, &addr); + log_radv("Received RS from non-link-local address %s. Ignoring", addr); + break; + + case -EMULTIHOP: + log_radv("Received RS with invalid hop limit. Ignoring."); + break; + + case -EPFNOSUPPORT: + log_radv("Received invalid source address from ICMPv6 socket. Ignoring."); + break; + + default: + log_radv_warning_errno(r, "Error receiving from ICMPv6 socket: %m"); + break; + } + + return 0; + } + + (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &src, &addr); + + r = radv_send(ra, &src, ra->lifetime); + if (r < 0) + log_radv_warning_errno(r, "Unable to send solicited Router Advertisment to %s: %m", addr); + else + log_radv("Sent solicited Router Advertisement to %s", addr); + + return 0; +} + +static usec_t radv_compute_timeout(usec_t min, usec_t max) { + assert_return(min <= max, SD_RADV_DEFAULT_MIN_TIMEOUT_USEC); + + return min + (random_u32() % (max - min)); +} + +static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) { + int r; + sd_radv *ra = userdata; + usec_t min_timeout = SD_RADV_DEFAULT_MIN_TIMEOUT_USEC; + usec_t max_timeout = SD_RADV_DEFAULT_MAX_TIMEOUT_USEC; + usec_t time_now, timeout; + char time_string[FORMAT_TIMESPAN_MAX]; + + assert(s); + assert(ra); + assert(ra->event); + + ra->timeout_event_source = sd_event_source_unref(ra->timeout_event_source); + + r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now); + if (r < 0) + goto fail; + + r = radv_send(ra, NULL, ra->lifetime); + if (r < 0) + log_radv_warning_errno(r, "Unable to send Router Advertisement: %m"); + + /* RFC 4861, Section 6.2.4, sending initial Router Advertisements */ + if (ra->ra_sent < SD_RADV_MAX_INITIAL_RTR_ADVERTISEMENTS) { + max_timeout = SD_RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC; + min_timeout = SD_RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC / 3; + } + + timeout = radv_compute_timeout(min_timeout, max_timeout); + + log_radv("Next Router Advertisement in %s", + format_timespan(time_string, FORMAT_TIMESPAN_MAX, + timeout, USEC_PER_SEC)); + + r = sd_event_add_time(ra->event, &ra->timeout_event_source, + clock_boottime_or_monotonic(), + time_now + timeout, MSEC_PER_SEC, + radv_timeout, ra); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(ra->timeout_event_source, + ra->event_priority); + if (r < 0) + goto fail; + + r = sd_event_source_set_description(ra->timeout_event_source, + "radv-timeout"); + if (r < 0) + goto fail; + + ra->ra_sent++; + +fail: + if (r < 0) + sd_radv_stop(ra); + + return 0; +} + +_public_ int sd_radv_stop(sd_radv *ra) { + int r; + + assert_return(ra, -EINVAL); + + log_radv("Stopping IPv6 Router Advertisement daemon"); + + /* RFC 4861, Section 6.2.5, send at least one Router Advertisement + with zero lifetime */ + r = radv_send(ra, NULL, 0); + if (r < 0) + log_radv_warning_errno(r, "Unable to send last Router Advertisement with router lifetime set to zero: %m"); + + radv_reset(ra); + ra->fd = safe_close(ra->fd); + ra->state = SD_RADV_STATE_IDLE; + + return 0; +} + +_public_ int sd_radv_start(sd_radv *ra) { + int r = 0; + + assert_return(ra, -EINVAL); + assert_return(ra->event, -EINVAL); + assert_return(ra->ifindex > 0, -EINVAL); + + if (ra->state != SD_RADV_STATE_IDLE) + return 0; + + r = sd_event_add_time(ra->event, &ra->timeout_event_source, + clock_boottime_or_monotonic(), 0, 0, + radv_timeout, ra); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(ra->timeout_event_source, + ra->event_priority); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(ra->timeout_event_source, + "radv-timeout"); + + r = icmp6_bind_router_advertisement(ra->ifindex); + if (r < 0) + goto fail; + + ra->fd = r; + + r = sd_event_add_io(ra->event, &ra->recv_event_source, ra->fd, EPOLLIN, radv_recv, ra); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(ra->recv_event_source, ra->event_priority); + if (r < 0) + goto fail; + + (void) sd_event_source_set_description(ra->recv_event_source, "radv-receive-message"); + + ra->state = SD_RADV_STATE_ADVERTISING; + + log_radv("Started IPv6 Router Advertisement daemon"); + + return 0; + + fail: + radv_reset(ra); + + return r; +} + +_public_ int sd_radv_set_ifindex(sd_radv *ra, int ifindex) { + assert_return(ra, -EINVAL); + assert_return(ifindex >= -1, -EINVAL); + + if (ra->state != SD_RADV_STATE_IDLE) + return -EBUSY; + + ra->ifindex = ifindex; + + return 0; +} + +_public_ int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) { + assert_return(ra, -EINVAL); + + if (ra->state != SD_RADV_STATE_IDLE) + return -EBUSY; + + if (mac_addr) + ra->mac_addr = *mac_addr; + else + zero(ra->mac_addr); + + return 0; +} + +_public_ int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) { + assert_return(ra, -EINVAL); + assert_return(mtu >= 1280, -EINVAL); + + if (ra->state != SD_RADV_STATE_IDLE) + return -EBUSY; + + ra->mtu = mtu; + + return 0; +} + +_public_ int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit) { + assert_return(ra, -EINVAL); + + if (ra->state != SD_RADV_STATE_IDLE) + return -EBUSY; + + ra->hop_limit = hop_limit; + + return 0; +} + +_public_ int sd_radv_set_router_lifetime(sd_radv *ra, uint32_t router_lifetime) { + assert_return(ra, -EINVAL); + + if (ra->state != SD_RADV_STATE_IDLE) + return -EBUSY; + + /* RFC 4191, Section 2.2, "...If the Router Lifetime is zero, the + preference value MUST be set to (00) by the sender..." */ + if (router_lifetime == 0 && + (ra->flags & (0x3 << 3)) != (SD_NDISC_PREFERENCE_MEDIUM << 3)) + return -ETIME; + + ra->lifetime = router_lifetime; + + return 0; +} + +_public_ int sd_radv_set_managed_information(sd_radv *ra, int managed) { + assert_return(ra, -EINVAL); + + if (ra->state != SD_RADV_STATE_IDLE) + return -EBUSY; + + SET_FLAG(ra->flags, ND_RA_FLAG_MANAGED, managed); + + return 0; +} + +_public_ int sd_radv_set_other_information(sd_radv *ra, int other) { + assert_return(ra, -EINVAL); + + if (ra->state != SD_RADV_STATE_IDLE) + return -EBUSY; + + SET_FLAG(ra->flags, ND_RA_FLAG_OTHER, other); + + return 0; +} + +_public_ int sd_radv_set_preference(sd_radv *ra, unsigned preference) { + int r = 0; + + assert_return(ra, -EINVAL); + assert_return(IN_SET(preference, + SD_NDISC_PREFERENCE_LOW, + SD_NDISC_PREFERENCE_MEDIUM, + SD_NDISC_PREFERENCE_HIGH), -EINVAL); + + ra->flags = (ra->flags & ~(0x3 << 3)) | (preference << 3); + + return r; +} + +_public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { + sd_radv_prefix *cur; + _cleanup_free_ char *addr_p = NULL; + + assert_return(ra, -EINVAL); + + if (!p) + return -EINVAL; + + LIST_FOREACH(prefix, cur, ra->prefixes) { + int r; + + r = in_addr_prefix_intersect(AF_INET6, + (union in_addr_union*) &cur->opt.in6_addr, + cur->opt.prefixlen, + (union in_addr_union*) &p->opt.in6_addr, + p->opt.prefixlen); + if (r > 0) { + _cleanup_free_ char *addr_cur = NULL; + + (void) in_addr_to_string(AF_INET6, + (union in_addr_union*) &cur->opt.in6_addr, + &addr_cur); + (void) in_addr_to_string(AF_INET6, + (union in_addr_union*) &p->opt.in6_addr, + &addr_p); + + log_radv("IPv6 prefix %s/%u already configured, ignoring %s/%u", + addr_cur, cur->opt.prefixlen, + addr_p, p->opt.prefixlen); + + return -EEXIST; + } + } + + p = sd_radv_prefix_ref(p); + + LIST_APPEND(prefix, ra->prefixes, p); + + ra->n_prefixes++; + + (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &p->opt.in6_addr, &addr_p); + log_radv("Added prefix %s/%d", addr_p, p->opt.prefixlen); + + return 0; +} + +_public_ int sd_radv_prefix_new(sd_radv_prefix **ret) { + _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL; + + assert_return(ret, -EINVAL); + + p = new0(sd_radv_prefix, 1); + if (!p) + return -ENOMEM; + + p->n_ref = 1; + + p->opt.type = ND_OPT_PREFIX_INFORMATION; + p->opt.length = (sizeof(p->opt) - 1) /8 + 1; + + p->opt.prefixlen = 64; + + /* RFC 4861, Section 6.2.1 */ + SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_ONLINK, true); + SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_AUTO, true); + p->opt.preferred_lifetime = htobe32(604800); + p->opt.valid_lifetime = htobe32(2592000); + + LIST_INIT(prefix, p); + + *ret = p; + p = NULL; + + return 0; +} + +_public_ sd_radv_prefix *sd_radv_prefix_ref(sd_radv_prefix *p) { + if (!p) + return NULL; + + assert(p->n_ref > 0); + p->n_ref++; + + return p; +} + +_public_ sd_radv_prefix *sd_radv_prefix_unref(sd_radv_prefix *p) { + if (!p) + return NULL; + + assert(p->n_ref > 0); + p->n_ref--; + + if (p->n_ref > 0) + return NULL; + + return mfree(p); +} + +_public_ int sd_radv_prefix_set_prefix(sd_radv_prefix *p, struct in6_addr *in6_addr, + unsigned char prefixlen) { + assert_return(p, -EINVAL); + assert_return(in6_addr, -EINVAL); + + if (prefixlen < 3 || prefixlen > 128) + return -EINVAL; + + if (prefixlen > 64) + /* unusual but allowed, log it */ + log_radv("Unusual prefix length %d greater than 64", prefixlen); + + p->opt.in6_addr = *in6_addr; + p->opt.prefixlen = prefixlen; + + return 0; +} + +_public_ int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink) { + assert_return(p, -EINVAL); + + SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_ONLINK, onlink); + + return 0; +} + +_public_ int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p, + int address_autoconfiguration) { + assert_return(p, -EINVAL); + + SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_AUTO, address_autoconfiguration); + + return 0; +} + +_public_ int sd_radv_prefix_set_valid_lifetime(sd_radv_prefix *p, + uint32_t valid_lifetime) { + assert_return(p, -EINVAL); + + p->opt.valid_lifetime = htobe32(valid_lifetime); + + return 0; +} + +_public_ int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix *p, + uint32_t preferred_lifetime) { + assert_return(p, -EINVAL); + + p->opt.preferred_lifetime = htobe32(preferred_lifetime); + + return 0; +} diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c index e81c508c7f..26f217835f 100644 --- a/src/libsystemd-network/test-dhcp-server.c +++ b/src/libsystemd-network/test-dhcp-server.c @@ -63,7 +63,7 @@ static int test_basic(sd_event *event) { assert_se(sd_dhcp_server_configure_pool(server, &address_any, 28, 0, 0) == -EINVAL); assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 38, 0, 0) == -ERANGE); assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0); - assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) == -EBUSY); + assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0); test_pool(&address_any, 1, -EINVAL); test_pool(&address_lo, 1, 0); diff --git a/src/libsystemd-network/test-ipv4ll.c b/src/libsystemd-network/test-ipv4ll.c index fe70697075..89864fd39c 100644 --- a/src/libsystemd-network/test-ipv4ll.c +++ b/src/libsystemd-network/test-ipv4ll.c @@ -17,7 +17,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <assert.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> @@ -72,10 +71,10 @@ int arp_send_probe(int fd, int ifindex, be32_t pa, const struct ether_addr *ha) { struct ether_arp ea = {}; - assert(fd >= 0); - assert(ifindex > 0); - assert(pa != 0); - assert(ha); + assert_se(fd >= 0); + assert_se(ifindex > 0); + assert_se(pa != 0); + assert_se(ha); return arp_network_send_raw_socket(fd, ifindex, &ea); } @@ -84,10 +83,10 @@ int arp_send_announcement(int fd, int ifindex, be32_t pa, const struct ether_addr *ha) { struct ether_arp ea = {}; - assert(fd >= 0); - assert(ifindex > 0); - assert(pa != 0); - assert(ha); + assert_se(fd >= 0); + assert_se(ifindex > 0); + assert_se(pa != 0); + assert_se(ha); return arp_network_send_raw_socket(fd, ifindex, &ea); } diff --git a/src/libsystemd-network/test-ndisc-ra.c b/src/libsystemd-network/test-ndisc-ra.c new file mode 100644 index 0000000000..6e6d05642d --- /dev/null +++ b/src/libsystemd-network/test-ndisc-ra.c @@ -0,0 +1,359 @@ +/*** + This file is part of systemd. + + Copyright (C) 2017 Intel Corporation. All rights reserved. + + 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/icmp6.h> +#include <arpa/inet.h> + +#include "sd-radv.h" + +#include "alloc-util.h" +#include "hexdecoct.h" +#include "icmp6-util.h" +#include "socket-util.h" +#include "strv.h" + +static struct ether_addr mac_addr = { + .ether_addr_octet = { 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53 } +}; + +static uint8_t advertisement[] = { + /* ICMPv6 Router Advertisement, no checksum */ + 0x86, 0x00, 0x00, 0x00, 0x40, 0xc0, 0x00, 0xb4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* Source Link Layer Address Option */ + 0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53, + /* Prefix Information Option */ + 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4, + 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* Prefix Information Option */ + 0x03, 0x04, 0x40, 0xc0, 0x00, 0x27, 0x8d, 0x00, + 0x00, 0x09, 0x3a, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* Prefix Information Option */ + 0x03, 0x04, 0x30, 0xc0, 0x00, 0x27, 0x8d, 0x00, + 0x00, 0x09, 0x3a, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x01, 0x0d, 0xad, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* Recursive DNS Server Option - not yet supported */ + 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + /* DNS Search List Option - not yet supported */ + 0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, + 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, + 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static sd_event_source *test_hangcheck; +static bool test_stopped; +static int test_fd[2]; +static sd_event_source *recv_router_advertisement; +static struct { + struct in6_addr address; + unsigned char prefixlen; + uint32_t valid; + uint32_t preferred; + bool succesful; +} prefix[] = { + { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, 64, + 500, 440, true }, + { { { { 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, 64, + /* indicate default valid and preferred lifetimes for the test code */ + 0, 0, true }, + { { { { 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, 58, + 0, 0, + /* indicate that this prefix already exists */ + false }, + { { { { 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, 120, + 0, 0, + /* indicate that this prefix already exists */ + false }, + { { { { 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, 12, + 0, 0, + /* indicate that this prefix already exists */ + false }, + { { { { 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x01, 0x0d, 0xad, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, 48, + 0, 0, true }, + { { { { 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x01, 0x0d, 0xad, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, 60, + 0, 0, + /* indicate that this prefix already exists */ + false }, +}; + +static int test_rs_hangcheck(sd_event_source *s, uint64_t usec, + void *userdata) { + assert_se(false); + + return 0; +} + +static void test_radv_prefix(void) { + sd_radv_prefix *p; + + printf("* %s\n", __FUNCTION__); + + assert_se(sd_radv_prefix_new(&p) >= 0); + + assert_se(sd_radv_prefix_set_onlink(NULL, true) < 0); + assert_se(sd_radv_prefix_set_onlink(p, true) >= 0); + assert_se(sd_radv_prefix_set_onlink(p, false) >= 0); + + assert_se(sd_radv_prefix_set_address_autoconfiguration(NULL, true) < 0); + assert_se(sd_radv_prefix_set_address_autoconfiguration(p, true) >= 0); + assert_se(sd_radv_prefix_set_address_autoconfiguration(p, false) >= 0); + + assert_se(sd_radv_prefix_set_valid_lifetime(NULL, true) < 0); + assert_se(sd_radv_prefix_set_valid_lifetime(p, ~0) >= 0); + assert_se(sd_radv_prefix_set_valid_lifetime(p, 42) >= 0); + assert_se(sd_radv_prefix_set_valid_lifetime(p, 0) >= 0); + + assert_se(sd_radv_prefix_set_preferred_lifetime(NULL, true) < 0); + assert_se(sd_radv_prefix_set_preferred_lifetime(p, ~0) >= 0); + assert_se(sd_radv_prefix_set_preferred_lifetime(p, 42) >= 0); + assert_se(sd_radv_prefix_set_preferred_lifetime(p, 0) >= 0); + + assert_se(sd_radv_prefix_set_prefix(NULL, NULL, 0) < 0); + assert_se(sd_radv_prefix_set_prefix(p, NULL, 0) < 0); + + assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 64) >= 0); + assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 0) < 0); + assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 1) < 0); + assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 2) < 0); + assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 3) >= 0); + assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 125) >= 0); + assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 128) >= 0); + assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 129) < 0); + assert_se(sd_radv_prefix_set_prefix(p, &prefix[0].address, 255) < 0); + + p = sd_radv_prefix_unref(p); + assert_se(!p); +} + +static void test_radv(void) { + sd_radv *ra; + + printf("* %s\n", __FUNCTION__); + + assert_se(sd_radv_new(&ra) >= 0); + assert_se(ra); + + assert_se(sd_radv_set_ifindex(NULL, 0) < 0); + assert_se(sd_radv_set_ifindex(ra, 0) >= 0); + assert_se(sd_radv_set_ifindex(ra, -1) >= 0); + assert_se(sd_radv_set_ifindex(ra, -2) < 0); + assert_se(sd_radv_set_ifindex(ra, 42) >= 0); + + assert_se(sd_radv_set_mac(NULL, NULL) < 0); + assert_se(sd_radv_set_mac(ra, NULL) >= 0); + assert_se(sd_radv_set_mac(ra, &mac_addr) >= 0); + + assert_se(sd_radv_set_mtu(NULL, 0) < 0); + assert_se(sd_radv_set_mtu(ra, 0) < 0); + assert_se(sd_radv_set_mtu(ra, 1279) < 0); + assert_se(sd_radv_set_mtu(ra, 1280) >= 0); + assert_se(sd_radv_set_mtu(ra, ~0) >= 0); + + assert_se(sd_radv_set_hop_limit(NULL, 0) < 0); + assert_se(sd_radv_set_hop_limit(ra, 0) >= 0); + assert_se(sd_radv_set_hop_limit(ra, ~0) >= 0); + + assert_se(sd_radv_set_router_lifetime(NULL, 0) < 0); + assert_se(sd_radv_set_router_lifetime(ra, 0) >= 0); + assert_se(sd_radv_set_router_lifetime(ra, ~0) >= 0); + + assert_se(sd_radv_set_preference(NULL, 0) < 0); + assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_LOW) >= 0); + assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_MEDIUM) >= 0); + assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_HIGH) >= 0); + assert_se(sd_radv_set_preference(ra, ~0) < 0); + + assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_HIGH) >= 0); + assert_se(sd_radv_set_router_lifetime(ra, 42000) >= 0); + assert_se(sd_radv_set_router_lifetime(ra, 0) < 0); + assert_se(sd_radv_set_preference(ra, SD_NDISC_PREFERENCE_MEDIUM) >= 0); + assert_se(sd_radv_set_router_lifetime(ra, 0) >= 0); + + assert_se(sd_radv_set_managed_information(NULL, true) < 0); + assert_se(sd_radv_set_managed_information(ra, true) >= 0); + assert_se(sd_radv_set_managed_information(ra, false) >= 0); + + assert_se(sd_radv_set_other_information(NULL, true) < 0); + assert_se(sd_radv_set_other_information(ra, true) >= 0); + assert_se(sd_radv_set_other_information(ra, false) >= 0); + + ra = sd_radv_unref(ra); + assert_se(!ra); +} + +int icmp6_bind_router_solicitation(int index) { + return -ENOSYS; +} + +int icmp6_bind_router_advertisement(int index) { + assert_se(index == 42); + + return test_fd[1]; +} + +int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) { + + return 0; +} + +int icmp6_receive(int fd, void *iov_base, size_t iov_len, + struct in6_addr *dst, triple_timestamp *timestamp) { + assert_se(read (fd, iov_base, iov_len) == (ssize_t)iov_len); + + if (timestamp) + triple_timestamp_get(timestamp); + + return 0; +} + +static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + sd_radv *ra = userdata; + unsigned char buf[120]; + size_t i; + + read(test_fd[0], &buf, sizeof(buf)); + + /* router lifetime must be zero when test is stopped */ + if (test_stopped) { + advertisement[6] = 0x00; + advertisement[7] = 0x00; + } + + printf ("Received Router Advertisement with lifetime %u\n", + (advertisement[6] << 8) + advertisement[7]); + + /* test only up to buf size, rest is not yet implemented */ + for (i = 0; i < sizeof(buf); i++) { + printf("0x%02x", buf[i]); + + assert_se(buf[i] == advertisement[i]); + + if ((i + 1) % 8) + printf(", "); + else + printf("\n"); + } + + if (test_stopped) { + sd_event *e; + + e = sd_radv_get_event(ra); + sd_event_exit(e, 0); + + return 0; + } + + assert_se(sd_radv_stop(ra) >= 0); + test_stopped = true; + + return 0; +} + +static void test_ra(void) { + sd_event *e; + sd_radv *ra; + usec_t time_now = now(clock_boottime_or_monotonic()); + unsigned int i; + + printf("* %s\n", __FUNCTION__); + + assert_se(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, test_fd) >= 0); + + assert_se(sd_event_new(&e) >= 0); + + assert_se(sd_radv_new(&ra) >= 0); + assert_se(ra); + + assert_se(sd_radv_attach_event(ra, e, 0) >= 0); + + assert_se(sd_radv_set_ifindex(ra, 42) >= 0); + assert_se(sd_radv_set_mac(ra, &mac_addr) >= 0); + assert_se(sd_radv_set_router_lifetime(ra, 180) >= 0); + assert_se(sd_radv_set_hop_limit(ra, 64) >= 0); + assert_se(sd_radv_set_managed_information(ra, true) >= 0); + assert_se(sd_radv_set_other_information(ra, true) >= 0); + + for (i = 0; i < ELEMENTSOF(prefix); i++) { + sd_radv_prefix *p; + + printf("Test prefix %u\n", i); + assert_se(sd_radv_prefix_new(&p) >= 0); + + assert_se(sd_radv_prefix_set_prefix(p, &prefix[i].address, + prefix[i].prefixlen) >= 0); + if (prefix[i].valid) + assert_se(sd_radv_prefix_set_valid_lifetime(p, prefix[i].valid) >= 0); + if (prefix[i].preferred) + assert_se(sd_radv_prefix_set_preferred_lifetime(p, prefix[i].preferred) >= 0); + + assert_se((sd_radv_add_prefix(ra, p) >= 0) == prefix[i].succesful); + assert_se(sd_radv_add_prefix(ra, p) < 0); + + p = sd_radv_prefix_unref(p); + assert_se(!p); + } + + assert_se(sd_event_add_io(e, &recv_router_advertisement, test_fd[0], + EPOLLIN, radv_recv, ra) >= 0); + + assert_se(sd_event_add_time(e, &test_hangcheck, clock_boottime_or_monotonic(), + time_now + 2 *USEC_PER_SEC, 0, + test_rs_hangcheck, NULL) >= 0); + + assert_se(sd_radv_start(ra) >= 0); + + sd_event_loop(e); + + test_hangcheck = sd_event_source_unref(test_hangcheck); + + ra = sd_radv_unref(ra); + assert_se(!ra); + + close(test_fd[0]); + + sd_event_unref(e); +} + +int main(int argc, char *argv[]) { + + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + test_radv_prefix(); + test_radv(); + test_ra(); + + printf("* done\n"); + return 0; +} diff --git a/src/libsystemd-network/test-ndisc-rs.c b/src/libsystemd-network/test-ndisc-rs.c index d9669488be..c3e1a8e340 100644 --- a/src/libsystemd-network/test-ndisc-rs.c +++ b/src/libsystemd-network/test-ndisc-rs.c @@ -27,6 +27,7 @@ #include "icmp6-util.h" #include "socket-util.h" #include "strv.h" +#include "ndisc-internal.h" static struct ether_addr mac_addr = { .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'} @@ -35,6 +36,7 @@ static struct ether_addr mac_addr = { static bool verbose = false; static sd_event_source *test_hangcheck; static int test_fd[2]; +static sd_ndisc *test_timeout_nd; typedef int (*send_ra_t)(uint8_t flags); static send_ra_t send_ra_function; @@ -193,6 +195,21 @@ int icmp6_bind_router_solicitation(int index) { return test_fd[0]; } +int icmp6_bind_router_advertisement(int index) { + + return -ENOSYS; +} + +int icmp6_receive(int fd, void *iov_base, size_t iov_len, + struct in6_addr *dst, triple_timestamp *timestamp) { + assert (read (fd, iov_base, iov_len) == (ssize_t)iov_len); + + if (timestamp) + triple_timestamp_get(timestamp); + + return 0; +} + static int send_ra(uint8_t flags) { uint8_t advertisement[] = { 0x86, 0x00, 0xde, 0x83, 0x40, 0xc0, 0x00, 0xb4, @@ -222,6 +239,9 @@ static int send_ra(uint8_t flags) { } int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) { + if (!send_ra_function) + return 0; + return send_ra_function(0); } @@ -305,6 +325,100 @@ static void test_rs(void) { sd_event_unref(e); } +static int test_timeout_value(uint8_t flags) { + static int count = 0; + static usec_t last = 0; + sd_ndisc *nd = test_timeout_nd; + usec_t min, max; + char time_string_min[FORMAT_TIMESPAN_MAX]; + char time_string_nd[FORMAT_TIMESPAN_MAX]; + char time_string_max[FORMAT_TIMESPAN_MAX]; + + assert_se(nd); + assert_se(nd->event); + + if (++count >= 20) + sd_event_exit(nd->event, 0); + + if (last == 0) { + /* initial RT = IRT + RAND*IRT */ + min = NDISC_ROUTER_SOLICITATION_INTERVAL - + NDISC_ROUTER_SOLICITATION_INTERVAL / 10; + max = NDISC_ROUTER_SOLICITATION_INTERVAL + + NDISC_ROUTER_SOLICITATION_INTERVAL / 10; + } else { + /* next RT = 2*RTprev + RAND*RTprev */ + min = 2 * last - last / 10; + max = 2 * last + last / 10; + } + + /* final RT > MRT */ + if (last * 2 > NDISC_MAX_ROUTER_SOLICITATION_INTERVAL) { + min = NDISC_MAX_ROUTER_SOLICITATION_INTERVAL - + NDISC_MAX_ROUTER_SOLICITATION_INTERVAL / 10; + max = NDISC_MAX_ROUTER_SOLICITATION_INTERVAL + + NDISC_MAX_ROUTER_SOLICITATION_INTERVAL / 10; + } + + format_timespan(time_string_min, FORMAT_TIMESPAN_MAX, + min, USEC_PER_MSEC); + format_timespan(time_string_nd, FORMAT_TIMESPAN_MAX, + nd->retransmit_time, USEC_PER_MSEC); + format_timespan(time_string_max, FORMAT_TIMESPAN_MAX, + max, USEC_PER_MSEC); + + log_info("backoff timeout interval %2d %s%s <= %s <= %s", + count, + (last * 2 > NDISC_MAX_ROUTER_SOLICITATION_INTERVAL)? "(max) ": "", + time_string_min, time_string_nd, time_string_max); + + assert_se(min <= nd->retransmit_time); + assert_se(max >= nd->retransmit_time); + + last = nd->retransmit_time; + + assert_se(sd_event_source_set_time(nd->timeout_event_source, 0) >= 0); + + return 0; +} + +static void test_timeout(void) { + sd_event *e; + sd_ndisc *nd; + usec_t time_now = now(clock_boottime_or_monotonic()); + + if (verbose) + printf("* %s\n", __FUNCTION__); + + send_ra_function = test_timeout_value; + + assert_se(sd_event_new(&e) >= 0); + + assert_se(sd_ndisc_new(&nd) >= 0); + assert_se(nd); + + test_timeout_nd = nd; + + assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0); + + assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0); + assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0); + + assert_se(sd_event_add_time(e, &test_hangcheck, clock_boottime_or_monotonic(), + time_now + 2U * USEC_PER_SEC, 0, + test_rs_hangcheck, NULL) >= 0); + + assert_se(sd_ndisc_start(nd) >= 0); + + sd_event_loop(e); + + test_hangcheck = sd_event_source_unref(test_hangcheck); + + nd = sd_ndisc_unref(nd); + + sd_event_unref(e); +} + int main(int argc, char *argv[]) { log_set_max_level(LOG_DEBUG); @@ -312,6 +426,7 @@ int main(int argc, char *argv[]) { log_open(); test_rs(); + test_timeout(); return 0; } diff --git a/src/libsystemd-network/test-sd-dhcp-lease.c b/src/libsystemd-network/test-sd-dhcp-lease.c new file mode 100644 index 0000000000..0f881809ab --- /dev/null +++ b/src/libsystemd-network/test-sd-dhcp-lease.c @@ -0,0 +1,90 @@ +#include <errno.h> + +#include "dhcp-lease-internal.h" +#include "macro.h" +#include "string-util.h" +#include "strv.h" + +/* According to RFC1035 section 4.1.4, a domain name in a message can be either: + * - a sequence of labels ending in a zero octet + * - a pointer + * - a sequence of labels ending with a pointer + */ +static void test_dhcp_lease_parse_search_domains_basic(void) { + int r; + _cleanup_strv_free_ char **domains = NULL; + static const uint8_t optionbuf[] = { + 0x03, 'F', 'O', 'O', 0x03, 'B', 'A', 'R', 0x00, + 0x04, 'A', 'B', 'C', 'D', 0x03, 'E', 'F', 'G', 0x00, + }; + + r = dhcp_lease_parse_search_domains(optionbuf, sizeof(optionbuf), &domains); + assert_se(r == 2); + assert_se(streq(domains[0], "FOO.BAR")); + assert_se(streq(domains[1], "ABCD.EFG")); +} + +static void test_dhcp_lease_parse_search_domains_ptr(void) { + int r; + _cleanup_strv_free_ char **domains = NULL; + static const uint8_t optionbuf[] = { + 0x03, 'F', 'O', 'O', 0x00, 0xC0, 0x00, + }; + + r = dhcp_lease_parse_search_domains(optionbuf, sizeof(optionbuf), &domains); + assert_se(r == 2); + assert_se(streq(domains[0], "FOO")); + assert_se(streq(domains[1], "FOO")); +} + +static void test_dhcp_lease_parse_search_domains_labels_and_ptr(void) { + int r; + _cleanup_strv_free_ char **domains = NULL; + static const uint8_t optionbuf[] = { + 0x03, 'F', 'O', 'O', 0x03, 'B', 'A', 'R', 0x00, + 0x03, 'A', 'B', 'C', 0xC0, 0x04, + }; + + r = dhcp_lease_parse_search_domains(optionbuf, sizeof(optionbuf), &domains); + assert_se(r == 2); + assert_se(streq(domains[0], "FOO.BAR")); + assert_se(streq(domains[1], "ABC.BAR")); +} + +/* Tests for exceptions. */ + +static void test_dhcp_lease_parse_search_domains_no_data(void) { + _cleanup_strv_free_ char **domains = NULL; + static const uint8_t optionbuf[3] = {0, 0, 0}; + + assert_se(dhcp_lease_parse_search_domains(NULL, 0, &domains) == -ENODATA); + assert_se(dhcp_lease_parse_search_domains(optionbuf, 0, &domains) == -ENODATA); +} + +static void test_dhcp_lease_parse_search_domains_loops(void) { + _cleanup_strv_free_ char **domains = NULL; + static const uint8_t optionbuf[] = { + 0x03, 'F', 'O', 'O', 0x00, 0x03, 'B', 'A', 'R', 0xC0, 0x06, + }; + + assert_se(dhcp_lease_parse_search_domains(optionbuf, sizeof(optionbuf), &domains) == -EBADMSG); +} + +static void test_dhcp_lease_parse_search_domains_wrong_len(void) { + _cleanup_strv_free_ char **domains = NULL; + static const uint8_t optionbuf[] = { + 0x03, 'F', 'O', 'O', 0x03, 'B', 'A', 'R', 0x00, + 0x04, 'A', 'B', 'C', 'D', 0x03, 'E', 'F', 'G', 0x00, + }; + + assert_se(dhcp_lease_parse_search_domains(optionbuf, sizeof(optionbuf) - 5, &domains) == -EBADMSG); +} + +int main(int argc, char *argv[]) { + test_dhcp_lease_parse_search_domains_basic(); + test_dhcp_lease_parse_search_domains_ptr(); + test_dhcp_lease_parse_search_domains_labels_and_ptr(); + test_dhcp_lease_parse_search_domains_no_data(); + test_dhcp_lease_parse_search_domains_loops(); + test_dhcp_lease_parse_search_domains_wrong_len(); +} diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index c1135ffa22..92cb790d49 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -517,3 +517,8 @@ global: sd_id128_get_machine_app_specific; sd_is_socket_sockaddr; } LIBSYSTEMD_232; + +LIBSYSTEMD_234 { +global: + sd_bus_message_appendv; +} LIBSYSTEMD_233; diff --git a/src/libsystemd/meson.build b/src/libsystemd/meson.build new file mode 100644 index 0000000000..ab69afee42 --- /dev/null +++ b/src/libsystemd/meson.build @@ -0,0 +1,96 @@ +sd_login_c = files('sd-login/sd-login.c') + +libsystemd_internal_sources = files(''' + sd-bus/bus-bloom.c + sd-bus/bus-bloom.h + sd-bus/bus-common-errors.c + sd-bus/bus-common-errors.h + sd-bus/bus-container.c + sd-bus/bus-container.h + sd-bus/bus-control.c + sd-bus/bus-control.h + sd-bus/bus-convenience.c + sd-bus/bus-creds.c + sd-bus/bus-creds.h + sd-bus/bus-dump.c + sd-bus/bus-dump.h + sd-bus/bus-error.c + sd-bus/bus-error.h + sd-bus/bus-gvariant.c + sd-bus/bus-gvariant.h + sd-bus/bus-internal.c + sd-bus/bus-internal.h + sd-bus/bus-introspect.c + sd-bus/bus-introspect.h + sd-bus/bus-kernel.c + sd-bus/bus-kernel.h + sd-bus/bus-match.c + sd-bus/bus-match.h + sd-bus/bus-message.c + sd-bus/bus-message.h + sd-bus/bus-objects.c + sd-bus/bus-objects.h + sd-bus/bus-protocol.h + sd-bus/bus-signature.c + sd-bus/bus-signature.h + sd-bus/bus-slot.c + sd-bus/bus-slot.h + sd-bus/bus-socket.c + sd-bus/bus-socket.h + sd-bus/bus-track.c + sd-bus/bus-track.h + sd-bus/bus-type.c + sd-bus/bus-type.h + sd-bus/kdbus.h + sd-bus/sd-bus.c + sd-daemon/sd-daemon.c + sd-device/device-enumerator-private.h + sd-device/device-enumerator.c + sd-device/device-internal.h + sd-device/device-private.c + sd-device/device-private.h + sd-device/device-util.h + sd-device/sd-device.c + sd-event/sd-event.c + sd-hwdb/hwdb-internal.h + sd-hwdb/hwdb-util.h + sd-hwdb/sd-hwdb.c + sd-id128/id128-util.c + sd-id128/id128-util.h + sd-id128/sd-id128.c + sd-netlink/local-addresses.c + sd-netlink/local-addresses.h + sd-netlink/netlink-internal.h + sd-netlink/netlink-message.c + sd-netlink/netlink-socket.c + sd-netlink/netlink-types.c + sd-netlink/netlink-types.h + sd-netlink/netlink-util.c + sd-netlink/netlink-util.h + sd-netlink/rtnl-message.c + sd-netlink/sd-netlink.c + sd-network/network-util.c + sd-network/network-util.h + sd-network/sd-network.c + sd-path/sd-path.c + sd-resolve/sd-resolve.c + sd-utf8/sd-utf8.c +'''.split()) + sd_login_c + +libsystemd_internal = static_library( + 'systemd', + libsystemd_internal_sources, + install : false, + include_directories : includes, + link_with : libbasic, + dependencies : [threads, + librt]) + +libsystemd_sym = 'src/libsystemd/libsystemd.sym' + +libsystemd_pc = configure_file( + input : 'libsystemd.pc.in', + output : 'libsystemd.pc', + configuration : substs) +install_data(libsystemd_pc, + install_dir : pkgconfiglibdir) diff --git a/src/libsystemd/sd-bus/DIFFERENCES b/src/libsystemd/sd-bus/DIFFERENCES deleted file mode 100644 index db269675a7..0000000000 --- a/src/libsystemd/sd-bus/DIFFERENCES +++ /dev/null @@ -1,25 +0,0 @@ -Known differences between dbus1 and kdbus: - -- NameAcquired/NameLost is gone entirely on kdbus backends if - libsystemd is used. It is still added in by systemd-bus-proxyd - for old dbus1 clients, and it is available if libsystemd is used - against the classic dbus1 daemon. If you want to write compatible - code with libsystem-bus you need to explicitly subscribe to - NameOwnerChanged signals and just ignore NameAcquired/NameLost - -- Applications have to deal with spurious signals they didn't expect, - due to the probabilistic bloom filters. They need to handle this - anyway, given that any client can send anything to arbitrary clients - anyway, even in dbus1, so not much changes. - -- clients of the system bus when kdbus is used must roll their own - security. Only legacy dbus1 clients get the old XML policy enforced, - which is implemented by systemd-bus-proxyd. - -- Serial numbers of synthesized messages are always (uint32_t) -1. - -- NameOwnerChanged is a synthetic message, generated locally and not - by the driver. On dbus1 only the Disconnected message was - synthesized like this. - -- There's no standard per-session bus anymore. Only a per-user bus. diff --git a/src/libsystemd/sd-bus/GVARIANT-SERIALIZATION b/src/libsystemd/sd-bus/GVARIANT-SERIALIZATION index 6aeb11364a..3110d57913 100644 --- a/src/libsystemd/sd-bus/GVARIANT-SERIALIZATION +++ b/src/libsystemd/sd-bus/GVARIANT-SERIALIZATION @@ -73,10 +73,9 @@ the reply_cookie/reply_serial additional header field has been increased from 32bit to 64bit, too! The header field identifiers have been extended from 8bit to -64bit. This has been done to simplify things (as kdbus otherwise uses -exclusively 64bit types, unless there is a strong reason not to), and -has no effect on the serialization size, as due to alignment for each -8bit header field identifier 56 bits of padding had to be added. +64bit. This has been done to simplify things, and has no effect +on the serialization size, as due to alignment for each 8bit +header field identifier 56 bits of padding had to be added. Note that the header size changed, due to these changes. However, consider that on dbus1 the beginning of the fields array contains the @@ -94,16 +93,12 @@ array, the size of the header on dbus1 and dbus2 stays identical, at And that's already it. -Note: to simplify parsing, valid kdbus/dbus2 messages must include the -entire fixed header and additional header fields in a single non-memfd -message part. Also, the signature string of the body variant all the -way to the end of the message must be in a single non-memfd part -too. The parts for this extended header and footer can be the same -one, and can also continue any amount of additional body bytes. - -Note: on kdbus only native endian messages marshalled in gvariant may - be sent. If a client receives a message in non-native endianness - or in dbus1 marshalling it shall ignore the message. +Note: To simplify parsing, valid dbus2 messages must include the entire + fixed header and additional header fields in a single non-memfd + message part. Also, the signature string of the body variant all the + way to the end of the message must be in a single non-memfd part + too. The parts for this extended header and footer can be the same + one, and can also continue any amount of additional body bytes. Note: The GVariant "MAYBE" type is not supported, so that messages can be fully converted forth and back between dbus1 and gvariant diff --git a/src/libsystemd/sd-bus/PORTING-DBUS1 b/src/libsystemd/sd-bus/PORTING-DBUS1 deleted file mode 100644 index 2dedb28bcf..0000000000 --- a/src/libsystemd/sd-bus/PORTING-DBUS1 +++ /dev/null @@ -1,535 +0,0 @@ -A few hints on supporting kdbus as backend in your favorite D-Bus library. - -~~~ - -Before you read this, have a look at the DIFFERENCES and -GVARIANT_SERIALIZATION texts you find in the same directory where you -found this. - -We invite you to port your favorite D-Bus protocol implementation -over to kdbus. However, there are a couple of complexities -involved. On kdbus we only speak GVariant marshaling, kdbus clients -ignore traffic in dbus1 marshaling. Thus, you need to add a second, -GVariant compatible marshaler to your library first. - -After you have done that: here's the basic principle how kdbus works: - -You connect to a bus by opening its bus node in /sys/fs/kdbus/. All -buses have a device node there, it starts with a numeric UID of the -owner of the bus, followed by a dash and a string identifying the -bus. The system bus is thus called /sys/fs/kdbus/0-system, and for user -buses the device node is /sys/fs/kdbus/1000-user (if 1000 is your user -id). - -(Before we proceed, please always keep a copy of libsystemd next -to you, ultimately that's where the details are, this document simply -is a rough overview to help you grok things.) - -CONNECTING - -To connect to a bus, simply open() its device node and issue the -KDBUS_CMD_HELLO call. That's it. Now you are connected. Do not send -Hello messages or so (as you would on dbus1), that does not exist for -kdbus. - -The structure you pass to the ioctl will contain a couple of -parameters that you need to know, to operate on the bus. - -There are two flags fields, one indicating features of the kdbus -kernel side ("conn_flags"), the other one ("bus_flags") indicating -features of the bus owner (i.e. systemd). Both flags fields are 64bit -in width. - -When calling into the ioctl, you need to place your own supported -feature bits into these fields. This tells the kernel about the -features you support. When the ioctl returns, it will contain the -features the kernel supports. - -If any of the higher 32bit are set on the two flags fields and your -client does not know what they mean, it must disconnect. The upper -32bit are used to indicate "incompatible" feature additions on the bus -system, the lower 32bit indicate "compatible" feature additions. A -client that does not support a "compatible" feature addition can go on -communicating with the bus, however a client that does not support an -"incompatible" feature must not proceed with the connection. When a -client encountes such an "incompatible" feature it should immediately -try the next bus address configured in the bus address string. - -The hello structure also contains another flags field "attach_flags" -which indicates metadata that is optionally attached to all incoming -messages. You probably want to set KDBUS_ATTACH_NAMES unconditionally -in it. This has the effect that all well-known names of a sender are -attached to all incoming messages. You need this information to -implement matches that match on a message sender name correctly. Of -course, you should only request the attachment of as little metadata -fields as you need. - -The kernel will return in the "id" field your unique id. This is a -simple numeric value. For compatibility with classic dbus1 simply -format this as string and prefix ":1.". - -The kernel will also return the bloom filter size and bloom filter -hash function number used for the signal broadcast bloom filter (see -below). - -The kernel will also return the bus ID of the bus in a 128bit field. - -The pool size field specifies the size of the memory mapped buffer. -After the calling the hello ioctl, you should memory map the kdbus -fd. In this memory mapped region, the kernel will place all your incoming -messages. - -SENDING MESSAGES - -Use the MSG_SEND ioctl to send a message to another peer. The ioctl -takes a structure that contains a variety of fields: - -The flags field corresponds closely to the old dbus1 message header -flags field, though the DONT_EXPECT_REPLY field got inverted into -EXPECT_REPLY. - -The dst_id/src_id field contains the unique id of the destination and -the sender. The sender field is overridden by the kernel usually, hence -you shouldn't fill it in. The destination field can also take the -special value KDBUS_DST_ID_BROADCAST for broadcast messages. For -messages intended to a well-known name set the field to -KDBUS_DST_ID_NAME, and attach the name in a special "items" entry to -the message (see below). - -The payload field indicates the payload. For all dbus traffic it -should carry the value 0x4442757344427573ULL. (Which encodes -'DBusDBus'). - -The cookie field corresponds with the "serial" field of classic -dbus1. We simply renamed it here (and extended it to 64bit) since we -didn't want to imply the monotonicity of the assignment the way the -word "serial" indicates it. - -When sending a message that expects a reply, you need to set the -EXPECT_REPLY flag in the message flag field. In this case you should -also fill out the "timeout_ns" value which indicates the timeout in -nsec for this call. If the peer does not respond in this time you will -get a notification of a timeout. Note that this is also used for -security purposes: a single reply messages is only allowed through the -bus as long as the timeout has not ended. With this timeout value you -hence "open a time window" in which the peer might respond to your -request and the policy allows the response to go through. - -When sending a message that is a reply, you need to fill in the -cookie_reply field, which is similar to the reply_serial field of -dbus1. Note that a message cannot have EXPECT_REPLY and a reply_serial -at the same time! - -This pretty much explains the ioctl header. The actual payload of the -data is now referenced in additional items that are attached to this -ioctl header structure at the end. When sending a message, you attach -items of the type PAYLOAD_VEC, PAYLOAD_MEMFD, FDS, BLOOM_FILTER, -DST_NAME to it: - - KDBUS_ITEM_PAYLOAD_VEC: contains a pointer + length pair for - referencing arbitrary user memory. This is how you reference most - of your data. It's a lot like the good old iovec structure of glibc. - - KDBUS_ITEM_PAYLOAD_MEMFD: for large data blocks it is preferable - to send prepared "memfds" (see below) over. This item contains an - fd for a memfd plus a size. - - KDBUS_ITEM_FDS: for sending over fds attach an item of this type with - an array of fds. - - KDBUS_ITEM_BLOOM_FILTER: the calculated bloom filter of this message, - only for undirected (broadcast) message. - - KDBUS_ITEM_DST_NAME: for messages that are directed to a well-known - name (instead of a unique name), this item contains the well-known - name field. - -A single message may consists of no, one or more payload items of type -PAYLOAD_VEC or PAYLOAD_MEMFD. D-Bus protocol implementations should -treat them as a single block that just happens to be split up into -multiple items. Some restrictions apply however: - - The message header in its entirety must be contained in a single - PAYLOAD_VEC item. - - You may only split your message up right in front of each GVariant - contained in the payload, as well is immediately before framing of a - Gvariant, as well after as any padding bytes if there are any. The - padding bytes must be wholly contained in the preceding - PAYLOAD_VEC/PAYLOAD_MEMFD item. You may not split up basic types - nor arrays of fixed types. The latter is necessary to allow APIs - to return direct pointers to linear arrays of numeric - values. Examples: The basic types "u", "s", "t" have to be in the - same payload item. The array of fixed types "ay", "ai" have to be - fully in contained in the same payload item. For an array "as" or - "a(si)" the only restriction however is to keep each string - individually in an uninterrupted item, to keep the framing of each - element and the array in a single uninterrupted item, however the - various strings might end up in different items. - -Note again, that splitting up messages into separate items is up to the -implementation. Also note that the kdbus kernel side might merge -separate items if it deems this to be useful. However, the order in -which items are contained in the message is left untouched. - -PAYLOAD_MEMFD items allow zero-copy data transfer (see below regarding -the memfd concept). Note however that the overhead of mapping these -makes them relatively expensive, and only worth the trouble for memory -blocks > 512K (this value appears to be quite universal across -architectures, as we tested). Thus we recommend sending PAYLOAD_VEC -items over for small messages and restore to PAYLOAD_MEMFD items for -messages > 512K. Since while building up the message you might not -know yet whether it will grow beyond this boundary a good approach is -to simply build the message unconditionally in a memfd -object. However, when the message is sealed to be sent away check for -the size limit. If the size of the message is < 512K, then simply send -the data as PAYLOAD_VEC and reuse the memfd. If it is >= 512K, seal -the memfd and send it as PAYLOAD_MEMFD, and allocate a new memfd for -the next message. - -RECEIVING MESSAGES - -Use the MSG_RECV ioctl to read a message from kdbus. This will return -an offset into the pool memory map, relative to its beginning. - -The received message structure more or less follows the structure of -the message originally sent. However, certain changes have been -made. In the header the src_id field will be filled in. - -The payload items might have gotten merged and PAYLOAD_VEC items are -not used. Instead, you will only find PAYLOAD_OFF and PAYLOAD_MEMFD -items. The former contain an offset and size into your memory mapped -pool where you find the payload. - -If during the HELLO ioctl you asked for getting metadata attached to -your message, you will find additional KDBUS_ITEM_CREDS, -KDBUS_ITEM_PID_COMM, KDBUS_ITEM_TID_COMM, KDBUS_ITEM_TIMESTAMP, -KDBUS_ITEM_EXE, KDBUS_ITEM_CMDLINE, KDBUS_ITEM_CGROUP, -KDBUS_ITEM_CAPS, KDBUS_ITEM_SECLABEL, KDBUS_ITEM_AUDIT items that -contain this metadata. This metadata will be gathered from the sender -at the point in time it sends the message. This information is -uncached, and since it is appended by the kernel, trustable. The -KDBUS_ITEM_SECLABEL item usually contains the SELinux security label, -if it is used. - -After processing the message you need to call the KDBUS_CMD_FREE -ioctl, which releases the message from the pool, and allows the kernel -to store another message there. Note that the memory used by the pool -is ordinary anonymous, swappable memory that is backed by tmpfs. Hence -there is no need to copy the message out of it quickly, instead you -can just leave it there as long as you need it and release it via the -FREE ioctl only after that's done. - -BLOOM FILTERS - -The kernel does not understand dbus marshaling, it will not look into -the message payload. To allow clients to subscribe to specific subsets -of the broadcast matches we employ bloom filters. - -When broadcasting messages, a bloom filter needs to be attached to the -message in a KDBUS_ITEM_BLOOM item (and only for broadcasting -messages!). If you don't know what bloom filters are, read up now on -Wikipedia. In short: they are a very efficient way how to -probabilistically check whether a certain word is contained in a -vocabulary. It knows no false negatives, but it does know false -positives. - -The parameters for the bloom filters that need to be included in -broadcast message is communicated to userspace as part of the hello -response structure (see above). By default it has the parameters m=512 -(bits in the filter), k=8 (nr of hash functions). Note however, that -this is subject to change in later versions, and userspace -implementations must be capable of handling m values between at least -m=8 and m=2^32, and k values between at least k=1 and k=32. The -underlying hash function is SipHash-2-4. It is used with a number of -constant (yet originally randomly generated) 128bit hash keys, more -specifically: - - b9,66,0b,f0,46,70,47,c1,88,75,c4,9c,54,b9,bd,15, - aa,a1,54,a2,e0,71,4b,39,bf,e1,dd,2e,9f,c5,4a,3b, - 63,fd,ae,be,cd,82,48,12,a1,6e,41,26,cb,fa,a0,c8, - 23,be,45,29,32,d2,46,2d,82,03,52,28,fe,37,17,f5, - 56,3b,bf,ee,5a,4f,43,39,af,aa,94,08,df,f0,fc,10, - 31,80,c8,73,c7,ea,46,d3,aa,25,75,0f,9e,4c,09,29, - 7d,f7,18,4b,7b,a4,44,d5,85,3c,06,e0,65,53,96,6d, - f2,77,e9,6f,93,b5,4e,71,9a,0c,34,88,39,25,bf,35 - -When calculating the first bit index into the bloom filter, the -SipHash-2-4 hash value is calculated for the input data and the first -16 bytes of the array above as hash key. Of the resulting 8 bytes of -output, as many full bytes are taken for the bit index as necessary, -starting from the output's first byte. For the second bit index the -same hash value is used, continuing with the next unused output byte, -and so on. Each time the bytes returned by the hash function are -depleted it is recalculated with the next 16 byte hash key from the -array above and the same input data. - -For each message to send across the bus we populate the bloom filter -with all possible matchable strings. If a client then wants to -subscribe to messages of this type, it simply tells the kernel to test -its own calculated bit mask against the bloom filter of each message. - -More specifically, the following strings are added to the bloom filter -of each message that is broadcasted: - - The string "interface:" suffixed by the interface name - - The string "member:" suffixed by the member name - - The string "path:" suffixed by the path name - - The string "path-slash-prefix:" suffixed with the path name, and - also all prefixes of the path name (cut off at "/"), also prefixed - with "path-slash-prefix". - - The string "message-type:" suffixed with the strings "signal", - "method_call", "error" or "method_return" for the respective message - type of the message. - - If the first argument of the message is a string, "arg0:" suffixed - with the first argument. - - If the first argument of the message is a string, "arg0-dot-prefix" - suffixed with the first argument, and also all prefixes of the - argument (cut off at "."), also prefixed with "arg0-dot-prefix". - - If the first argument of the message is a string, - "arg0-slash-prefix" suffixed with the first argument, and also all - prefixes of the argument (cut off at "/"), also prefixed with - "arg0-slash-prefix". - - Similar for all further arguments that are strings up to 63, for the - arguments and their "dot" and "slash" prefixes. On the first - argument that is not a string, addition to the bloom filter should be - stopped however. - -(Note that the bloom filter does not contain sender nor receiver -names!) - -When a client wants to subscribe to messages matching a certain -expression, it should calculate the bloom mask following the same -algorithm. The kernel will then simply test the mask against the -attached bloom filters. - -Note that bloom filters are probabilistic, which means that clients -might get messages they did not expect. Your bus protocol -implementation must be capable of dealing with these unexpected -messages (which it needs to anyway, given that transfers are -relatively unrestricted on kdbus and people can send you all kinds of -non-sense). - -If a client connects to a bus whose bloom filter metrics (i.e. filter -size and number of hash functions) are outside of the range the client -supports it must immediately disconnect and continue connection with -the next bus address of the bus connection string. - -INSTALLING MATCHES - -To install matches for broadcast messages, use the KDBUS_CMD_ADD_MATCH -ioctl. It takes a structure that contains an encoded match expression, -and that is followed by one or more items, which are combined in an -AND way. (Meaning: a message is matched exactly when all items -attached to the original ioctl struct match). - -To match against other user messages add a KDBUS_ITEM_BLOOM item in -the match (see above). Note that the bloom filter does not include -matches to the sender names. To additionally check against sender -names, use the KDBUS_ITEM_ID (for unique id matches) and -KDBUS_ITEM_NAME (for well-known name matches) item types. - -To match against kernel generated messages (see below) you should add -items of the same type as the kernel messages include, -i.e. KDBUS_ITEM_NAME_ADD, KDBUS_ITEM_NAME_REMOVE, -KDBUS_ITEM_NAME_CHANGE, KDBUS_ITEM_ID_ADD, KDBUS_ITEM_ID_REMOVE and -fill them out. Note however, that you have some wildcards in this -case, for example the .id field of KDBUS_ITEM_ID_ADD/KDBUS_ITEM_ID_REMOVE -structures may be set to 0 to match against any id addition/removal. - -Note that dbus match strings do no map 1:1 to these ioctl() calls. In -many cases (where the match string is "underspecified") you might need -to issue up to six different ioctl() calls for the same match. For -example, the empty match (which matches against all messages), would -translate into one KDBUS_ITEM_BLOOM ioctl, one KDBUS_ITEM_NAME_ADD, -one KDBUS_ITEM_NAME_CHANGE, one KDBUS_ITEM_NAME_REMOVE, one -KDBUS_ITEM_ID_ADD and one KDBUS_ITEM_ID_REMOVE. - -When creating a match, you may attach a "cookie" value to them, which -is used for deleting this match again. The cookie can be selected freely -by the client. When issuing KDBUS_CMD_REMOVE_MATCH, simply pass the -same cookie as before and all matches matching the same "cookie" value -will be removed. This is particularly handy for the case where multiple -ioctl()s are added for a single match strings. - -MEMFDS - -memfds may be sent across kdbus via KDBUS_ITEM_PAYLOAD_MEMFD items -attached to messages. If this is done, the data included in the memfd -is considered part of the payload stream of a message, and are treated -the same way as KDBUS_ITEM_PAYLOAD_VEC by the receiving side. It is -possible to interleave KDBUS_ITEM_PAYLOAD_MEMFD and -KDBUS_ITEM_PAYLOAD_VEC items freely, by the reader they will be -considered a single stream of bytes in the order these items appear in -the message, that just happens to be split up at various places -(regarding rules how they may be split up, see above). The kernel will -refuse taking KDBUS_ITEM_PAYLOAD_MEMFD items that refer to memfds that -are not sealed. - -Note that sealed memfds may be unsealed again if they are not mapped -you have the only fd reference to them. - -Alternatively to sending memfds as KDBUS_ITEM_PAYLOAD_MEMFD items -(where they are just a part of the payload stream of a message) you can -also simply attach any memfd to a message using -KDBUS_ITEM_PAYLOAD_FDS. In this case, the memfd contents is not -considered part of the payload stream of the message, but simply fds -like any other, that happen to be attached to the message. - -MESSAGES FROM THE KERNEL - -A couple of messages previously generated by the dbus1 bus driver are -now generated by the kernel. Since the kernel does not understand the -payload marshaling, they are generated by the kernel in a different -format. This is indicated with the "payload type" field of the -messages set to 0. Library implementations should take these messages -and synthesize traditional driver messages for them on reception. - -More specifically: - - Instead of the NameOwnerChanged, NameLost, NameAcquired signals - there are kernel messages containing KDBUS_ITEM_NAME_ADD, - KDBUS_ITEM_NAME_REMOVE, KDBUS_ITEM_NAME_CHANGE, KDBUS_ITEM_ID_ADD, - KDBUS_ITEM_ID_REMOVE items are generated (each message will contain - exactly one of these items). Note that in libsystemd we have - obsoleted NameLost/NameAcquired messages, since they are entirely - redundant to NameOwnerChanged. This library will hence only - synthesize NameOwnerChanged messages from these kernel messages, - and never generate NameLost/NameAcquired. If your library needs to - stay compatible to the old dbus1 userspace, you possibly might need - to synthesize both a NameOwnerChanged and NameLost/NameAcquired - message from the same kernel message. - - When a method call times out, a KDBUS_ITEM_REPLY_TIMEOUT message is - generated. This should be synthesized into a method error reply - message to the original call. - - When a method call fails because the peer terminated the connection - before responding, a KDBUS_ITEM_REPLY_DEAD message is - generated. Similarly, it should be synthesized into a method error - reply message. - -For synthesized messages we recommend setting the cookie field to -(uint32_t) -1 (and not (uint64_t) -1!), so that the cookie is not 0 -(which the dbus1 spec does not allow), but clearly recognizable as -synthetic. - -Note that the KDBUS_ITEM_NAME_XYZ messages will actually inform you -about all kinds of names, including activatable ones. Classic dbus1 -NameOwnerChanged messages OTOH are only generated when a name is -really acquired on the bus and not just simply activatable. This means -you must explicitly check for the case where an activatable name -becomes acquired or an acquired name is lost and returns to be -activatable. - -NAME REGISTRY - -To acquire names on the bus, use the KDBUS_CMD_NAME_ACQUIRE ioctl(). It -takes a flags field similar to dbus1's RequestName() bus driver call, -however the NO_QUEUE flag got inverted into a QUEUE flag instead. - -To release a previously acquired name use the KDBUS_CMD_NAME_RELEASE -ioctl(). - -To list acquired names use the KDBUS_CMD_CONN_INFO ioctl. It may be -used to list unique names, well known names as well as activatable -names and clients currently queuing for ownership of a well-known -name. The ioctl will return an offset into the memory pool. After -reading all the data you need, you need to release this via the -KDBUS_CMD_FREE ioctl(), similar how you release a received message. - -CREDENTIALS - -kdbus can optionally attach various kinds of metadata about the sender at -the point of time of sending ("credentials") to messages, on request -of the receiver. This is both supported on directed and undirected -(broadcast) messages. The metadata to attach is selected at time of -the HELLO ioctl of the receiver via a flags field (see above). Note -that clients must be able to handle that messages contain more -metadata than they asked for themselves, to simplify implementation of -broadcasting in the kernel. The receiver should not rely on this data -to be around though, even though it will be correct if it happens to -be attached. In order to avoid programming errors in applications, we -recommend though not passing this data on to clients that did not -explicitly ask for it. - -Credentials may also be queried for a well-known or unique name. Use -the KDBUS_CMD_CONN_INFO for this. It will return an offset to the pool -area again, which will contain the same credential items as messages -have attached. Note that when issuing the ioctl, you can select a -different set of credentials to gather, than what was originally requested -for being attached to incoming messages. - -Credentials are always specific to the sender's domain that was -current at the time of sending, and of the process that opened the -bus connection at the time of opening it. Note that this latter data -is cached! - -POLICY - -The kernel enforces only very limited policy on names. It will not do -access filtering by userspace payload, and thus not by interface or -method name. - -This ultimately means that most fine-grained policy enforcement needs -to be done by the receiving process. We recommend using PolicyKit for -any more complex checks. However, libraries should make simple static -policy decisions regarding privileged/unprivileged method calls -easy. We recommend doing this by enabling KDBUS_ATTACH_CAPS and -KDBUS_ATTACH_CREDS for incoming messages, and then discerning client -access by some capability, or if sender and receiver UIDs match. - -BUS ADDRESSES - -When connecting to kdbus use the "kernel:" protocol prefix in DBus -address strings. The device node path is encoded in its "path=" -parameter. - -Client libraries should use the following connection string when -connecting to the system bus: - - kernel:path=/sys/fs/kdbus/0-system/bus;unix:path=/var/run/dbus/system_bus_socket - -This will ensure that kdbus is preferred over the legacy AF_UNIX -socket, but compatibility is kept. For the user bus use: - - kernel:path=/sys/fs/kdbus/$UID-user/bus;unix:path=$XDG_RUNTIME_DIR/bus - -With $UID replaced by the callers numer user ID, and $XDG_RUNTIME_DIR -following the XDG basedir spec. - -Of course the $DBUS_SYSTEM_BUS_ADDRESS and $DBUS_SESSION_BUS_ADDRESS -variables should still take precedence. - -DBUS SERVICE FILES - -Activatable services for kdbus may not use classic dbus1 service -activation files. Instead, programs should drop in native systemd -.service and .busname unit files, so that they are treated uniformly -with other types of units and activation of the system. - -Note that this results in a major difference to classic dbus1: -activatable bus names can be established at any time in the boot process. -This is unlike dbus1 where activatable names are unconditionally available -as long as dbus-daemon is running. Being able to control when -activatable names are established is essential to allow usage of kdbus -during early boot and in initrds, without the risk of triggering -services too early. - -DISCLAIMER - -This all is so far just the status quo. We are putting this together, because -we are quite confident that further API changes will be smaller, but -to make this very clear: this is all subject to change, still! - -We invite you to port over your favorite dbus library to this new -scheme, but please be prepared to make minor changes when we still -change these interfaces! diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c index 9e58ffbd88..b56bb07713 100644 --- a/src/libsystemd/sd-bus/bus-control.c +++ b/src/libsystemd/sd-bus/bus-control.c @@ -264,10 +264,13 @@ static int kernel_get_list(sd_bus *bus, uint64_t flags, char ***x) { if ((flags & KDBUS_LIST_UNIQUE) && name->id != previous_id && !(name->flags & KDBUS_HELLO_ACTIVATOR)) { char *n; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" if (asprintf(&n, ":1.%llu", name->id) < 0) { r = -ENOMEM; goto fail; } +#pragma GCC diagnostic pop r = strv_consume(x, n); if (r < 0) @@ -711,10 +714,13 @@ int bus_get_name_creds_kdbus( } if (mask & SD_BUS_CREDS_UNIQUE_NAME) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" if (asprintf(&c->unique_name, ":1.%llu", conn_info->id) < 0) { r = -ENOMEM; goto fail; } +#pragma GCC diagnostic pop c->mask |= SD_BUS_CREDS_UNIQUE_NAME; } @@ -780,6 +786,8 @@ static int bus_get_name_creds_dbus1( } if (mask != 0) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + bool need_pid, need_uid, need_selinux, need_separate_calls; c = bus_creds_new(); if (!c) return -ENOMEM; @@ -792,99 +800,216 @@ static int bus_get_name_creds_dbus1( c->mask |= SD_BUS_CREDS_UNIQUE_NAME; } - if ((mask & SD_BUS_CREDS_PID) || - ((mask & SD_BUS_CREDS_AUGMENT) && - (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| - SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| - SD_BUS_CREDS_SUPPLEMENTARY_GIDS| - SD_BUS_CREDS_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE| - SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID| - SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS| - SD_BUS_CREDS_SELINUX_CONTEXT| - SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID)))) { + need_pid = (mask & SD_BUS_CREDS_PID) || + ((mask & SD_BUS_CREDS_AUGMENT) && + (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| + SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| + SD_BUS_CREDS_SUPPLEMENTARY_GIDS| + SD_BUS_CREDS_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE| + SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID| + SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS| + SD_BUS_CREDS_SELINUX_CONTEXT| + SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID))); + need_uid = mask & SD_BUS_CREDS_EUID; + need_selinux = mask & SD_BUS_CREDS_SELINUX_CONTEXT; - uint32_t u; + if (need_pid + need_uid + need_selinux > 1) { + + /* If we need more than one of the credentials, then use GetConnectionCredentials() */ r = sd_bus_call_method( bus, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", - "GetConnectionUnixProcessID", - NULL, + "GetConnectionCredentials", + &error, &reply, "s", - unique ? unique : name); - if (r < 0) - return r; + unique ?: name); - r = sd_bus_message_read(reply, "u", &u); - if (r < 0) - return r; + if (r < 0) { - pid = u; - if (mask & SD_BUS_CREDS_PID) { - c->pid = u; - c->mask |= SD_BUS_CREDS_PID; - } + if (!sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) + return r; - reply = sd_bus_message_unref(reply); - } + /* If we got an unknown method error, fall back to the invidual calls... */ + need_separate_calls = true; + sd_bus_error_free(&error); - if (mask & SD_BUS_CREDS_EUID) { - uint32_t u; + } else { + need_separate_calls = false; - r = sd_bus_call_method( - bus, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "GetConnectionUnixUser", - NULL, - &reply, - "s", - unique ? unique : name); - if (r < 0) - return r; + r = sd_bus_message_enter_container(reply, 'a', "{sv}"); + if (r < 0) + return r; - r = sd_bus_message_read(reply, "u", &u); - if (r < 0) - return r; + for (;;) { + const char *m; - c->euid = u; - c->mask |= SD_BUS_CREDS_EUID; + r = sd_bus_message_enter_container(reply, 'e', "sv"); + if (r < 0) + return r; + if (r == 0) + break; - reply = sd_bus_message_unref(reply); - } + r = sd_bus_message_read(reply, "s", &m); + if (r < 0) + return r; - if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - const void *p = NULL; - size_t sz = 0; + if (need_uid && streq(m, "UnixUserID")) { + uint32_t u; - r = sd_bus_call_method( - bus, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "GetConnectionSELinuxSecurityContext", - &error, - &reply, - "s", - unique ? unique : name); - if (r < 0) { - if (!sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown")) + r = sd_bus_message_read(reply, "v", "u", &u); + if (r < 0) + return r; + + c->euid = u; + c->mask |= SD_BUS_CREDS_EUID; + + } else if (need_pid && streq(m, "ProcessID")) { + uint32_t p; + + r = sd_bus_message_read(reply, "v", "u", &p); + if (r < 0) + return r; + + pid = p; + if (mask & SD_BUS_CREDS_PID) { + c->pid = p; + c->mask |= SD_BUS_CREDS_PID; + } + + } else if (need_selinux && streq(m, "LinuxSecurityLabel")) { + const void *p = NULL; + size_t sz = 0; + + r = sd_bus_message_enter_container(reply, 'v', "ay"); + if (r < 0) + return r; + + r = sd_bus_message_read_array(reply, 'y', &p, &sz); + if (r < 0) + return r; + + free(c->label); + c->label = strndup(p, sz); + if (!c->label) + return -ENOMEM; + + c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return r; + } else { + r = sd_bus_message_skip(reply, "v"); + if (r < 0) + return r; + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return r; + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) return r; - } else { - r = sd_bus_message_read_array(reply, 'y', &p, &sz); + + if (need_pid && pid == 0) + return -EPROTO; + } + + } else /* When we only need a single field, then let's use separate calls */ + need_separate_calls = true; + + if (need_separate_calls) { + if (need_pid) { + uint32_t u; + + r = sd_bus_call_method( + bus, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "GetConnectionUnixProcessID", + NULL, + &reply, + "s", + unique ?: name); if (r < 0) return r; - c->label = strndup(p, sz); - if (!c->label) - return -ENOMEM; + r = sd_bus_message_read(reply, "u", &u); + if (r < 0) + return r; - c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; + pid = u; + if (mask & SD_BUS_CREDS_PID) { + c->pid = u; + c->mask |= SD_BUS_CREDS_PID; + } + + reply = sd_bus_message_unref(reply); + } + + if (need_uid) { + uint32_t u; + + r = sd_bus_call_method( + bus, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "GetConnectionUnixUser", + NULL, + &reply, + "s", + unique ? unique : name); + if (r < 0) + return r; + + r = sd_bus_message_read(reply, "u", &u); + if (r < 0) + return r; + + c->euid = u; + c->mask |= SD_BUS_CREDS_EUID; + + reply = sd_bus_message_unref(reply); + } + + if (need_selinux) { + const void *p = NULL; + size_t sz = 0; + + r = sd_bus_call_method( + bus, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "GetConnectionSELinuxSecurityContext", + &error, + &reply, + "s", + unique ? unique : name); + if (r < 0) { + if (!sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown")) + return r; + + /* no data is fine */ + } else { + r = sd_bus_message_read_array(reply, 'y', &p, &sz); + if (r < 0) + return r; + + c->label = strndup(p, sz); + if (!c->label) + return -ENOMEM; + + c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; + } } } @@ -917,9 +1042,17 @@ _public_ int sd_bus_get_name_creds( if (!bus->bus_client) return -EINVAL; + /* Turn off augmenting if this isn't a local connection. If the connection is not local, then /proc is not + * going to match. */ + if (!bus->is_local) + mask &= ~SD_BUS_CREDS_AUGMENT; + if (streq(name, "org.freedesktop.DBus.Local")) return -EINVAL; + if (streq(name, "org.freedesktop.DBus")) + return sd_bus_get_owner_creds(bus, mask, creds); + if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; @@ -1040,6 +1173,9 @@ _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **r if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; + if (!bus->is_local) + mask &= ~SD_BUS_CREDS_AUGMENT; + if (bus->is_kernel) return bus_get_owner_creds_kdbus(bus, mask, ret); else diff --git a/src/libsystemd/sd-bus/bus-convenience.c b/src/libsystemd/sd-bus/bus-convenience.c index 2d06bf541f..04158cae4d 100644 --- a/src/libsystemd/sd-bus/bus-convenience.c +++ b/src/libsystemd/sd-bus/bus-convenience.c @@ -48,7 +48,7 @@ _public_ int sd_bus_emit_signal( va_list ap; va_start(ap, types); - r = bus_message_append_ap(m, types, ap); + r = sd_bus_message_appendv(m, types, ap); va_end(ap); if (r < 0) return r; @@ -85,7 +85,7 @@ _public_ int sd_bus_call_method_async( va_list ap; va_start(ap, types); - r = bus_message_append_ap(m, types, ap); + r = sd_bus_message_appendv(m, types, ap); va_end(ap); if (r < 0) return r; @@ -123,7 +123,7 @@ _public_ int sd_bus_call_method( va_list ap; va_start(ap, types); - r = bus_message_append_ap(m, types, ap); + r = sd_bus_message_appendv(m, types, ap); va_end(ap); if (r < 0) goto fail; @@ -162,7 +162,7 @@ _public_ int sd_bus_reply_method_return( va_list ap; va_start(ap, types); - r = bus_message_append_ap(m, types, ap); + r = sd_bus_message_appendv(m, types, ap); va_end(ap); if (r < 0) return r; @@ -493,7 +493,7 @@ _public_ int sd_bus_set_property( goto fail; va_start(ap, type); - r = bus_message_append_ap(m, type, ap); + r = sd_bus_message_appendv(m, type, ap); va_end(ap); if (r < 0) goto fail; diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h index bb0414c4d6..3575ea8cde 100644 --- a/src/libsystemd/sd-bus/bus-internal.h +++ b/src/libsystemd/sd-bus/bus-internal.h @@ -212,6 +212,7 @@ struct sd_bus { bool exit_on_disconnect:1; bool exited:1; bool exit_triggered:1; + bool is_local:1; int use_memfd; diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c index c82caeb3fc..ca6aee7c06 100644 --- a/src/libsystemd/sd-bus/bus-kernel.c +++ b/src/libsystemd/sd-bus/bus-kernel.c @@ -51,6 +51,8 @@ #include "user-util.h" #include "util.h" +#pragma GCC diagnostic ignored "-Wformat" + #define UNIQUE_NAME_MAX (3+DECIMAL_STR_MAX(uint64_t)) int bus_kernel_parse_unique_name(const char *s, uint64_t *id) { diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c index 5cec804e32..da6fd3b896 100644 --- a/src/libsystemd/sd-bus/bus-message.c +++ b/src/libsystemd/sd-bus/bus-message.c @@ -2341,7 +2341,7 @@ static int type_stack_pop(TypeStack *stack, unsigned max, unsigned *i, const cha return 1; } -int bus_message_append_ap( +_public_ int sd_bus_message_appendv( sd_bus_message *m, const char *types, va_list ap) { @@ -2351,10 +2351,10 @@ int bus_message_append_ap( unsigned stack_ptr = 0; int r; - assert(m); - - if (!types) - return 0; + assert_return(m, -EINVAL); + assert_return(types, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(!m->poisoned, -ESTALE); n_array = (unsigned) -1; n_struct = strlen(types); @@ -2555,7 +2555,7 @@ _public_ int sd_bus_message_append(sd_bus_message *m, const char *types, ...) { assert_return(!m->poisoned, -ESTALE); va_start(ap, types); - r = bus_message_append_ap(m, types, ap); + r = sd_bus_message_appendv(m, types, ap); va_end(ap); return r; diff --git a/src/libsystemd/sd-bus/bus-message.h b/src/libsystemd/sd-bus/bus-message.h index 4710c106b9..a59aa73833 100644 --- a/src/libsystemd/sd-bus/bus-message.h +++ b/src/libsystemd/sd-bus/bus-message.h @@ -220,8 +220,6 @@ int bus_message_from_malloc( int bus_message_get_arg(sd_bus_message *m, unsigned i, const char **str); int bus_message_get_arg_strv(sd_bus_message *m, unsigned i, char ***strv); -int bus_message_append_ap(sd_bus_message *m, const char *types, va_list ap); - int bus_message_parse_fields(sd_bus_message *m); struct bus_body_part *message_append_part(sd_bus_message *m); diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c index 9bd07ffcab..98911d5203 100644 --- a/src/libsystemd/sd-bus/bus-objects.c +++ b/src/libsystemd/sd-bus/bus-objects.c @@ -974,8 +974,10 @@ static int process_introspect( /* Nothing?, let's see if we exist at all, and if not * refuse to do anything */ r = bus_node_exists(bus, n, m->path, require_fallback); - if (r <= 0) + if (r <= 0) { + r = bus_maybe_reply_error(m, r, &error); goto finish; + } if (bus->nodes_modified) { r = 0; goto finish; @@ -1057,6 +1059,22 @@ static int object_manager_serialize_path( if (r < 0) return r; + r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0); + if (r < 0) + return r; + found_something = true; } @@ -1183,7 +1201,7 @@ static int process_get_managed_objects( r = get_child_nodes(bus, m->path, n, CHILDREN_RECURSIVE, &s, &error); if (r < 0) - return r; + return bus_maybe_reply_error(m, r, &error); if (bus->nodes_modified) return 0; @@ -1198,7 +1216,7 @@ static int process_get_managed_objects( SET_FOREACH(path, s, i) { r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error); if (r < 0) - return r; + return bus_maybe_reply_error(m, r, &error); if (bus->nodes_modified) return 0; @@ -1328,7 +1346,7 @@ static int object_find_and_run( if (!*found_object) { r = bus_node_exists(bus, n, m->path, require_fallback); if (r < 0) - return r; + return bus_maybe_reply_error(m, r, NULL); if (bus->nodes_modified) return 0; if (r > 0) diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c index e6ed15eb71..8b25002f01 100644 --- a/src/libsystemd/sd-bus/bus-socket.c +++ b/src/libsystemd/sd-bus/bus-socket.c @@ -607,7 +607,7 @@ static void bus_get_peercred(sd_bus *b) { b->ucred_valid = getpeercred(b->input_fd, &b->ucred) >= 0; /* Get the SELinux context of the peer */ - if (mac_selinux_have()) { + if (mac_selinux_use()) { r = getpeersec(b->input_fd, &b->label); if (r < 0 && r != -EOPNOTSUPP) log_debug_errno(r, "Failed to determine peer security context: %m"); diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index e809942278..2f065c2657 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -588,6 +588,8 @@ static int parse_unix_address(sd_bus *b, const char **p, char **guid) { b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + 1 + l; } + b->is_local = true; + return 0; } @@ -655,6 +657,8 @@ static int parse_tcp_address(sd_bus *b, const char **p, char **guid) { freeaddrinfo(result); + b->is_local = false; + return 0; } @@ -737,6 +741,9 @@ static int parse_exec_address(sd_bus *b, const char **p, char **guid) { b->exec_path = path; b->exec_argv = argv; + + b->is_local = false; + return 0; fail: @@ -780,6 +787,8 @@ static int parse_kernel_address(sd_bus *b, const char **p, char **guid) { b->kernel = path; path = NULL; + b->is_local = true; + return 0; } @@ -838,6 +847,7 @@ static int parse_container_unix_address(sd_bus *b, const char **p, char **guid) b->sockaddr.un.sun_family = AF_UNIX; strncpy(b->sockaddr.un.sun_path, "/var/run/dbus/system_bus_socket", sizeof(b->sockaddr.un.sun_path)); b->sockaddr_size = SOCKADDR_UN_LEN(b->sockaddr.un); + b->is_local = false; return 0; } @@ -898,6 +908,8 @@ static int parse_container_kernel_address(sd_bus *b, const char **p, char **guid if (r < 0) return r; + b->is_local = false; + return 0; } @@ -1179,6 +1191,7 @@ _public_ int sd_bus_open(sd_bus **ret) { /* We don't know whether the bus is trusted or not, so better * be safe, and authenticate everything */ b->trusted = false; + b->is_local = false; b->attach_flags |= KDBUS_ATTACH_CAPS | KDBUS_ATTACH_CREDS; b->creds_mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_EFFECTIVE_CAPS; @@ -1227,6 +1240,7 @@ _public_ int sd_bus_open_system(sd_bus **ret) { b->trusted = false; b->attach_flags |= KDBUS_ATTACH_CAPS | KDBUS_ATTACH_CREDS; b->creds_mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_EFFECTIVE_CAPS; + b->is_local = true; r = sd_bus_start(b); if (r < 0) @@ -1293,6 +1307,7 @@ _public_ int sd_bus_open_user(sd_bus **ret) { /* We don't do any per-method access control on the user * bus. */ b->trusted = true; + b->is_local = true; r = sd_bus_start(b); if (r < 0) @@ -1364,6 +1379,7 @@ _public_ int sd_bus_open_system_remote(sd_bus **ret, const char *host) { bus->bus_client = true; bus->trusted = false; bus->is_system = true; + bus->is_local = false; r = sd_bus_start(bus); if (r < 0) @@ -1413,6 +1429,7 @@ _public_ int sd_bus_open_system_machine(sd_bus **ret, const char *machine) { bus->bus_client = true; bus->trusted = false; bus->is_system = true; + bus->is_local = false; r = sd_bus_start(bus); if (r < 0) diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c index 233a21a523..0b33ab7a3a 100644 --- a/src/libsystemd/sd-bus/test-bus-objects.c +++ b/src/libsystemd/sd-bus/test-bus-objects.c @@ -525,8 +525,6 @@ int main(int argc, char *argv[]) { void *p; int r, q; - zero(c); - c.automatic_integer_property = 4711; assert_se(c.automatic_string_property = strdup("dudeldu")); diff --git a/src/libsystemd/sd-bus/test-bus-track.c b/src/libsystemd/sd-bus/test-bus-track.c index 4beb61f05a..06c6167511 100644 --- a/src/libsystemd/sd-bus/test-bus-track.c +++ b/src/libsystemd/sd-bus/test-bus-track.c @@ -17,7 +17,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sd-bus.h> +#include "sd-bus.h" #include "macro.h" diff --git a/src/libsystemd/sd-bus/test-bus-vtable-cc.cc b/src/libsystemd/sd-bus/test-bus-vtable-cc.cc new file mode 120000 index 0000000000..abee398630 --- /dev/null +++ b/src/libsystemd/sd-bus/test-bus-vtable-cc.cc @@ -0,0 +1 @@ +test-bus-vtable.c
\ No newline at end of file diff --git a/src/libsystemd/sd-bus/test-bus-vtable.c b/src/libsystemd/sd-bus/test-bus-vtable.c new file mode 100644 index 0000000000..fd9ad81217 --- /dev/null +++ b/src/libsystemd/sd-bus/test-bus-vtable.c @@ -0,0 +1,81 @@ +#include <stdbool.h> +#include <stddef.h> + +/* We use system assert.h here, because we don't want to keep macro.h and log.h C++ compatible */ +#undef NDEBUG +#include <assert.h> +#include <errno.h> + +#include "sd-bus-vtable.h" + +#define DEFAULT_BUS_PATH "unix:path=/run/dbus/system_bus_socket" + +struct context { + bool quit; + char *something; + char *automatic_string_property; + uint32_t automatic_integer_property; +}; + +static int handler(sd_bus_message *m, void *userdata, sd_bus_error *error) { + return 1; +} + +static int value_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) { + return 1; +} + +static int get_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) { + return 1; +} + +static int set_handler(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, void *userdata, sd_bus_error *error) { + return 1; +} + +static const sd_bus_vtable vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_METHOD("AlterSomething", "s", "s", handler, 0), + SD_BUS_METHOD("Exit", "", "", handler, 0), + SD_BUS_METHOD_WITH_OFFSET("AlterSomething2", "s", "s", handler, 200, 0), + SD_BUS_METHOD_WITH_OFFSET("Exit2", "", "", handler, 200, 0), + SD_BUS_PROPERTY("Value", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Value2", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + SD_BUS_PROPERTY("Value3", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Value4", "s", value_handler, 10, 0), + SD_BUS_PROPERTY("AnExplicitProperty", "s", NULL, offsetof(struct context, something), + SD_BUS_VTABLE_PROPERTY_EXPLICIT|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + SD_BUS_WRITABLE_PROPERTY("Something", "s", get_handler, set_handler, 0, 0), + SD_BUS_WRITABLE_PROPERTY("AutomaticStringProperty", "s", NULL, NULL, + offsetof(struct context, automatic_string_property), 0), + SD_BUS_WRITABLE_PROPERTY("AutomaticIntegerProperty", "u", NULL, NULL, + offsetof(struct context, automatic_integer_property), 0), + SD_BUS_METHOD("NoOperation", NULL, NULL, NULL, 0), + SD_BUS_SIGNAL("DummySignal", "b", 0), + SD_BUS_SIGNAL("DummySignal2", "so", 0), + SD_BUS_VTABLE_END +}; + +static void test_vtable(void) { + sd_bus *bus = NULL; + struct context c = {}; + int r; + + assert(sd_bus_new(&bus) >= 0); + + assert(sd_bus_add_object_vtable(bus, NULL, "/foo", "org.freedesktop.systemd.testVtable", vtable, &c) >= 0); + assert(sd_bus_add_object_vtable(bus, NULL, "/foo", "org.freedesktop.systemd.testVtable2", vtable, &c) >= 0); + + assert(sd_bus_set_address(bus, DEFAULT_BUS_PATH) >= 0); + r = sd_bus_start(bus); + assert(r == 0 || /* success */ + r == -ENOENT /* dbus is inactive */ ); + + sd_bus_unref(bus); +} + +int main(int argc, char **argv) { + test_vtable(); + + return 0; +} diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c index 86f8935a14..ebb8b2d160 100644 --- a/src/libsystemd/sd-device/device-enumerator.c +++ b/src/libsystemd/sd-device/device-enumerator.c @@ -631,10 +631,8 @@ static int enumerator_scan_devices_tag(sd_device_enumerator *enumerator, const c if (!dir) { if (errno == ENOENT) return 0; - else { - log_error("sd-device-enumerator: could not open tags directory %s: %m", path); - return -errno; - } + else + return log_error_errno(errno, "sd-device-enumerator: could not open tags directory %s: %m", path); } /* TODO: filter away subsystems? */ @@ -758,10 +756,8 @@ static int parent_crawl_children(sd_device_enumerator *enumerator, const char *p int r = 0; dir = opendir(path); - if (!dir) { - log_debug("sd-device-enumerate: could not open parent directory %s: %m", path); - return -errno; - } + if (!dir) + return log_debug_errno(errno, "sd-device-enumerate: could not open parent directory %s: %m", path); FOREACH_DIRENT_ALL(dent, dir, return -errno) { _cleanup_free_ char *child = NULL; diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c index 9082d377f4..b4cd676c12 100644 --- a/src/libsystemd/sd-device/device-private.c +++ b/src/libsystemd/sd-device/device-private.c @@ -778,12 +778,12 @@ int device_rename(sd_device *device, const char *name) { r = sd_device_get_property_value(device, "INTERFACE", &interface); if (r >= 0) { - r = device_add_property_internal(device, "INTERFACE", name); + /* like DEVPATH_OLD, INTERFACE_OLD is not saved to the db, but only stays around for the current event */ + r = device_add_property_internal(device, "INTERFACE_OLD", interface); if (r < 0) return r; - /* like DEVPATH_OLD, INTERFACE_OLD is not saved to the db, but only stays around for the current event */ - r = device_add_property_internal(device, "INTERFACE_OLD", interface); + r = device_add_property_internal(device, "INTERFACE", name); if (r < 0) return r; } else if (r != -ENOENT) diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index 04ead29338..ab36aa2e32 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -195,8 +195,7 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) { /* this is not a valid device */ return -ENODEV; - log_debug("sd-device: %s does not have an uevent file: %m", syspath); - return -errno; + return log_debug_errno(errno, "sd-device: %s does not have an uevent file: %m", syspath); } } else { /* everything else just needs to be a directory */ @@ -325,6 +324,10 @@ _public_ int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *s if (access(syspath, F_OK) >= 0) return sd_device_new_from_syspath(ret, syspath); + syspath = strjoina("/sys/firmware/", subsystem, "/", sysname); + if (access(syspath, F_OK) >= 0) + return sd_device_new_from_syspath(ret, syspath); + return -ENODEV; } @@ -1892,6 +1895,7 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, r = device_add_sysattr_value(device, sysattr, value); if (r < 0) return r; + value = NULL; return -ENXIO; } diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c index c0e5e06a18..8425378f34 100644 --- a/src/libsystemd/sd-event/test-event.c +++ b/src/libsystemd/sd-event/test-event.c @@ -17,6 +17,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <sys/wait.h> + #include "sd-event.h" #include "fd-util.h" diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c index cc89f2de2e..b1f6c5305f 100644 --- a/src/libsystemd/sd-id128/sd-id128.c +++ b/src/libsystemd/sd-id128/sd-id128.c @@ -289,7 +289,7 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) { assert_return(ret, -EINVAL); - r = dev_urandom(&t, sizeof(t)); + r = acquire_random_bytes(&t, sizeof t, true); if (r < 0) return r; diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c index d2cfbdf5b0..5fb7fd99d5 100644 --- a/src/libsystemd/sd-login/sd-login.c +++ b/src/libsystemd/sd-login/sd-login.c @@ -56,59 +56,73 @@ */ _public_ int sd_pid_get_session(pid_t pid, char **session) { + int r; assert_return(pid >= 0, -EINVAL); assert_return(session, -EINVAL); - return cg_pid_get_session(pid, session); + r = cg_pid_get_session(pid, session); + return IN_SET(r, -ENXIO, -ENOMEDIUM) ? -ENODATA : r; } _public_ int sd_pid_get_unit(pid_t pid, char **unit) { + int r; assert_return(pid >= 0, -EINVAL); assert_return(unit, -EINVAL); - return cg_pid_get_unit(pid, unit); + r = cg_pid_get_unit(pid, unit); + return IN_SET(r, -ENXIO, -ENOMEDIUM) ? -ENODATA : r; } _public_ int sd_pid_get_user_unit(pid_t pid, char **unit) { + int r; assert_return(pid >= 0, -EINVAL); assert_return(unit, -EINVAL); - return cg_pid_get_user_unit(pid, unit); + r = cg_pid_get_user_unit(pid, unit); + return IN_SET(r, -ENXIO, -ENOMEDIUM) ? -ENODATA : r; } _public_ int sd_pid_get_machine_name(pid_t pid, char **name) { + int r; assert_return(pid >= 0, -EINVAL); assert_return(name, -EINVAL); - return cg_pid_get_machine_name(pid, name); + r = cg_pid_get_machine_name(pid, name); + return IN_SET(r, -ENXIO, -ENOMEDIUM) ? -ENODATA : r; } _public_ int sd_pid_get_slice(pid_t pid, char **slice) { + int r; assert_return(pid >= 0, -EINVAL); assert_return(slice, -EINVAL); - return cg_pid_get_slice(pid, slice); + r = cg_pid_get_slice(pid, slice); + return IN_SET(r, -ENXIO, -ENOMEDIUM) ? -ENODATA : r; } _public_ int sd_pid_get_user_slice(pid_t pid, char **slice) { + int r; assert_return(pid >= 0, -EINVAL); assert_return(slice, -EINVAL); - return cg_pid_get_user_slice(pid, slice); + r = cg_pid_get_user_slice(pid, slice); + return IN_SET(r, -ENXIO, -ENOMEDIUM) ? -ENODATA : r; } _public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid) { + int r; assert_return(pid >= 0, -EINVAL); assert_return(uid, -EINVAL); - return cg_pid_get_owner_uid(pid, uid); + r = cg_pid_get_owner_uid(pid, uid); + return IN_SET(r, -ENXIO, -ENOMEDIUM) ? -ENODATA : r; } _public_ int sd_pid_get_cgroup(pid_t pid, char **cgroup) { @@ -279,7 +293,7 @@ _public_ int sd_uid_get_state(uid_t uid, char**state) { return -ENOMEM; } - if (r < 0) { + else if (r < 0) { free(s); return r; } @@ -687,7 +701,7 @@ _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **ui r = parse_env_file(p, NEWLINE, "SESSIONS", &s, - "ACTIVE_SESSIONS", &t, + "UIDS", &t, NULL); if (r == -ENOENT) return -ENXIO; @@ -723,7 +737,7 @@ _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **ui r = parse_uid(k, b + i); if (r < 0) - continue; + return r; i++; } @@ -784,11 +798,27 @@ _public_ int sd_seat_can_graphical(const char *seat) { } _public_ int sd_get_seats(char ***seats) { - return get_files_in_directory("/run/systemd/seats/", seats); + int r; + + r = get_files_in_directory("/run/systemd/seats/", seats); + if (r == -ENOENT) { + if (seats) + *seats = NULL; + return 0; + } + return r; } _public_ int sd_get_sessions(char ***sessions) { - return get_files_in_directory("/run/systemd/sessions/", sessions); + int r; + + r = get_files_in_directory("/run/systemd/sessions/", sessions); + if (r == -ENOENT) { + if (sessions) + *sessions = NULL; + return 0; + } + return r; } _public_ int sd_get_uids(uid_t **users) { @@ -799,8 +829,14 @@ _public_ int sd_get_uids(uid_t **users) { _cleanup_free_ uid_t *l = NULL; d = opendir("/run/systemd/users/"); - if (!d) + if (!d) { + if (errno == ENOENT) { + if (users) + *users = NULL; + return 0; + } return -errno; + } FOREACH_DIRENT_ALL(de, d, return -errno) { int k; @@ -842,12 +878,16 @@ _public_ int sd_get_uids(uid_t **users) { } _public_ int sd_get_machine_names(char ***machines) { - char **l = NULL, **a, **b; + _cleanup_strv_free_ char **l = NULL; + char **a, **b; int r; - assert_return(machines, -EINVAL); - r = get_files_in_directory("/run/systemd/machines/", &l); + if (r == -ENOENT) { + if (machines) + *machines = NULL; + return 0; + } if (r < 0) return r; @@ -855,7 +895,7 @@ _public_ int sd_get_machine_names(char ***machines) { r = 0; /* Filter out the unit: symlinks */ - for (a = l, b = l; *a; a++) { + for (a = b = l; *a; a++) { if (startswith(*a, "unit:") || !machine_name_is_valid(*a)) free(*a); else { @@ -868,7 +908,10 @@ _public_ int sd_get_machine_names(char ***machines) { *b = NULL; } - *machines = l; + if (machines) { + *machines = l; + l = NULL; + } return r; } diff --git a/src/libsystemd/sd-login/test-login.c b/src/libsystemd/sd-login/test-login.c index 9de33d85db..b618b79b28 100644 --- a/src/libsystemd/sd-login/test-login.c +++ b/src/libsystemd/sd-login/test-login.c @@ -25,173 +25,199 @@ #include "alloc-util.h" #include "fd-util.h" #include "format-util.h" +#include "log.h" #include "string-util.h" #include "strv.h" #include "util.h" +static char* format_uids(char **buf, uid_t* uids, int count) { + int pos = 0, k, inc; + size_t size = (DECIMAL_STR_MAX(uid_t) + 1) * count + 1; + + assert_se(*buf = malloc(size)); + + for (k = 0; k < count; k++) { + sprintf(*buf + pos, "%s"UID_FMT"%n", k > 0 ? " " : "", uids[k], &inc); + pos += inc; + } + + assert_se(pos < (ssize_t)size); + (*buf)[pos] = '\0'; + + return *buf; +} + static void test_login(void) { _cleanup_close_pair_ int pair[2] = { -1, -1 }; - _cleanup_free_ char *pp = NULL, *qq = NULL; + _cleanup_free_ char *pp = NULL, *qq = NULL, + *display_session = NULL, *cgroup = NULL, + *display = NULL, *remote_user = NULL, *remote_host = NULL, + *type = NULL, *class = NULL, *state = NULL, *state2 = NULL, + *seat = NULL, *session = NULL, + *unit = NULL, *user_unit = NULL, *slice = NULL; int r, k; uid_t u, u2; - char *seat, *type, *class, *display, *remote_user, *remote_host, *display_session, *cgroup; - char *session; - char *state; - char *session2; - char *t; - char **seats, **sessions, **machines; - uid_t *uids; - unsigned n; - struct pollfd pollfd; - sd_login_monitor *m = NULL; + char *t, **seats, **sessions; - assert_se(sd_pid_get_session(0, &session) == 0); - printf("session = %s\n", session); - - assert_se(sd_pid_get_owner_uid(0, &u2) == 0); - printf("user = "UID_FMT"\n", u2); + r = sd_pid_get_unit(0, &unit); + assert_se(r >= 0 || r == -ENODATA); + log_info("sd_pid_get_unit(0, …) → \"%s\"", strna(unit)); - assert_se(sd_pid_get_cgroup(0, &cgroup) == 0); - printf("cgroup = %s\n", cgroup); - free(cgroup); + r = sd_pid_get_user_unit(0, &user_unit); + assert_se(r >= 0 || r == -ENODATA); + log_info("sd_pid_get_user_unit(0, …) → \"%s\"", strna(user_unit)); - display_session = NULL; - r = sd_uid_get_display(u2, &display_session); + r = sd_pid_get_slice(0, &slice); assert_se(r >= 0 || r == -ENODATA); - printf("user's display session = %s\n", strna(display_session)); - free(display_session); + log_info("sd_pid_get_slice(0, …) → \"%s\"", strna(slice)); - assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == 0); - sd_peer_get_session(pair[0], &pp); - sd_peer_get_session(pair[1], &qq); - assert_se(streq_ptr(pp, qq)); + r = sd_pid_get_session(0, &session); + if (r < 0) { + log_warning_errno(r, "sd_pid_get_session(0, …): %m"); + if (r == -ENODATA) + log_info("Seems we are not running in a session, skipping some tests."); + } else { + log_info("sd_pid_get_session(0, …) → \"%s\"", session); - r = sd_uid_get_sessions(u2, false, &sessions); - assert_se(r >= 0); - assert_se(r == (int) strv_length(sessions)); - assert_se(t = strv_join(sessions, ", ")); - strv_free(sessions); - printf("sessions = %s\n", t); - free(t); + assert_se(sd_pid_get_owner_uid(0, &u2) == 0); + log_info("sd_pid_get_owner_uid(0, …) → "UID_FMT, u2); - assert_se(r == sd_uid_get_sessions(u2, false, NULL)); + assert_se(sd_pid_get_cgroup(0, &cgroup) == 0); + log_info("sd_pid_get_cgroup(0, …) → \"%s\"", cgroup); - r = sd_uid_get_seats(u2, false, &seats); - assert_se(r >= 0); - assert_se(r == (int) strv_length(seats)); - assert_se(t = strv_join(seats, ", ")); - strv_free(seats); - printf("seats = %s\n", t); - free(t); + r = sd_uid_get_display(u2, &display_session); + assert_se(r >= 0 || r == -ENODATA); + log_info("sd_uid_get_display("UID_FMT", …) → \"%s\"", + u2, strnull(display_session)); - assert_se(r == sd_uid_get_seats(u2, false, NULL)); + assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == 0); + sd_peer_get_session(pair[0], &pp); + sd_peer_get_session(pair[1], &qq); + assert_se(streq_ptr(pp, qq)); - r = sd_session_is_active(session); - assert_se(r >= 0); - printf("active = %s\n", yes_no(r)); + r = sd_uid_get_sessions(u2, false, &sessions); + assert_se(r >= 0); + assert_se(r == (int) strv_length(sessions)); + assert_se(t = strv_join(sessions, " ")); + strv_free(sessions); + log_info("sd_uid_get_sessions("UID_FMT", …) → [%i] \"%s\"", u2, r, t); + free(t); - r = sd_session_is_remote(session); - assert_se(r >= 0); - printf("remote = %s\n", yes_no(r)); + assert_se(r == sd_uid_get_sessions(u2, false, NULL)); - r = sd_session_get_state(session, &state); - assert_se(r >= 0); - printf("state = %s\n", state); - free(state); + r = sd_uid_get_seats(u2, false, &seats); + assert_se(r >= 0); + assert_se(r == (int) strv_length(seats)); + assert_se(t = strv_join(seats, " ")); + strv_free(seats); + log_info("sd_uid_get_seats("UID_FMT", …) → [%i] \"%s\"", u2, r, t); + free(t); - assert_se(sd_session_get_uid(session, &u) >= 0); - printf("uid = "UID_FMT"\n", u); - assert_se(u == u2); + assert_se(r == sd_uid_get_seats(u2, false, NULL)); + } - assert_se(sd_session_get_type(session, &type) >= 0); - printf("type = %s\n", type); - free(type); + if (session) { + r = sd_session_is_active(session); + assert_se(r >= 0); + log_info("sd_session_is_active(\"%s\") → %s", session, yes_no(r)); - assert_se(sd_session_get_class(session, &class) >= 0); - printf("class = %s\n", class); - free(class); + r = sd_session_is_remote(session); + assert_se(r >= 0); + log_info("sd_session_is_remote(\"%s\") → %s", session, yes_no(r)); - display = NULL; - r = sd_session_get_display(session, &display); - assert_se(r >= 0 || r == -ENODATA); - printf("display = %s\n", strna(display)); - free(display); + r = sd_session_get_state(session, &state); + assert_se(r >= 0); + log_info("sd_session_get_state(\"%s\") → \"%s\"", session, state); - remote_user = NULL; - r = sd_session_get_remote_user(session, &remote_user); - assert_se(r >= 0 || r == -ENODATA); - printf("remote_user = %s\n", strna(remote_user)); - free(remote_user); + assert_se(sd_session_get_uid(session, &u) >= 0); + log_info("sd_session_get_uid(\"%s\") → "UID_FMT, session, u); + assert_se(u == u2); - remote_host = NULL; - r = sd_session_get_remote_host(session, &remote_host); - assert_se(r >= 0 || r == -ENODATA); - printf("remote_host = %s\n", strna(remote_host)); - free(remote_host); + assert_se(sd_session_get_type(session, &type) >= 0); + log_info("sd_session_get_type(\"%s\") → \"%s\"", session, type); - assert_se(sd_session_get_seat(session, &seat) >= 0); - printf("seat = %s\n", seat); + assert_se(sd_session_get_class(session, &class) >= 0); + log_info("sd_session_get_class(\"%s\") → \"%s\"", session, class); - r = sd_seat_can_multi_session(seat); - assert_se(r >= 0); - printf("can do multi session = %s\n", yes_no(r)); + r = sd_session_get_display(session, &display); + assert_se(r >= 0 || r == -ENODATA); + log_info("sd_session_get_display(\"%s\") → \"%s\"", session, strna(display)); - r = sd_seat_can_tty(seat); - assert_se(r >= 0); - printf("can do tty = %s\n", yes_no(r)); + r = sd_session_get_remote_user(session, &remote_user); + assert_se(r >= 0 || r == -ENODATA); + log_info("sd_session_get_remote_user(\"%s\") → \"%s\"", + session, strna(remote_user)); - r = sd_seat_can_graphical(seat); - assert_se(r >= 0); - printf("can do graphical = %s\n", yes_no(r)); + r = sd_session_get_remote_host(session, &remote_host); + assert_se(r >= 0 || r == -ENODATA); + log_info("sd_session_get_remote_host(\"%s\") → \"%s\"", + session, strna(remote_host)); - assert_se(sd_uid_get_state(u, &state) >= 0); - printf("state = %s\n", state); + r = sd_session_get_seat(session, &seat); + if (r >= 0) { + assert_se(seat); - assert_se(sd_uid_is_on_seat(u, 0, seat) > 0); + log_info("sd_session_get_display(\"%s\") → \"%s\"", session, seat); - k = sd_uid_is_on_seat(u, 1, seat); - assert_se(k >= 0); - assert_se(!!r == !!r); + r = sd_seat_can_multi_session(seat); + assert_se(r >= 0); + log_info("sd_session_can_multi_seat(\"%s\") → %s", seat, yes_no(r)); - assert_se(sd_seat_get_active(seat, &session2, &u2) >= 0); - printf("session2 = %s\n", session2); - printf("uid2 = "UID_FMT"\n", u2); + r = sd_seat_can_tty(seat); + assert_se(r >= 0); + log_info("sd_session_can_tty(\"%s\") → %s", seat, yes_no(r)); - r = sd_seat_get_sessions(seat, &sessions, &uids, &n); - assert_se(r >= 0); - printf("n_sessions = %i\n", r); - assert_se(r == (int) strv_length(sessions)); - assert_se(t = strv_join(sessions, ", ")); - strv_free(sessions); - printf("sessions = %s\n", t); - free(t); - printf("uids ="); - for (k = 0; k < (int) n; k++) - printf(" "UID_FMT, uids[k]); - printf("\n"); - free(uids); + r = sd_seat_can_graphical(seat); + assert_se(r >= 0); + log_info("sd_session_can_graphical(\"%s\") → %s", seat, yes_no(r)); + } else { + log_info_errno(r, "sd_session_get_display(\"%s\"): %m", session); + assert_se(r == -ENODATA); + } - assert_se(sd_seat_get_sessions(seat, NULL, NULL, NULL) == r); + assert_se(sd_uid_get_state(u, &state2) >= 0); + log_info("sd_uid_get_state("UID_FMT", …) → %s", u, state2); + } + + if (seat) { + _cleanup_free_ char *session2 = NULL, *buf = NULL; + _cleanup_free_ uid_t *uids = NULL; + unsigned n; + + assert_se(sd_uid_is_on_seat(u, 0, seat) > 0); + + k = sd_uid_is_on_seat(u, 1, seat); + assert_se(k >= 0); + assert_se(!!k == !!r); - free(session); - free(state); - free(session2); - free(seat); + assert_se(sd_seat_get_active(seat, &session2, &u2) >= 0); + log_info("sd_seat_get_active(\"%s\", …) → \"%s\", "UID_FMT, seat, session2, u2); + + r = sd_seat_get_sessions(seat, &sessions, &uids, &n); + assert_se(r >= 0); + assert_se(r == (int) strv_length(sessions)); + assert_se(t = strv_join(sessions, " ")); + strv_free(sessions); + log_info("sd_seat_get_sessions(\"%s\", …) → %i, \"%s\", [%i] {%s}", + seat, r, t, n, format_uids(&buf, uids, n)); + free(t); + + assert_se(sd_seat_get_sessions(seat, NULL, NULL, NULL) == r); + } r = sd_get_seats(&seats); assert_se(r >= 0); assert_se(r == (int) strv_length(seats)); assert_se(t = strv_join(seats, ", ")); strv_free(seats); - printf("n_seats = %i\n", r); - printf("seats = %s\n", t); - free(t); + log_info("sd_get_seats(…) → [%i] \"%s\"", r, t); + t = mfree(t); assert_se(sd_get_seats(NULL) == r); r = sd_seat_get_active(NULL, &t, NULL); - assert_se(r >= 0); - printf("active session on current seat = %s\n", t); + assert_se(IN_SET(r, 0, -ENODATA)); + log_info("sd_seat_get_active(NULL, …) (active session on current seat) → %s", strnull(t)); free(t); r = sd_get_sessions(&sessions); @@ -199,40 +225,48 @@ static void test_login(void) { assert_se(r == (int) strv_length(sessions)); assert_se(t = strv_join(sessions, ", ")); strv_free(sessions); - printf("n_sessions = %i\n", r); - printf("sessions = %s\n", t); + log_info("sd_get_sessions(…) → [%i] \"%s\"", r, t); free(t); assert_se(sd_get_sessions(NULL) == r); - r = sd_get_uids(&uids); - assert_se(r >= 0); + { + _cleanup_free_ uid_t *uids = NULL; + _cleanup_free_ char *buf = NULL; + + r = sd_get_uids(&uids); + assert_se(r >= 0); + log_info("sd_get_uids(…) → [%i] {%s}", r, format_uids(&buf, uids, r)); - printf("uids ="); - for (k = 0; k < r; k++) - printf(" "UID_FMT, uids[k]); - printf("\n"); - free(uids); + assert_se(sd_get_uids(NULL) == r); + } - printf("n_uids = %i\n", r); - assert_se(sd_get_uids(NULL) == r); + { + _cleanup_strv_free_ char **machines = NULL; + _cleanup_free_ char *buf = NULL; - r = sd_get_machine_names(&machines); - assert_se(r >= 0); - assert_se(r == (int) strv_length(machines)); - assert_se(t = strv_join(machines, ", ")); - strv_free(machines); - printf("n_machines = %i\n", r); - printf("machines = %s\n", t); - free(t); + r = sd_get_machine_names(&machines); + assert_se(r >= 0); + assert_se(r == (int) strv_length(machines)); + assert_se(buf = strv_join(machines, " ")); + log_info("sd_get_machines(…) → [%i] \"%s\"", r, buf); + + assert_se(sd_get_machine_names(NULL) == r); + } +} + +static void test_monitor(void) { + sd_login_monitor *m = NULL; + unsigned n; + int r; r = sd_login_monitor_new("session", &m); assert_se(r >= 0); for (n = 0; n < 5; n++) { + struct pollfd pollfd = {}; usec_t timeout, nw; - zero(pollfd); assert_se((pollfd.fd = sd_login_monitor_get_fd(m)) >= 0); assert_se((pollfd.events = sd_login_monitor_get_events(m)) >= 0); @@ -258,7 +292,12 @@ int main(int argc, char* argv[]) { log_parse_environment(); log_open(); + log_info("/* Information printed is from the live system */"); + test_login(); + if (streq_ptr(argv[1], "-m")) + test_monitor(); + return 0; } diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c index 654a22fe3b..e8c8abac2a 100644 --- a/src/libsystemd/sd-netlink/netlink-message.c +++ b/src/libsystemd/sd-netlink/netlink-message.c @@ -104,7 +104,8 @@ int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) { assert_return(m->hdr->nlmsg_type == RTM_GETLINK || m->hdr->nlmsg_type == RTM_GETADDR || m->hdr->nlmsg_type == RTM_GETROUTE || - m->hdr->nlmsg_type == RTM_GETNEIGH, + m->hdr->nlmsg_type == RTM_GETNEIGH || + m->hdr->nlmsg_type == RTM_GETADDRLABEL , -EINVAL); SET_FLAG(m->hdr->nlmsg_flags, NLM_F_DUMP, dump); diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c index ff0e99558e..923f7dd10c 100644 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ b/src/libsystemd/sd-netlink/netlink-types.c @@ -26,6 +26,7 @@ #include <linux/veth.h> #include <linux/if_bridge.h> #include <linux/if_addr.h> +#include <linux/if_addrlabel.h> #include <linux/if.h> #include <linux/ip.h> #include <linux/if_link.h> @@ -170,6 +171,9 @@ static const NLType rtnl_link_info_data_vxlan_types[] = { [IFLA_VXLAN_REMCSUM_RX] = { .type = NETLINK_TYPE_U8 }, [IFLA_VXLAN_GBP] = { .type = NETLINK_TYPE_FLAG }, [IFLA_VXLAN_REMCSUM_NOPARTIAL] = { .type = NETLINK_TYPE_FLAG }, + [IFLA_VXLAN_COLLECT_METADATA] = { .type = NETLINK_TYPE_U8 }, + [IFLA_VXLAN_LABEL] = { .type = NETLINK_TYPE_U32 }, + [IFLA_VXLAN_GPE] = { .type = NETLINK_TYPE_FLAG }, }; static const NLType rtnl_bond_arp_target_types[] = { @@ -283,6 +287,19 @@ static const NLType rtnl_link_info_data_vrf_types[] = { [IFLA_VRF_TABLE] = { .type = NETLINK_TYPE_U32 }, }; +static const NLType rtnl_link_info_data_geneve_types[] = { + [IFLA_GENEVE_ID] = { .type = NETLINK_TYPE_U32 }, + [IFLA_GENEVE_TTL] = { .type = NETLINK_TYPE_U8 }, + [IFLA_GENEVE_TOS] = { .type = NETLINK_TYPE_U8 }, + [IFLA_GENEVE_PORT] = { .type = NETLINK_TYPE_U16 }, + [IFLA_GENEVE_REMOTE] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_GENEVE_REMOTE6] = { .type = NETLINK_TYPE_IN_ADDR }, + [IFLA_GENEVE_UDP_CSUM] = { .type = NETLINK_TYPE_U8 }, + [IFLA_GENEVE_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_U8 }, + [IFLA_GENEVE_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_U8 }, + [IFLA_GENEVE_LABEL] = { .type = NETLINK_TYPE_U32 }, +}; + /* these strings must match the .kind entries in the kernel */ static const char* const nl_union_link_info_data_table[] = { [NL_UNION_LINK_INFO_DATA_BOND] = "bond", @@ -305,6 +322,7 @@ static const char* const nl_union_link_info_data_table[] = { [NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL] = "ip6tnl", [NL_UNION_LINK_INFO_DATA_VRF] = "vrf", [NL_UNION_LINK_INFO_DATA_VCAN] = "vcan", + [NL_UNION_LINK_INFO_DATA_GENEVE] = "geneve", }; DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData); @@ -346,6 +364,8 @@ static const NLTypeSystem rtnl_link_info_data_type_systems[] = { .types = rtnl_link_info_data_ip6tnl_types }, [NL_UNION_LINK_INFO_DATA_VRF] = { .count = ELEMENTSOF(rtnl_link_info_data_vrf_types), .types = rtnl_link_info_data_vrf_types }, + [NL_UNION_LINK_INFO_DATA_GENEVE] = { .count = ELEMENTSOF(rtnl_link_info_data_geneve_types), + .types = rtnl_link_info_data_geneve_types }, }; static const NLTypeSystemUnion rtnl_link_info_data_type_system_union = { @@ -567,22 +587,35 @@ static const NLTypeSystem rtnl_neigh_type_system = { .types = rtnl_neigh_types, }; +static const NLType rtnl_addrlabel_types[] = { + [IFAL_ADDRESS] = { .type = NETLINK_TYPE_IN_ADDR, .size = sizeof(struct in6_addr) }, + [IFAL_LABEL] = { .type = NETLINK_TYPE_U32 }, +}; + +static const NLTypeSystem rtnl_addrlabel_type_system = { + .count = ELEMENTSOF(rtnl_addrlabel_types), + .types = rtnl_addrlabel_types, +}; + static const NLType rtnl_types[] = { - [NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 }, - [NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = sizeof(struct nlmsgerr) }, - [RTM_NEWLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, - [RTM_DELLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, - [RTM_GETLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, - [RTM_SETLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, - [RTM_NEWADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, - [RTM_DELADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, - [RTM_GETADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, - [RTM_NEWROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, - [RTM_DELROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, - [RTM_GETROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, - [RTM_NEWNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, - [RTM_DELNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, - [RTM_GETNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, + [NLMSG_DONE] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = 0 }, + [NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = sizeof(struct nlmsgerr) }, + [RTM_NEWLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, + [RTM_DELLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, + [RTM_GETLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, + [RTM_SETLINK] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, + [RTM_NEWADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, + [RTM_DELADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, + [RTM_GETADDR] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_address_type_system, .size = sizeof(struct ifaddrmsg) }, + [RTM_NEWROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, + [RTM_DELROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, + [RTM_GETROUTE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, + [RTM_NEWNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, + [RTM_DELNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, + [RTM_GETNEIGH] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, + [RTM_NEWADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) }, + [RTM_DELADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) }, + [RTM_GETADDRLABEL] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_addrlabel_type_system, .size = sizeof(struct ifaddrlblmsg) }, }; const NLTypeSystem type_system_root = { diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h index 42e96173de..ae65c1d8e4 100644 --- a/src/libsystemd/sd-netlink/netlink-types.h +++ b/src/libsystemd/sd-netlink/netlink-types.h @@ -88,6 +88,7 @@ typedef enum NLUnionLinkInfoData { NL_UNION_LINK_INFO_DATA_IP6TNL_TUNNEL, NL_UNION_LINK_INFO_DATA_VRF, NL_UNION_LINK_INFO_DATA_VCAN, + NL_UNION_LINK_INFO_DATA_GENEVE, _NL_UNION_LINK_INFO_DATA_MAX, _NL_UNION_LINK_INFO_DATA_INVALID = -1 } NLUnionLinkInfoData; diff --git a/src/libsystemd/sd-netlink/netlink-util.c b/src/libsystemd/sd-netlink/netlink-util.c index 73b9ac0258..6b660b7dbf 100644 --- a/src/libsystemd/sd-netlink/netlink-util.c +++ b/src/libsystemd/sd-netlink/netlink-util.c @@ -116,51 +116,6 @@ int rtnl_message_new_synthetic_error(int error, uint32_t serial, sd_netlink_mess return 0; } -bool rtnl_message_type_is_neigh(uint16_t type) { - switch (type) { - case RTM_NEWNEIGH: - case RTM_GETNEIGH: - case RTM_DELNEIGH: - return true; - default: - return false; - } -} - -bool rtnl_message_type_is_route(uint16_t type) { - switch (type) { - case RTM_NEWROUTE: - case RTM_GETROUTE: - case RTM_DELROUTE: - return true; - default: - return false; - } -} - -bool rtnl_message_type_is_link(uint16_t type) { - switch (type) { - case RTM_NEWLINK: - case RTM_SETLINK: - case RTM_GETLINK: - case RTM_DELLINK: - return true; - default: - return false; - } -} - -bool rtnl_message_type_is_addr(uint16_t type) { - switch (type) { - case RTM_NEWADDR: - case RTM_GETADDR: - case RTM_DELADDR: - return true; - default: - return false; - } -} - int rtnl_log_parse_error(int r) { return log_error_errno(r, "Failed to parse netlink message: %m"); } diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h index f49bf4eaa6..215af12406 100644 --- a/src/libsystemd/sd-netlink/netlink-util.h +++ b/src/libsystemd/sd-netlink/netlink-util.h @@ -27,10 +27,25 @@ int rtnl_message_new_synthetic_error(int error, uint32_t serial, sd_netlink_mess uint32_t rtnl_message_get_serial(sd_netlink_message *m); void rtnl_message_seal(sd_netlink_message *m); -bool rtnl_message_type_is_link(uint16_t type); -bool rtnl_message_type_is_addr(uint16_t type); -bool rtnl_message_type_is_route(uint16_t type); -bool rtnl_message_type_is_neigh(uint16_t type); +static inline bool rtnl_message_type_is_neigh(uint16_t type) { + return IN_SET(type, RTM_NEWNEIGH, RTM_GETNEIGH, RTM_DELNEIGH); +} + +static inline bool rtnl_message_type_is_route(uint16_t type) { + return IN_SET(type, RTM_NEWROUTE, RTM_GETROUTE, RTM_DELROUTE); +} + +static inline bool rtnl_message_type_is_link(uint16_t type) { + return IN_SET(type, RTM_NEWLINK, RTM_SETLINK, RTM_GETLINK, RTM_DELLINK); +} + +static inline bool rtnl_message_type_is_addr(uint16_t type) { + return IN_SET(type, RTM_NEWADDR, RTM_GETADDR, RTM_DELADDR); +} + +static inline bool rtnl_message_type_is_addrlabel(uint16_t type) { + return IN_SET(type, RTM_NEWADDRLABEL, RTM_DELADDRLABEL, RTM_GETADDRLABEL); +} int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name); int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, const struct ether_addr *mac, unsigned mtu); diff --git a/src/libsystemd/sd-netlink/rtnl-message.c b/src/libsystemd/sd-netlink/rtnl-message.c index b543b5f20c..12c51ffe2e 100644 --- a/src/libsystemd/sd-netlink/rtnl-message.c +++ b/src/libsystemd/sd-netlink/rtnl-message.c @@ -18,6 +18,7 @@ ***/ #include <netinet/in.h> +#include <linux/if_addrlabel.h> #include <stdbool.h> #include <unistd.h> @@ -700,3 +701,56 @@ int sd_rtnl_message_get_family(sd_netlink_message *m, int *family) { return -EOPNOTSUPP; } + +int sd_rtnl_message_new_addrlabel(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int ifindex, int ifal_family) { + struct ifaddrlblmsg *addrlabel; + int r; + + assert_return(rtnl_message_type_is_addrlabel(nlmsg_type), -EINVAL); + assert_return(ret, -EINVAL); + + r = message_new(rtnl, ret, nlmsg_type); + if (r < 0) + return r; + + if (nlmsg_type == RTM_NEWADDRLABEL) + (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; + + addrlabel = NLMSG_DATA((*ret)->hdr); + + addrlabel->ifal_family = ifal_family; + addrlabel->ifal_index = ifindex; + + return 0; +} + +int sd_rtnl_message_addrlabel_set_prefixlen(sd_netlink_message *m, unsigned char prefixlen) { + struct ifaddrlblmsg *addrlabel; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addrlabel(m->hdr->nlmsg_type), -EINVAL); + + addrlabel = NLMSG_DATA(m->hdr); + + if (prefixlen > 128) + return -ERANGE; + + addrlabel->ifal_prefixlen = prefixlen; + + return 0; +} + +int sd_rtnl_message_addrlabel_get_prefixlen(sd_netlink_message *m, unsigned char *prefixlen) { + struct ifaddrlblmsg *addrlabel; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_addrlabel(m->hdr->nlmsg_type), -EINVAL); + + addrlabel = NLMSG_DATA(m->hdr); + + *prefixlen = addrlabel->ifal_prefixlen; + + return 0; +} diff --git a/src/libudev/libudev-monitor.c b/src/libudev/libudev-monitor.c index a40329d732..8287694c49 100644 --- a/src/libudev/libudev-monitor.c +++ b/src/libudev/libudev-monitor.c @@ -681,7 +681,7 @@ retry: udev_device = udev_device_new_from_nulstr(udev_monitor->udev, &buf.raw[bufpos], buflen - bufpos); if (!udev_device) { - log_debug("could not create device: %m"); + log_debug_errno(errno, "could not create device: %m"); return NULL; } diff --git a/src/libudev/libudev.c b/src/libudev/libudev.c index d8e13288b0..5f2225f402 100644 --- a/src/libudev/libudev.c +++ b/src/libudev/libudev.c @@ -103,82 +103,6 @@ _public_ struct udev *udev_new(void) { } udev->refcount = 1; - f = fopen("/etc/udev/udev.conf", "re"); - if (f != NULL) { - char line[UTIL_LINE_SIZE]; - unsigned line_nr = 0; - - while (fgets(line, sizeof(line), f)) { - size_t len; - char *key; - char *val; - - line_nr++; - - /* find key */ - key = line; - while (isspace(key[0])) - key++; - - /* comment or empty line */ - if (key[0] == '#' || key[0] == '\0') - continue; - - /* split key/value */ - val = strchr(key, '='); - if (val == NULL) { - log_debug("/etc/udev/udev.conf:%u: missing assignment, skipping line.", line_nr); - continue; - } - val[0] = '\0'; - val++; - - /* find value */ - while (isspace(val[0])) - val++; - - /* terminate key */ - len = strlen(key); - if (len == 0) - continue; - while (isspace(key[len-1])) - len--; - key[len] = '\0'; - - /* terminate value */ - len = strlen(val); - if (len == 0) - continue; - while (isspace(val[len-1])) - len--; - val[len] = '\0'; - - if (len == 0) - continue; - - /* unquote */ - if (val[0] == '"' || val[0] == '\'') { - if (len == 1 || val[len-1] != val[0]) { - log_debug("/etc/udev/udev.conf:%u: inconsistent quoting, skipping line.", line_nr); - continue; - } - val[len-1] = '\0'; - val++; - } - - if (streq(key, "udev_log")) { - int prio; - - prio = util_log_priority(val); - if (prio < 0) - log_debug("/etc/udev/udev.conf:%u: invalid log level '%s', ignoring.", line_nr, val); - else - log_set_max_level(prio); - continue; - } - } - } - return udev; } diff --git a/src/libudev/libudev.pc.in b/src/libudev/libudev.pc.in index 770c92209e..1becae45fd 100644 --- a/src/libudev/libudev.pc.in +++ b/src/libudev/libudev.pc.in @@ -12,6 +12,6 @@ includedir=@includedir@ Name: libudev Description: Library to access udev device information -Version: @VERSION@ +Version: @PACKAGE_VERSION@ Libs: -L${libdir} -ludev Cflags: -I${includedir} diff --git a/src/libudev/meson.build b/src/libudev/meson.build new file mode 100644 index 0000000000..1378f9a251 --- /dev/null +++ b/src/libudev/meson.build @@ -0,0 +1,41 @@ +libudev_sources = files(''' + libudev-private.h + libudev-device-internal.h + libudev.c + libudev-list.c + libudev-util.c + libudev-device.c + libudev-device-private.c + libudev-enumerate.c + libudev-monitor.c + libudev-queue.c + libudev-hwdb.c +'''.split()) + +############################################################ + +libudev_sym = 'libudev.sym' +libudev_sym_path = '@0@/@1@'.format(meson.current_source_dir(), libudev_sym) +libudev = shared_library( + 'udev', + libudev_sources, + version : '1.6.6', + include_directories : includes, + link_args : ['-shared', + '-Wl,--version-script=' + libudev_sym_path], + link_with : [libbasic, + libsystemd_internal], + dependencies : [threads], + link_depends : libudev_sym, + install : true, + install_dir : rootlibdir) + +install_headers('libudev.h') +libudev_h_path = '@0@/libudev.h'.format(meson.current_source_dir()) + +libudev_pc = configure_file( + input : 'libudev.pc.in', + output : 'libudev.pc', + configuration : substs) +install_data(libudev_pc, + install_dir : pkgconfiglibdir) diff --git a/src/locale/keymap-util.c b/src/locale/keymap-util.c index da72bee4a9..105d9b0ee4 100644 --- a/src/locale/keymap-util.c +++ b/src/locale/keymap-util.c @@ -361,8 +361,9 @@ int x11_write_data(Context *c) { fchmod(fileno(f), 0644); - fputs("# Read and parsed by systemd-localed. It's probably wise not to edit this file\n" - "# manually too freely.\n" + fputs("# Written by systemd-localed(8), read by systemd-localed and Xorg. It's\n" + "# probably wise not to edit this file manually. Use localectl(1) to\n" + "# instruct systemd-localed to update it.\n" "Section \"InputClass\"\n" " Identifier \"system-keyboard\"\n" " MatchIsKeyboard \"on\"\n", f); diff --git a/src/locale/localed.c b/src/locale/localed.c index 1cb049e74a..b4798d674c 100644 --- a/src/locale/localed.c +++ b/src/locale/localed.c @@ -436,7 +436,10 @@ static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char const char *fmt; fmt = strjoina("libxkbcommon: ", format); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" log_internalv(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, fmt, args); +#pragma GCC diagnostic pop } #define LOAD_SYMBOL(symbol, dl, name) \ diff --git a/src/locale/meson.build b/src/locale/meson.build new file mode 100644 index 0000000000..d7dd113c8d --- /dev/null +++ b/src/locale/meson.build @@ -0,0 +1,42 @@ +systemd_localed_sources = files(''' + localed.c + keymap-util.c + keymap-util.h +'''.split()) + +localectl_sources = files('localectl.c') + +if conf.get('ENABLE_LOCALED', false) + install_data('org.freedesktop.locale1.conf', + install_dir : dbuspolicydir) + install_data('org.freedesktop.locale1.service', + install_dir : dbussystemservicedir) + + custom_target( + 'org.freedesktop.locale1.policy', + input : 'org.freedesktop.locale1.policy.in', + output : 'org.freedesktop.locale1.policy', + command : intltool_command, + install : install_polkit, + install_dir : polkitpolicydir) +endif + +# If you know a way that allows the same variables to be used +# in sources list and concatenated to a string for test_env, +# let me know. +kbd_model_map = join_paths(meson.current_source_dir(), 'kbd-model-map') +language_fallback_map = join_paths(meson.current_source_dir(), 'language-fallback-map') + +if conf.get('ENABLE_LOCALED', false) + install_data('kbd-model-map', + 'language-fallback-map', + install_dir : pkgdatadir) +endif + +tests += [ + [['src/locale/test-keymap-util.c', + 'src/locale/keymap-util.c', + 'src/locale/keymap-util.h'], + [libshared], + [libdl]], +] diff --git a/src/login/70-power-switch.rules b/src/login/70-power-switch.rules index e2855b50f7..394a80f1f8 100644 --- a/src/login/70-power-switch.rules +++ b/src/login/70-power-switch.rules @@ -7,12 +7,7 @@ ACTION=="remove", GOTO="power_switch_end" -SUBSYSTEM=="input", KERNEL=="event*", SUBSYSTEMS=="acpi", TAG+="power-switch" -SUBSYSTEM=="input", KERNEL=="event*", KERNELS=="thinkpad_acpi", TAG+="power-switch" -SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="twl4030_pwrbutton", TAG+="power-switch" -SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="tps65217_pwr_but", TAG+="power-switch" -SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="* WMI hotkeys", TAG+="power-switch" -SUBSYSTEM=="input", KERNEL=="event*", \ - SUBSYSTEMS=="platform", DRIVERS=="gpio-keys", ATTRS{keys}=="*,116|116,*|116|*,116,*", TAG+="power-switch" +SUBSYSTEM=="input", KERNEL=="event*", ENV{ID_INPUT_SWITCH}=="1", TAG+="power-switch" +SUBSYSTEM=="input", KERNEL=="event*", ENV{ID_INPUT_KEY}=="1", TAG+="power-switch" LABEL="power_switch_end" diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 7dea5c0859..1862e8983c 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -907,6 +907,8 @@ static int show_session(int argc, char *argv[], void *userdata) { bool properties, new_line = false; sd_bus *bus = userdata; int r, i; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *path = NULL; assert(bus); assert(argv); @@ -916,20 +918,28 @@ static int show_session(int argc, char *argv[], void *userdata) { pager_open(arg_no_pager, false); if (argc <= 1) { - /* If not argument is specified inspect the manager - * itself */ + const char *session, *p = "/org/freedesktop/login1/session/self"; + if (properties) + /* If no argument is specified inspect the manager itself */ return show_properties(bus, "/org/freedesktop/login1", &new_line); /* And in the pretty case, show data of the calling session */ - return print_session_status_info(bus, "/org/freedesktop/login1/session/self", &new_line); + session = getenv("XDG_SESSION_ID"); + if (session) { + r = get_session_path(bus, session, &error, &path); + if (r < 0) { + log_error("Failed to get session path: %s", bus_error_message(&error, r)); + return r; + } + p = path; + } + + return print_session_status_info(bus, p, &new_line); } for (i = 1; i < argc; i++) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_free_ char *path = NULL; - - r = get_session_path(bus, argv[1], &error, &path); + r = get_session_path(bus, argv[i], &error, &path); if (r < 0) { log_error("Failed to get session path: %s", bus_error_message(&error, r)); return r; @@ -1074,12 +1084,11 @@ static int activate(int argc, char *argv[], void *userdata) { polkit_agent_open_if_enabled(); if (argc < 2) { - /* No argument? Let's convert this into the empty - * session name, which the calls will then resolve to - * the caller's session. */ + /* No argument? Let's either use $XDG_SESSION_ID (if specified), or an empty + * session name, in which case logind will try to guess our session. */ short_argv[0] = argv[0]; - short_argv[1] = (char*) ""; + short_argv[1] = getenv("XDG_SESSION_ID") ?: (char*) ""; short_argv[2] = NULL; argv = short_argv; @@ -1155,8 +1164,11 @@ static int enable_linger(int argc, char *argv[], void *userdata) { b = streq(argv[0], "enable-linger"); if (argc < 2) { + /* No argument? Let's either use $XDG_SESSION_ID (if specified), or an empty + * session name, in which case logind will try to guess our session. */ + short_argv[0] = argv[0]; - short_argv[1] = (char*) ""; + short_argv[1] = getenv("XDG_SESSION_ID") ?: (char*) ""; short_argv[2] = NULL; argv = short_argv; argc = 2; @@ -1386,8 +1398,10 @@ static int help(int argc, char *argv[], void *userdata) { " --kill-who=WHO Who to send signal to\n" " -s --signal=SIGNAL Which signal to send\n" " -n --lines=INTEGER Number of journal entries to show\n" - " -o --output=STRING Change journal output mode (short, short-monotonic,\n" - " verbose, export, json, json-pretty, json-sse, cat)\n\n" + " -o --output=STRING Change journal output mode (short, short-precise,\n" + " short-iso, short-iso-precise, short-full,\n" + " short-monotonic, short-unix, verbose, export,\n" + " json, json-pretty, json-sse, cat)\n" "Session Commands:\n" " list-sessions List sessions\n" " session-status [ID...] Show session status\n" diff --git a/src/login/logind-button.c b/src/login/logind-button.c index d739af8ea2..e53dd63c29 100644 --- a/src/login/logind-button.c +++ b/src/login/logind-button.c @@ -32,6 +32,18 @@ #include "string-util.h" #include "util.h" +#define CONST_MAX4(a, b, c, d) CONST_MAX(CONST_MAX(a, b), CONST_MAX(c, d)) + +#define ULONG_BITS (sizeof(unsigned long)*8) + +static bool bitset_get(const unsigned long *bits, unsigned i) { + return (bits[i / ULONG_BITS] >> (i % ULONG_BITS)) & 1UL; +} + +static void bitset_put(unsigned long *bits, unsigned i) { + bits[i / ULONG_BITS] |= (unsigned long) 1 << (i % ULONG_BITS); +} + Button* button_new(Manager *m, const char *name) { Button *b; @@ -231,6 +243,95 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u return 0; } +static int button_suitable(Button *b) { + unsigned long types[CONST_MAX(EV_KEY, EV_SW)/ULONG_BITS+1]; + + assert(b); + assert(b->fd); + + if (ioctl(b->fd, EVIOCGBIT(EV_SYN, sizeof(types)), types) < 0) + return -errno; + + if (bitset_get(types, EV_KEY)) { + unsigned long keys[CONST_MAX4(KEY_POWER, KEY_POWER2, KEY_SLEEP, KEY_SUSPEND)/ULONG_BITS+1]; + + if (ioctl(b->fd, EVIOCGBIT(EV_KEY, sizeof(keys)), keys) < 0) + return -errno; + + if (bitset_get(keys, KEY_POWER) || + bitset_get(keys, KEY_POWER2) || + bitset_get(keys, KEY_SLEEP) || + bitset_get(keys, KEY_SUSPEND)) + return true; + } + + if (bitset_get(types, EV_SW)) { + unsigned long switches[CONST_MAX(SW_LID, SW_DOCK)/ULONG_BITS+1]; + + if (ioctl(b->fd, EVIOCGBIT(EV_SW, sizeof(switches)), switches) < 0) + return -errno; + + if (bitset_get(switches, SW_LID) || + bitset_get(switches, SW_DOCK)) + return true; + } + + return false; +} + +static int button_set_mask(Button *b) { + unsigned long + types[CONST_MAX(EV_KEY, EV_SW)/ULONG_BITS+1] = {}, + keys[CONST_MAX4(KEY_POWER, KEY_POWER2, KEY_SLEEP, KEY_SUSPEND)/ULONG_BITS+1] = {}, + switches[CONST_MAX(SW_LID, SW_DOCK)/ULONG_BITS+1] = {}; + struct input_mask mask; + + assert(b); + assert(b->fd >= 0); + + bitset_put(types, EV_KEY); + bitset_put(types, EV_SW); + + mask = (struct input_mask) { + .type = EV_SYN, + .codes_size = sizeof(types), + .codes_ptr = PTR_TO_UINT64(types), + }; + + if (ioctl(b->fd, EVIOCSMASK, &mask) < 0) + /* Log only at debug level if the kernel doesn't do EVIOCSMASK yet */ + return log_full_errno(IN_SET(errno, ENOTTY, EOPNOTSUPP, EINVAL) ? LOG_DEBUG : LOG_WARNING, + errno, "Failed to set EV_SYN event mask on /dev/input/%s: %m", b->name); + + bitset_put(keys, KEY_POWER); + bitset_put(keys, KEY_POWER2); + bitset_put(keys, KEY_SLEEP); + bitset_put(keys, KEY_SUSPEND); + + mask = (struct input_mask) { + .type = EV_KEY, + .codes_size = sizeof(keys), + .codes_ptr = PTR_TO_UINT64(keys), + }; + + if (ioctl(b->fd, EVIOCSMASK, &mask) < 0) + return log_warning_errno(errno, "Failed to set EV_KEY event mask on /dev/input/%s: %m", b->name); + + bitset_put(switches, SW_LID); + bitset_put(switches, SW_DOCK); + + mask = (struct input_mask) { + .type = EV_SW, + .codes_size = sizeof(switches), + .codes_ptr = PTR_TO_UINT64(switches), + }; + + if (ioctl(b->fd, EVIOCSMASK, &mask) < 0) + return log_warning_errno(errno, "Failed to set EV_SW event mask on /dev/input/%s: %m", b->name); + + return 0; +} + int button_open(Button *b) { char *p, name[256]; int r; @@ -243,13 +344,23 @@ int button_open(Button *b) { b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); if (b->fd < 0) - return log_warning_errno(errno, "Failed to open %s: %m", b->name); + return log_warning_errno(errno, "Failed to open %s: %m", p); + + r = button_suitable(b); + if (r < 0) + return log_warning_errno(r, "Failed to determine whether input device is relevant to us: %m"); + if (r == 0) { + log_debug("Device %s does not expose keys or switches relevant to us, ignoring.", p); + return -EADDRNOTAVAIL; + } if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) { r = log_error_errno(errno, "Failed to get input name: %m"); goto fail; } + (void) button_set_mask(b); + r = sd_event_add_io(b->manager->event, &b->io_event_source, b->fd, EPOLLIN, button_dispatch, b); if (r < 0) { log_error_errno(r, "Failed to add button event: %m"); @@ -266,7 +377,7 @@ fail: } int button_check_switches(Button *b) { - uint8_t switches[SW_MAX/8+1] = {}; + unsigned long switches[CONST_MAX(SW_LID, SW_DOCK)/ULONG_BITS+1] = {}; assert(b); if (b->fd < 0) @@ -275,8 +386,8 @@ int button_check_switches(Button *b) { if (ioctl(b->fd, EVIOCGSW(sizeof(switches)), switches) < 0) return -errno; - b->lid_closed = (switches[SW_LID/8] >> (SW_LID % 8)) & 1; - b->docked = (switches[SW_DOCK/8] >> (SW_DOCK % 8)) & 1; + b->lid_closed = bitset_get(switches, SW_LID); + b->docked = bitset_get(switches, SW_DOCK); if (b->lid_closed) button_install_check_event_source(b); diff --git a/src/login/logind-core.c b/src/login/logind-core.c index eff5a4a36f..ebe1d68634 100644 --- a/src/login/logind-core.c +++ b/src/login/logind-core.c @@ -269,7 +269,11 @@ int manager_process_button_device(Manager *m, struct udev_device *d) { sn = "seat0"; button_set_seat(b, sn); - button_open(b); + + r = button_open(b); + if (r < 0) /* event device doesn't have any keys or switches relevant to us? (or any other error + * opening the device?) let's close the button again. */ + button_free(b); } return 0; diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index c6be596af3..c9b7d99818 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -65,6 +65,9 @@ int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const ch return r; r = sd_bus_creds_get_session(creds, &name); + if (r == -ENXIO) + return sd_bus_error_setf(error, BUS_ERROR_NO_SESSION_FOR_PID, + "Caller does not belong to any known session"); if (r < 0) return r; } diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c index 5ca42b1ca2..1e6f383738 100644 --- a/src/login/logind-inhibit.c +++ b/src/login/logind-inhibit.c @@ -347,7 +347,7 @@ InhibitWhat manager_inhibit_what(Manager *m, InhibitMode mm) { assert(m); HASHMAP_FOREACH(i, m->inhibitors, j) - if (i->mode == mm) + if (i->mode == mm && i->started) what |= i->what; return what; @@ -388,6 +388,9 @@ bool manager_is_inhibited( assert(w > 0 && w < _INHIBIT_WHAT_MAX); HASHMAP_FOREACH(i, m->inhibitors, j) { + if (!i->started) + continue; + if (!(i->what & w)) continue; diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c index 22dea5db1f..22e5349a67 100644 --- a/src/login/logind-session-dbus.c +++ b/src/login/logind-session-dbus.c @@ -396,7 +396,7 @@ static int method_take_control(sd_bus_message *message, void *userdata, sd_bus_e if (uid != 0 && (force || uid != s->user->uid)) return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may take control"); - r = session_set_controller(s, sd_bus_message_get_sender(message), force); + r = session_set_controller(s, sd_bus_message_get_sender(message), force, true); if (r < 0) return r; @@ -444,14 +444,23 @@ static int method_take_device(sd_bus_message *message, void *userdata, sd_bus_er * equivalent). */ return sd_bus_error_setf(error, BUS_ERROR_DEVICE_IS_TAKEN, "Device already taken"); - r = session_device_new(s, dev, &sd); + r = session_device_new(s, dev, true, &sd); if (r < 0) return r; + r = session_device_save(sd); + if (r < 0) + goto error; + r = sd_bus_reply_method_return(message, "hb", sd->fd, !sd->active); if (r < 0) - session_device_free(sd); + goto error; + + session_save(s); + return 0; +error: + session_device_free(sd); return r; } @@ -478,6 +487,8 @@ static int method_release_device(sd_bus_message *message, void *userdata, sd_bus return sd_bus_error_setf(error, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken"); session_device_free(sd); + session_save(s); + return sd_bus_reply_method_return(message, NULL); } diff --git a/src/login/logind-session-device.c b/src/login/logind-session-device.c index 4055a23277..05af2045d2 100644 --- a/src/login/logind-session-device.c +++ b/src/login/logind-session-device.c @@ -30,6 +30,8 @@ #include "fd-util.h" #include "logind-session-device.h" #include "missing.h" +#include "parse-util.h" +#include "sd-daemon.h" #include "util.h" enum SessionDeviceNotifications { @@ -206,7 +208,10 @@ static int session_device_start(SessionDevice *sd) { r = session_device_open(sd, true); if (r < 0) return r; - close_nointr(sd->fd); + /* For evdev devices, the file descriptor might be left + * uninitialized. This might happen while resuming into a + * session and logind has been restarted right before. */ + safe_close(sd->fd); sd->fd = r; break; case DEVICE_TYPE_UNKNOWN: @@ -341,7 +346,7 @@ err_dev: return r; } -int session_device_new(Session *s, dev_t dev, SessionDevice **out) { +int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **out) { SessionDevice *sd; int r; @@ -370,22 +375,24 @@ int session_device_new(Session *s, dev_t dev, SessionDevice **out) { goto error; } - /* Open the device for the first time. We need a valid fd to pass back - * to the caller. If the session is not active, this _might_ immediately - * revoke access and thus invalidate the fd. But this is still needed - * to pass a valid fd back. */ - sd->active = session_is_active(s); - r = session_device_open(sd, sd->active); - if (r < 0) { - /* EINVAL _may_ mean a master is active; retry inactive */ - if (sd->active && r == -EINVAL) { - sd->active = false; - r = session_device_open(sd, false); + if (open_device) { + /* Open the device for the first time. We need a valid fd to pass back + * to the caller. If the session is not active, this _might_ immediately + * revoke access and thus invalidate the fd. But this is still needed + * to pass a valid fd back. */ + sd->active = session_is_active(s); + r = session_device_open(sd, sd->active); + if (r < 0) { + /* EINVAL _may_ mean a master is active; retry inactive */ + if (sd->active && r == -EINVAL) { + sd->active = false; + r = session_device_open(sd, false); + } + if (r < 0) + goto error; } - if (r < 0) - goto error; + sd->fd = r; } - sd->fd = r; LIST_PREPEND(sd_by_device, sd->device->session_devices, sd); @@ -435,15 +442,16 @@ void session_device_complete_pause(SessionDevice *sd) { void session_device_resume_all(Session *s) { SessionDevice *sd; Iterator i; - int r; assert(s); HASHMAP_FOREACH(sd, s->devices, i) { if (!sd->active) { - r = session_device_start(sd); - if (!r) - session_device_notify(sd, SESSION_DEVICE_RESUME); + if (session_device_start(sd) < 0) + continue; + if (session_device_save(sd) < 0) + continue; + session_device_notify(sd, SESSION_DEVICE_RESUME); } } } @@ -478,3 +486,36 @@ unsigned int session_device_try_pause_all(Session *s) { return num_pending; } + +int session_device_save(SessionDevice *sd) { + _cleanup_free_ char *state = NULL; + int r; + + assert(sd); + + /* Store device fd in PID1. It will send it back to us on + * restart so revocation will continue to work. To make things + * simple, send fds for all type of devices even if they don't + * support the revocation mechanism so we don't have to handle + * them differently later. + * + * Note: for device supporting revocation, PID1 will drop a + * stored fd automatically if the corresponding device is + * revoked. */ + r = asprintf(&state, "FDSTORE=1\n" + "FDNAME=session-%s", sd->session->id); + if (r < 0) + return -ENOMEM; + + return sd_pid_notify_with_fds(0, false, state, &sd->fd, 1); +} + +void session_device_attach_fd(SessionDevice *sd, int fd, bool active) { + assert(fd > 0); + assert(sd); + assert(sd->fd < 0); + assert(!sd->active); + + sd->fd = fd; + sd->active = active; +} diff --git a/src/login/logind-session-device.h b/src/login/logind-session-device.h index 7c8503583f..83aef1e18c 100644 --- a/src/login/logind-session-device.h +++ b/src/login/logind-session-device.h @@ -44,10 +44,13 @@ struct SessionDevice { LIST_FIELDS(struct SessionDevice, sd_by_device); }; -int session_device_new(Session *s, dev_t dev, SessionDevice **out); +int session_device_new(Session *s, dev_t dev, bool open_device, SessionDevice **out); void session_device_free(SessionDevice *sd); void session_device_complete_pause(SessionDevice *sd); void session_device_resume_all(Session *s); void session_device_pause_all(Session *s); unsigned int session_device_try_pause_all(Session *s); + +int session_device_save(SessionDevice *sd); +void session_device_attach_fd(SessionDevice *sd, int fd, bool active); diff --git a/src/login/logind-session.c b/src/login/logind-session.c index 4a168906d6..42dfecaffb 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -152,6 +152,18 @@ void session_set_user(Session *s, User *u) { LIST_PREPEND(sessions_by_user, u->sessions, s); } +static void session_save_devices(Session *s, FILE *f) { + SessionDevice *sd; + Iterator i; + + if (!hashmap_isempty(s->devices)) { + fprintf(f, "DEVICES="); + HASHMAP_FOREACH(sd, s->devices, i) + fprintf(f, "%u:%u ", major(sd->dev), minor(sd->dev)); + fprintf(f, "\n"); + } +} + int session_save(Session *s) { _cleanup_free_ char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; @@ -281,8 +293,10 @@ int session_save(Session *s) { s->timestamp.realtime, s->timestamp.monotonic); - if (s->controller) + if (s->controller) { fprintf(f, "CONTROLLER=%s\n", s->controller); + session_save_devices(s, f); + } r = fflush_and_check(f); if (r < 0) @@ -304,6 +318,43 @@ fail: return log_error_errno(r, "Failed to save session data %s: %m", s->state_file); } +static int session_load_devices(Session *s, const char *devices) { + const char *p; + int r = 0; + + assert(s); + + for (p = devices;;) { + _cleanup_free_ char *word = NULL; + SessionDevice *sd; + dev_t dev; + int k; + + k = extract_first_word(&p, &word, NULL, 0); + if (k == 0) + break; + if (k < 0) { + r = k; + break; + } + + k = parse_dev(word, &dev); + if (k < 0) { + r = k; + continue; + } + + /* The file descriptors for loaded devices will be reattached later. */ + k = session_device_new(s, dev, false, &sd); + if (k < 0) + r = k; + } + + if (r < 0) + log_error_errno(r, "Loading session devices for session %s failed: %m", s->id); + + return r; +} int session_load(Session *s) { _cleanup_free_ char *remote = NULL, @@ -317,7 +368,9 @@ int session_load(Session *s) { *uid = NULL, *realtime = NULL, *monotonic = NULL, - *controller = NULL; + *controller = NULL, + *active = NULL, + *devices = NULL; int k, r; @@ -345,6 +398,8 @@ int session_load(Session *s) { "REALTIME", &realtime, "MONOTONIC", &monotonic, "CONTROLLER", &controller, + "ACTIVE", &active, + "DEVICES", &devices, NULL); if (r < 0) @@ -447,10 +502,17 @@ int session_load(Session *s) { if (monotonic) timestamp_deserialize(monotonic, &s->timestamp.monotonic); + if (active) { + k = parse_boolean(active); + if (k >= 0) + s->was_active = k; + } + if (controller) { - if (bus_name_has_owner(s->manager->bus, controller, NULL) > 0) - session_set_controller(s, controller, false); - else + if (bus_name_has_owner(s->manager->bus, controller, NULL) > 0) { + session_set_controller(s, controller, false, false); + session_load_devices(s, devices); + } else session_restore_vt(s); } @@ -1170,7 +1232,7 @@ static int on_bus_track(sd_bus_track *track, void *userdata) { return 0; } -int session_set_controller(Session *s, const char *sender, bool force) { +int session_set_controller(Session *s, const char *sender, bool force, bool prepare) { _cleanup_free_ char *name = NULL; int r; @@ -1202,11 +1264,14 @@ int session_set_controller(Session *s, const char *sender, bool force) { * Note that we reset the VT on ReleaseControl() and if the controller * exits. * If logind crashes/restarts, we restore the controller during restart - * or reset the VT in case it crashed/exited, too. */ - r = session_prepare_vt(s); - if (r < 0) { - s->track = sd_bus_track_unref(s->track); - return r; + * (without preparing the VT since the controller has probably overridden + * VT state by now) or reset the VT in case it crashed/exited, too. */ + if (prepare) { + r = session_prepare_vt(s); + if (r < 0) { + s->track = sd_bus_track_unref(s->track); + return r; + } } session_release_controller(s, true); diff --git a/src/login/logind-session.h b/src/login/logind-session.h index ffb7cd2d41..12b9d86f55 100644 --- a/src/login/logind-session.h +++ b/src/login/logind-session.h @@ -111,6 +111,8 @@ struct Session { bool started:1; bool stopping:1; + bool was_active:1; + sd_bus_message *create_message; sd_event_source *timer_event_source; @@ -176,7 +178,7 @@ void session_restore_vt(Session *s); void session_leave_vt(Session *s); bool session_is_controller(Session *s, const char *sender); -int session_set_controller(Session *s, const char *sender, bool force); +int session_set_controller(Session *s, const char *sender, bool force, bool prepare); void session_drop_controller(Session *s); int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/login/logind.c b/src/login/logind.c index 19bae294a4..fb70972b80 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -409,10 +409,71 @@ static int manager_enumerate_users(Manager *m) { return r; } +static int manager_attach_fds(Manager *m) { + _cleanup_strv_free_ char **fdnames = NULL; + int n, i, fd; + + /* Upon restart, PID1 will send us back all fds of session devices + * that we previously opened. Each file descriptor is associated + * with a given session. The session ids are passed through FDNAMES. */ + + n = sd_listen_fds_with_names(true, &fdnames); + if (n <= 0) + return n; + + for (i = 0; i < n; i++) { + struct stat st; + SessionDevice *sd; + Session *s; + char *id; + + fd = SD_LISTEN_FDS_START + i; + + id = startswith(fdnames[i], "session-"); + if (!id) + continue; + + s = hashmap_get(m->sessions, id); + if (!s) { + /* If the session doesn't exist anymore, the associated session + * device attached to this fd doesn't either. Let's simply close + * this fd. */ + log_debug("Failed to attach fd for unknown session: %s", id); + close_nointr(fd); + continue; + } + + if (fstat(fd, &st) < 0) { + /* The device is allowed to go away at a random point, in which + * case fstat failing is expected. */ + log_debug_errno(errno, "Failed to stat device fd for session %s: %m", id); + close_nointr(fd); + continue; + } + + sd = hashmap_get(s->devices, &st.st_rdev); + if (!sd) { + /* Weird we got an fd for a session device which wasn't + * recorded in the session state file... */ + log_warning("Got fd for missing session device [%u:%u] in session %s", + major(st.st_rdev), minor(st.st_rdev), s->id); + close_nointr(fd); + continue; + } + + log_debug("Attaching fd to session device [%u:%u] for session %s", + major(st.st_rdev), minor(st.st_rdev), s->id); + + session_device_attach_fd(sd, fd, s->was_active); + } + + return 0; +} + static int manager_enumerate_sessions(Manager *m) { _cleanup_closedir_ DIR *d = NULL; struct dirent *de; - int r = 0; + int r = 0, k; assert(m); @@ -427,7 +488,6 @@ static int manager_enumerate_sessions(Manager *m) { FOREACH_DIRENT(de, d, return -errno) { struct Session *s; - int k; if (!dirent_is_file(de)) continue; @@ -441,7 +501,6 @@ static int manager_enumerate_sessions(Manager *m) { k = manager_add_session(m, de->d_name, &s); if (k < 0) { log_error_errno(k, "Failed to add session by file name %s: %m", de->d_name); - r = k; continue; } @@ -453,6 +512,12 @@ static int manager_enumerate_sessions(Manager *m) { r = k; } + /* We might be restarted and PID1 could have sent us back the + * session device fds we previously saved. */ + k = manager_attach_fds(m); + if (k < 0) + log_warning_errno(k, "Failed to reattach session device fds: %m"); + return r; } @@ -1004,10 +1069,10 @@ static int manager_parse_config_file(Manager *m) { assert(m); return config_parse_many_nulstr(PKGSYSCONFDIR "/logind.conf", - CONF_PATHS_NULSTR("systemd/logind.conf.d"), - "Login\0", - config_item_perf_lookup, logind_gperf_lookup, - false, m); + CONF_PATHS_NULSTR("systemd/logind.conf.d"), + "Login\0", + config_item_perf_lookup, logind_gperf_lookup, + false, m); } static int manager_dispatch_reload_signal(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { diff --git a/src/login/meson.build b/src/login/meson.build new file mode 100644 index 0000000000..26bdbec424 --- /dev/null +++ b/src/login/meson.build @@ -0,0 +1,104 @@ +systemd_logind_sources = files(''' + logind.c + logind.h +'''.split()) + +logind_gperf_c = custom_target( + 'logind_gperf.c', + input : 'logind-gperf.gperf', + output : 'logind-gperf.c', + command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@']) + +systemd_logind_sources += [logind_gperf_c] + + +liblogind_core_sources = files(''' + logind-core.c + logind-device.c + logind-device.h + logind-button.c + logind-button.h + logind-action.c + logind-action.h + logind-seat.c + logind-seat.h + logind-session.c + logind-session.h + logind-session-device.c + logind-session-device.h + logind-user.c + logind-user.h + logind-inhibit.c + logind-inhibit.h + logind-dbus.c + logind-session-dbus.c + logind-seat-dbus.c + logind-user-dbus.c + logind-utmp.c + logind-acl.h +'''.split()) + +logind_acl_c = files('logind-acl.c') +if conf.get('HAVE_ACL', false) + liblogind_core_sources += logind_acl_c +endif + +liblogind_core = static_library( + 'logind-core', + liblogind_core_sources, + include_directories : includes, + dependencies : [libacl]) + +loginctl_sources = files(''' + loginctl.c + sysfs-show.h + sysfs-show.c +'''.split()) + +if conf.get('ENABLE_LOGIND', false) + logind_conf = configure_file( + input : 'logind.conf.in', + output : 'logind.conf', + configuration : substs) + install_data(logind_conf, + install_dir : pkgsysconfdir) + + pam_systemd_sym = 'src/login/pam_systemd.sym' + pam_systemd_c = files('pam_systemd.c') + + install_data('org.freedesktop.login1.conf', + install_dir : dbuspolicydir) + install_data('org.freedesktop.login1.service', + install_dir : dbussystemservicedir) + + custom_target( + 'org.freedesktop.login1.policy', + input : 'org.freedesktop.login1.policy.in', + output : 'org.freedesktop.login1.policy', + command : intltool_command, + install : install_polkit, + install_dir : polkitpolicydir) + + install_data('70-power-switch.rules', + '70-uaccess.rules', + install_dir : udevrulesdir) + + foreach file : ['71-seat.rules', + '73-seat-late.rules'] + gen = configure_file( + input : file + '.in', + output : file, + configuration : substs) + install_data(gen, + install_dir : udevrulesdir) + endforeach + + custom_target( + 'systemd-user', + input : 'systemd-user.m4', + output: 'systemd-user', + command : [m4, '-P'] + m4_defines + ['@INPUT@'], + capture : true, + install : pamconfdir != 'no', + install_dir : pamconfdir) +endif diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c index 2f69e2c7b7..18e0e34896 100644 --- a/src/machine/image-dbus.c +++ b/src/machine/image-dbus.c @@ -17,6 +17,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <sys/file.h> #include <sys/mount.h> #include "alloc-util.h" diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 28384286fb..a385e6819b 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -340,6 +340,7 @@ static int list_machines(int argc, char *argv[], void *userdata) { printf("No machines.\n"); } + r = 0; out: clean_machine_info(machines, n_machines); return r; @@ -2693,9 +2694,10 @@ static int help(int argc, char *argv[], void *userdata) { " --mkdir Create directory before bind mounting, if missing\n" " -n --lines=INTEGER Number of journal entries to show\n" " --max-addresses=INTEGER Number of internet addresses to show at most\n" - " -o --output=STRING Change journal output mode (short,\n" - " short-monotonic, verbose, export, json,\n" - " json-pretty, json-sse, cat)\n" + " -o --output=STRING Change journal output mode (short, short-precise,\n" + " short-iso, short-iso-precise, short-full,\n" + " short-monotonic, short-unix, verbose, export,\n" + " json, json-pretty, json-sse, cat)\n" " --verify=MODE Verification mode for downloaded images (no,\n" " checksum, signature)\n" " --force Download image even if already exists\n\n" diff --git a/src/machine/meson.build b/src/machine/meson.build new file mode 100644 index 0000000000..1a0813323c --- /dev/null +++ b/src/machine/meson.build @@ -0,0 +1,45 @@ +systemd_machined_sources = files(''' + machined.c + machined.h +'''.split()) + +libmachine_core_sources = files(''' + machine.c + machine.h + machined-dbus.c + machine-dbus.c + machine-dbus.h + image-dbus.c + image-dbus.h + operation.c + operation.h +'''.split()) + +libmachine_core = static_library( + 'machine-core', + libmachine_core_sources, + include_directories : includes, + dependencies : [threads]) + +if conf.get('ENABLE_MACHINED', false) + install_data('org.freedesktop.machine1.conf', + install_dir : dbuspolicydir) + install_data('org.freedesktop.machine1.service', + install_dir : dbussystemservicedir) + + custom_target( + 'org.freedesktop.machine1.policy', + input : 'org.freedesktop.machine1.policy.in', + output : 'org.freedesktop.machine1.policy', + command : intltool_command, + install : install_polkit, + install_dir : polkitpolicydir) +endif + +tests += [ + [['src/machine/test-machine-tables.c'], + [libmachine_core, + libshared], + [threads], + 'ENABLE_MACHINED'], +] diff --git a/src/machine/operation.c b/src/machine/operation.c index f7d5310f44..9b2d13dde1 100644 --- a/src/machine/operation.c +++ b/src/machine/operation.c @@ -17,6 +17,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <sys/wait.h> + #include "alloc-util.h" #include "fd-util.h" #include "operation.h" diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c index b709166aa9..ed6578d540 100644 --- a/src/mount/mount-tool.c +++ b/src/mount/mount-tool.c @@ -25,12 +25,16 @@ #include "bus-error.h" #include "bus-unit-util.h" #include "bus-util.h" +#include "dirent-util.h" #include "escape.h" +#include "fd-util.h" +#include "fileio.h" #include "fstab-util.h" #include "pager.h" #include "parse-util.h" #include "path-util.h" #include "spawn-polkit-agent.h" +#include "stat-util.h" #include "strv.h" #include "udev-util.h" #include "unit-name.h" @@ -77,7 +81,9 @@ static void polkit_agent_open_if_enabled(void) { } static void help(void) { - printf("%s [OPTIONS...] WHAT [WHERE]\n\n" + printf("systemd-mount [OPTIONS...] WHAT [WHERE]\n" + "systemd-mount [OPTIONS...] --list\n" + "%s [OPTIONS...] %sWHAT|WHERE...\n\n" "Establish a mount or auto-mount point transiently.\n\n" " -h --help Show this help\n" " --version Show package version\n" @@ -100,8 +106,9 @@ 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); + " -u --umount Unmount mount points\n", + program_invocation_short_name, + streq(program_invocation_short_name, "systemd-umount") ? "" : "--umount "); } static int parse_argv(int argc, char *argv[]) { @@ -297,6 +304,21 @@ static int parse_argv(int argc, char *argv[]) { log_error("Listing devices only supported locally."); return -EOPNOTSUPP; } + } else if (arg_action == ACTION_UMOUNT) { + if (optind >= argc) { + log_error("At least one argument required."); + return -EINVAL; + } + + if (arg_transport != BUS_TRANSPORT_LOCAL) { + int i; + + for (i = optind; i < argc; i++) + if (!path_is_absolute(argv[i]) ) { + log_error("Only absolute path is supported: %s", argv[i]); + return -EINVAL; + } + } } else { if (optind >= argc) { log_error("At least one argument required."); @@ -308,14 +330,58 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } - arg_mount_what = fstab_node_to_udev_node(argv[optind]); - if (!arg_mount_what) - return log_oom(); + if (arg_transport == BUS_TRANSPORT_LOCAL) { + _cleanup_free_ char *u = NULL, *p = NULL; - if (argc > optind+1) { - r = path_make_absolute_cwd(argv[optind+1], &arg_mount_where); + u = fstab_node_to_udev_node(argv[optind]); + if (!u) + return log_oom(); + + r = path_make_absolute_cwd(u, &p); if (r < 0) return log_error_errno(r, "Failed to make path absolute: %m"); + + arg_mount_what = canonicalize_file_name(p); + if (!arg_mount_what) + return log_error_errno(errno, "Failed to canonicalize path: %m"); + + } else { + arg_mount_what = strdup(argv[optind+1]); + if (!arg_mount_what) + return log_oom(); + + path_kill_slashes(arg_mount_what); + + if (!path_is_absolute(arg_mount_what)) { + log_error("Only absolute path is supported: %s", arg_mount_what); + return -EINVAL; + } + } + + if (argc > optind+1) { + if (arg_transport == BUS_TRANSPORT_LOCAL) { + _cleanup_free_ char *p = NULL; + + r = path_make_absolute_cwd(argv[optind+1], &p); + if (r < 0) + return log_error_errno(r, "Failed to make path absolute: %m"); + + arg_mount_where = canonicalize_file_name(p); + if (!arg_mount_where) + return log_error_errno(errno, "Failed to canonicalize path: %m"); + + } else { + arg_mount_where = strdup(argv[optind+1]); + if (!arg_mount_where) + return log_oom(); + + path_kill_slashes(arg_mount_where); + + if (!path_is_absolute(arg_mount_where)) { + log_error("Only absolute path is supported: %s", arg_mount_where); + return -EINVAL; + } + } } else arg_discover = true; @@ -619,9 +685,127 @@ static int start_transient_automount( return 0; } +static int find_mount_points(const char *what, char ***list) { + _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; + _cleanup_strv_free_ char **l = NULL; + size_t bufsize = 0, n = 0; + + assert(what); + assert(list); + + /* Returns all mount points obtained from /proc/self/mountinfo in *list, + * and the number of mount points as return value. */ + + proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); + if (!proc_self_mountinfo) + return log_error_errno(errno, "Can't open /proc/self/mountinfo: %m"); + + for (;;) { + _cleanup_free_ char *path = NULL, *where = NULL, *dev = NULL; + int r; + + r = fscanf(proc_self_mountinfo, + "%*s " /* (1) mount id */ + "%*s " /* (2) parent id */ + "%*s " /* (3) major:minor */ + "%*s " /* (4) root */ + "%ms " /* (5) mount point */ + "%*s" /* (6) mount options */ + "%*[^-]" /* (7) optional fields */ + "- " /* (8) separator */ + "%*s " /* (9) file system type */ + "%ms" /* (10) mount source */ + "%*s" /* (11) mount options 2 */ + "%*[^\n]", /* some rubbish at the end */ + &path, &dev); + if (r != 2) { + if (r == EOF) + break; + + continue; + } + + if (!streq(what, dev)) + continue; + + r = cunescape(path, UNESCAPE_RELAX, &where); + if (r < 0) + continue; + + /* one extra slot is needed for the terminating NULL */ + if (!GREEDY_REALLOC(l, bufsize, n + 2)) + return log_oom(); + + l[n++] = where; + where = NULL; + } + + if (!GREEDY_REALLOC(l, bufsize, n + 1)) + return log_oom(); + + l[n] = NULL; + *list = l; + l = NULL; /* avoid freeing */ + + return n; +} + +static int find_loop_device(const char *backing_file, char **loop_dev) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + _cleanup_free_ char *l = NULL; + + assert(backing_file); + assert(loop_dev); + + d = opendir("/sys/devices/virtual/block"); + if (!d) + return -errno; + + FOREACH_DIRENT(de, d, return -errno) { + _cleanup_free_ char *sys = NULL, *fname = NULL; + int r; + + dirent_ensure_type(d, de); + + if (de->d_type != DT_DIR) + continue; + + if (!startswith(de->d_name, "loop")) + continue; + + sys = strjoin("/sys/devices/virtual/block/", de->d_name, "/loop/backing_file"); + if (!sys) + return -ENOMEM; + + r = read_one_line_file(sys, &fname); + if (r < 0) { + log_debug_errno(r, "Failed to read %s, ignoring: %m", sys); + continue; + } + + if (files_same(fname, backing_file, 0) <= 0) + continue; + + l = strjoin("/dev/", de->d_name); + if (!l) + return -ENOMEM; + + break; + } + + if (!l) + return -ENXIO; + + *loop_dev = l; + l = NULL; /* avoid freeing */ + + return 0; +} + static int stop_mount( sd_bus *bus, - char **argv, + const char *where, const char *suffix) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; @@ -636,9 +820,9 @@ static int stop_mount( return log_error_errno(r, "Could not watch jobs: %m"); } - r = unit_name_from_path(arg_mount_where, suffix, &mount_unit); + r = unit_name_from_path(where, suffix, &mount_unit); if (r < 0) - return log_error_errno(r, "Failed to make mount unit name: %m"); + return log_error_errno(r, "Failed to make mount unit name from path %s: %m", where); r = sd_bus_message_new_method_call( bus, @@ -680,28 +864,162 @@ static int stop_mount( 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()); + ansi_highlight(), where, ansi_normal()); return 0; } static int stop_mounts( sd_bus *bus, - char **argv) { + const char *where) { int r; - r = stop_mount(bus, argv + optind, ".mount"); + if (path_equal(where, "/")) { + log_error("Refusing to operate on root directory: %s", where); + return -EINVAL; + } + + if (!path_is_safe(where)) { + log_error("Path contains unsafe components: %s", where); + return -EINVAL; + } + + r = stop_mount(bus, where, ".mount"); if (r < 0) return r; - r = stop_mount(bus, argv + optind, ".automount"); + r = stop_mount(bus, where, ".automount"); if (r < 0) return r; return 0; } +static int umount_by_device(sd_bus *bus, const char *what) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + _cleanup_udev_unref_ struct udev *udev = NULL; + _cleanup_strv_free_ char **list = NULL; + struct stat st; + const char *v; + char **l; + int r, r2 = 0; + + assert(what); + + if (stat(what, &st) < 0) + return log_error_errno(errno, "Can't stat %s: %m", what); + + if (!S_ISBLK(st.st_mode)) { + log_error("Not a block device: %s", what); + return -ENOTBLK; + } + + udev = udev_new(); + if (!udev) + return log_oom(); + + d = udev_device_new_from_devnum(udev, 'b', st.st_rdev); + if (!d) + return log_oom(); + + v = udev_device_get_property_value(d, "ID_FS_USAGE"); + if (!streq_ptr(v, "filesystem")) { + log_error("%s does not contain a known file system.", what); + return -EINVAL; + } + + v = udev_device_get_property_value(d, "SYSTEMD_MOUNT_WHERE"); + if (!isempty(v)) + r2 = stop_mounts(bus, v); + + r = find_mount_points(what, &list); + if (r < 0) + return r; + + for (l = list; *l; l++) { + r = stop_mounts(bus, *l); + if (r < 0) + r2 = r; + } + + return r2; +} + +static int umount_loop(sd_bus *bus, const char *backing_file) { + _cleanup_free_ char *loop_dev = NULL; + int r; + + assert(backing_file); + + r = find_loop_device(backing_file, &loop_dev); + if (r < 0) + return log_error_errno(r, r == -ENXIO ? "File %s is not mounted." : "Can't get loop device for %s: %m", backing_file); + + return umount_by_device(bus, loop_dev); +} + +static int action_umount( + sd_bus *bus, + int argc, + char **argv) { + + int i, r, r2 = 0; + + if (arg_transport != BUS_TRANSPORT_LOCAL) { + for (i = optind; i < argc; i++) { + _cleanup_free_ char *p = NULL; + + p = strdup(argv[i]); + if (!p) + return log_oom(); + + path_kill_slashes(p); + + r = stop_mounts(bus, p); + if (r < 0) + r2 = r; + } + return r2; + } + + for (i = optind; i < argc; i++) { + _cleanup_free_ char *u = NULL, *a = NULL, *p = NULL; + struct stat st; + + u = fstab_node_to_udev_node(argv[i]); + if (!u) + return log_oom(); + + r = path_make_absolute_cwd(u, &a); + if (r < 0) { + r2 = log_error_errno(r, "Failed to make path absolute: %m"); + continue; + } + + p = canonicalize_file_name(a); + + if (stat(p, &st) < 0) + return log_error_errno(errno, "Can't stat %s: %m", p); + + if (S_ISBLK(st.st_mode)) + r = umount_by_device(bus, p); + else if (S_ISREG(st.st_mode)) + r = umount_loop(bus, p); + else if (S_ISDIR(st.st_mode)) + r = stop_mounts(bus, p); + else { + log_error("Invalid file type: %s", p); + r = -EINVAL; + } + + if (r < 0) + r2 = r; + } + + return r2; +} + static int acquire_mount_type(struct udev_device *d) { const char *v; @@ -789,6 +1107,8 @@ static int acquire_mount_where(struct udev_device *d) { } escaped = xescape(name, "\\"); + if (!escaped) + return log_oom(); if (!filename_is_valid(escaped)) return 0; @@ -803,6 +1123,32 @@ static int acquire_mount_where(struct udev_device *d) { return 1; } +static int acquire_mount_where_for_loop_dev(const char *loop_dev) { + _cleanup_strv_free_ char **list = NULL; + int r; + + if (arg_mount_where) + return 0; + + r = find_mount_points(loop_dev, &list); + if (r < 0) + return r; + else if (r == 0) { + log_error("Can't find mount point of %s. It is expected that %s is already mounted on a place.", loop_dev, loop_dev); + return -EINVAL; + } else if (r >= 2) { + log_error("%s is mounted on %d places. It is expected that %s is mounted on a place.", loop_dev, r, loop_dev); + return -EINVAL; + } + + arg_mount_where = strdup(list[0]); + if (!arg_mount_where) + return log_oom(); + + log_debug("Discovered where=%s", arg_mount_where); + return 1; +} + static int acquire_description(struct udev_device *d) { const char *model, *label; @@ -874,27 +1220,97 @@ static int acquire_removable(struct udev_device *d) { return 1; } -static int discover_device(void) { +static int discover_loop_backing_file(void) { _cleanup_udev_device_unref_ struct udev_device *d = NULL; _cleanup_udev_unref_ struct udev *udev = NULL; + _cleanup_free_ char *loop_dev = NULL; struct stat st; const char *v; int r; - if (!arg_discover) + r = find_loop_device(arg_mount_what, &loop_dev); + if (r < 0 && r != -ENXIO) + return log_error_errno(errno, "Can't get loop device for %s: %m", arg_mount_what); + + if (r == -ENXIO) { + _cleanup_free_ char *escaped = NULL; + + if (arg_mount_where) + return 0; + + escaped = xescape(basename(arg_mount_what), "\\"); + if (!escaped) + return log_oom(); + if (!filename_is_valid(escaped)) { + log_error("Escaped name %s is not a valid filename.", escaped); + return -EINVAL; + } + + arg_mount_where = strjoin("/run/media/system/", escaped); + if (!arg_mount_where) + return log_oom(); + + log_debug("Discovered where=%s", arg_mount_where); return 0; + } + + if (stat(loop_dev, &st) < 0) + return log_error_errno(errno, "Can't stat %s: %m", loop_dev); - if (!is_device_path(arg_mount_what)) { - log_error("Discovery only supported for block devices, don't know what to do."); + if (!S_ISBLK(st.st_mode)) { + log_error("Invalid file type: %s", loop_dev); return -EINVAL; } + udev = udev_new(); + if (!udev) + return log_oom(); + + d = udev_device_new_from_devnum(udev, 'b', st.st_rdev); + if (!d) + return log_oom(); + + v = udev_device_get_property_value(d, "ID_FS_USAGE"); + if (!streq_ptr(v, "filesystem")) { + log_error("%s does not contain a known file system.", arg_mount_what); + return -EINVAL; + } + + r = acquire_mount_type(d); + if (r < 0) + return r; + + r = acquire_mount_options(d); + if (r < 0) + return r; + + r = acquire_mount_where_for_loop_dev(loop_dev); + if (r < 0) + return r; + + r = acquire_description(d); + if (r < 0) + return r; + + return 0; +} + +static int discover_device(void) { + _cleanup_udev_device_unref_ struct udev_device *d = NULL; + _cleanup_udev_unref_ struct udev *udev = NULL; + struct stat st; + const char *v; + int r; + if (stat(arg_mount_what, &st) < 0) return log_error_errno(errno, "Can't stat %s: %m", arg_mount_what); + if (S_ISREG(st.st_mode)) + return discover_loop_backing_file(); + if (!S_ISBLK(st.st_mode)) { - log_error("Path %s is not a block device, don't know what to do.", arg_mount_what); - return -ENOTBLK; + log_error("Invalid file type: %s", arg_mount_what); + return -EINVAL; } udev = udev_new(); @@ -907,7 +1323,7 @@ static int discover_device(void) { v = udev_device_get_property_value(d, "ID_FS_USAGE"); if (!streq_ptr(v, "filesystem")) { - log_error("%s does not contain a file system.", arg_mount_what); + log_error("%s does not contain a known file system.", arg_mount_what); return -EINVAL; } @@ -1130,17 +1546,35 @@ int main(int argc, char* argv[]) { goto finish; } - r = discover_device(); - if (r < 0) + r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus); + if (r < 0) { + log_error_errno(r, "Failed to create bus connection: %m"); + goto finish; + } + + if (arg_action == ACTION_UMOUNT) { + r = action_umount(bus, argc, argv); + goto finish; + } + + if (!path_is_safe(arg_mount_what)) { + log_error("Path contains unsafe components: %s", arg_mount_what); + r = -EINVAL; goto finish; + } + + if (arg_discover) { + r = discover_device(); + if (r < 0) + goto finish; + } + if (!arg_mount_where) { log_error("Can't figure out where to mount %s.", arg_mount_what); r = -EINVAL; goto finish; } - path_kill_slashes(arg_mount_where); - if (path_equal(arg_mount_where, "/")) { log_error("Refusing to operate on root directory."); r = -EINVAL; @@ -1148,7 +1582,7 @@ int main(int argc, char* argv[]) { } if (!path_is_safe(arg_mount_where)) { - log_error("Path is contains unsafe components."); + log_error("Path contains unsafe components: %s", arg_mount_where); r = -EINVAL; goto finish; } @@ -1171,12 +1605,6 @@ int main(int argc, char* argv[]) { } } - r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus); - if (r < 0) { - log_error_errno(r, "Failed to create bus connection: %m"); - goto finish; - } - switch (arg_action) { case ACTION_MOUNT: @@ -1188,10 +1616,6 @@ 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/meson.build b/src/network/meson.build new file mode 100644 index 0000000000..35ecd86379 --- /dev/null +++ b/src/network/meson.build @@ -0,0 +1,150 @@ +sources = files(''' + netdev/bond.c + netdev/bond.h + netdev/bridge.c + netdev/bridge.h + netdev/dummy.c + netdev/dummy.h + netdev/ipvlan.c + netdev/ipvlan.h + netdev/macvlan.c + netdev/macvlan.h + netdev/netdev.c + netdev/netdev.h + netdev/tunnel.c + netdev/tunnel.h + netdev/tuntap.c + netdev/tuntap.h + netdev/vcan.c + netdev/vcan.h + netdev/veth.c + netdev/veth.h + netdev/vlan.c + netdev/vlan.h + netdev/vrf.c + netdev/vrf.h + netdev/vxlan.c + netdev/vxlan.h + netdev/geneve.c + netdev/geneve.h + networkd-address-label.c + networkd-address-label.h + networkd-address-pool.c + networkd-address-pool.h + networkd-address.c + networkd-address.h + networkd-brvlan.c + networkd-brvlan.h + networkd-conf.c + networkd-conf.h + networkd-dhcp4.c + networkd-dhcp6.c + networkd-fdb.c + networkd-fdb.h + networkd-ipv4ll.c + networkd-ipv6-proxy-ndp.c + networkd-ipv6-proxy-ndp.h + networkd-link-bus.c + networkd-link.c + networkd-link.h + networkd-lldp-tx.c + networkd-lldp-tx.h + networkd-manager-bus.c + networkd-manager.c + networkd-manager.h + networkd-ndisc.c + networkd-ndisc.h + networkd-radv.c + networkd-radv.h + networkd-network-bus.c + networkd-network.c + networkd-network.h + networkd-route.c + networkd-route.h + networkd-util.c + networkd-util.h +'''.split()) + +systemd_networkd_sources = files('networkd.c') + +systemd_networkd_wait_online_sources = files(''' + wait-online/link.c + wait-online/link.h + wait-online/manager.c + wait-online/manager.h + wait-online/wait-online.c +'''.split()) + network_internal_h + +networkctl_sources = files('networkctl.c') + +network_include_dir = include_directories('.') + +if conf.get('ENABLE_NETWORKD', false) + networkd_gperf_c = custom_target( + 'networkd-gperf.c', + input : 'networkd-gperf.gperf', + output : 'networkd-gperf.c', + command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@']) + + networkd_network_gperf_c = custom_target( + 'networkd-network-gperf.c', + input : 'networkd-network-gperf.gperf', + output : 'networkd-network-gperf.c', + command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@']) + + netdev_gperf_c = custom_target( + 'netdev-gperf.c', + input : 'netdev/netdev-gperf.gperf', + output : 'netdev-gperf.c', + command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@']) + + libnetworkd_core = static_library( + 'networkd-core', + sources, + network_internal_h, + networkd_gperf_c, + networkd_network_gperf_c, + netdev_gperf_c, + include_directories : includes, + link_with : [libshared]) + + install_data('org.freedesktop.network1.conf', + install_dir : dbuspolicydir) + install_data('org.freedesktop.network1.service', + install_dir : dbussystemservicedir) + if install_polkit + install_data('systemd-networkd.rules', + install_dir : polkitrulesdir) + endif + if install_polkit_pkla + install_data('systemd-networkd.pkla', + install_dir : polkitpkladir) + endif + + tests += [ + [['src/network/test-networkd-conf.c'], + [libnetworkd_core, + libsystemd_network, + libudev], + []], + + [['src/network/test-network.c'], + [libnetworkd_core, + libudev_internal, + libsystemd_network, + libshared], + [threads]], + + [['src/network/test-network-tables.c', + 'src/network/test-network-tables.c', + test_tables_h], + [libnetworkd_core, + libudev_internal, + libudev_core, + libsystemd_network, + libshared], + [threads], + '', '', [], + [network_include_dir] + libudev_core_includes], + ] +endif diff --git a/src/network/netdev/bridge.c b/src/network/netdev/bridge.c index 9fdcb55376..cf6c591f8b 100644 --- a/src/network/netdev/bridge.c +++ b/src/network/netdev/bridge.c @@ -24,6 +24,7 @@ #include "netlink-util.h" #include "netdev/bridge.h" #include "networkd-manager.h" +#include "vlan-util.h" /* callback for brige netdev's parameter set */ static int netdev_bridge_set_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { @@ -102,7 +103,7 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_mess return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_PRIORITY attribute: %m"); } - if (b->default_pvid > 0) { + if (b->default_pvid != VLANID_INVALID) { r = sd_netlink_message_append_u16(req, IFLA_BR_VLAN_DEFAULT_PVID, b->default_pvid); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_VLAN_DEFAULT_PVID attribute: %m"); @@ -160,6 +161,7 @@ static void bridge_init(NetDev *n) { b->mcast_snooping = -1; b->vlan_filtering = -1; b->stp = -1; + b->default_pvid = VLANID_INVALID; b->forward_delay = USEC_INFINITY; } diff --git a/src/network/netdev/geneve.c b/src/network/netdev/geneve.c new file mode 100644 index 0000000000..e71ea58a10 --- /dev/null +++ b/src/network/netdev/geneve.c @@ -0,0 +1,315 @@ +/*** + This file is part of systemd. + + Copyright 2017 Susant Sahani + + 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 <net/if.h> + +#include "alloc-util.h" +#include "conf-parser.h" +#include "extract-word.h" +#include "geneve.h" +#include "parse-util.h" +#include "sd-netlink.h" +#include "string-util.h" +#include "strv.h" +#include "missing.h" +#include "networkd-manager.h" + +#define GENEVE_FLOW_LABEL_MAX_MASK 0xFFFFFU +#define DEFAULT_GENEVE_DESTINATION_PORT 6081 + +/* callback for geneve netdev's created without a backing Link */ +static int geneve_netdev_create_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_netdev_unref_ NetDev *netdev = userdata; + int r; + + assert(netdev->state != _NETDEV_STATE_INVALID); + + r = sd_netlink_message_get_errno(m); + if (r == -EEXIST) + log_netdev_info(netdev, "Geneve netdev exists, using existing without changing its parameters"); + else if (r < 0) { + log_netdev_warning_errno(netdev, r, "Geneve netdev could not be created: %m"); + netdev_drop(netdev); + + return 1; + } + + log_netdev_debug(netdev, "Geneve created"); + + return 1; +} + +static int netdev_geneve_create(NetDev *netdev) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + Geneve *v; + int r; + + assert(netdev); + + v = GENEVE(netdev); + + r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not allocate RTM_NEWLINK message: %m"); + + r = sd_netlink_message_append_string(m, IFLA_IFNAME, netdev->ifname); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IFNAME, attribute: %m"); + + if (netdev->mac) { + r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, netdev->mac); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_ADDRESS attribute: %m"); + } + + if (netdev->mtu) { + r = sd_netlink_message_append_u32(m, IFLA_MTU, netdev->mtu); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_MTU attribute: %m"); + } + + r = sd_netlink_message_open_container(m, IFLA_LINKINFO); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); + + r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, netdev_kind_to_string(netdev->kind)); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m"); + + if (v->id <= GENEVE_VID_MAX) { + r = sd_netlink_message_append_u32(m, IFLA_GENEVE_ID, v->id); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_ID attribute: %m"); + } + + if (!in_addr_is_null(v->remote_family, &v->remote)) { + + if (v->remote_family == AF_INET) + r = sd_netlink_message_append_in_addr(m, IFLA_GENEVE_REMOTE, &v->remote.in); + else + r = sd_netlink_message_append_in6_addr(m, IFLA_GENEVE_REMOTE6, &v->remote.in6); + + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_GROUP attribute: %m"); + + } + + if (v->ttl) { + r = sd_netlink_message_append_u8(m, IFLA_GENEVE_TTL, v->ttl); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_TTL attribute: %m"); + } + + r = sd_netlink_message_append_u8(m, IFLA_GENEVE_TOS, v->tos); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_TOS attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_GENEVE_UDP_CSUM, v->udpcsum); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_UDP_CSUM attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, v->udp6zerocsumtx); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_UDP_ZERO_CSUM6_TX attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, v->udp6zerocsumrx); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_UDP_ZERO_CSUM6_RX attribute: %m"); + + if (v->dest_port != DEFAULT_GENEVE_DESTINATION_PORT) { + r = sd_netlink_message_append_u16(m, IFLA_GENEVE_PORT, htobe16(v->dest_port)); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_PORT attribute: %m"); + } + + if (v->flow_label > 0) { + r = sd_netlink_message_append_u32(m, IFLA_GENEVE_LABEL, htobe32(v->flow_label)); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_LABEL attribute: %m"); + } + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m"); + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); + + r = sd_netlink_call_async(netdev->manager->rtnl, m, geneve_netdev_create_handler, netdev, 0, NULL); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m"); + + netdev_ref(netdev); + + netdev->state = NETDEV_STATE_CREATING; + + log_netdev_debug(netdev, "Creating"); + + + return r; +} + +int config_parse_geneve_vni(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) { + Geneve *v = userdata; + uint32_t f; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = safe_atou32(rvalue, &f); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Geneve VNI '%s'.", rvalue); + return 0; + } + + if (f > GENEVE_VID_MAX){ + log_syntax(unit, LOG_ERR, filename, line, r, "Geneve VNI out is of range '%s'.", rvalue); + return 0; + } + + v->id = f; + + return 0; +} + +int config_parse_geneve_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) { + Geneve *v = userdata; + union in_addr_union *addr = data, buffer; + int r, f; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = in_addr_from_string_auto(rvalue, &f, &buffer); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "geneve '%s' address is invalid, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + r = in_addr_is_multicast(f, &buffer); + if (r > 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "geneve invalid multicast '%s' address, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + v->remote_family = f; + *addr = buffer; + + return 0; +} + +int config_parse_geneve_flow_label(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Geneve *v = userdata; + uint32_t f; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = safe_atou32(rvalue, &f); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Geneve flow label '%s'.", rvalue); + return 0; + } + + if (f & ~GENEVE_FLOW_LABEL_MAX_MASK) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Geneve flow label '%s' not valid. Flow label range should be [0-1048575].", rvalue); + return 0; + } + + v->flow_label = f; + + return 0; +} + +static int netdev_geneve_verify(NetDev *netdev, const char *filename) { + Geneve *v = GENEVE(netdev); + + assert(netdev); + assert(v); + assert(filename); + + if (v->ttl == 0) { + log_warning("Invalid Geneve TTL value '0' configured in '%s'. Ignoring", filename); + return -EINVAL; + } + + return 0; +} + +static void geneve_init(NetDev *netdev) { + Geneve *v; + + assert(netdev); + + v = GENEVE(netdev); + + assert(v); + + v->id = GENEVE_VID_MAX + 1; + v->dest_port = DEFAULT_GENEVE_DESTINATION_PORT; + v->udpcsum = false; + v->udp6zerocsumtx = false; + v->udp6zerocsumrx = false; +} + +const NetDevVTable geneve_vtable = { + .object_size = sizeof(Geneve), + .init = geneve_init, + .sections = "Match\0NetDev\0GENEVE\0", + .create = netdev_geneve_create, + .create_type = NETDEV_CREATE_INDEPENDENT, + .config_verify = netdev_geneve_verify, +}; diff --git a/src/network/netdev/geneve.h b/src/network/netdev/geneve.h new file mode 100644 index 0000000000..bde28bac55 --- /dev/null +++ b/src/network/netdev/geneve.h @@ -0,0 +1,85 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2017 Susant Sahani + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +typedef struct Geneve Geneve; + +#include "in-addr-util.h" +#include "netdev.h" +#include "networkd-link.h" +#include "networkd-network.h" + +#define GENEVE_VID_MAX (1u << 24) - 1 + +struct Geneve { + NetDev meta; + + uint32_t id; + uint32_t flow_label; + + int remote_family; + + uint8_t tos; + uint8_t ttl; + + uint16_t dest_port; + + bool udpcsum; + bool udp6zerocsumtx; + bool udp6zerocsumrx; + + union in_addr_union remote; +}; + +DEFINE_NETDEV_CAST(GENEVE, Geneve); +extern const NetDevVTable geneve_vtable; + +int config_parse_geneve_vni(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_geneve_address(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata); + +int config_parse_geneve_flow_label(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata); diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index e19fa9817e..8b235a4857 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -4,6 +4,7 @@ #include "network-internal.h" #include "netdev/bond.h" #include "netdev/bridge.h" +#include "netdev/geneve.h" #include "netdev/ipvlan.h" #include "netdev/macvlan.h" #include "netdev/tunnel.h" @@ -36,6 +37,10 @@ NetDev.Kind, config_parse_netdev_kind, 0, NetDev.MTUBytes, config_parse_iec_size, 0, offsetof(NetDev, mtu) NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac) VLAN.Id, config_parse_vlanid, 0, offsetof(VLan, id) +VLAN.GVRP, config_parse_tristate, 0, offsetof(VLan, gvrp) +VLAN.MVRP, config_parse_tristate, 0, offsetof(VLan, mvrp) +VLAN.LooseBinding, config_parse_tristate, 0, offsetof(VLan, loose_binding) +VLAN.ReorderHeader, config_parse_tristate, 0, offsetof(VLan, reorder_hdr) MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode) @@ -77,7 +82,17 @@ VXLAN.FDBAgeingSec, config_parse_sec, 0, VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy) VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb) VXLAN.PortRange, config_parse_port_range, 0, 0 -VXLAN.DestinationPort, config_parse_destination_port, 0, offsetof(VxLan, dest_port) +VXLAN.DestinationPort, config_parse_ip_port, 0, offsetof(VxLan, dest_port) +VXLAN.FlowLabel, config_parse_flow_label, 0, 0 +GENEVE.Id, config_parse_geneve_vni, 0, offsetof(Geneve, id) +GENEVE.Remote, config_parse_geneve_address, 0, offsetof(Geneve, remote) +GENEVE.TOS, config_parse_uint8, 0, offsetof(Geneve, tos) +GENEVE.TTL, config_parse_uint8, 0, offsetof(Geneve, ttl) +GENEVE.UDPChecksum, config_parse_bool, 0, offsetof(Geneve, udpcsum) +GENEVE.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumrx) +GENEVE.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumtx) +GENEVE.DestinationPort, config_parse_ip_port, 0, offsetof(Geneve, dest_port) +GENEVE.FlowLabel, config_parse_geneve_flow_label, 0, 0 Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) @@ -113,7 +128,7 @@ Bridge.MaxAgeSec, config_parse_sec, 0, Bridge.AgeingTimeSec, config_parse_sec, 0, offsetof(Bridge, ageing_time) Bridge.ForwardDelaySec, config_parse_sec, 0, offsetof(Bridge, forward_delay) Bridge.Priority, config_parse_uint16, 0, offsetof(Bridge, priority) -Bridge.DefaultPVID, config_parse_vlanid, 0, offsetof(Bridge, default_pvid) +Bridge.DefaultPVID, config_parse_default_port_vlanid, 0, offsetof(Bridge, default_pvid) Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier) Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping) Bridge.VLANFiltering, config_parse_tristate, 0, offsetof(Bridge, vlan_filtering) diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index 9b9e83d9db..43884581ca 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -28,6 +28,7 @@ #include "network-internal.h" #include "netdev/netdev.h" #include "networkd-manager.h" +#include "networkd-link.h" #include "siphash24.h" #include "stat-util.h" #include "string-table.h" @@ -35,6 +36,7 @@ #include "netdev/bridge.h" #include "netdev/bond.h" +#include "netdev/geneve.h" #include "netdev/vlan.h" #include "netdev/macvlan.h" #include "netdev/ipvlan.h" @@ -69,6 +71,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = { [NETDEV_KIND_IP6TNL] = &ip6tnl_vtable, [NETDEV_KIND_VRF] = &vrf_vtable, [NETDEV_KIND_VCAN] = &vcan_vtable, + [NETDEV_KIND_GENEVE] = &geneve_vtable, }; static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = { @@ -94,6 +97,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = { [NETDEV_KIND_IP6TNL] = "ip6tnl", [NETDEV_KIND_VRF] = "vrf", [NETDEV_KIND_VCAN] = "vcan", + [NETDEV_KIND_GENEVE] = "geneve", }; DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind); @@ -218,6 +222,13 @@ static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_netlink_message_h assert(link); assert(callback); + if (link->flags & IFF_UP) { + log_netdev_debug(netdev, "Link '%s' was up when attempting to enslave it. Bringing link down.", link->ifname); + r = link_down(link); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not bring link down: %m"); + } + r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_SETLINK, link->ifindex); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not allocate RTM_SETLINK message: %m"); diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h index 37c7431213..a961e2ac3b 100644 --- a/src/network/netdev/netdev.h +++ b/src/network/netdev/netdev.h @@ -57,6 +57,7 @@ typedef enum NetDevKind { NETDEV_KIND_TAP, NETDEV_KIND_VRF, NETDEV_KIND_VCAN, + NETDEV_KIND_GENEVE, _NETDEV_KIND_MAX, _NETDEV_KIND_INVALID = -1 } NetDevKind; diff --git a/src/network/netdev/vlan.c b/src/network/netdev/vlan.c index 28c061fa4f..6f41633ead 100644 --- a/src/network/netdev/vlan.c +++ b/src/network/netdev/vlan.c @@ -17,12 +17,14 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <linux/if_vlan.h> #include <net/if.h> #include "netdev/vlan.h" #include "vlan-util.h" static int netdev_vlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) { + struct ifla_vlan_flags flags = {}; VLan *v; int r; @@ -38,6 +40,30 @@ static int netdev_vlan_fill_message_create(NetDev *netdev, Link *link, sd_netlin if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VLAN_ID attribute: %m"); + if (v->gvrp != -1) { + flags.mask |= VLAN_FLAG_GVRP; + SET_FLAG(flags.flags, VLAN_FLAG_GVRP, v->gvrp); + } + + if (v->mvrp != -1) { + flags.mask |= VLAN_FLAG_MVRP; + SET_FLAG(flags.flags, VLAN_FLAG_MVRP, v->mvrp); + } + + if (v->reorder_hdr != -1) { + flags.mask |= VLAN_FLAG_REORDER_HDR; + SET_FLAG(flags.flags, VLAN_FLAG_REORDER_HDR, v->reorder_hdr); + } + + if (v->loose_binding != -1) { + flags.mask |= VLAN_FLAG_LOOSE_BINDING; + SET_FLAG(flags.flags, VLAN_FLAG_LOOSE_BINDING, v->loose_binding); + } + + r = sd_netlink_message_append_data(req, IFLA_VLAN_FLAGS, &flags, sizeof(struct ifla_vlan_flags)); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VLAN_FLAGS attribute: %m"); + return 0; } @@ -66,6 +92,10 @@ static void vlan_init(NetDev *netdev) { assert(v); v->id = VLANID_INVALID; + v->gvrp = -1; + v->mvrp = -1; + v->loose_binding = -1; + v->reorder_hdr = -1; } const NetDevVTable vlan_vtable = { diff --git a/src/network/netdev/vlan.h b/src/network/netdev/vlan.h index fade899997..780d61262a 100644 --- a/src/network/netdev/vlan.h +++ b/src/network/netdev/vlan.h @@ -27,6 +27,11 @@ struct VLan { NetDev meta; uint16_t id; + + int gvrp; + int mvrp; + int loose_binding; + int reorder_hdr; }; DEFINE_NETDEV_CAST(VLAN, VLan); diff --git a/src/network/netdev/vxlan.c b/src/network/netdev/vxlan.c index b677b000fd..b5b7aec2c0 100644 --- a/src/network/netdev/vxlan.c +++ b/src/network/netdev/vxlan.c @@ -157,6 +157,10 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_PORT_RANGE attribute: %m"); } + r = sd_netlink_message_append_u32(m, IFLA_VXLAN_LABEL, htobe32(v->flow_label)); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LABEL attribute: %m"); + if (v->group_policy) { r = sd_netlink_message_append_flag(m, IFLA_VXLAN_GBP); if (r < 0) @@ -267,18 +271,18 @@ int config_parse_port_range(const char *unit, return 0; } -int config_parse_destination_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_flow_label(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { VxLan *v = userdata; - uint16_t port; + unsigned f; int r; assert(filename); @@ -286,13 +290,19 @@ int config_parse_destination_port(const char *unit, assert(rvalue); assert(data); - r = parse_ip_port(rvalue, &port); + r = safe_atou(rvalue, &f); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse VXLAN destination port '%s'.", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse VXLAN flow label '%s'.", rvalue); + return 0; + } + + if (f & ~VXLAN_FLOW_LABEL_MAX_MASK) { + log_syntax(unit, LOG_ERR, filename, line, r, + "VXLAN flow label '%s' not valid. Flow label range should be [0-1048575].", rvalue); return 0; } - v->dest_port = port; + v->flow_label = f; return 0; } diff --git a/src/network/netdev/vxlan.h b/src/network/netdev/vxlan.h index dca58e7fe6..1eeda022a2 100644 --- a/src/network/netdev/vxlan.h +++ b/src/network/netdev/vxlan.h @@ -25,6 +25,7 @@ typedef struct VxLan VxLan; #include "netdev/netdev.h" #define VXLAN_VID_MAX (1u << 24) - 1 +#define VXLAN_FLOW_LABEL_MAX_MASK 0xFFFFFU struct VxLan { NetDev meta; @@ -40,6 +41,7 @@ struct VxLan { unsigned tos; unsigned ttl; unsigned max_fdb; + unsigned flow_label; uint16_t dest_port; @@ -84,13 +86,13 @@ int config_parse_port_range(const char *unit, void *data, void *userdata); -int config_parse_destination_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_flow_label(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata); diff --git a/src/network/networkctl.c b/src/network/networkctl.c index 6f7f41bf7d..54d54c8fd5 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -18,6 +18,7 @@ ***/ #include <getopt.h> +#include <linux/if_addrlabel.h> #include <net/if.h> #include <stdbool.h> @@ -35,6 +36,7 @@ #include "hwdb-util.h" #include "local-addresses.h" #include "locale-util.h" +#include "macro.h" #include "netlink-util.h" #include "pager.h" #include "parse-util.h" @@ -569,6 +571,76 @@ static int dump_addresses( return 0; } +static int dump_address_labels(sd_netlink *rtnl) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; + sd_netlink_message *m; + int r; + + assert(rtnl); + + r = sd_rtnl_message_new_addrlabel(rtnl, &req, RTM_GETADDRLABEL, 0, AF_INET6); + if (r < 0) + return log_error_errno(r, "Could not allocate RTM_GETADDRLABEL message: %m"); + + r = sd_netlink_message_request_dump(req, true); + if (r < 0) + return r; + + r = sd_netlink_call(rtnl, req, 0, &reply); + if (r < 0) + return r; + + printf("%10s/%s %30s\n", "Prefix", "Prefixlen", "Label"); + + for (m = reply; m; m = sd_netlink_message_next(m)) { + _cleanup_free_ char *pretty = NULL; + union in_addr_union prefix = {}; + uint8_t prefixlen; + uint32_t label; + + r = sd_netlink_message_get_errno(m); + if (r < 0) { + log_error_errno(r, "got error: %m"); + continue; + } + + r = sd_netlink_message_read_u32(m, IFAL_LABEL, &label); + if (r < 0 && r != -ENODATA) { + log_error_errno(r, "Could not read IFAL_LABEL, ignoring: %m"); + continue; + } + + r = sd_netlink_message_read_in6_addr(m, IFAL_ADDRESS, &prefix.in6); + if (r < 0) + continue; + + r = in_addr_to_string(AF_INET6, &prefix, &pretty); + if (r < 0) + continue; + + r = sd_rtnl_message_addrlabel_get_prefixlen(m, &prefixlen); + if (r < 0) + continue; + + printf("%10s/%-5u %30u\n", pretty, prefixlen, label); + } + + return 0; +} + +static int list_address_labels(int argc, char *argv[], void *userdata) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + int r; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + dump_address_labels(rtnl); + + return 0; +} + static int open_lldp_neighbors(int ifindex, FILE **ret) { _cleanup_free_ char *p = NULL; FILE *f; @@ -1043,6 +1115,7 @@ static void help(void) { " list [LINK...] List links\n" " status [LINK...] Show link status\n" " lldp [LINK...] Show LLDP neighbors\n" + " label Show current address label entries in the kernel\n" , program_invocation_short_name); } @@ -1107,6 +1180,7 @@ static int networkctl_main(int argc, char *argv[]) { { "list", VERB_ANY, VERB_ANY, VERB_DEFAULT, list_links }, { "status", VERB_ANY, VERB_ANY, 0, link_status }, { "lldp", VERB_ANY, VERB_ANY, 0, link_lldp_status }, + { "label", VERB_ANY, VERB_ANY, 0, list_address_labels}, {} }; diff --git a/src/network/networkd-address-label.c b/src/network/networkd-address-label.c new file mode 100644 index 0000000000..b89995ec44 --- /dev/null +++ b/src/network/networkd-address-label.c @@ -0,0 +1,223 @@ +/*** + This file is part of systemd. + + Copyright 2017 Susant Sahani + + 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 <net/if.h> +#include <linux/if_addrlabel.h> + +#include "alloc-util.h" +#include "conf-parser.h" +#include "networkd-address-label.h" +#include "netlink-util.h" +#include "networkd-manager.h" +#include "parse-util.h" +#include "socket-util.h" + +int address_label_new(AddressLabel **ret) { + _cleanup_address_label_free_ AddressLabel *addrlabel = NULL; + + addrlabel = new0(AddressLabel, 1); + if (!addrlabel) + return -ENOMEM; + + *ret = addrlabel; + addrlabel = NULL; + + return 0; +} + +void address_label_free(AddressLabel *label) { + if (!label) + return; + + if (label->network) { + LIST_REMOVE(labels, label->network->address_labels, label); + assert(label->network->n_address_labels > 0); + label->network->n_address_labels--; + + if (label->section) { + hashmap_remove(label->network->address_labels_by_section, label->section); + network_config_section_free(label->section); + } + } + + free(label); +} + +static int address_label_new_static(Network *network, const char *filename, unsigned section_line, AddressLabel **ret) { + _cleanup_network_config_section_free_ NetworkConfigSection *n = NULL; + _cleanup_address_label_free_ AddressLabel *label = NULL; + int r; + + assert(network); + assert(ret); + assert(!!filename == (section_line > 0)); + + r = network_config_section_new(filename, section_line, &n); + if (r < 0) + return r; + + label = hashmap_get(network->address_labels_by_section, n); + if (label) { + *ret = label; + label = NULL; + + return 0; + } + + r = address_label_new(&label); + if (r < 0) + return r; + + label->section = n; + n = NULL; + + r = hashmap_put(network->address_labels_by_section, label->section, label); + if (r < 0) + return r; + + label->network = network; + LIST_APPEND(labels, network->address_labels, label); + network->n_address_labels++; + + *ret = label; + label = NULL; + + return 0; +} + +int address_label_configure( + AddressLabel *label, + Link *link, + sd_netlink_message_handler_t callback, + bool update) { + + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(label); + assert(link); + assert(link->ifindex > 0); + assert(link->manager); + assert(link->manager->rtnl); + + r = sd_rtnl_message_new_addrlabel(link->manager->rtnl, &req, RTM_NEWADDRLABEL, + link->ifindex, AF_INET6); + if (r < 0) + return log_error_errno(r, "Could not allocate RTM_NEWADDR message: %m"); + + r = sd_rtnl_message_addrlabel_set_prefixlen(req, label->prefixlen); + if (r < 0) + return log_error_errno(r, "Could not set prefixlen: %m"); + + r = sd_netlink_message_append_u32(req, IFAL_LABEL, label->label); + if (r < 0) + return log_error_errno(r, "Could not append IFAL_LABEL attribute: %m"); + + r = sd_netlink_message_append_in6_addr(req, IFA_ADDRESS, &label->in_addr.in6); + if (r < 0) + return log_error_errno(r, "Could not append IFA_ADDRESS attribute: %m"); + + r = sd_netlink_call_async(link->manager->rtnl, req, callback, link, 0, NULL); + if (r < 0) + return log_error_errno(r, "Could not send rtnetlink message: %m"); + + link_ref(link); + + return 0; +} + +int config_parse_address_label_prefix(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_address_label_free_ AddressLabel *n = NULL; + Network *network = userdata; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = address_label_new_static(network, filename, section_line, &n); + if (r < 0) + return r; + + r = in_addr_prefix_from_string(rvalue, AF_INET6, &n->in_addr, &n->prefixlen); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Address label is invalid, ignoring assignment: %s", rvalue); + return 0; + } + + n = NULL; + + return 0; +} + +int config_parse_address_label( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_address_label_free_ AddressLabel *n = NULL; + Network *network = userdata; + uint32_t k; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = address_label_new_static(network, filename, section_line, &n); + if (r < 0) + return r; + + r = safe_atou32(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address label, ignoring: %s", rvalue); + return 0; + } + + if (k == 0xffffffffUL) { + log_syntax(unit, LOG_ERR, filename, line, r, "Adress label is invalid, ignoring: %s", rvalue); + return 0; + } + + n->label = k; + n = NULL; + + return 0; +} diff --git a/src/network/networkd-address-label.h b/src/network/networkd-address-label.h new file mode 100644 index 0000000000..8724ea8cb5 --- /dev/null +++ b/src/network/networkd-address-label.h @@ -0,0 +1,58 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2017 Susant Sahani + + 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 <inttypes.h> +#include <stdbool.h> + +#include "in-addr-util.h" + +typedef struct AddressLabel AddressLabel; + +#include "networkd-link.h" +#include "networkd-network.h" + +typedef struct Network Network; +typedef struct Link Link; +typedef struct NetworkConfigSection NetworkConfigSection; + +struct AddressLabel { + Network *network; + Link *link; + NetworkConfigSection *section; + + unsigned char prefixlen; + uint32_t label; + + union in_addr_union in_addr; + + LIST_FIELDS(AddressLabel, labels); +}; + +int address_label_new(AddressLabel **ret); +void address_label_free(AddressLabel *label); + +DEFINE_TRIVIAL_CLEANUP_FUNC(AddressLabel*, address_label_free); +#define _cleanup_address_label_free_ _cleanup_(address_label_freep) + +int address_label_configure(AddressLabel *address, Link *link, sd_netlink_message_handler_t callback, bool update); + +int config_parse_address_label(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_address_label_prefix(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-address.c b/src/network/networkd-address.c index 2e6c763aba..d66b3a288f 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -932,3 +932,251 @@ bool address_is_ready(const Address *a) { return !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)); } + +int config_parse_router_preference(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; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + if (streq(rvalue, "high")) + network->router_preference = SD_NDISC_PREFERENCE_HIGH; + else if (STR_IN_SET(rvalue, "medium", "normal", "default")) + network->router_preference = SD_NDISC_PREFERENCE_MEDIUM; + else if (streq(rvalue, "low")) + network->router_preference = SD_NDISC_PREFERENCE_LOW; + else + log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router preference '%s' is invalid, ignoring assignment: %m", rvalue); + + return 0; +} + +void prefix_free(Prefix *prefix) { + if (!prefix) + return; + + if (prefix->network) { + LIST_REMOVE(prefixes, prefix->network->static_prefixes, prefix); + assert(prefix->network->n_static_prefixes > 0); + prefix->network->n_static_prefixes--; + + if (prefix->section) + hashmap_remove(prefix->network->prefixes_by_section, + prefix->section); + } + + prefix->radv_prefix = sd_radv_prefix_unref(prefix->radv_prefix); + + free(prefix); +} + +int prefix_new(Prefix **ret) { + Prefix *prefix = NULL; + + prefix = new0(Prefix, 1); + if (!prefix) + return -ENOMEM; + + if (sd_radv_prefix_new(&prefix->radv_prefix) < 0) + return -ENOMEM; + + *ret = prefix; + prefix = NULL; + + return 0; +} + +int prefix_new_static(Network *network, const char *filename, + unsigned section_line, Prefix **ret) { + _cleanup_network_config_section_free_ NetworkConfigSection *n = NULL; + _cleanup_prefix_free_ Prefix *prefix = NULL; + int r; + + assert(network); + assert(ret); + assert(!!filename == (section_line > 0)); + + if (filename) { + r = network_config_section_new(filename, section_line, &n); + if (r < 0) + return r; + + if (section_line) { + prefix = hashmap_get(network->prefixes_by_section, n); + if (prefix) { + *ret = prefix; + prefix = NULL; + + return 0; + } + } + } + + r = prefix_new(&prefix); + if (r < 0) + return r; + + if (filename) { + prefix->section = n; + n = NULL; + + r = hashmap_put(network->prefixes_by_section, prefix->section, + prefix); + if (r < 0) + return r; + } + + prefix->network = network; + LIST_APPEND(prefixes, network->static_prefixes, prefix); + network->n_static_prefixes++; + + *ret = prefix; + prefix = NULL; + + return 0; +} + +int config_parse_prefix(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_prefix_free_ Prefix *p = NULL; + uint8_t prefixlen = 64; + union in_addr_union in6addr; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = prefix_new_static(network, filename, section_line, &p); + if (r < 0) + return r; + + r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Prefix is invalid, ignoring assignment: %s", rvalue); + return 0; + } + + if (sd_radv_prefix_set_prefix(p->radv_prefix, &in6addr.in6, prefixlen) < 0) + return -EADDRNOTAVAIL; + + log_syntax(unit, LOG_INFO, filename, line, r, "Found prefix %s", rvalue); + + p = NULL; + + return 0; +} + +int config_parse_prefix_flags(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_prefix_free_ Prefix *p = NULL; + int r, val; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = prefix_new_static(network, filename, section_line, &p); + if (r < 0) + return r; + + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address flag, ignoring: %s", rvalue); + return 0; + } + + val = r; + + if (streq(lvalue, "OnLink")) + r = sd_radv_prefix_set_onlink(p->radv_prefix, val); + else if (streq(lvalue, "AddressAutoconfiguration")) + r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, val); + if (r < 0) + return r; + + p = NULL; + + return 0; +} + +int config_parse_prefix_lifetime(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Network *network = userdata; + _cleanup_prefix_free_ Prefix *p = NULL; + usec_t usec; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = prefix_new_static(network, filename, section_line, &p); + if (r < 0) + return r; + + r = parse_sec(rvalue, &usec); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue); + return 0; + } + + /* a value of 0xffffffff represents infinity */ + if (streq(lvalue, "PreferredLifetimeSec")) + r = sd_radv_prefix_set_preferred_lifetime(p->radv_prefix, + DIV_ROUND_UP(usec, USEC_PER_SEC)); + else if (streq(lvalue, "ValidLifetimeSec")) + r = sd_radv_prefix_set_valid_lifetime(p->radv_prefix, + DIV_ROUND_UP(usec, USEC_PER_SEC)); + if (r < 0) + return r; + + p = NULL; + + return 0; +}; diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 71a07ea7a3..065328482e 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -25,6 +25,7 @@ #include "in-addr-util.h" typedef struct Address Address; +typedef struct Prefix Prefix; #include "networkd-link.h" #include "networkd-network.h" @@ -35,6 +36,15 @@ typedef struct Network Network; typedef struct Link Link; typedef struct NetworkConfigSection NetworkConfigSection; +struct Prefix { + Network *network; + NetworkConfigSection *section; + + sd_radv_prefix *radv_prefix; + + LIST_FIELDS(Prefix, prefixes); +}; + struct Address { Network *network; NetworkConfigSection *section; @@ -79,8 +89,20 @@ bool address_is_ready(const Address *a); DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free); #define _cleanup_address_free_ _cleanup_(address_freep) +int prefix_new(Prefix **ret); +void prefix_free(Prefix *prefix); +int prefix_new_static(Network *network, const char *filename, unsigned section, + Prefix **ret); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Prefix*, prefix_free); +#define _cleanup_prefix_free_ _cleanup_(prefix_freep) + int config_parse_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_broadcast(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_label(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_lifetime(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_address_flags(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_router_preference(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_prefix(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_prefix_flags(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_prefix_lifetime(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/network/networkd-conf.c b/src/network/networkd-conf.c index aaa27f311d..e28e018116 100644 --- a/src/network/networkd-conf.c +++ b/src/network/networkd-conf.c @@ -32,10 +32,10 @@ int manager_parse_config_file(Manager *m) { assert(m); return config_parse_many_nulstr(PKGSYSCONFDIR "/networkd.conf", - CONF_PATHS_NULSTR("systemd/networkd.conf.d"), - "DHCP\0", - config_item_perf_lookup, networkd_gperf_lookup, - false, m); + CONF_PATHS_NULSTR("systemd/networkd.conf.d"), + "DHCP\0", + config_item_perf_lookup, networkd_gperf_lookup, + false, m); } static const char* const duid_type_table[_DUID_TYPE_MAX] = { diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index c5c5b95c8f..9229b5753c 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -52,8 +52,21 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, return 1; } +static int route_scope_from_address(const Route *route, const struct in_addr *self_addr) { + assert(route); + assert(self_addr); + + if (in_addr_is_localhost(AF_INET, &route->dst) || + (self_addr->s_addr && route->dst.in.s_addr == self_addr->s_addr)) + return RT_SCOPE_HOST; + else if (in4_addr_is_null(&route->gw.in)) + return RT_SCOPE_LINK; + else + return RT_SCOPE_UNIVERSE; +} + static int link_set_dhcp_routes(Link *link) { - struct in_addr gateway; + struct in_addr gateway, address; _cleanup_free_ sd_dhcp_route **static_routes = NULL; int r, n, i; @@ -64,19 +77,18 @@ static int link_set_dhcp_routes(Link *link) { if (!link->network->dhcp_use_routes) return 0; + r = sd_dhcp_lease_get_address(link->dhcp_lease, &address); + if (r < 0) + return log_link_warning_errno(link, r, "DHCP error: could not get address: %m"); + r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway); if (r < 0 && r != -ENODATA) return log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m"); if (r >= 0) { - struct in_addr address; _cleanup_route_free_ Route *route = NULL; _cleanup_route_free_ Route *route_gw = NULL; - r = sd_dhcp_lease_get_address(link->dhcp_lease, &address); - if (r < 0) - return log_link_warning_errno(link, r, "DHCP error: could not get address: %m"); - r = route_new(&route); if (r < 0) return log_link_error_errno(link, r, "Could not allocate route: %m"); @@ -141,6 +153,7 @@ static int link_set_dhcp_routes(Link *link) { assert_se(sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen) >= 0); route->priority = link->network->dhcp_route_metric; route->table = link->network->dhcp_route_table; + route->scope = route_scope_from_address(route, &address); r = route_configure(route, link, dhcp4_route_handler); if (r < 0) diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 7ba05dbec3..d2b10107b8 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -109,7 +109,7 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, void link->ipv4ll_address = true; - if (link->ipv4ll_route == true) + if (link->ipv4ll_route) link_check_ready(link); return 1; @@ -179,12 +179,22 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) { switch(event) { case SD_IPV4LL_EVENT_STOP: + r = ipv4ll_address_lost(link); + if (r < 0) { + link_enter_failed(link); + return; + } + break; case SD_IPV4LL_EVENT_CONFLICT: r = ipv4ll_address_lost(link); if (r < 0) { link_enter_failed(link); return; } + + r = sd_ipv4ll_restart(ll); + if (r < 0) + log_link_warning(link, "Could not acquire IPv4 link-local address"); break; case SD_IPV4LL_EVENT_BIND: r = ipv4ll_address_claimed(ll, link); diff --git a/src/network/networkd-ipv6-proxy-ndp.c b/src/network/networkd-ipv6-proxy-ndp.c index 11c1cd9268..00790c0c13 100644 --- a/src/network/networkd-ipv6-proxy-ndp.c +++ b/src/network/networkd-ipv6-proxy-ndp.c @@ -38,6 +38,9 @@ static bool ipv6_proxy_ndp_is_needed(Link *link) { if (!link->network) return false; + if (link->network->ipv6_proxy_ndp != -1) + return link->network->ipv6_proxy_ndp; + if (link->network->n_ipv6_proxy_ndp_addresses == 0) return false; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 0c1229336b..4c57fa1793 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -32,6 +32,7 @@ #include "networkd-lldp-tx.h" #include "networkd-manager.h" #include "networkd-ndisc.h" +#include "networkd-radv.h" #include "set.h" #include "socket-util.h" #include "stdio-util.h" @@ -119,6 +120,15 @@ static bool link_ipv6_enabled(Link *link) { return link_ipv6ll_enabled(link) || network_has_static_ipv6_addresses(link->network); } +static bool link_radv_enabled(Link *link) { + assert(link); + + if (!link_ipv6ll_enabled(link)) + return false; + + return link->network->router_prefix_delegation; +} + static bool link_lldp_rx_enabled(Link *link) { assert(link); @@ -131,7 +141,10 @@ static bool link_lldp_rx_enabled(Link *link) { if (!link->network) return false; - if (link->network->bridge) + /* LLDP should be handled on bridge slaves as those have a direct + * connection to their peers not on the bridge master. Linux doesn't + * even (by default) forward lldp packets to the bridge master.*/ + if (streq_ptr("bridge", link->kind)) return false; return link->network->lldp_mode != LLDP_MODE_NO; @@ -521,6 +534,7 @@ static void link_free(Link *link) { sd_ipv4ll_unref(link->ipv4ll); sd_dhcp6_client_unref(link->dhcp6_client); sd_ndisc_unref(link->ndisc); + sd_radv_unref(link->radv); if (link->manager) hashmap_remove(link->manager->links, INT_TO_PTR(link->ifindex)); @@ -640,6 +654,12 @@ static int link_stop_clients(Link *link) { r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Discovery: %m"); } + if (link->radv) { + k = sd_radv_stop(link->radv); + if (k < 0) + r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Advertisement: %m"); + } + link_lldp_emit_stop(link); return r; } @@ -853,6 +873,35 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda return 1; } +static int address_label_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(rtnl); + assert(m); + assert(link); + assert(link->ifname); + assert(link->link_messages > 0); + + link->link_messages--; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) + log_link_warning_errno(link, r, "could not set address label: %m"); + else if (r >= 0) + manager_rtnl_process_address(rtnl, m, link->manager); + + if (link->link_messages == 0) { + log_link_debug(link, "Addresses label set"); + link_enter_set_routes(link); + } + + return 1; +} + static int link_push_uplink_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) { _cleanup_free_ struct in_addr *addresses = NULL; size_t n_addresses = 0, n_allocated = 0; @@ -965,6 +1014,7 @@ static int link_set_bridge_fdb(Link *link) { } static int link_enter_set_addresses(Link *link) { + AddressLabel *label; Address *ad; int r; @@ -989,6 +1039,17 @@ static int link_enter_set_addresses(Link *link) { link->link_messages++; } + LIST_FOREACH(labels, label, link->network->address_labels) { + r = address_label_configure(label, link, address_label_handler, false); + if (r < 0) { + log_link_warning_errno(link, r, "Could not set address label: %m"); + link_enter_failed(link); + return r; + } + + link->link_messages++; + } + /* now that we can figure out a default address for the dhcp server, start it */ if (link_dhcp4_server_enabled(link)) { @@ -1325,6 +1386,11 @@ static int link_set_bridge(Link *link) { if (r < 0) return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_COST attribute: %m"); } + if (link->network->priority != LINK_BRIDGE_PORT_PRIORITY_INVALID) { + r = sd_netlink_message_append_u16(req, IFLA_BRPORT_PRIORITY, link->network->priority); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_PRIORITY attribute: %m"); + } r = sd_netlink_message_close_container(req); if (r < 0) @@ -1508,6 +1574,17 @@ static int link_acquire_ipv6_conf(Link *link) { return log_link_warning_errno(link, r, "Could not start IPv6 Router Discovery: %m"); } + if (link_radv_enabled(link)) { + assert(link->radv); + assert(in_addr_is_link_local(AF_INET6, (const union in_addr_union*)&link->ipv6ll_address) > 0); + + log_link_debug(link, "Starting IPv6 Router Advertisements"); + + r = sd_radv_start(link->radv); + if (r < 0 && r != -EBUSY) + return log_link_warning_errno(link, r, "Could not start IPv6 Router Advertisement: %m"); + } + return 0; } @@ -1598,7 +1675,7 @@ static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda return 1; } -static int link_up(Link *link) { +int link_up(Link *link) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; uint8_t ipv6ll_mode; int r; @@ -1719,7 +1796,7 @@ static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, void *user return 1; } -static int link_down(Link *link) { +int link_down(Link *link) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -2058,6 +2135,12 @@ static int link_joined(Link *link) { log_link_error_errno(link, r, "Could not set bridge vlan: %m"); } + /* Skip setting up addresses until it gets carrier, + or it would try to set addresses twice, + which is bad for non-idempotent steps. */ + if (!link_has_carrier(link)) + return 0; + return link_enter_set_addresses(link); } @@ -2366,7 +2449,7 @@ static int link_drop_foreign_config(Link *link) { } static int link_drop_config(Link *link) { - Address *address; + Address *address, *pool_address; Route *route; Iterator i; int r; @@ -2379,6 +2462,15 @@ static int link_drop_config(Link *link) { r = address_remove(address, link, link_address_remove_handler); if (r < 0) return r; + + /* If this address came from an address pool, clean up the pool */ + LIST_FOREACH(addresses, pool_address, link->pool_addresses) { + if (address_equal(address, pool_address)) { + LIST_REMOVE(addresses, link->pool_addresses, pool_address); + address_free(pool_address); + break; + } + } } SET_FOREACH(route, link->routes, i) { @@ -2516,6 +2608,12 @@ static int link_configure(Link *link) { return r; } + if (link_radv_enabled(link)) { + r = radv_configure(link); + if (r < 0) + return r; + } + if (link_lldp_rx_enabled(link)) { r = sd_lldp_new(&link->lldp); if (r < 0) @@ -2962,6 +3060,8 @@ static int link_carrier_lost(Link *link) { return r; } + (void) sd_dhcp_server_stop(link->dhcp_server); + r = link_drop_config(link); if (r < 0) return r; @@ -3052,6 +3152,12 @@ int link_update(Link *link, sd_netlink_message *m) { return r; } } + + if (link->radv) { + r = sd_radv_set_mtu(link->radv, link->mtu); + if (r < 0) + return log_link_warning_errno(link, r, "Could not set MTU for Router Advertisement: %m"); + } } /* The kernel may broadcast NEWLINK messages without the MAC address @@ -3120,6 +3226,12 @@ int link_update(Link *link, sd_netlink_message *m) { if (r < 0) return log_link_warning_errno(link, r, "Could not update DHCPv6 DUID: %m"); } + + if (link->radv) { + r = sd_radv_set_mac(link->radv, &link->mac); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update MAC for Router Advertisement: %m"); + } } } @@ -3220,6 +3332,7 @@ int link_save(Link *link) { sd_dhcp6_lease *dhcp6_lease = NULL; const char *dhcp_domainname = NULL; char **dhcp6_domains = NULL; + char **dhcp_domains = NULL; unsigned j; if (link->dhcp6_client) { @@ -3329,13 +3442,16 @@ int link_save(Link *link) { fputc('\n', f); if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) { - if (link->dhcp_lease) + if (link->dhcp_lease) { (void) sd_dhcp_lease_get_domainname(link->dhcp_lease, &dhcp_domainname); + (void) sd_dhcp_lease_get_search_domains(link->dhcp_lease, &dhcp_domains); + } if (dhcp6_lease) (void) sd_dhcp6_lease_get_domains(dhcp6_lease, &dhcp6_domains); } fputs("DOMAINS=", f); + space = false; fputstrv(f, link->network->search_domains, NULL, &space); if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) { @@ -3343,6 +3459,8 @@ int link_save(Link *link) { if (dhcp_domainname) fputs_with_space(f, dhcp_domainname, NULL, &space); + if (dhcp_domains) + fputstrv(f, dhcp_domains, NULL, &space); if (dhcp6_domains) fputstrv(f, dhcp6_domains, NULL, &space); @@ -3353,13 +3471,16 @@ int link_save(Link *link) { fputc('\n', f); fputs("ROUTE_DOMAINS=", f); - fputstrv(f, link->network->route_domains, NULL, NULL); + space = false; + fputstrv(f, link->network->route_domains, NULL, &space); if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE) { NDiscDNSSL *dd; if (dhcp_domainname) fputs_with_space(f, dhcp_domainname, NULL, &space); + if (dhcp_domains) + fputstrv(f, dhcp_domains, NULL, &space); if (dhcp6_domains) fputstrv(f, dhcp6_domains, NULL, &space); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index e6190fbe57..6479f4a2e5 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -28,6 +28,7 @@ #include "sd-ipv4ll.h" #include "sd-lldp.h" #include "sd-ndisc.h" +#include "sd-radv.h" #include "sd-netlink.h" #include "list.h" @@ -117,6 +118,8 @@ typedef struct Link { Set *ndisc_rdnss; Set *ndisc_dnssl; + sd_radv *radv; + sd_dhcp6_client *dhcp6_client; bool rtnl_extended_attrs; @@ -138,6 +141,9 @@ int link_get(Manager *m, int ifindex, Link **ret); int link_add(Manager *manager, sd_netlink_message *message, Link **ret); void link_drop(Link *link); +int link_up(Link *link); +int link_down(Link *link); + int link_address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata); int link_route_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index ea1c320809..5f10b4f993 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -961,15 +961,20 @@ static int manager_save(Manager *m) { if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) { const char *domainname; + char **domains = NULL; + OrderedSet *target_domains = (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) ? search_domains : route_domains; r = sd_dhcp_lease_get_domainname(link->dhcp_lease, &domainname); if (r >= 0) { + r = ordered_set_put_strdup(target_domains, domainname); + if (r < 0) + return r; + } else if (r != -ENODATA) + return r; - if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) - r = ordered_set_put_strdup(search_domains, domainname); - else - r = ordered_set_put_strdup(route_domains, domainname); - + r = sd_dhcp_lease_get_search_domains(link->dhcp_lease, &domains); + if (r >= 0) { + r = ordered_set_put_strdupv(target_domains, domains); if (r < 0) return r; } else if (r != -ENODATA) diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 4fd5d8ae70..d52b511bb5 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -27,6 +27,7 @@ #define NDISC_DNSSL_MAX 64U #define NDISC_RDNSS_MAX 64U +#define NDISC_PREFIX_LFT_MIN 7200U static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { _cleanup_link_unref_ Link *link = userdata; @@ -152,13 +153,21 @@ static void ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { static void ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) { _cleanup_address_free_ Address *address = NULL; - uint32_t lifetime_valid, lifetime_preferred; + Address *existing_address; + uint32_t lifetime_valid, lifetime_preferred, lifetime_remaining; + usec_t time_now; unsigned prefixlen; int r; assert(link); assert(rt); + r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to get RA timestamp: %m"); + return; + } + r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen); if (r < 0) { log_link_error_errno(link, r, "Failed to get prefix length: %m"); @@ -207,7 +216,24 @@ static void ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router * address->prefixlen = prefixlen; address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR; address->cinfo.ifa_prefered = lifetime_preferred; - address->cinfo.ifa_valid = lifetime_valid; + + /* see RFC4862 section 5.5.3.e */ + r = address_get(link, address->family, &address->in_addr, address->prefixlen, &existing_address); + if (r > 0) { + lifetime_remaining = existing_address->cinfo.tstamp / 100 + existing_address->cinfo.ifa_valid - time_now / USEC_PER_SEC; + if (lifetime_valid > NDISC_PREFIX_LFT_MIN || lifetime_valid > lifetime_remaining) + address->cinfo.ifa_valid = lifetime_valid; + else if (lifetime_remaining <= NDISC_PREFIX_LFT_MIN) + address->cinfo.ifa_valid = lifetime_remaining; + else + address->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN; + } else if (lifetime_valid > 0) + address->cinfo.ifa_valid = lifetime_valid; + else + return; /* see RFC4862 section 5.5.3.d */ + + if (address->cinfo.ifa_valid == 0) + return; r = address_configure(address, link, ndisc_netlink_handler, true); if (r < 0) { diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 68052ba544..a2d38501a5 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -63,6 +63,7 @@ Network.IPv6AcceptRA, config_parse_tristate, Network.IPv6AcceptRouterAdvertisements, config_parse_tristate, 0, offsetof(Network, ipv6_accept_ra) Network.IPv6DuplicateAddressDetection, config_parse_int, 0, offsetof(Network, ipv6_dad_transmits) Network.IPv6HopLimit, config_parse_int, 0, offsetof(Network, ipv6_hop_limit) +Network.IPv6ProxyNDP, config_parse_tristate, 0, offsetof(Network, ipv6_proxy_ndp) Network.ActiveSlave, config_parse_bool, 0, offsetof(Network, active_slave) Network.PrimarySlave, config_parse_bool, 0, offsetof(Network, primary_slave) Network.IPv4ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp) @@ -79,6 +80,8 @@ Address.DuplicateAddressDetection, config_parse_address_flags, Address.ManageTemporaryAddress, config_parse_address_flags, 0, 0 Address.PrefixRoute, config_parse_address_flags, 0, 0 Address.AutoJoin, config_parse_address_flags, 0, 0 +IPv6AddressLabel.Prefix, config_parse_address_label_prefix, 0, 0 +IPv6AddressLabel.Label, config_parse_address_label, 0, 0 Route.Gateway, config_parse_gateway, 0, 0 Route.Destination, config_parse_destination, 0, 0 Route.Source, config_parse_destination, 0, 0 @@ -86,6 +89,9 @@ Route.Metric, config_parse_route_priority, Route.Scope, config_parse_route_scope, 0, 0 Route.PreferredSource, config_parse_preferred_src, 0, 0 Route.Table, config_parse_route_table, 0, 0 +Route.GatewayOnlink, config_parse_gateway_onlink, 0, 0 +Route.IPv6Preference, config_parse_ipv6_route_preference, 0, 0 +Route.Protocol, config_parse_route_protocol, 0, 0 DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier) DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns) DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp) @@ -119,17 +125,28 @@ DHCPServer.EmitTimezone, config_parse_bool, DHCPServer.Timezone, config_parse_timezone, 0, offsetof(Network, dhcp_server_timezone) DHCPServer.PoolOffset, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_offset) DHCPServer.PoolSize, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_size) -Bridge.Cost, config_parse_unsigned, 0, offsetof(Network, cost) +Bridge.Cost, config_parse_uint32, 0, offsetof(Network, cost) Bridge.UseBPDU, config_parse_bool, 0, offsetof(Network, use_bpdu) Bridge.HairPin, config_parse_bool, 0, offsetof(Network, hairpin) Bridge.FastLeave, config_parse_bool, 0, offsetof(Network, fast_leave) Bridge.AllowPortToBeRoot, config_parse_bool, 0, offsetof(Network, allow_port_to_be_root) Bridge.UnicastFlood, config_parse_bool, 0, offsetof(Network, unicast_flood) +Bridge.Priority, config_parse_bridge_port_priority, 0, offsetof(Network, priority) BridgeFDB.MACAddress, config_parse_fdb_hwaddr, 0, 0 BridgeFDB.VLANId, config_parse_fdb_vlan_id, 0, 0 BridgeVLAN.PVID, config_parse_brvlan_pvid, 0, 0 BridgeVLAN.VLAN, config_parse_brvlan_vlan, 0, 0 BridgeVLAN.EgressUntagged, config_parse_brvlan_untagged, 0, 0 +Network.IPv6PrefixDelegation, config_parse_bool, 0, offsetof(Network, router_prefix_delegation) +IPv6PrefixDelegation.RouterLifetimeSec, config_parse_sec, 0, offsetof(Network, router_lifetime_usec) +IPv6PrefixDelegation.Managed, config_parse_bool, 0, offsetof(Network, router_managed) +IPv6PrefixDelegation.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information) +IPv6PrefixDelegation.RouterPreference, config_parse_router_preference, 0, 0 +IPv6Prefix.Prefix, config_parse_prefix, 0, 0 +IPv6Prefix.OnLink, config_parse_prefix_flags, 0, 0 +IPv6Prefix.AddressAutoconfiguration, config_parse_prefix_flags, 0, 0 +IPv6Prefix.ValidLifetimeSec, config_parse_prefix_lifetime, 0, 0 +IPv6Prefix.PreferredLifetimeSec, config_parse_prefix_lifetime, 0, 0 /* backwards compatibility: do not add new entries to this section */ Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local) DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index ab372568de..6f2ae66d40 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -114,6 +114,8 @@ static int network_load_one(Manager *manager, const char *filename) { LIST_HEAD_INIT(network->static_routes); LIST_HEAD_INIT(network->static_fdb_entries); LIST_HEAD_INIT(network->ipv6_proxy_ndp_addresses); + LIST_HEAD_INIT(network->address_labels); + LIST_HEAD_INIT(network->static_prefixes); network->stacked_netdevs = hashmap_new(&string_hash_ops); if (!network->stacked_netdevs) @@ -131,6 +133,14 @@ static int network_load_one(Manager *manager, const char *filename) { if (!network->fdb_entries_by_section) return log_oom(); + network->address_labels_by_section = hashmap_new(&network_config_hash_ops); + if (!network->address_labels_by_section) + log_oom(); + + network->prefixes_by_section = hashmap_new(&network_config_hash_ops); + if (!network->prefixes_by_section) + return log_oom(); + network->filename = strdup(filename); if (!network->filename) return log_oom(); @@ -165,6 +175,7 @@ static int network_load_one(Manager *manager, const char *filename) { network->use_bpdu = true; network->allow_port_to_be_root = true; network->unicast_flood = true; + network->priority = LINK_BRIDGE_PORT_PRIORITY_INVALID; network->lldp_mode = LLDP_MODE_ROUTERS_ONLY; @@ -178,6 +189,7 @@ static int network_load_one(Manager *manager, const char *filename) { network->ipv6_accept_ra = -1; network->ipv6_dad_transmits = -1; network->ipv6_hop_limit = -1; + network->ipv6_proxy_ndp = -1; network->duid.type = _DUID_TYPE_INVALID; network->proxy_arp = -1; network->arp = -1; @@ -191,6 +203,7 @@ static int network_load_one(Manager *manager, const char *filename) { "Link\0" "Network\0" "Address\0" + "IPv6AddressLabel\0" "Route\0" "DHCP\0" "DHCPv4\0" /* compat */ @@ -199,7 +212,9 @@ static int network_load_one(Manager *manager, const char *filename) { "IPv6NDPProxyAddress\0" "Bridge\0" "BridgeFDB\0" - "BridgeVLAN\0", + "BridgeVLAN\0" + "IPv6PrefixDelegation\0" + "IPv6Prefix\0", config_item_perf_lookup, network_network_gperf_lookup, false, network); if (r < 0) @@ -270,6 +285,8 @@ void network_free(Network *network) { Address *address; FdbEntry *fdb_entry; IPv6ProxyNDPAddress *ipv6_proxy_ndp_address; + AddressLabel *label; + Prefix *prefix; Iterator i; if (!network) @@ -317,9 +334,17 @@ void network_free(Network *network) { while ((ipv6_proxy_ndp_address = network->ipv6_proxy_ndp_addresses)) ipv6_proxy_ndp_address_free(ipv6_proxy_ndp_address); + while ((label = network->address_labels)) + address_label_free(label); + + while ((prefix = network->static_prefixes)) + prefix_free(prefix); + hashmap_free(network->addresses_by_section); hashmap_free(network->routes_by_section); hashmap_free(network->fdb_entries_by_section); + hashmap_free(network->address_labels_by_section); + hashmap_free(network->prefixes_by_section); if (network->manager) { if (network->manager->networks) @@ -428,7 +453,7 @@ int network_apply(Network *network, Link *link) { if (network->ipv4ll_route) { Route *route; - r = route_new_static(network, "Network", 0, &route); + r = route_new_static(network, NULL, 0, &route); if (r < 0) return r; diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 4ce066a764..b31921947d 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -28,6 +28,7 @@ #include "resolve-util.h" #include "networkd-address.h" +#include "networkd-address-label.h" #include "networkd-brvlan.h" #include "networkd-fdb.h" #include "networkd-lldp-tx.h" @@ -157,13 +158,21 @@ struct Network { AddressFamilyBoolean link_local; bool ipv4ll_route; + /* IPv6 prefix delegation support */ + bool router_prefix_delegation; + usec_t router_lifetime_usec; + uint8_t router_preference; + bool router_managed; + bool router_other_information; + /* Bridge Support */ bool use_bpdu; bool hairpin; bool fast_leave; bool allow_port_to_be_root; bool unicast_flood; - unsigned cost; + uint32_t cost; + uint16_t priority; bool use_br_vlan; uint16_t pvid; @@ -176,6 +185,7 @@ struct Network { int ipv6_accept_ra; int ipv6_dad_transmits; int ipv6_hop_limit; + int ipv6_proxy_ndp; int proxy_arp; bool ipv6_accept_ra_use_dns; @@ -201,15 +211,21 @@ struct Network { LIST_HEAD(Route, static_routes); LIST_HEAD(FdbEntry, static_fdb_entries); LIST_HEAD(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses); + LIST_HEAD(AddressLabel, address_labels); + LIST_HEAD(Prefix, static_prefixes); unsigned n_static_addresses; unsigned n_static_routes; unsigned n_static_fdb_entries; unsigned n_ipv6_proxy_ndp_addresses; + unsigned n_address_labels; + unsigned n_static_prefixes; Hashmap *addresses_by_section; Hashmap *routes_by_section; Hashmap *fdb_entries_by_section; + Hashmap *address_labels_by_section; + Hashmap *prefixes_by_section; struct in_addr_data *dns; unsigned n_dns; diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c new file mode 100644 index 0000000000..af9e116936 --- /dev/null +++ b/src/network/networkd-radv.c @@ -0,0 +1,79 @@ +/*** + This file is part of systemd. + + Copyright (C) 2017 Intel Corporation. All rights reserved. + + 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/icmp6.h> +#include <arpa/inet.h> + +#include "networkd-address.h" +#include "networkd-radv.h" +#include "sd-radv.h" + +int radv_configure(Link *link) { + int r; + Prefix *p; + + assert(link); + assert(link->network); + + r = sd_radv_new(&link->radv); + if (r < 0) + return r; + + r = sd_radv_attach_event(link->radv, NULL, 0); + if (r < 0) + return r; + + r = sd_radv_set_mac(link->radv, &link->mac); + if (r < 0) + return r; + + r = sd_radv_set_ifindex(link->radv, link->ifindex); + if (r < 0) + return r; + + r = sd_radv_set_managed_information(link->radv, link->network->router_managed); + if (r < 0) + return r; + + r = sd_radv_set_other_information(link->radv, link->network->router_other_information); + if (r < 0) + return r; + + /* a value of 0xffffffff represents infinity, 0x0 means this host is + not a router */ + r = sd_radv_set_router_lifetime(link->radv, + DIV_ROUND_UP(link->network->router_lifetime_usec, USEC_PER_SEC)); + if (r < 0) + return r; + + if (link->network->router_lifetime_usec > 0) { + r = sd_radv_set_preference(link->radv, + link->network->router_preference); + if (r < 0) + return r; + } + + LIST_FOREACH(prefixes, p, link->network->static_prefixes) { + r = sd_radv_add_prefix(link->radv, p->radv_prefix); + if (r != -EEXIST && r < 0) + return r; + } + + return 0; +} diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h new file mode 100644 index 0000000000..a186b111a1 --- /dev/null +++ b/src/network/networkd-radv.h @@ -0,0 +1,24 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2017 Intel Corporation. All rights reserved. + + 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 "networkd-link.h" + +int radv_configure(Link *link); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 570083f180..e5d61ce8cc 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -17,6 +17,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <linux/icmpv6.h> + #include "alloc-util.h" #include "conf-parser.h" #include "in-addr-util.h" @@ -228,7 +230,7 @@ int route_get(Link *link, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, - unsigned char table, + uint32_t table, Route **ret) { Route route, *existing; @@ -270,7 +272,7 @@ static int route_add_internal( unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, - unsigned char table, + uint32_t table, Route **ret) { _cleanup_route_free_ Route *route = NULL; @@ -316,7 +318,7 @@ int route_add_foreign( unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, - unsigned char table, + uint32_t table, Route **ret) { return route_add_internal(link, &link->routes_foreign, family, dst, dst_prefixlen, tos, priority, table, ret); @@ -329,7 +331,7 @@ int route_add( unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, - unsigned char table, + uint32_t table, Route **ret) { Route *route; @@ -755,10 +757,9 @@ int config_parse_destination(const char *unit, Network *network = userdata; _cleanup_route_free_ Route *n = NULL; - const char *address, *e; union in_addr_union buffer; unsigned char prefixlen; - int r, f; + int r; assert(filename); assert(section); @@ -770,45 +771,20 @@ int config_parse_destination(const char *unit, if (r < 0) return r; - /* Destination|Source=address/prefixlen */ - - /* address */ - e = strchr(rvalue, '/'); - if (e) - address = strndupa(rvalue, e - rvalue); - else - address = rvalue; - - r = in_addr_from_string_auto(address, &f, &buffer); + r = in_addr_prefix_from_string(rvalue, AF_INET, &buffer, &prefixlen); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Destination is invalid, ignoring assignment: %s", address); - return 0; - } - - if (f != AF_INET && f != AF_INET6) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown address family, ignoring assignment: %s", address); - return 0; - } - - /* prefixlen */ - if (e) { - r = safe_atou8(e + 1, &prefixlen); + r = in_addr_prefix_from_string(rvalue, AF_INET6, &buffer, &prefixlen); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Route destination prefix length is invalid, ignoring assignment: %s", e + 1); + log_syntax(unit, LOG_ERR, filename, line, r, + "Route %s= prefix is invalid, ignoring assignment: %s", + lvalue, rvalue); return 0; } - } else { - switch (f) { - case AF_INET: - prefixlen = 32; - break; - case AF_INET6: - prefixlen = 128; - break; - } - } - n->family = f; + n->family = AF_INET6; + } else + n->family = AF_INET; + if (streq(lvalue, "Destination")) { n->dst = buffer; n->dst_prefixlen = prefixlen; @@ -939,3 +915,111 @@ int config_parse_route_table(const char *unit, return 0; } + +int config_parse_gateway_onlink(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_route_free_ Route *n = NULL; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = route_new_static(network, filename, section_line, &n); + if (r < 0) + return r; + + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Could not parse gateway onlink \"%s\", ignoring assignment: %m", rvalue); + return 0; + } + + SET_FLAG(n->flags, RTNH_F_ONLINK, r); + n = NULL; + + return 0; +} + +int config_parse_ipv6_route_preference(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_route_free_ Route *n = NULL; + int r; + + r = route_new_static(network, filename, section_line, &n); + if (r < 0) + return r; + + if (streq(rvalue, "low")) + n->pref = ICMPV6_ROUTER_PREF_LOW; + else if (streq(rvalue, "medium")) + n->pref = ICMPV6_ROUTER_PREF_MEDIUM; + else if (streq(rvalue, "high")) + n->pref = ICMPV6_ROUTER_PREF_HIGH; + else { + log_syntax(unit, LOG_ERR, filename, line, 0, "Unknown route preference: %s", rvalue); + return 0; + } + + n = NULL; + + return 0; +} + +int config_parse_route_protocol(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_route_free_ Route *n = NULL; + int r; + + r = route_new_static(network, filename, section_line, &n); + if (r < 0) + return r; + + if (streq(rvalue, "kernel")) + n->protocol = RTPROT_KERNEL; + else if (streq(rvalue, "boot")) + n->protocol = RTPROT_BOOT; + else if (streq(rvalue, "static")) + n->protocol = RTPROT_STATIC; + else { + r = safe_atou8(rvalue , &n->protocol); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse route protocol \"%s\", ignoring assignment: %m", rvalue); + return 0; + } + } + + n = NULL; + + return 0; +} diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 4ebfa0f0bd..3f389489da 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -59,9 +59,9 @@ void route_free(Route *route); int route_configure(Route *route, Link *link, sd_netlink_message_handler_t callback); int route_remove(Route *route, Link *link, sd_netlink_message_handler_t callback); -int route_get(Link *link, int family, const union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, unsigned char table, Route **ret); -int route_add(Link *link, int family, const union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, unsigned char table, Route **ret); -int route_add_foreign(Link *link, int family, const union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, unsigned char table, Route **ret); +int route_get(Link *link, int family, const union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, uint32_t table, Route **ret); +int route_add(Link *link, int family, const union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, uint32_t table, Route **ret); +int route_add_foreign(Link *link, int family, const union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, uint32_t table, Route **ret); int route_update(Route *route, const union in_addr_union *src, unsigned char src_prefixlen, const union in_addr_union *gw, const union in_addr_union *prefsrc, unsigned char scope, unsigned char protocol); int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata); @@ -75,3 +75,6 @@ int config_parse_destination(const char *unit, const char *filename, unsigned li int config_parse_route_priority(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_route_scope(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_route_table(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_gateway_onlink(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_ipv6_route_preference(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_route_protocol(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/meson.build b/src/nspawn/meson.build new file mode 100644 index 0000000000..b6ac6006ab --- /dev/null +++ b/src/nspawn/meson.build @@ -0,0 +1,40 @@ +systemd_nspawn_sources = files(''' + nspawn.c + nspawn-settings.c + nspawn-settings.h + nspawn-mount.c + nspawn-mount.h + nspawn-network.c + nspawn-network.h + nspawn-expose-ports.c + nspawn-expose-ports.h + nspawn-cgroup.c + nspawn-cgroup.h + nspawn-seccomp.c + nspawn-seccomp.h + nspawn-register.c + nspawn-register.h + nspawn-setuid.c + nspawn-setuid.h + nspawn-stub-pid1.c + nspawn-stub-pid1.h + nspawn-patch-uid.c + nspawn-patch-uid.h +'''.split()) + +nspawn_gperf_c = custom_target( + 'nspawn-gperf.c', + input : 'nspawn-gperf.gperf', + output : 'nspawn-gperf.c', + command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@']) + +systemd_nspawn_sources += [nspawn_gperf_c] + +tests += [ + [['src/nspawn/test-patch-uid.c', + 'src/nspawn/nspawn-patch-uid.c', + 'src/nspawn/nspawn-patch-uid.h'], + [libshared], + [libacl], + '', 'manual'], +] diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c index d749756437..fd565c09cd 100644 --- a/src/nspawn/nspawn-cgroup.c +++ b/src/nspawn/nspawn-cgroup.c @@ -49,7 +49,7 @@ static int chown_cgroup_path(const char *path, uid_t uid_shift) { "cgroup.subtree_control") if (fchownat(fd, fn, uid_shift, uid_shift, 0) < 0) log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno, - "Failed to chown() cgroup file %s, ignoring: %m", fn); + "Failed to chown \"%s/%s\", ignoring: %m", path, fn); return 0; } diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index d276994120..ac7290732e 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -552,7 +552,7 @@ int mount_all(const char *dest, { NULL, "/proc/sysrq-trigger", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, /* ... then, make it r/o */ /* outer child mounts */ - { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME, MOUNT_FATAL }, + { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, MOUNT_FATAL }, { "tmpfs", "/sys", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV, MOUNT_FATAL|MOUNT_APPLY_APIVFS_NETNS }, { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, MOUNT_FATAL|MOUNT_APPLY_APIVFS_RO }, /* skipped if above was mounted */ { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, MOUNT_FATAL }, /* skipped if above was mounted */ diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c index 428cc04de0..aa61aaaa79 100644 --- a/src/nspawn/nspawn-network.c +++ b/src/nspawn/nspawn-network.c @@ -19,6 +19,7 @@ #include <linux/veth.h> #include <net/if.h> +#include <sys/file.h> #include "libudev.h" #include "sd-id128.h" diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c index e3ab39faea..5b0faf809c 100644 --- a/src/nspawn/nspawn-register.c +++ b/src/nspawn/nspawn-register.c @@ -27,6 +27,77 @@ #include "strv.h" #include "util.h" +static int append_machine_properties( + sd_bus_message *m, + CustomMount *mounts, + unsigned n_mounts, + int kill_signal, + char **properties) { + + unsigned j; + int r; + + assert(m); + + r = sd_bus_message_append(m, "(sv)", "DevicePolicy", "s", "closed"); + if (r < 0) + return bus_log_create_error(r); + + /* If you make changes here, also make sure to update systemd-nspawn@.service, to keep the device policies in + * sync regardless if we are run with or without the --keep-unit switch. */ + r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 2, + /* Allow the container to + * access and create the API + * device nodes, so that + * PrivateDevices= in the + * container can work + * fine */ + "/dev/net/tun", "rwm", + /* Allow the container + * access to ptys. However, + * do not permit the + * container to ever create + * these device nodes. */ + "char-pts", "rw"); + if (r < 0) + return bus_log_create_error(r); + + for (j = 0; j < n_mounts; j++) { + CustomMount *cm = mounts + j; + + if (cm->type != CUSTOM_MOUNT_BIND) + continue; + + r = is_device_node(cm->source); + if (r == -ENOENT) { + /* The bind source might only appear as the image is put together, hence don't complain */ + log_debug_errno(r, "Bind mount source %s not found, ignoring: %m", cm->source); + continue; + } + if (r < 0) + return log_error_errno(r, "Failed to stat %s: %m", cm->source); + + if (r) { + r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 1, + cm->source, cm->read_only ? "r" : "rw"); + if (r < 0) + return log_error_errno(r, "Failed to append message arguments: %m"); + } + } + + if (kill_signal != 0) { + r = sd_bus_message_append(m, "(sv)", "KillSignal", "i", kill_signal); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "(sv)", "KillMode", "s", "mixed"); + if (r < 0) + return bus_log_create_error(r); + } + + return 0; +} + int register_machine( const char *machine_name, pid_t pid, @@ -68,7 +139,6 @@ int register_machine( local_ifindex > 0 ? 1 : 0, local_ifindex); } else { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - unsigned j; r = sd_bus_message_new_method_call( bus, @@ -103,63 +173,14 @@ int register_machine( return bus_log_create_error(r); } - r = sd_bus_message_append(m, "(sv)", "DevicePolicy", "s", "closed"); - if (r < 0) - return bus_log_create_error(r); - - /* If you make changes here, also make sure to update - * systemd-nspawn@.service, to keep the device - * policies in sync regardless if we are run with or - * without the --keep-unit switch. */ - r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 2, - /* Allow the container to - * access and create the API - * device nodes, so that - * PrivateDevices= in the - * container can work - * fine */ - "/dev/net/tun", "rwm", - /* Allow the container - * access to ptys. However, - * do not permit the - * container to ever create - * these device nodes. */ - "char-pts", "rw"); + r = append_machine_properties( + m, + mounts, + n_mounts, + kill_signal, + properties); if (r < 0) - return bus_log_create_error(r); - - for (j = 0; j < n_mounts; j++) { - CustomMount *cm = mounts + j; - - if (cm->type != CUSTOM_MOUNT_BIND) - continue; - - r = is_device_node(cm->source); - if (r == -ENOENT) { - /* The bind source might only appear as the image is put together, hence don't complain */ - log_debug_errno(r, "Bind mount source %s not found, ignoring: %m", cm->source); - continue; - } - if (r < 0) - return log_error_errno(r, "Failed to stat %s: %m", cm->source); - - if (r) { - r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 1, - cm->source, cm->read_only ? "r" : "rw"); - if (r < 0) - return log_error_errno(r, "Failed to append message arguments: %m"); - } - } - - if (kill_signal != 0) { - r = sd_bus_message_append(m, "(sv)", "KillSignal", "i", kill_signal); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(m, "(sv)", "KillMode", "s", "mixed"); - if (r < 0) - return bus_log_create_error(r); - } + return r; r = bus_append_unit_property_assignment_many(m, properties); if (r < 0) @@ -229,3 +250,104 @@ int terminate_machine(pid_t pid) { return 0; } + +int allocate_scope( + const char *machine_name, + pid_t pid, + const char *slice, + CustomMount *mounts, + unsigned n_mounts, + int kill_signal, + char **properties) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; + _cleanup_free_ char *scope = NULL; + const char *description, *object; + int r; + + r = sd_bus_default_system(&bus); + if (r < 0) + return log_error_errno(r, "Failed to open system bus: %m"); + + r = bus_wait_for_jobs_new(bus, &w); + if (r < 0) + return log_error_errno(r, "Could not watch job: %m"); + + r = unit_name_mangle_with_suffix(machine_name, UNIT_NAME_NOGLOB, ".scope", &scope); + if (r < 0) + return log_error_errno(r, "Failed to mangle scope name: %m"); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "ss", scope, "fail"); + if (r < 0) + return bus_log_create_error(r); + + /* Properties */ + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return bus_log_create_error(r); + + description = strjoina("Container ", machine_name); + + r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)", + "PIDs", "au", 1, pid, + "Description", "s", description, + "Delegate", "b", 1, + "Slice", "s", isempty(slice) ? "machine.slice" : slice); + if (r < 0) + return bus_log_create_error(r); + + r = append_machine_properties( + m, + mounts, + n_mounts, + kill_signal, + properties); + if (r < 0) + return r; + + r = bus_append_unit_property_assignment_many(m, properties); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + /* No auxiliary units */ + r = sd_bus_message_append( + m, + "a(sa(sv))", + 0); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) { + log_error("Failed to allocate scope: %s", bus_error_message(&error, r)); + return r; + } + + 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, false); + if (r < 0) + return r; + + return 0; +} diff --git a/src/nspawn/nspawn-register.h b/src/nspawn/nspawn-register.h index 304c5a485b..6694b3f6b1 100644 --- a/src/nspawn/nspawn-register.h +++ b/src/nspawn/nspawn-register.h @@ -27,3 +27,5 @@ int register_machine(const char *machine_name, pid_t pid, const char *directory, sd_id128_t uuid, int local_ifindex, const char *slice, CustomMount *mounts, unsigned n_mounts, int kill_signal, char **properties, bool keep_unit, const char *service); int terminate_machine(pid_t pid); + +int allocate_scope(const char *machine_name, pid_t pid, const char *slice, CustomMount *mounts, unsigned n_mounts, int kill_signal, char **properties); diff --git a/src/nspawn/nspawn-stub-pid1.c b/src/nspawn/nspawn-stub-pid1.c index 7f2a53a245..0c48434db8 100644 --- a/src/nspawn/nspawn-stub-pid1.c +++ b/src/nspawn/nspawn-stub-pid1.c @@ -18,9 +18,9 @@ ***/ #include <sys/reboot.h> -#include <sys/unistd.h> #include <sys/wait.h> #include <sys/prctl.h> +#include <unistd.h> #include "fd-util.h" #include "log.h" @@ -188,7 +188,16 @@ int stub_pid1(sd_id128_t uuid) { else assert_not_reached("Got unexpected signal"); - /* (void) kill_and_sigcont(pid, SIGTERM); */ + r = kill_and_sigcont(pid, SIGTERM); + + /* Let's send a SIGHUP after the SIGTERM, as shells tend to ignore SIGTERM but do react to SIGHUP. We + * do it strictly in this order, so that the SIGTERM is dispatched first, and SIGHUP second for those + * processes which handle both. That's because services tend to bind configuration reload or something + * else to SIGHUP. */ + + if (r != -ESRCH) + (void) kill(pid, SIGHUP); + quit_usec = now(CLOCK_MONOTONIC) + DEFAULT_TIMEOUT_USEC; } diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 1fc0501c2e..8a5fedd4b0 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -18,7 +18,7 @@ ***/ #ifdef HAVE_BLKID -#include <blkid/blkid.h> +#include <blkid.h> #endif #include <errno.h> #include <getopt.h> @@ -389,12 +389,10 @@ static void parse_mount_settings_env(void) { if (r < 0) { log_warning_errno(r, "Failed to parse SYSTEMD_NSPAWN_API_VFS_WRITABLE from environment, ignoring."); return; - } else if (r > 0) - arg_mount_settings &= ~MOUNT_APPLY_APIVFS_RO; - else - arg_mount_settings |= MOUNT_APPLY_APIVFS_RO; + } - arg_mount_settings &= ~MOUNT_APPLY_APIVFS_NETNS; + SET_FLAG(arg_mount_settings, MOUNT_APPLY_APIVFS_RO, r == 0); + SET_FLAG(arg_mount_settings, MOUNT_APPLY_APIVFS_NETNS, false); } static int parse_argv(int argc, char *argv[]) { @@ -1085,8 +1083,8 @@ static int parse_argv(int argc, char *argv[]) { if (arg_userns_mode == USER_NAMESPACE_PICK) arg_userns_chown = true; - if (arg_keep_unit && cg_pid_get_owner_uid(0, NULL) >= 0) { - log_error("--keep-unit may not be used when invoked from a user session."); + if (arg_keep_unit && arg_register && cg_pid_get_owner_uid(0, NULL) >= 0) { + log_error("--keep-unit --register=yes may not be used when invoked from a user session."); return -EINVAL; } @@ -1158,6 +1156,10 @@ static int parse_argv(int argc, char *argv[]) { arg_caps_retain = (arg_caps_retain | plus | (arg_private_network ? 1ULL << CAP_NET_ADMIN : 0)) & ~minus; + r = cg_unified_flush(); + if (r < 0) + return log_error_errno(r, "Failed to determine whether the unified cgroups hierarchy is used: %m"); + e = getenv("SYSTEMD_NSPAWN_CONTAINER_SERVICE"); if (e) arg_container_service_name = e; @@ -1321,17 +1323,32 @@ static int setup_timezone(const char *dest) { return 0; } -static int resolved_running(void) { +static int resolved_listening(void) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_free_ char *dns_stub_listener_mode = NULL; int r; - /* Check if resolved is running */ + /* Check if resolved is listening */ r = sd_bus_open_system(&bus); if (r < 0) return r; - return bus_name_has_owner(bus, "org.freedesktop.resolve1", NULL); + r = bus_name_has_owner(bus, "org.freedesktop.resolve1", NULL); + if (r <= 0) + return r; + + r = sd_bus_get_property_string(bus, + "org.freedesktop.resolve1", + "/org/freedesktop/resolve1", + "org.freedesktop.resolve1.Manager", + "DNSStubListener", + NULL, + &dns_stub_listener_mode); + if (r < 0) + return r; + + return STR_IN_SET(dns_stub_listener_mode, "udp", "yes"); } static int setup_resolv_conf(const char *dest) { @@ -1358,7 +1375,7 @@ static int setup_resolv_conf(const char *dest) { } if (access("/usr/lib/systemd/resolv.conf", F_OK) >= 0 && - resolved_running() > 0) { + resolved_listening() > 0) { /* resolved is enabled on the host. In this, case bind mount its static resolv.conf file into the * container, so that the container can use the host's resolver. Given that network namespacing is @@ -2012,7 +2029,7 @@ static int determine_names(void) { if (r < 0) return log_error_errno(r, "Failed to find image for machine '%s': %m", arg_machine); if (r == 0) { - log_error("No image for machine '%s': %m", arg_machine); + log_error("No image for machine '%s'.", arg_machine); return -ENOENT; } @@ -3370,7 +3387,19 @@ static int run(int master, arg_container_service_name); if (r < 0) return r; - } + } else if (!arg_keep_unit) { + r = allocate_scope( + arg_machine, + *pid, + arg_slice, + arg_custom_mounts, arg_n_custom_mounts, + arg_kill_signal, + arg_property); + if (r < 0) + return r; + + } else if (arg_slice || arg_property) + log_notice("Machine and scope registration turned off, --slice= and --property= settings will have no effect."); r = sync_cgroup(*pid, arg_unified_cgroup_hierarchy, arg_uid_shift); if (r < 0) @@ -3530,10 +3559,6 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); - r = cg_unified_flush(); - if (r < 0) - return log_error_errno(r, "Failed to determine whether the unified cgroups hierarchy is used: %m"); - /* Make sure rename_process() in the stub init process can work */ saved_argv = argv; saved_argc = argc; diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c index d155625e11..ec059d9586 100644 --- a/src/nss-resolve/nss-resolve.c +++ b/src/nss-resolve/nss-resolve.c @@ -17,7 +17,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <dlfcn.h> #include <errno.h> #include <netdb.h> #include <nss.h> @@ -39,20 +38,6 @@ NSS_GETHOSTBYADDR_PROTOTYPES(resolve); #define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC) -typedef void (*voidfunc_t)(void); - -static voidfunc_t find_fallback(const char *module, const char *symbol) { - void *dl; - - /* Try to find a fallback NSS module symbol */ - - dl = dlopen(module, RTLD_LAZY|RTLD_NODELETE); - if (!dl) - return NULL; - - return dlsym(dl, symbol); -} - static bool bus_error_shall_fallback(sd_bus_error *e) { return sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN) || sd_bus_error_has_name(e, SD_BUS_ERROR_NAME_HAS_NO_OWNER) || @@ -151,7 +136,7 @@ enum nss_status _nss_resolve_gethostbyname4_r( r = sd_bus_open_system(&bus); if (r < 0) - goto fallback; + goto fail; r = sd_bus_message_new_method_call( bus, @@ -179,13 +164,14 @@ enum nss_status _nss_resolve_gethostbyname4_r( return NSS_STATUS_NOTFOUND; } - if (bus_error_shall_fallback(&error)) - goto fallback; + /* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails, + allowing falling back to other nss modules. Treat all other error conditions as + NOTFOUND. This includes DNSSEC errors and suchlike. (We don't use UNAVAIL in this + case so that the nsswitch.conf configuration can distuingish such executed but + negative replies from complete failure to talk to resolved). */ + if (!bus_error_shall_fallback(&error)) + ret = NSS_STATUS_NOTFOUND; - /* Treat all other error conditions as NOTFOUND, and fail. This includes DNSSEC errors and - suchlike. (We don't use UNAVAIL in this case so that the nsswitch.conf configuration can distuingish - such executed but negative replies from complete failure to talk to resolved. */ - ret = NSS_STATUS_NOTFOUND; goto fail; } @@ -286,17 +272,6 @@ enum nss_status _nss_resolve_gethostbyname4_r( return NSS_STATUS_SUCCESS; -fallback: - { - _nss_gethostbyname4_r_t fallback; - - fallback = (_nss_gethostbyname4_r_t) - find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname4_r"); - - if (fallback) - return fallback(name, pat, buffer, buflen, errnop, h_errnop, ttlp); - } - fail: *errnop = -r; *h_errnop = NO_RECOVERY; @@ -339,7 +314,7 @@ enum nss_status _nss_resolve_gethostbyname3_r( r = sd_bus_open_system(&bus); if (r < 0) - goto fallback; + goto fail; r = sd_bus_message_new_method_call( bus, @@ -367,10 +342,9 @@ enum nss_status _nss_resolve_gethostbyname3_r( return NSS_STATUS_NOTFOUND; } - if (bus_error_shall_fallback(&error)) - goto fallback; + if (!bus_error_shall_fallback(&error)) + ret = NSS_STATUS_NOTFOUND; - ret = NSS_STATUS_NOTFOUND; goto fail; } @@ -484,16 +458,6 @@ enum nss_status _nss_resolve_gethostbyname3_r( return NSS_STATUS_SUCCESS; -fallback: - { - _nss_gethostbyname3_r_t fallback; - - fallback = (_nss_gethostbyname3_r_t) - find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname3_r"); - if (fallback) - return fallback(name, af, result, buffer, buflen, errnop, h_errnop, ttlp, canonp); - } - fail: *errnop = -r; *h_errnop = NO_RECOVERY; @@ -540,7 +504,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( r = sd_bus_open_system(&bus); if (r < 0) - goto fallback; + goto fail; r = sd_bus_message_new_method_call( bus, @@ -576,10 +540,9 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( return NSS_STATUS_NOTFOUND; } - if (bus_error_shall_fallback(&error)) - goto fallback; + if (!bus_error_shall_fallback(&error)) + ret = NSS_STATUS_NOTFOUND; - ret = NSS_STATUS_NOTFOUND; goto fail; } @@ -674,17 +637,6 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( return NSS_STATUS_SUCCESS; -fallback: - { - _nss_gethostbyaddr2_r_t fallback; - - fallback = (_nss_gethostbyaddr2_r_t) - find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r"); - - if (fallback) - return fallback(addr, len, af, result, buffer, buflen, errnop, h_errnop, ttlp); - } - fail: *errnop = -r; *h_errnop = NO_RECOVERY; diff --git a/src/rc-local-generator/rc-local-generator.c b/src/rc-local-generator/rc-local-generator.c index b704ca3b4b..db3bf5bd21 100644 --- a/src/rc-local-generator/rc-local-generator.c +++ b/src/rc-local-generator/rc-local-generator.c @@ -28,14 +28,6 @@ #include "string-util.h" #include "util.h" -#ifndef RC_LOCAL_SCRIPT_PATH_START -#define RC_LOCAL_SCRIPT_PATH_START "/etc/rc.d/rc.local" -#endif - -#ifndef RC_LOCAL_SCRIPT_PATH_STOP -#define RC_LOCAL_SCRIPT_PATH_STOP "/sbin/halt.local" -#endif - static const char *arg_dest = "/tmp"; static int add_symlink(const char *service, const char *where) { diff --git a/src/resolve/dns_type-to-name.awk b/src/resolve/dns_type-to-name.awk new file mode 100644 index 0000000000..badb1824b5 --- /dev/null +++ b/src/resolve/dns_type-to-name.awk @@ -0,0 +1,11 @@ +BEGIN{ + print "const char *dns_type_to_string(int type) {\n\tswitch(type) {" +} +{ + printf " case DNS_TYPE_%s: return ", $1; + sub(/_/, "-"); + printf "\"%s\";\n", $1 +} +END{ + print " default: return NULL;\n\t}\n}\n" +} diff --git a/src/resolve/generate-dns_type-gperf.py b/src/resolve/generate-dns_type-gperf.py new file mode 100755 index 0000000000..8a0b43c277 --- /dev/null +++ b/src/resolve/generate-dns_type-gperf.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +"""Generate %-from-name.gperf from %-list.txt +""" + +import sys + +name, prefix, input = sys.argv[1:] + +print("""\ +struct {}_name {{ const char* name; int id; }}; +%null-strings +%%""".format(name)) + +for line in open(input): + line = line.rstrip() + s = line.replace('_', '-') + print("{}, {}{}".format(s, prefix, line)) diff --git a/src/resolve/generate-dns_type-list.sed b/src/resolve/generate-dns_type-list.sed new file mode 100644 index 0000000000..b7bc30f1f2 --- /dev/null +++ b/src/resolve/generate-dns_type-list.sed @@ -0,0 +1 @@ +s/.* DNS_TYPE_(\w+).*/\1/p diff --git a/src/resolve/meson.build b/src/resolve/meson.build new file mode 100644 index 0000000000..fe228784fa --- /dev/null +++ b/src/resolve/meson.build @@ -0,0 +1,187 @@ +basic_dns_sources = files(''' + resolved-dns-dnssec.c + resolved-dns-dnssec.h + resolved-dns-packet.c + resolved-dns-packet.h + resolved-dns-rr.c + resolved-dns-rr.h + resolved-dns-answer.c + resolved-dns-answer.h + resolved-dns-question.c + resolved-dns-question.h + dns-type.c +'''.split()) + +dns_type_h = files('dns-type.h')[0] + +systemd_resolved_only_sources = files(''' + resolved.c + resolved-manager.c + resolved-manager.h + resolved-conf.c + resolved-conf.h + resolved-resolv-conf.c + resolved-resolv-conf.h + resolved-bus.c + resolved-bus.h + resolved-link.h + resolved-link.c + resolved-link-bus.c + resolved-link-bus.h + resolved-llmnr.h + resolved-llmnr.c + resolved-mdns.h + resolved-mdns.c + resolved-def.h + resolved-dns-query.h + resolved-dns-query.c + resolved-dns-synthesize.h + resolved-dns-synthesize.c + resolved-dns-transaction.h + resolved-dns-transaction.c + resolved-dns-scope.h + resolved-dns-scope.c + resolved-dns-server.h + resolved-dns-server.c + resolved-dns-search-domain.h + resolved-dns-search-domain.c + resolved-dns-cache.h + resolved-dns-cache.c + resolved-dns-zone.h + resolved-dns-zone.c + resolved-dns-stream.h + resolved-dns-stream.c + resolved-dns-trust-anchor.h + resolved-dns-trust-anchor.c + resolved-dns-stub.h + resolved-dns-stub.c + resolved-etc-hosts.h + resolved-etc-hosts.c +'''.split()) + +systemd_resolve_only_sources = files('resolve-tool.c') + +############################################################ + +dns_type_list_txt = custom_target( + 'dns_type-list.txt', + input : ['generate-dns_type-list.sed', dns_type_h], + output : 'dns_type-list.txt', + command : [sed, '-n', '-r', '-f', '@INPUT0@', '@INPUT1@'], + capture : true) + +generate_dns_type_gperf = find_program('generate-dns_type-gperf.py') + +dns_type_headers = [dns_type_h] +foreach item : [['dns_type', dns_type_list_txt, 'dns_type', 'DNS_TYPE_']] + + fname = '@0@-from-name.gperf'.format(item[0]) + gperf_file = custom_target( + fname, + input : item[1], + output : fname, + command : [generate_dns_type_gperf, item[2], item[3], '@INPUT@'], + capture : true) + + fname = '@0@-from-name.h'.format(item[0]) + target1 = custom_target( + fname, + input : gperf_file, + output : fname, + command : [gperf, + '-L', 'ANSI-C', '-t', '--ignore-case', + '-N', 'lookup_@0@'.format(item[2]), + '-H', 'hash_@0@_name'.format(item[2]), + '-p', '-C', + '@INPUT@'], + capture : true) + + fname = '@0@-to-name.h'.format(item[0]) + awkscript = '@0@-to-name.awk'.format(item[0]) + target2 = custom_target( + fname, + input : [awkscript, item[1]], + output : fname, + command : [awk, '-f', '@INPUT0@', '@INPUT1@'], + capture : true) + + dns_type_headers += [target1, target2] +endforeach + +resolved_gperf_c = custom_target( + 'resolved_gperf.c', + input : 'resolved-gperf.gperf', + output : 'resolved-gperf.c', + command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@']) + +systemd_resolved_sources = (basic_dns_sources + + [resolved_gperf_c] + + systemd_resolved_only_sources + + dns_type_headers) + +systemd_resolve_sources = (basic_dns_sources + + systemd_resolve_only_sources + + dns_type_headers) + +if conf.get('ENABLE_RESOLVED', false) + install_data('org.freedesktop.resolve1.conf', + install_dir : dbuspolicydir) + install_data('org.freedesktop.resolve1.service', + install_dir : dbussystemservicedir) + + resolved_conf = configure_file( + input : 'resolved.conf.in', + output : 'resolved.conf', + configuration : substs) + install_data(resolved_conf, + install_dir : pkgsysconfdir) + + install_data('resolv.conf', + install_dir : rootlibexecdir) +endif + +tests += [ + [['src/resolve/test-resolve-tables.c', + basic_dns_sources, + dns_type_headers, + 'src/shared/test-tables.h'], + [], + [libgcrypt, + libgpg_error, + libm], + 'ENABLE_RESOLVED'], + + [['src/resolve/test-dns-packet.c', + basic_dns_sources, + dns_type_headers], + [], + [libgcrypt, + libgpg_error, + libm], + 'ENABLE_RESOLVED'], + + [['src/resolve/test-resolved-packet.c', + basic_dns_sources, + dns_type_headers], + [], + [libgcrypt, + libgpg_error, + libm], + 'ENABLE_RESOLVED'], + + [['src/resolve/test-dnssec.c', + basic_dns_sources, + dns_type_headers], + [], + [libgcrypt, + libgpg_error, + libm], + 'ENABLE_RESOLVED'], + + [['src/resolve/test-dnssec-complex.c', + 'src/resolve/dns-type.c', + dns_type_headers], + [], + [], + 'ENABLE_RESOLVED', 'manual'], +] diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c index 32537ce6e8..c62058917f 100644 --- a/src/resolve/resolve-tool.c +++ b/src/resolve/resolve-tool.c @@ -114,8 +114,8 @@ static void print_source(uint64_t flags, usec_t rtt) { flags & SD_RESOLVED_DNS ? " DNS" :"", flags & SD_RESOLVED_LLMNR_IPV4 ? " LLMNR/IPv4" : "", flags & SD_RESOLVED_LLMNR_IPV6 ? " LLMNR/IPv6" : "", - flags & SD_RESOLVED_MDNS_IPV4 ? "mDNS/IPv4" : "", - flags & SD_RESOLVED_MDNS_IPV6 ? "mDNS/IPv6" : ""); + flags & SD_RESOLVED_MDNS_IPV4 ? " mDNS/IPv4" : "", + flags & SD_RESOLVED_MDNS_IPV6 ? " mDNS/IPv6" : ""); assert_se(format_timespan(rtt_str, sizeof(rtt_str), rtt, 100)); diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 2c50109388..5aa2348576 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -345,10 +345,10 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, return r; r = dns_question_new_address(&question_idna, family, hostname, true); - if (r < 0) + if (r < 0 && r != -EALREADY) return r; - r = dns_query_new(m, &q, question_utf8, question_idna, ifindex, flags); + r = dns_query_new(m, &q, question_utf8, question_idna ?: question_utf8, ifindex, flags); if (r < 0) return r; @@ -1450,6 +1450,8 @@ static int bus_property_get_ntas( return sd_bus_message_close_container(reply); } +static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_dns_stub_listener_mode, dns_stub_listener_mode, DnsStubListenerMode); + static int bus_method_reset_statistics(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; DnsScope *s; @@ -1577,6 +1579,7 @@ static const sd_bus_vtable resolve_vtable[] = { SD_BUS_PROPERTY("DNSSECStatistics", "(tttt)", bus_property_get_dnssec_statistics, 0, 0), SD_BUS_PROPERTY("DNSSECSupported", "b", bus_property_get_dnssec_supported, 0, 0), SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", bus_property_get_ntas, 0, 0), + SD_BUS_PROPERTY("DNSStubListener", "s", bus_property_get_dns_stub_listener_mode, offsetof(Manager, dns_stub_listener_mode), 0), SD_BUS_METHOD("ResolveHostname", "isit", "a(iiay)st", bus_method_resolve_hostname, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ResolveAddress", "iiayt", "a(is)t", bus_method_resolve_address, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index abf3263178..75636e0e56 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -233,10 +233,10 @@ int manager_parse_config_file(Manager *m) { assert(m); r = config_parse_many_nulstr(PKGSYSCONFDIR "/resolved.conf", - CONF_PATHS_NULSTR("systemd/resolved.conf.d"), - "Resolve\0", - config_item_perf_lookup, resolved_gperf_lookup, - false, m); + CONF_PATHS_NULSTR("systemd/resolved.conf.d"), + "Resolve\0", + config_item_perf_lookup, resolved_gperf_lookup, + false, m); if (r < 0) return r; @@ -246,6 +246,12 @@ int manager_parse_config_file(Manager *m) { return r; } +#ifndef HAVE_GCRYPT + if (m->dnssec_mode != DNSSEC_NO) { + log_warning("DNSSEC option cannot be enabled or set to allow-downgrade when systemd-resolved is built without gcrypt support. Turning off DNSSEC support."); + m->dnssec_mode = DNSSEC_NO; + } +#endif return 0; } diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 652970284e..49a04615d4 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -28,6 +28,9 @@ #define EDNS0_OPT_DO (1<<15) +#define DNS_PACKET_SIZE_START 512u +assert_cc(DNS_PACKET_SIZE_START > DNS_PACKET_HEADER_SIZE) + typedef struct DnsPacketRewinder { DnsPacket *packet; size_t saved_rindex; @@ -41,19 +44,28 @@ static void rewind_dns_packet(DnsPacketRewinder *rewinder) { #define INIT_REWINDER(rewinder, p) do { rewinder.packet = p; rewinder.saved_rindex = p->rindex; } while (0) #define CANCEL_REWINDER(rewinder) do { rewinder.packet = NULL; } while (0) -int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t mtu) { +int dns_packet_new(DnsPacket **ret, DnsProtocol protocol, size_t min_alloc_dsize) { DnsPacket *p; size_t a; assert(ret); - if (mtu <= UDP_PACKET_HEADER_SIZE) + /* The caller may not check what is going to be truly allocated, so do not allow to + * allocate a DNS packet bigger than DNS_PACKET_SIZE_MAX. + */ + if (min_alloc_dsize > DNS_PACKET_SIZE_MAX) { + log_error("Requested packet data size too big: %zu", min_alloc_dsize); + return -EFBIG; + } + + /* When dns_packet_new() is called with min_alloc_dsize == 0, allocate more than the + * absolute minimum (which is the dns packet header size), to avoid + * resizing immediately again after appending the first data to the packet. + */ + if (min_alloc_dsize < DNS_PACKET_HEADER_SIZE) a = DNS_PACKET_SIZE_START; else - a = mtu - UDP_PACKET_HEADER_SIZE; - - if (a < DNS_PACKET_HEADER_SIZE) - a = DNS_PACKET_HEADER_SIZE; + a = min_alloc_dsize; /* round up to next page size */ a = PAGE_ALIGN(ALIGN(sizeof(DnsPacket)) + a) - ALIGN(sizeof(DnsPacket)); @@ -127,13 +139,13 @@ void dns_packet_set_flags(DnsPacket *p, bool dnssec_checking_disabled, bool trun } } -int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t mtu, bool dnssec_checking_disabled) { +int dns_packet_new_query(DnsPacket **ret, DnsProtocol protocol, size_t min_alloc_dsize, bool dnssec_checking_disabled) { DnsPacket *p; int r; assert(ret); - r = dns_packet_new(&p, protocol, mtu); + r = dns_packet_new(&p, protocol, min_alloc_dsize); if (r < 0) return r; @@ -2269,6 +2281,9 @@ int dns_packet_is_reply_for(DnsPacket *p, const DnsResourceKey *key) { if (r < 0) return r; + if (!p->question) + return 0; + if (p->question->n_keys != 1) return 0; diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 2c92392e4d..a65d6d38cf 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -58,15 +58,13 @@ struct DnsPacketHeader { /* The various DNS protocols deviate in how large a packet can grow, but the TCP transport has a 16bit size field, hence that appears to be the absolute maximum. */ -#define DNS_PACKET_SIZE_MAX 0xFFFF +#define DNS_PACKET_SIZE_MAX 0xFFFFu /* RFC 1035 say 512 is the maximum, for classic unicast DNS */ -#define DNS_PACKET_UNICAST_SIZE_MAX 512 +#define DNS_PACKET_UNICAST_SIZE_MAX 512u /* With EDNS0 we can use larger packets, default to 4096, which is what is commonly used */ -#define DNS_PACKET_UNICAST_SIZE_LARGE_MAX 4096 - -#define DNS_PACKET_SIZE_START 512 +#define DNS_PACKET_UNICAST_SIZE_LARGE_MAX 4096u struct DnsPacket { int n_ref; @@ -185,8 +183,8 @@ static inline unsigned DNS_PACKET_RRCOUNT(DnsPacket *p) { (unsigned) DNS_PACKET_ARCOUNT(p); } -int dns_packet_new(DnsPacket **p, DnsProtocol protocol, size_t mtu); -int dns_packet_new_query(DnsPacket **p, DnsProtocol protocol, size_t mtu, bool dnssec_checking_disabled); +int dns_packet_new(DnsPacket **p, DnsProtocol protocol, size_t min_alloc_dsize); +int dns_packet_new_query(DnsPacket **p, DnsProtocol protocol, size_t min_alloc_dsize, bool dnssec_checking_disabled); void dns_packet_set_flags(DnsPacket *p, bool dnssec_checking_disabled, bool truncated); diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c index c8b502d1cd..24f3e8e351 100644 --- a/src/resolve/resolved-dns-question.c +++ b/src/resolve/resolved-dns-question.c @@ -309,8 +309,14 @@ int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bo r = dns_name_apply_idna(name, &buf); if (r < 0) return r; - - name = buf; + if (r > 0 && !streq(name, buf)) + name = buf; + else + /* We did not manage to create convert the idna name, or it's + * the same as the original name. We assume the caller already + * created an uncoverted question, so let's not repeat work + * unnecessarily. */ + return -EALREADY; } q = dns_question_new(family == AF_UNSPEC ? 2 : 1); @@ -422,8 +428,8 @@ int dns_question_new_service( r = dns_name_apply_idna(domain, &buf); if (r < 0) return r; - - domain = buf; + if (r > 0) + domain = buf; } r = dns_service_join(service, type, domain, &joined); diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index 5498f7b9cb..b3d37525f4 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -17,7 +17,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sd-messages.h> +#include "sd-messages.h" #include "alloc-util.h" #include "resolved-dns-server.h" @@ -304,7 +304,10 @@ void dns_server_packet_received(DnsServer *s, int protocol, DnsServerFeatureLeve if (s->max_rtt < rtt) { s->max_rtt = rtt; s->resend_timeout = CLAMP(s->max_rtt * 2, DNS_TIMEOUT_MIN_USEC, DNS_TIMEOUT_MAX_USEC); - } + } else if (s->resend_timeout > rtt) + /* If we received the packet faster than the resend_timeout, bias + * the resend_timeout back to the rtt. */ + s->resend_timeout = CLAMP((2 * s->resend_timeout + rtt) / 3, DNS_TIMEOUT_MIN_USEC, DNS_TIMEOUT_MAX_USEC); } void dns_server_packet_lost(DnsServer *s, int protocol, DnsServerFeatureLevel level, usec_t usec) { @@ -716,9 +719,9 @@ DnsServer *manager_set_dns_server(Manager *m, DnsServer *s) { return s; if (s) - log_info("Switching to %s DNS server %s.", - dns_server_type_to_string(s->type), - dns_server_string(s)); + log_debug("Switching to %s DNS server %s.", + dns_server_type_to_string(s->type), + dns_server_string(s)); dns_server_unref(m->current_dns_server); m->current_dns_server = dns_server_ref(s); diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index ff2ad9c1de..3075f62b5e 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -17,7 +17,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sd-messages.h> +#include "sd-messages.h" #include "af-list.h" #include "alloc-util.h" diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index 7e9f9e5a20..dda9875063 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -17,7 +17,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sd-messages.h> +#include "sd-messages.h" #include "alloc-util.h" #include "conf-files.h" diff --git a/src/resolve/resolved-gperf.gperf b/src/resolve/resolved-gperf.gperf index 446f85cdf4..5153563b99 100644 --- a/src/resolve/resolved-gperf.gperf +++ b/src/resolve/resolved-gperf.gperf @@ -18,6 +18,7 @@ Resolve.DNS, config_parse_dns_servers, DNS_SERVER_SYSTEM, Resolve.FallbackDNS, config_parse_dns_servers, DNS_SERVER_FALLBACK, 0 Resolve.Domains, config_parse_search_domains, 0, 0 Resolve.LLMNR, config_parse_resolve_support, 0, offsetof(Manager, llmnr_support) +Resolve.MulticastDNS, config_parse_resolve_support, 0, offsetof(Manager, mdns_support) Resolve.DNSSEC, config_parse_dnssec_mode, 0, offsetof(Manager, dnssec_mode) Resolve.Cache, config_parse_bool, 0, offsetof(Manager, enable_cache) Resolve.DNSStubListener, config_parse_dns_stub_listener_mode, 0, offsetof(Manager, dns_stub_listener_mode) diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index 3f7f9035cf..61a3f20362 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -28,6 +28,8 @@ #include "mkdir.h" #include "parse-util.h" #include "resolved-link.h" +#include "resolved-llmnr.h" +#include "resolved-mdns.h" #include "string-util.h" #include "strv.h" @@ -311,6 +313,12 @@ void link_set_dnssec_mode(Link *l, DnssecMode mode) { assert(l); +#ifndef HAVE_GCRYPT + if (mode == DNSSEC_YES || mode == DNSSEC_ALLOW_DOWNGRADE) + log_warning("DNSSEC option for the link cannot be enabled or set to allow-downgrade when systemd-resolved is built without gcrypt support. Turning off DNSSEC support."); + return; +#endif + if (l->dnssec_mode == mode) return; @@ -523,10 +531,25 @@ static void link_read_settings(Link *l) { } int link_update(Link *l) { + int r; + assert(l); link_read_settings(l); link_load_user(l); + + if (l->llmnr_support != RESOLVE_SUPPORT_NO) { + r = manager_llmnr_start(l->manager); + if (r < 0) + return r; + } + + if (l->mdns_support != RESOLVE_SUPPORT_NO) { + r = manager_mdns_start(l->manager); + if (r < 0) + return r; + } + link_allocate_scopes(l); link_add_rrs(l, false); @@ -539,7 +562,7 @@ bool link_relevant(Link *l, int family, bool local_multicast) { assert(l); - /* A link is relevant for local multicast traffic if it isn't a loopback or pointopoint device, has a link + /* A link is relevant for local multicast traffic if it isn't a loopback device, has a link * beat, can do multicast and has at least one link-local (or better) IP address. * * A link is relevant for non-multicast traffic if it isn't a loopback device, has a link beat, and has at @@ -552,9 +575,6 @@ bool link_relevant(Link *l, int family, bool local_multicast) { return false; if (local_multicast) { - if (l->flags & IFF_POINTOPOINT) - return false; - if ((l->flags & IFF_MULTICAST) != IFF_MULTICAST) return false; } @@ -594,7 +614,7 @@ DnsServer* link_set_dns_server(Link *l, DnsServer *s) { return s; if (s) - log_info("Switching to DNS server %s for interface %s.", dns_server_string(s), l->name); + log_debug("Switching to DNS server %s for interface %s.", dns_server_string(s), l->name); dns_server_unref(l->current_dns_server); l->current_dns_server = dns_server_ref(s); diff --git a/src/resolve/resolved-llmnr.c b/src/resolve/resolved-llmnr.c index 3516af58ee..29396e9973 100644 --- a/src/resolve/resolved-llmnr.c +++ b/src/resolve/resolved-llmnr.c @@ -77,7 +77,7 @@ int manager_llmnr_start(Manager *m) { return 0; eaddrinuse: - log_warning("There appears to be another LLMNR responder running. Turning off LLMNR support."); + log_warning("Another LLMNR responder prohibits binding the socket to the same port. Turning off LLMNR support."); m->llmnr_support = RESOLVE_SUPPORT_NO; manager_llmnr_stop(m); @@ -136,56 +136,75 @@ int manager_llmnr_ipv4_udp_fd(Manager *m) { m->llmnr_ipv4_udp_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (m->llmnr_ipv4_udp_fd < 0) - return -errno; + return log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to create socket: %m"); /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */ r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set IP_TTL: %m"); goto fail; } r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set IP_MULTICAST_TTL: %m"); goto fail; } r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one)); if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv4_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set IP_MULTICAST_LOOP: %m"); goto fail; } r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set IP_PKTINFO: %m"); goto fail; } r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set IP_RECVTTL: %m"); goto fail; } /* Disable Don't-Fragment bit in the IP header */ r = setsockopt(m->llmnr_ipv4_udp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set IP_MTU_DISCOVER: %m"); goto fail; } + /* first try to bind without SO_REUSEADDR to detect another LLMNR responder */ r = bind(m->llmnr_ipv4_udp_fd, &sa.sa, sizeof(sa.in)); if (r < 0) { - r = -errno; - goto fail; + if (errno != EADDRINUSE) { + r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to bind socket: %m"); + goto fail; + } + + log_warning("LLMNR-IPv4(UDP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers."); + + /* try again with SO_REUSEADDR */ + r = setsockopt(m->llmnr_ipv4_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set SO_REUSEADDR: %m"); + goto fail; + } + + r = bind(m->llmnr_ipv4_udp_fd, &sa.sa, sizeof(sa.in)); + if (r < 0) { + r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to bind socket: %m"); + goto fail; + } + } else { + /* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */ + r = setsockopt(m->llmnr_ipv4_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = log_error_errno(errno, "LLMNR-IPv4(UDP): Failed to set SO_REUSEADDR: %m"); + goto fail; + } } r = sd_event_add_io(m->event, &m->llmnr_ipv4_udp_event_source, m->llmnr_ipv4_udp_fd, EPOLLIN, on_llmnr_packet, m); @@ -216,55 +235,74 @@ int manager_llmnr_ipv6_udp_fd(Manager *m) { m->llmnr_ipv6_udp_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (m->llmnr_ipv6_udp_fd < 0) - return -errno; + return log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to create socket: %m"); r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set IPV6_UNICAST_HOPS: %m"); goto fail; } /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */ r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set IPV6_MULTICAST_HOPS: %m"); goto fail; } r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set IPV6_MULTICAST_LOOP: %m"); goto fail; } r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv6_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set IPV6_V6ONLY: %m"); goto fail; } r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set IPV6_RECVPKTINFO: %m"); goto fail; } r = setsockopt(m->llmnr_ipv6_udp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set IPV6_RECVHOPLIMIT: %m"); goto fail; } + /* first try to bind without SO_REUSEADDR to detect another LLMNR responder */ r = bind(m->llmnr_ipv6_udp_fd, &sa.sa, sizeof(sa.in6)); if (r < 0) { - r = -errno; - goto fail; + if (errno != EADDRINUSE) { + r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to bind socket: %m"); + goto fail; + } + + log_warning("LLMNR-IPv6(UDP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers."); + + /* try again with SO_REUSEADDR */ + r = setsockopt(m->llmnr_ipv6_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set SO_REUSEADDR: %m"); + goto fail; + } + + r = bind(m->llmnr_ipv6_udp_fd, &sa.sa, sizeof(sa.in6)); + if (r < 0) { + r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to bind socket: %m"); + goto fail; + } + } else { + /* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */ + r = setsockopt(m->llmnr_ipv6_udp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = log_error_errno(errno, "LLMNR-IPv6(UDP): Failed to set SO_REUSEADDR: %m"); + goto fail; + } } r = sd_event_add_io(m->event, &m->llmnr_ipv6_udp_event_source, m->llmnr_ipv6_udp_fd, EPOLLIN, on_llmnr_packet, m); @@ -338,49 +376,68 @@ int manager_llmnr_ipv4_tcp_fd(Manager *m) { m->llmnr_ipv4_tcp_fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (m->llmnr_ipv4_tcp_fd < 0) - return -errno; + return log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to create socket: %m"); /* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */ r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_TTL, &one, sizeof(one)); if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv4_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to set IP_TTL: %m"); goto fail; } r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to set IP_PKTINFO: %m"); goto fail; } r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to set IP_RECVTTL: %m"); goto fail; } /* Disable Don't-Fragment bit in the IP header */ r = setsockopt(m->llmnr_ipv4_tcp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to set IP_MTU_DISCOVER: %m"); goto fail; } + /* first try to bind without SO_REUSEADDR to detect another LLMNR responder */ r = bind(m->llmnr_ipv4_tcp_fd, &sa.sa, sizeof(sa.in)); if (r < 0) { - r = -errno; - goto fail; + if (errno != EADDRINUSE) { + r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to bind socket: %m"); + goto fail; + } + + log_warning("LLMNR-IPv4(TCP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers."); + + /* try again with SO_REUSEADDR */ + r = setsockopt(m->llmnr_ipv4_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to set SO_REUSEADDR: %m"); + goto fail; + } + + r = bind(m->llmnr_ipv4_tcp_fd, &sa.sa, sizeof(sa.in)); + if (r < 0) { + r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to bind socket: %m"); + goto fail; + } + } else { + /* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */ + r = setsockopt(m->llmnr_ipv4_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to set SO_REUSEADDR: %m"); + goto fail; + } } r = listen(m->llmnr_ipv4_tcp_fd, SOMAXCONN); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv4(TCP): Failed to listen the stream: %m"); goto fail; } @@ -412,48 +469,67 @@ int manager_llmnr_ipv6_tcp_fd(Manager *m) { m->llmnr_ipv6_tcp_fd = socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (m->llmnr_ipv6_tcp_fd < 0) - return -errno; + return log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to create socket: %m"); /* RFC 4795, section 2.5. requires setting the TTL of TCP streams to 1 */ r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &one, sizeof(one)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to set IPV6_UNICAST_HOPS: %m"); goto fail; } r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->llmnr_ipv6_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to set IPV6_V6ONLY: %m"); goto fail; } r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to set IPV6_RECVPKTINFO: %m"); goto fail; } r = setsockopt(m->llmnr_ipv6_tcp_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to set IPV6_RECVHOPLIMIT: %m"); goto fail; } + /* first try to bind without SO_REUSEADDR to detect another LLMNR responder */ r = bind(m->llmnr_ipv6_tcp_fd, &sa.sa, sizeof(sa.in6)); if (r < 0) { - r = -errno; - goto fail; + if (errno != EADDRINUSE) { + r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to bind socket: %m"); + goto fail; + } + + log_warning("LLMNR-IPv6(TCP): There appears to be another LLMNR responder running, or previously systemd-resolved crashed with some outstanding transfers."); + + /* try again with SO_REUSEADDR */ + r = setsockopt(m->llmnr_ipv6_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to set SO_REUSEADDR: %m"); + goto fail; + } + + r = bind(m->llmnr_ipv6_tcp_fd, &sa.sa, sizeof(sa.in6)); + if (r < 0) { + r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to bind socket: %m"); + goto fail; + } + } else { + /* enable SO_REUSEADDR for the case that the user really wants multiple LLMNR responders */ + r = setsockopt(m->llmnr_ipv6_tcp_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to set SO_REUSEADDR: %m"); + goto fail; + } } r = listen(m->llmnr_ipv6_tcp_fd, SOMAXCONN); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "LLMNR-IPv6(TCP): Failed to listen the stream: %m"); goto fail; } diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index c4e4409fe3..b6620875ea 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -21,6 +21,10 @@ #include <poll.h> #include <sys/ioctl.h> +#ifdef HAVE_LIBIDN2 +#include <idn2.h> +#endif + #include "af-list.h" #include "alloc-util.h" #include "dirent-util.h" @@ -324,9 +328,14 @@ static int manager_network_monitor_listen(Manager *m) { static int determine_hostname(char **full_hostname, char **llmnr_hostname, char **mdns_hostname) { _cleanup_free_ char *h = NULL, *n = NULL; +#if defined(HAVE_LIBIDN2) + _cleanup_free_ char *utf8 = NULL; +#elif defined(HAVE_LIBIDN) + int k; +#endif char label[DNS_LABEL_MAX]; - const char *p; - int r, k; + const char *p, *decoded; + int r; assert(full_hostname); assert(llmnr_hostname); @@ -339,7 +348,7 @@ static int determine_hostname(char **full_hostname, char **llmnr_hostname, char return log_debug_errno(r, "Can't determine system hostname: %m"); p = h; - r = dns_label_unescape(&p, label, sizeof(label)); + r = dns_label_unescape(&p, label, sizeof label); if (r < 0) return log_error_errno(r, "Failed to unescape host name: %m"); if (r == 0) { @@ -347,7 +356,16 @@ static int determine_hostname(char **full_hostname, char **llmnr_hostname, char return -EINVAL; } - k = dns_label_undo_idna(label, r, label, sizeof(label)); +#if defined(HAVE_LIBIDN2) + r = idn2_to_unicode_8z8z(label, &utf8, 0); + if (r != IDN2_OK) + return log_error("Failed to undo IDNA: %s", idn2_strerror(r)); + assert(utf8_is_valid(utf8)); + + r = strlen(utf8); + decoded = utf8; +#elif defined(HAVE_LIBIDN) + k = dns_label_undo_idna(label, r, label, sizeof label); if (k < 0) return log_error_errno(k, "Failed to undo IDNA: %m"); if (k > 0) @@ -357,8 +375,12 @@ static int determine_hostname(char **full_hostname, char **llmnr_hostname, char log_error("System hostname is not UTF-8 clean."); return -EINVAL; } + decoded = label; +#else + decoded = label; /* no decoding */ +#endif - r = dns_label_escape_new(label, r, &n); + r = dns_label_escape_new(decoded, r, &n); if (r < 0) return log_error_errno(r, "Failed to escape host name: %m"); @@ -561,7 +583,7 @@ int manager_new(Manager **ret) { r = manager_parse_config_file(m); if (r < 0) - return r; + log_warning_errno(r, "Failed to parse configuration file: %m"); r = sd_event_default(&m->event); if (r < 0) @@ -612,14 +634,6 @@ int manager_start(Manager *m) { if (r < 0) return r; - r = manager_llmnr_start(m); - if (r < 0) - return r; - - r = manager_mdns_start(m); - if (r < 0) - return r; - return 0; } diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c index c40e8f75f0..415dc1a532 100644 --- a/src/resolve/resolved-mdns.c +++ b/src/resolve/resolved-mdns.c @@ -60,7 +60,7 @@ int manager_mdns_start(Manager *m) { return 0; eaddrinuse: - log_warning("There appears to be another mDNS responder running. Turning off mDNS support."); + log_warning("Another mDNS responder prohibits binding the socket to the same port. Turning off mDNS support."); m->mdns_support = RESOLVE_SUPPORT_NO; manager_mdns_stop(m); @@ -217,55 +217,75 @@ int manager_mdns_ipv4_fd(Manager *m) { m->mdns_ipv4_fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (m->mdns_ipv4_fd < 0) - return -errno; + return log_error_errno(errno, "mDNS-IPv4: Failed to create socket: %m"); r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "mDNS-IPv4: Failed to set IP_TTL: %m"); goto fail; } r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "mDNS-IPv4: Failed to set IP_MULTICAST_TTL: %m"); goto fail; } r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MULTICAST_LOOP, &one, sizeof(one)); if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->mdns_ipv4_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if (r < 0) { - r = -errno; + r = log_error_errno(errno, "mDNS-IPv4: Failed to set IP_MULTICAST_LOOP: %m"); goto fail; } r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "mDNS-IPv4: Failed to set IP_PKTINFO: %m"); goto fail; } r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_RECVTTL, &one, sizeof(one)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "mDNS-IPv4: Failed to set IP_RECVTTL: %m"); goto fail; } /* Disable Don't-Fragment bit in the IP header */ r = setsockopt(m->mdns_ipv4_fd, IPPROTO_IP, IP_MTU_DISCOVER, &pmtu, sizeof(pmtu)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "mDNS-IPv4: Failed to set IP_MTU_DISCOVER: %m"); goto fail; } + /* See the section 15.1 of RFC6762 */ + /* first try to bind without SO_REUSEADDR to detect another mDNS responder */ r = bind(m->mdns_ipv4_fd, &sa.sa, sizeof(sa.in)); if (r < 0) { - r = -errno; - goto fail; + if (errno != EADDRINUSE) { + r = log_error_errno(errno, "mDNS-IPv4: Failed to bind socket: %m"); + goto fail; + } + + log_warning("mDNS-IPv4: There appears to be another mDNS responder running, or previously systemd-resolved crashed with some outstanding transfers."); + + /* try again with SO_REUSEADDR */ + r = setsockopt(m->mdns_ipv4_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = log_error_errno(errno, "mDNS-IPv4: Failed to set SO_REUSEADDR: %m"); + goto fail; + } + + r = bind(m->mdns_ipv4_fd, &sa.sa, sizeof(sa.in)); + if (r < 0) { + r = log_error_errno(errno, "mDNS-IPv4: Failed to bind socket: %m"); + goto fail; + } + } else { + /* enable SO_REUSEADDR for the case that the user really wants multiple mDNS responders */ + r = setsockopt(m->mdns_ipv4_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = log_error_errno(errno, "mDNS-IPv4: Failed to set SO_REUSEADDR: %m"); + goto fail; + } } r = sd_event_add_io(m->event, &m->mdns_ipv4_event_source, m->mdns_ipv4_fd, EPOLLIN, on_mdns_packet, m); @@ -294,55 +314,75 @@ int manager_mdns_ipv6_fd(Manager *m) { m->mdns_ipv6_fd = socket(AF_INET6, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (m->mdns_ipv6_fd < 0) - return -errno; + return log_error_errno(errno, "mDNS-IPv6: Failed to create socket: %m"); r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "mDNS-IPv6: Failed to set IPV6_UNICAST_HOPS: %m"); goto fail; } /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */ r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "mDNS-IPv6: Failed to set IPV6_MULTICAST_HOPS: %m"); goto fail; } r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &one, sizeof(one)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "mDNS-IPv6: Failed to set IPV6_MULTICAST_LOOP: %m"); goto fail; } r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); if (r < 0) { - r = -errno; - goto fail; - } - - r = setsockopt(m->mdns_ipv6_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if (r < 0) { - r = -errno; + r = log_error_errno(errno, "mDNS-IPv6: Failed to set IPV6_V6ONLY: %m"); goto fail; } r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "mDNS-IPv6: Failed to set IPV6_RECVPKTINFO: %m"); goto fail; } r = setsockopt(m->mdns_ipv6_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one)); if (r < 0) { - r = -errno; + r = log_error_errno(errno, "mDNS-IPv6: Failed to set IPV6_RECVHOPLIMIT: %m"); goto fail; } + /* See the section 15.1 of RFC6762 */ + /* first try to bind without SO_REUSEADDR to detect another mDNS responder */ r = bind(m->mdns_ipv6_fd, &sa.sa, sizeof(sa.in6)); if (r < 0) { - r = -errno; - goto fail; + if (errno != EADDRINUSE) { + r = log_error_errno(errno, "mDNS-IPv6: Failed to bind socket: %m"); + goto fail; + } + + log_warning("mDNS-IPv6: There appears to be another mDNS responder running, or previously systemd-resolved crashed with some outstanding transfers."); + + /* try again with SO_REUSEADDR */ + r = setsockopt(m->mdns_ipv6_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = log_error_errno(errno, "mDNS-IPv6: Failed to set SO_REUSEADDR: %m"); + goto fail; + } + + r = bind(m->mdns_ipv6_fd, &sa.sa, sizeof(sa.in6)); + if (r < 0) { + r = log_error_errno(errno, "mDNS-IPv6: Failed to bind socket: %m"); + goto fail; + } + } else { + /* enable SO_REUSEADDR for the case that the user really wants multiple mDNS responders */ + r = setsockopt(m->mdns_ipv6_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + if (r < 0) { + r = log_error_errno(errno, "mDNS-IPv6: Failed to set SO_REUSEADDR: %m"); + goto fail; + } } r = sd_event_add_io(m->event, &m->mdns_ipv6_event_source, m->mdns_ipv6_fd, EPOLLIN, on_mdns_packet, m); diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in index 60afa151e3..e6b20620e2 100644 --- a/src/resolve/resolved.conf.in +++ b/src/resolve/resolved.conf.in @@ -16,6 +16,7 @@ #FallbackDNS=@DNS_SERVERS@ #Domains= #LLMNR=yes +#MulticastDNS=yes #DNSSEC=@DEFAULT_DNSSEC_MODE@ #Cache=yes #DNSStubListener=udp diff --git a/src/resolve/test-dnssec-complex.c b/src/resolve/test-dnssec-complex.c index 3d7074af11..090b2fac23 100644 --- a/src/resolve/test-dnssec-complex.c +++ b/src/resolve/test-dnssec-complex.c @@ -218,7 +218,7 @@ int main(int argc, char* argv[]) { test_hostname_lookup(bus, "poettering.de", AF_INET, NULL); test_hostname_lookup(bus, "poettering.de", AF_INET6, NULL); -#ifdef HAVE_LIBIDN +#if defined(HAVE_LIBIDN2) || defined(HAVE_LIBIDN) /* Unsigned A with IDNA conversion necessary */ test_hostname_lookup(bus, "pöttering.de", AF_UNSPEC, NULL); test_hostname_lookup(bus, "pöttering.de", AF_INET, NULL); diff --git a/src/resolve/test-resolved-packet.c b/src/resolve/test-resolved-packet.c new file mode 100644 index 0000000000..1b0041214b --- /dev/null +++ b/src/resolve/test-resolved-packet.c @@ -0,0 +1,48 @@ +/*** + This file is part of systemd + + Copyright 2017 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "log.h" +#include "resolved-dns-packet.h" + +static void test_dns_packet_new(void) { + size_t i; + _cleanup_(dns_packet_unrefp) DnsPacket *p2 = NULL; + + for (i = 0; i <= DNS_PACKET_SIZE_MAX; i++) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + + assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, i) == 0); + + log_debug("dns_packet_new: %zu → %zu", i, p->allocated); + assert_se(p->allocated >= MIN(DNS_PACKET_SIZE_MAX, i)); + } + + assert_se(dns_packet_new(&p2, DNS_PROTOCOL_DNS, DNS_PACKET_SIZE_MAX + 1) == -EFBIG); +} + +int main(int argc, char **argv) { + + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + test_dns_packet_new(); + + return 0; +} diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c index 127cbe44e3..903a187861 100644 --- a/src/shared/base-filesystem.c +++ b/src/shared/base-filesystem.c @@ -51,6 +51,9 @@ static const BaseFilesystem table[] = { { "usr", 0755, NULL, NULL }, { "var", 0755, NULL, NULL }, { "etc", 0755, NULL, NULL }, + { "proc", 0755, NULL, NULL, true }, + { "sys", 0755, NULL, NULL, true }, + { "dev", 0755, NULL, NULL, true }, #if defined(__i386__) || defined(__x86_64__) { "lib64", 0, "usr/lib/x86_64-linux-gnu\0" "usr/lib64\0", "ld-linux-x86-64.so.2" }, @@ -117,6 +120,8 @@ int base_filesystem_create(const char *root, uid_t uid, gid_t gid) { if (!table[i].ignore_failure) return -errno; + + continue; } if (uid != UID_INVALID || gid != UID_INVALID) { diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index aae69f6da5..5cbe663fa8 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -266,7 +266,8 @@ 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", "RootImage")) + "ProtectHome", "SELinuxContext", "Restart", "RootImage", + "NotifyAccess")) r = sd_bus_message_append(m, "v", "s", eq); else if (streq(field, "SyslogLevel")) { @@ -389,6 +390,33 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "v", "i", (int32_t) n); + } else if (streq(field, "FileDescriptorStoreMax")) { + unsigned u; + + r = safe_atou(eq, &u); + if (r < 0) + return log_error_errno(r, "Failed to parse file descriptor store limit: %s", eq); + + r = sd_bus_message_append(m, "v", "u", (uint32_t) u); + + } else if (streq(field, "IOSchedulingClass")) { + int c; + + c = ioprio_class_from_string(eq); + if (c < 0) + return log_error_errno(r, "Failed to parse IO scheduling class: %s", eq); + + r = sd_bus_message_append(m, "v", "i", (int32_t) c); + + } else if (streq(field, "IOSchedulingPriority")) { + int q; + + r = ioprio_parse_priority(eq, &q); + if (r < 0) + return log_error_errno(r, "Failed to parse IO scheduling priority: %s", eq); + + r = sd_bus_message_append(m, "v", "i", (int32_t) q); + } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) { const char *p; @@ -860,7 +888,7 @@ static void log_job_error_with_service_result(const char* service, const char *r assert(service); - service_shell_quoted = shell_maybe_quote(service); + service_shell_quoted = shell_maybe_quote(service, ESCAPE_BACKSLASH); if (extra_args) { _cleanup_free_ char *t; diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 8ddfb584ea..207b5e66fc 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -725,13 +725,12 @@ int bus_print_property(const char *name, sd_bus_message *property, bool value, b return r; if (all || !isempty(s)) { - _cleanup_free_ char *escaped = NULL; + bool good; - escaped = xescape(s, "\n"); - if (!escaped) - return -ENOMEM; - - print_property(name, "%s", escaped); + /* This property has a single value, so we need to take + * care not to print a new line, everything else is OK. */ + good = !strchr(s, '\n'); + print_property(name, "%s", good ? s : "[unprintable]"); } return 1; @@ -852,16 +851,16 @@ int bus_print_property(const char *name, sd_bus_message *property, bool value, b return r; while ((r = sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) > 0) { - _cleanup_free_ char *escaped = NULL; + bool good; if (first && !value) printf("%s=", name); - escaped = xescape(str, "\n "); - if (!escaped) - return -ENOMEM; + /* This property has multiple space-seperated values, so + * neither spaces not newlines can be allowed in a value. */ + good = str[strcspn(str, " \n")] == '\0'; - printf("%s%s", first ? "" : " ", escaped); + printf("%s%s", first ? "" : " ", good ? str : "[unprintable]"); first = false; } diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c index 8765cf2f49..436130edea 100644 --- a/src/shared/cgroup-show.c +++ b/src/shared/cgroup-show.c @@ -24,8 +24,6 @@ #include <stdlib.h> #include <string.h> -#include <systemd/sd-bus.h> - #include "alloc-util.h" #include "bus-error.h" #include "bus-util.h" diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h index 736f0f34c8..1764f76744 100644 --- a/src/shared/cgroup-show.h +++ b/src/shared/cgroup-show.h @@ -22,7 +22,7 @@ #include <stdbool.h> #include <sys/types.h> -#include <systemd/sd-bus.h> +#include "sd-bus.h" #include "logs-show.h" #include "output-mode.h" diff --git a/src/shared/condition.c b/src/shared/condition.c index 0b77d2c22d..1af74c61f0 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -24,6 +24,7 @@ #include <stdlib.h> #include <string.h> #include <sys/stat.h> +#include <sys/types.h> #include <time.h> #include <unistd.h> @@ -52,6 +53,7 @@ #include "stat-util.h" #include "string-table.h" #include "string-util.h" +#include "user-util.h" #include "util.h" #include "virt.h" @@ -138,6 +140,60 @@ static int condition_test_kernel_command_line(Condition *c) { return false; } +static int condition_test_user(Condition *c) { + uid_t id; + int r; + _cleanup_free_ char *username = NULL; + const char *u; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_USER); + + r = parse_uid(c->parameter, &id); + if (r >= 0) + return id == getuid() || id == geteuid(); + + if (streq("@system", c->parameter)) + return getuid() <= SYSTEM_UID_MAX || geteuid() <= SYSTEM_UID_MAX; + + username = getusername_malloc(); + if (!username) + return -ENOMEM; + + if (streq(username, c->parameter)) + return 1; + + if (getpid() == 1) + return streq(c->parameter, "root"); + + u = c->parameter; + r = get_user_creds(&u, &id, NULL, NULL, NULL); + if (r < 0) + return 0; + + return id == getuid() || id == geteuid(); +} + +static int condition_test_group(Condition *c) { + gid_t id; + int r; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_GROUP); + + r = parse_gid(c->parameter, &id); + if (r >= 0) + return in_gid(id); + + /* Avoid any NSS lookups if we are PID1 */ + if (getpid() == 1) + return streq(c->parameter, "root"); + + return in_group(c->parameter) > 0; +} + static int condition_test_virtualization(Condition *c) { int b, v; @@ -235,7 +291,7 @@ static int condition_test_security(Condition *c) { assert(c->type == CONDITION_SECURITY); if (streq(c->parameter, "selinux")) - return mac_selinux_have(); + return mac_selinux_use(); if (streq(c->parameter, "smack")) return mac_smack_use(); if (streq(c->parameter, "apparmor")) @@ -475,6 +531,8 @@ int condition_test(Condition *c) { [CONDITION_ARCHITECTURE] = condition_test_architecture, [CONDITION_NEEDS_UPDATE] = condition_test_needs_update, [CONDITION_FIRST_BOOT] = condition_test_first_boot, + [CONDITION_USER] = condition_test_user, + [CONDITION_GROUP] = condition_test_group, [CONDITION_NULL] = condition_test_null, }; @@ -538,6 +596,8 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = { [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty", [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty", [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable", + [CONDITION_USER] = "ConditionUser", + [CONDITION_GROUP] = "ConditionGroup", [CONDITION_NULL] = "ConditionNull" }; @@ -562,6 +622,8 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = { [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty", [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty", [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable", + [CONDITION_USER] = "AssertUser", + [CONDITION_GROUP] = "AssertGroup", [CONDITION_NULL] = "AssertNull" }; diff --git a/src/shared/condition.h b/src/shared/condition.h index bdda04b770..d0b592bc43 100644 --- a/src/shared/condition.h +++ b/src/shared/condition.h @@ -49,6 +49,9 @@ typedef enum ConditionType { CONDITION_NULL, + CONDITION_USER, + CONDITION_GROUP, + _CONDITION_TYPE_MAX, _CONDITION_TYPE_INVALID = -1 } ConditionType; diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index 265ac83dc0..e08402e3d2 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -506,6 +506,7 @@ int config_parse_many( DEFINE_PARSER(int, int, safe_atoi); DEFINE_PARSER(long, long, safe_atoli); +DEFINE_PARSER(uint8, uint8_t, safe_atou8); DEFINE_PARSER(uint16, uint16_t, safe_atou16); DEFINE_PARSER(uint32, uint32_t, safe_atou32); DEFINE_PARSER(uint64, uint64_t, safe_atou64); @@ -614,6 +615,7 @@ int config_parse_bool(const char* unit, int k; bool *b = data; + bool fatal = ltype; assert(filename); assert(lvalue); @@ -622,8 +624,10 @@ int config_parse_bool(const char* unit, k = parse_boolean(rvalue); if (k < 0) { - log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue); - return 0; + log_syntax(unit, LOG_ERR, filename, line, k, + "Failed to parse boolean value%s: %s", + fatal ? "" : ", ignoring", rvalue); + return fatal ? -ENOEXEC : 0; } *b = !!k; @@ -714,6 +718,7 @@ int config_parse_path( void *userdata) { char **s = data, *n; + bool fatal = ltype; assert(filename); assert(lvalue); @@ -722,12 +727,14 @@ int config_parse_path( if (!utf8_is_valid(rvalue)) { log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue); - return 0; + return fatal ? -ENOEXEC : 0; } if (!path_is_absolute(rvalue)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", rvalue); - return 0; + log_syntax(unit, LOG_ERR, filename, line, 0, + "Not an absolute path%s: %s", + fatal ? "" : ", ignoring", rvalue); + return fatal ? -ENOEXEC : 0; } n = strdup(rvalue); @@ -792,7 +799,7 @@ int config_parse_strv(const char *unit, } if (!utf8_is_valid(word)) { - log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue); + log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word); free(word); continue; } @@ -959,3 +966,40 @@ int config_parse_ifname( return 0; } + +int config_parse_ip_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) { + + uint16_t *s = data; + uint16_t port; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + *s = 0; + return 0; + } + + r = parse_ip_port(rvalue, &port); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse port '%s'.", rvalue); + return 0; + } + + *s = port; + + return 0; +} diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index 26ff3df16f..ce1113485d 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -119,6 +119,7 @@ int config_parse_many( int config_parse_int(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unsigned(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_long(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_uint8(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_uint16(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_uint32(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_uint64(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); @@ -139,6 +140,7 @@ int config_parse_log_level(const char *unit, const char *filename, unsigned line int config_parse_signal(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_personality(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_ifname(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_ip_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); #define DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg) \ int function(const char *unit, \ diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 39e724c51a..505a83f54f 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -20,7 +20,6 @@ #ifdef HAVE_LIBCRYPTSETUP #include <libcryptsetup.h> #endif -#include <linux/dm-ioctl.h> #include <sys/mount.h> #include "architecture.h" @@ -32,6 +31,7 @@ #include "fs-util.h" #include "gpt.h" #include "hexdecoct.h" +#include "linux-3.13/dm-ioctl.h" #include "mount-util.h" #include "path-util.h" #include "stat-util.h" @@ -42,7 +42,7 @@ #include "udev-util.h" #include "xattr-util.h" -static int probe_filesystem(const char *node, char **ret_fstype) { +_unused_ static int probe_filesystem(const char *node, char **ret_fstype) { #ifdef HAVE_BLKID _cleanup_blkid_free_probe_ blkid_probe b = NULL; const char *fstype; @@ -301,7 +301,7 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI _cleanup_udev_device_unref_ struct udev_device *q; unsigned long long pflags; blkid_partition pp; - const char *node; + const char *node, *sysname; dev_t qn; int nr; @@ -316,6 +316,12 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI if (st.st_rdev == qn) continue; + /* Filter out weird MMC RPMB partitions, which cannot reasonably be read, see + * https://github.com/systemd/systemd/issues/5806 */ + sysname = udev_device_get_sysname(q); + if (sysname && startswith(sysname, "mmcblk") && endswith(sysname, "rpmb")) + continue; + node = udev_device_get_devnode(q); if (!node) continue; @@ -951,7 +957,7 @@ int dissected_image_decrypt( * * = 0 → There was nothing to decrypt * > 0 → Decrypted successfully - * -ENOKEY → There's some to decrypt but no key was supplied + * -ENOKEY → There's something to decrypt but no key was supplied * -EKEYREJECTED → Passed key was not correct */ diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 33debadb15..12c4d65dd3 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -17,9 +17,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#ifdef HAVE_LIBIDN -#include <idna.h> -#include <stringprep.h> +#if defined(HAVE_LIBIDN2) +# include <idn2.h> +#elif defined(HAVE_LIBIDN) +# include <idna.h> +# include <stringprep.h> #endif #include <endian.h> @@ -299,8 +301,8 @@ int dns_label_escape_new(const char *p, size_t l, char **ret) { return r; } -int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) { #ifdef HAVE_LIBIDN +int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) { _cleanup_free_ uint32_t *input = NULL; size_t input_size, l; const char *p; @@ -348,13 +350,9 @@ int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded decoded[l] = 0; return (int) l; -#else - return 0; -#endif } int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) { -#ifdef HAVE_LIBIDN size_t input_size, output_size; _cleanup_free_ uint32_t *input = NULL; _cleanup_free_ char *result = NULL; @@ -399,10 +397,8 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, decoded[w] = 0; return w; -#else - return 0; -#endif } +#endif int dns_name_concat(const char *a, const char *b, char **_ret) { _cleanup_free_ char *ret = NULL; @@ -1274,6 +1270,26 @@ int dns_name_common_suffix(const char *a, const char *b, const char **ret) { } int dns_name_apply_idna(const char *name, char **ret) { + /* Return negative on error, 0 if not implemented, positive on success. */ + +#if defined(HAVE_LIBIDN2) + int r; + + assert(name); + assert(ret); + + r = idn2_lookup_u8((uint8_t*) name, (uint8_t**) ret, + IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL); + if (r == IDN2_OK) + return 1; /* *ret has been written */ + log_debug("idn2_lookup_u8(\"%s\") failed: %s", name, idn2_strerror(r)); + if (r == IDN2_2HYPHEN) + /* The name has two hypens — forbidden by IDNA2008 in some cases */ + return 0; + if (IN_SET(r, IDN2_TOO_BIG_DOMAIN, IDN2_TOO_BIG_LABEL)) + return -ENOSPC; + return -EINVAL; +#elif defined(HAVE_LIBIDN) _cleanup_free_ char *buf = NULL; size_t n = 0, allocated = 0; bool first = true; @@ -1309,7 +1325,7 @@ int dns_name_apply_idna(const char *name, char **ret) { else buf[n++] = '.'; - n +=r; + n += r; } if (n > DNS_HOSTNAME_MAX) @@ -1322,7 +1338,10 @@ int dns_name_apply_idna(const char *name, char **ret) { *ret = buf; buf = NULL; - return (int) n; + return 1; +#else + return 0; +#endif } int dns_name_is_valid_or_address(const char *name) { diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index 03f160369c..fca025def0 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -51,8 +51,10 @@ static inline int dns_name_parent(const char **name) { return dns_label_unescape(name, NULL, DNS_LABEL_MAX); } +#if defined(HAVE_LIBIDN) int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max); +#endif int dns_name_concat(const char *a, const char *b, char **ret); diff --git a/src/shared/efivars.c b/src/shared/efivars.c index 8631a5a5d9..8229e6b183 100644 --- a/src/shared/efivars.c +++ b/src/shared/efivars.c @@ -269,6 +269,7 @@ int efi_set_variable( _cleanup_close_ int fd = -1; assert(name); + assert(value); if (asprintf(&p, "/sys/firmware/efi/efivars/%s-%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c index c3106f1ae9..ec2e868ca8 100644 --- a/src/shared/fstab-util.c +++ b/src/shared/fstab-util.c @@ -34,18 +34,43 @@ #include "strv.h" #include "util.h" -bool fstab_is_mount_point(const char *mount) { +int fstab_has_fstype(const char *fstype) { _cleanup_endmntent_ FILE *f = NULL; struct mntent *m; f = setmntent("/etc/fstab", "re"); if (!f) - return false; + return errno == ENOENT ? false : -errno; - while ((m = getmntent(f))) - if (path_equal(m->mnt_dir, mount)) + for (;;) { + errno = 0; + m = getmntent(f); + if (!m) + return errno != 0 ? -errno : false; + + if (streq(m->mnt_type, fstype)) return true; + } + return false; +} + +int fstab_is_mount_point(const char *mount) { + _cleanup_endmntent_ FILE *f = NULL; + struct mntent *m; + f = setmntent("/etc/fstab", "re"); + if (!f) + return errno == ENOENT ? false : -errno; + + for (;;) { + errno = 0; + m = getmntent(f); + if (!m) + return errno != 0 ? -errno : false; + + if (path_equal(m->mnt_dir, mount)) + return true; + } return false; } diff --git a/src/shared/fstab-util.h b/src/shared/fstab-util.h index 679f6902f7..bbf0441351 100644 --- a/src/shared/fstab-util.h +++ b/src/shared/fstab-util.h @@ -24,7 +24,8 @@ #include "macro.h" -bool fstab_is_mount_point(const char *mount); +int fstab_is_mount_point(const char *mount); +int fstab_has_fstype(const char *fstype); int fstab_filter_options(const char *opts, const char *names, const char **namefound, char **value, char **filtered); diff --git a/src/shared/generator.c b/src/shared/generator.c index 9a069b2f97..6a78ebbda7 100644 --- a/src/shared/generator.c +++ b/src/shared/generator.c @@ -167,12 +167,13 @@ int generator_write_timeouts( usec_t u; int r; - r = fstab_filter_options(opts, "comment=systemd.device-timeout\0" "x-systemd.device-timeout\0", + r = fstab_filter_options(opts, "comment=systemd.device-timeout\0" + "x-systemd.device-timeout\0", NULL, &timeout, filtered); if (r <= 0) return r; - r = parse_sec(timeout, &u); + r = parse_sec_fix_0(timeout, &u); if (r < 0) { log_warning("Failed to parse timeout for %s, ignoring: %s", where, timeout); return 0; @@ -188,10 +189,49 @@ int generator_write_timeouts( return write_drop_in_format(dir, unit, 50, "device-timeout", "# Automatically generated by %s\n\n" - "[Unit]\nJobTimeoutSec=%s", + "[Unit]\nJobRunningTimeoutSec=%s", program_invocation_short_name, timeout); } +int generator_write_device_deps( + const char *dir, + const char *what, + const char *where, + const char *opts) { + + /* fstab records that specify _netdev option should apply the network + * ordering on the actual device depending on network connection. If we + * are not mounting real device (NFS, CIFS), we rely on _netdev effect + * on the mount unit itself. */ + + _cleanup_free_ char *node = NULL, *unit = NULL; + int r; + + if (!fstab_test_option(opts, "_netdev\0")) + return 0; + + node = fstab_node_to_udev_node(what); + if (!node) + return log_oom(); + + /* Nothing to apply dependencies to. */ + if (!is_device_path(node)) + return 0; + + r = unit_name_from_path(node, ".device", &unit); + if (r < 0) + return log_error_errno(r, "Failed to make unit name from path: %m"); + + /* See mount_add_default_dependencies for explanation why we create such + * dependencies. */ + return write_drop_in_format(dir, unit, 50, "netdev-dependencies", + "# Automatically generated by %s\n\n" + "[Unit]\n" + "After=" SPECIAL_NETWORK_ONLINE_TARGET " " SPECIAL_NETWORK_TARGET "\n" + "Wants=" SPECIAL_NETWORK_ONLINE_TARGET "\n", + program_invocation_short_name); +} + int generator_write_initrd_root_device_deps(const char *dir, const char *what) { _cleanup_free_ char *unit = NULL; int r; diff --git a/src/shared/generator.h b/src/shared/generator.h index a6017c1b76..825d934c8e 100644 --- a/src/shared/generator.h +++ b/src/shared/generator.h @@ -35,6 +35,12 @@ int generator_write_timeouts( const char *opts, char **filtered); +int generator_write_device_deps( + const char *dir, + const char *what, + const char *where, + const char *opts); + int generator_write_initrd_root_device_deps( const char *dir, const char *what); diff --git a/src/shared/install.c b/src/shared/install.c index 58c8e852b2..d0a291b819 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -422,7 +422,7 @@ static bool chroot_symlinks_same(const char *root, const char *wd, const char *a a = strjoina(path_is_absolute(a) ? root : wd, "/", a); b = strjoina(path_is_absolute(b) ? root : wd, "/", b); - return path_equal_or_files_same(a, b); + return path_equal_or_files_same(a, b, 0); } static int create_symlink( @@ -3045,7 +3045,7 @@ int unit_file_get_list( if (errno == ENOENT) continue; if (IN_SET(errno, ENOTDIR, EACCES)) { - log_debug("Failed to open \"%s\": %m", *i); + log_debug_errno(errno, "Failed to open \"%s\": %m", *i); continue; } diff --git a/src/shared/linux-3.13/dm-ioctl.h b/src/shared/linux-3.13/dm-ioctl.h new file mode 100644 index 0000000000..c8a4302093 --- /dev/null +++ b/src/shared/linux-3.13/dm-ioctl.h @@ -0,0 +1,355 @@ +/* + * Copyright (C) 2001 - 2003 Sistina Software (UK) Limited. + * Copyright (C) 2004 - 2009 Red Hat, Inc. All rights reserved. + * + * This file is released under the LGPL. + */ + +#ifndef _LINUX_DM_IOCTL_V4_H +#define _LINUX_DM_IOCTL_V4_H + +#include <linux/types.h> + +#define DM_DIR "mapper" /* Slashes not supported */ +#define DM_CONTROL_NODE "control" +#define DM_MAX_TYPE_NAME 16 +#define DM_NAME_LEN 128 +#define DM_UUID_LEN 129 + +/* + * A traditional ioctl interface for the device mapper. + * + * Each device can have two tables associated with it, an + * 'active' table which is the one currently used by io passing + * through the device, and an 'inactive' one which is a table + * that is being prepared as a replacement for the 'active' one. + * + * DM_VERSION: + * Just get the version information for the ioctl interface. + * + * DM_REMOVE_ALL: + * Remove all dm devices, destroy all tables. Only really used + * for debug. + * + * DM_LIST_DEVICES: + * Get a list of all the dm device names. + * + * DM_DEV_CREATE: + * Create a new device, neither the 'active' or 'inactive' table + * slots will be filled. The device will be in suspended state + * after creation, however any io to the device will get errored + * since it will be out-of-bounds. + * + * DM_DEV_REMOVE: + * Remove a device, destroy any tables. + * + * DM_DEV_RENAME: + * Rename a device or set its uuid if none was previously supplied. + * + * DM_SUSPEND: + * This performs both suspend and resume, depending which flag is + * passed in. + * Suspend: This command will not return until all pending io to + * the device has completed. Further io will be deferred until + * the device is resumed. + * Resume: It is no longer an error to issue this command on an + * unsuspended device. If a table is present in the 'inactive' + * slot, it will be moved to the active slot, then the old table + * from the active slot will be _destroyed_. Finally the device + * is resumed. + * + * DM_DEV_STATUS: + * Retrieves the status for the table in the 'active' slot. + * + * DM_DEV_WAIT: + * Wait for a significant event to occur to the device. This + * could either be caused by an event triggered by one of the + * targets of the table in the 'active' slot, or a table change. + * + * DM_TABLE_LOAD: + * Load a table into the 'inactive' slot for the device. The + * device does _not_ need to be suspended prior to this command. + * + * DM_TABLE_CLEAR: + * Destroy any table in the 'inactive' slot (ie. abort). + * + * DM_TABLE_DEPS: + * Return a set of device dependencies for the 'active' table. + * + * DM_TABLE_STATUS: + * Return the targets status for the 'active' table. + * + * DM_TARGET_MSG: + * Pass a message string to the target at a specific offset of a device. + * + * DM_DEV_SET_GEOMETRY: + * Set the geometry of a device by passing in a string in this format: + * + * "cylinders heads sectors_per_track start_sector" + * + * Beware that CHS geometry is nearly obsolete and only provided + * for compatibility with dm devices that can be booted by a PC + * BIOS. See struct hd_geometry for range limits. Also note that + * the geometry is erased if the device size changes. + */ + +/* + * All ioctl arguments consist of a single chunk of memory, with + * this structure at the start. If a uuid is specified any + * lookup (eg. for a DM_INFO) will be done on that, *not* the + * name. + */ +struct dm_ioctl { + /* + * The version number is made up of three parts: + * major - no backward or forward compatibility, + * minor - only backwards compatible, + * patch - both backwards and forwards compatible. + * + * All clients of the ioctl interface should fill in the + * version number of the interface that they were + * compiled with. + * + * All recognised ioctl commands (ie. those that don't + * return -ENOTTY) fill out this field, even if the + * command failed. + */ + __u32 version[3]; /* in/out */ + __u32 data_size; /* total size of data passed in + * including this struct */ + + __u32 data_start; /* offset to start of data + * relative to start of this struct */ + + __u32 target_count; /* in/out */ + __s32 open_count; /* out */ + __u32 flags; /* in/out */ + + /* + * event_nr holds either the event number (input and output) or the + * udev cookie value (input only). + * The DM_DEV_WAIT ioctl takes an event number as input. + * The DM_SUSPEND, DM_DEV_REMOVE and DM_DEV_RENAME ioctls + * use the field as a cookie to return in the DM_COOKIE + * variable with the uevents they issue. + * For output, the ioctls return the event number, not the cookie. + */ + __u32 event_nr; /* in/out */ + __u32 padding; + + __u64 dev; /* in/out */ + + char name[DM_NAME_LEN]; /* device name */ + char uuid[DM_UUID_LEN]; /* unique identifier for + * the block device */ + char data[7]; /* padding or data */ +}; + +/* + * Used to specify tables. These structures appear after the + * dm_ioctl. + */ +struct dm_target_spec { + __u64 sector_start; + __u64 length; + __s32 status; /* used when reading from kernel only */ + + /* + * Location of the next dm_target_spec. + * - When specifying targets on a DM_TABLE_LOAD command, this value is + * the number of bytes from the start of the "current" dm_target_spec + * to the start of the "next" dm_target_spec. + * - When retrieving targets on a DM_TABLE_STATUS command, this value + * is the number of bytes from the start of the first dm_target_spec + * (that follows the dm_ioctl struct) to the start of the "next" + * dm_target_spec. + */ + __u32 next; + + char target_type[DM_MAX_TYPE_NAME]; + + /* + * Parameter string starts immediately after this object. + * Be careful to add padding after string to ensure correct + * alignment of subsequent dm_target_spec. + */ +}; + +/* + * Used to retrieve the target dependencies. + */ +struct dm_target_deps { + __u32 count; /* Array size */ + __u32 padding; /* unused */ + __u64 dev[0]; /* out */ +}; + +/* + * Used to get a list of all dm devices. + */ +struct dm_name_list { + __u64 dev; + __u32 next; /* offset to the next record from + the _start_ of this */ + char name[0]; +}; + +/* + * Used to retrieve the target versions + */ +struct dm_target_versions { + __u32 next; + __u32 version[3]; + + char name[0]; +}; + +/* + * Used to pass message to a target + */ +struct dm_target_msg { + __u64 sector; /* Device sector */ + + char message[0]; +}; + +/* + * If you change this make sure you make the corresponding change + * to dm-ioctl.c:lookup_ioctl() + */ +enum { + /* Top level cmds */ + DM_VERSION_CMD = 0, + DM_REMOVE_ALL_CMD, + DM_LIST_DEVICES_CMD, + + /* device level cmds */ + DM_DEV_CREATE_CMD, + DM_DEV_REMOVE_CMD, + DM_DEV_RENAME_CMD, + DM_DEV_SUSPEND_CMD, + DM_DEV_STATUS_CMD, + DM_DEV_WAIT_CMD, + + /* Table level cmds */ + DM_TABLE_LOAD_CMD, + DM_TABLE_CLEAR_CMD, + DM_TABLE_DEPS_CMD, + DM_TABLE_STATUS_CMD, + + /* Added later */ + DM_LIST_VERSIONS_CMD, + DM_TARGET_MSG_CMD, + DM_DEV_SET_GEOMETRY_CMD +}; + +#define DM_IOCTL 0xfd + +#define DM_VERSION _IOWR(DM_IOCTL, DM_VERSION_CMD, struct dm_ioctl) +#define DM_REMOVE_ALL _IOWR(DM_IOCTL, DM_REMOVE_ALL_CMD, struct dm_ioctl) +#define DM_LIST_DEVICES _IOWR(DM_IOCTL, DM_LIST_DEVICES_CMD, struct dm_ioctl) + +#define DM_DEV_CREATE _IOWR(DM_IOCTL, DM_DEV_CREATE_CMD, struct dm_ioctl) +#define DM_DEV_REMOVE _IOWR(DM_IOCTL, DM_DEV_REMOVE_CMD, struct dm_ioctl) +#define DM_DEV_RENAME _IOWR(DM_IOCTL, DM_DEV_RENAME_CMD, struct dm_ioctl) +#define DM_DEV_SUSPEND _IOWR(DM_IOCTL, DM_DEV_SUSPEND_CMD, struct dm_ioctl) +#define DM_DEV_STATUS _IOWR(DM_IOCTL, DM_DEV_STATUS_CMD, struct dm_ioctl) +#define DM_DEV_WAIT _IOWR(DM_IOCTL, DM_DEV_WAIT_CMD, struct dm_ioctl) + +#define DM_TABLE_LOAD _IOWR(DM_IOCTL, DM_TABLE_LOAD_CMD, struct dm_ioctl) +#define DM_TABLE_CLEAR _IOWR(DM_IOCTL, DM_TABLE_CLEAR_CMD, struct dm_ioctl) +#define DM_TABLE_DEPS _IOWR(DM_IOCTL, DM_TABLE_DEPS_CMD, struct dm_ioctl) +#define DM_TABLE_STATUS _IOWR(DM_IOCTL, DM_TABLE_STATUS_CMD, struct dm_ioctl) + +#define DM_LIST_VERSIONS _IOWR(DM_IOCTL, DM_LIST_VERSIONS_CMD, struct dm_ioctl) + +#define DM_TARGET_MSG _IOWR(DM_IOCTL, DM_TARGET_MSG_CMD, struct dm_ioctl) +#define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) + +#define DM_VERSION_MAJOR 4 +#define DM_VERSION_MINOR 27 +#define DM_VERSION_PATCHLEVEL 0 +#define DM_VERSION_EXTRA "-ioctl (2013-10-30)" + +/* Status bits */ +#define DM_READONLY_FLAG (1 << 0) /* In/Out */ +#define DM_SUSPEND_FLAG (1 << 1) /* In/Out */ +#define DM_PERSISTENT_DEV_FLAG (1 << 3) /* In */ + +/* + * Flag passed into ioctl STATUS command to get table information + * rather than current status. + */ +#define DM_STATUS_TABLE_FLAG (1 << 4) /* In */ + +/* + * Flags that indicate whether a table is present in either of + * the two table slots that a device has. + */ +#define DM_ACTIVE_PRESENT_FLAG (1 << 5) /* Out */ +#define DM_INACTIVE_PRESENT_FLAG (1 << 6) /* Out */ + +/* + * Indicates that the buffer passed in wasn't big enough for the + * results. + */ +#define DM_BUFFER_FULL_FLAG (1 << 8) /* Out */ + +/* + * This flag is now ignored. + */ +#define DM_SKIP_BDGET_FLAG (1 << 9) /* In */ + +/* + * Set this to avoid attempting to freeze any filesystem when suspending. + */ +#define DM_SKIP_LOCKFS_FLAG (1 << 10) /* In */ + +/* + * Set this to suspend without flushing queued ios. + * Also disables flushing uncommitted changes in the thin target before + * generating statistics for DM_TABLE_STATUS and DM_DEV_WAIT. + */ +#define DM_NOFLUSH_FLAG (1 << 11) /* In */ + +/* + * If set, any table information returned will relate to the inactive + * table instead of the live one. Always check DM_INACTIVE_PRESENT_FLAG + * is set before using the data returned. + */ +#define DM_QUERY_INACTIVE_TABLE_FLAG (1 << 12) /* In */ + +/* + * If set, a uevent was generated for which the caller may need to wait. + */ +#define DM_UEVENT_GENERATED_FLAG (1 << 13) /* Out */ + +/* + * If set, rename changes the uuid not the name. Only permitted + * if no uuid was previously supplied: an existing uuid cannot be changed. + */ +#define DM_UUID_FLAG (1 << 14) /* In */ + +/* + * If set, all buffers are wiped after use. Use when sending + * or requesting sensitive data such as an encryption key. + */ +#define DM_SECURE_DATA_FLAG (1 << 15) /* In */ + +/* + * If set, a message generated output data. + */ +#define DM_DATA_OUT_FLAG (1 << 16) /* Out */ + +/* + * If set with DM_DEV_REMOVE or DM_REMOVE_ALL this indicates that if + * the device cannot be removed immediately because it is still in use + * it should instead be scheduled for removal when it gets closed. + * + * On return from DM_DEV_REMOVE, DM_DEV_STATUS or other ioctls, this + * flag indicates that the device is scheduled to be removed when it + * gets closed. + */ +#define DM_DEFERRED_REMOVE (1 << 17) /* In/Out */ + +#endif /* _LINUX_DM_IOCTL_H */ diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 72c43e80cb..02ae4265c6 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -264,6 +264,8 @@ static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, Ou } } else { + char usec[7]; + gettime_r = (flags & OUTPUT_UTC) ? gmtime_r : localtime_r; t = (time_t) (x / USEC_PER_SEC); @@ -275,9 +277,19 @@ static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, Ou case OUTPUT_SHORT_ISO: if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm)) <= 0) { - log_error("Failed for format ISO time"); + log_error("Failed to format ISO time"); + return -EINVAL; + } + break; + + case OUTPUT_SHORT_ISO_PRECISE: + /* No usec in strftime, so we leave space and copy over */ + if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S.xxxxxx%z", gettime_r(&t, &tm)) <= 0) { + log_error("Failed to format ISO-precise time"); return -EINVAL; } + xsprintf(usec, "%06"PRI_USEC, x % USEC_PER_SEC); + memcpy(buf + 20, usec, 6); break; case OUTPUT_SHORT: @@ -473,6 +485,7 @@ static int output_verbose( _cleanup_free_ char *cursor = NULL; uint64_t realtime = 0; char ts[FORMAT_TIMESTAMP_MAX + 7]; + const char *timestamp; int r; assert(f); @@ -508,10 +521,10 @@ static int output_verbose( if (r < 0) return log_error_errno(r, "Failed to get cursor: %m"); + timestamp = flags & OUTPUT_UTC ? format_timestamp_us_utc(ts, sizeof ts, realtime) + : format_timestamp_us(ts, sizeof ts, realtime); fprintf(f, "%s [%s]\n", - flags & OUTPUT_UTC ? - format_timestamp_us_utc(ts, sizeof(ts), realtime) : - format_timestamp_us(ts, sizeof(ts), realtime), + timestamp ?: "(no timestamp)", cursor); JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) { @@ -949,6 +962,7 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])( [OUTPUT_SHORT] = output_short, [OUTPUT_SHORT_ISO] = output_short, + [OUTPUT_SHORT_ISO_PRECISE] = output_short, [OUTPUT_SHORT_PRECISE] = output_short, [OUTPUT_SHORT_MONOTONIC] = output_short, [OUTPUT_SHORT_UNIX] = output_short, @@ -977,7 +991,6 @@ int output_journal( n_columns = columns(); ret = output_funcs[mode](f, j, mode, n_columns, flags); - fflush(stdout); if (ellipsized && ret > 0) *ellipsized = true; diff --git a/src/shared/meson.build b/src/shared/meson.build new file mode 100644 index 0000000000..2eaef11a2d --- /dev/null +++ b/src/shared/meson.build @@ -0,0 +1,158 @@ +shared_sources = ''' + acl-util.h + acpi-fpdt.c + acpi-fpdt.h + apparmor-util.c + apparmor-util.h + ask-password-api.c + ask-password-api.h + base-filesystem.c + base-filesystem.h + boot-timestamps.c + boot-timestamps.h + bus-unit-util.c + bus-unit-util.h + bus-util.c + bus-util.h + cgroup-show.c + cgroup-show.h + clean-ipc.c + clean-ipc.h + condition.c + condition.h + conf-parser.c + conf-parser.h + dev-setup.c + dev-setup.h + dissect-image.c + dissect-image.h + dns-domain.c + dns-domain.h + dropin.c + dropin.h + efivars.c + efivars.h + fdset.c + fdset.h + firewall-util.h + fstab-util.c + fstab-util.h + gcrypt-util.c + gcrypt-util.h + generator.c + generator.h + gpt.h + ima-util.c + ima-util.h + import-util.c + import-util.h + initreq.h + install.c + install.h + install-printf.c + install-printf.h + journal-util.c + journal-util.h + logs-show.c + logs-show.h + loop-util.c + loop-util.h + machine-image.c + machine-image.h + machine-pool.c + machine-pool.h + nsflags.c + nsflags.h + output-mode.c + output-mode.h + pager.c + pager.h + path-lookup.c + path-lookup.h + ptyfwd.c + ptyfwd.h + resolve-util.c + resolve-util.h + seccomp-util.h + sleep-config.c + sleep-config.h + spawn-ask-password-agent.c + spawn-ask-password-agent.h + spawn-polkit-agent.c + spawn-polkit-agent.h + specifier.c + specifier.h + switch-root.c + switch-root.h + sysctl-util.c + sysctl-util.h + tests.c + tests.h + udev-util.h + udev-util.c + uid-range.c + uid-range.h + utmp-wtmp.h + vlan-util.c + vlan-util.h + volatile-util.c + volatile-util.h + watchdog.c + watchdog.h +'''.split() + +test_tables_h = files('test-tables.h') +shared_sources += [test_tables_h] + +if conf.get('HAVE_ACL', false) + shared_sources += ['acl-util.c'] +endif + +if conf.get('HAVE_UTMP', false) + shared_sources += ['utmp-wtmp.c'] +endif + +if conf.get('HAVE_SECCOMP', false) + shared_sources += ['seccomp-util.c'] +endif + +if conf.get('HAVE_LIBIPTC', false) + shared_sources += ['firewall-util.c'] +endif + +libshared_name = 'systemd-shared-@0@'.format(meson.project_version()) + +libshared_deps = [threads, + librt, + libcap, + libacl, + libcryptsetup, + libgcrypt, + libiptc, + libseccomp, + libselinux, + libidn, + libxz, + liblz4, + libblkid] + +libshared = shared_library( + libshared_name, + shared_sources, + basic_sources, + journal_internal_sources, + libsystemd_internal_sources, + libudev_sources, + include_directories : includes, + link_args : ['-shared'], + c_args : ['-fvisibility=default'], + dependencies : libshared_deps, + install : true, + install_dir : rootlibexecdir) + +libshared_static = static_library( + libshared_name, + shared_sources, + basic_sources, + include_directories : includes, + dependencies : libshared_deps) diff --git a/src/shared/output-mode.c b/src/shared/output-mode.c index 67d8208ad2..29dcba9f6b 100644 --- a/src/shared/output-mode.c +++ b/src/shared/output-mode.c @@ -24,6 +24,7 @@ static const char *const output_mode_table[_OUTPUT_MODE_MAX] = { [OUTPUT_SHORT] = "short", [OUTPUT_SHORT_FULL] = "short-full", [OUTPUT_SHORT_ISO] = "short-iso", + [OUTPUT_SHORT_ISO_PRECISE] = "short-iso-precise", [OUTPUT_SHORT_PRECISE] = "short-precise", [OUTPUT_SHORT_MONOTONIC] = "short-monotonic", [OUTPUT_SHORT_UNIX] = "short-unix", diff --git a/src/shared/output-mode.h b/src/shared/output-mode.h index ff29dafcb5..2a1bfd98d0 100644 --- a/src/shared/output-mode.h +++ b/src/shared/output-mode.h @@ -25,6 +25,7 @@ typedef enum OutputMode { OUTPUT_SHORT, OUTPUT_SHORT_FULL, OUTPUT_SHORT_ISO, + OUTPUT_SHORT_ISO_PRECISE, OUTPUT_SHORT_PRECISE, OUTPUT_SHORT_MONOTONIC, OUTPUT_SHORT_UNIX, diff --git a/src/shared/pager.c b/src/shared/pager.c index f00ba9e1e7..4d7b02c63c 100644 --- a/src/shared/pager.c +++ b/src/shared/pager.c @@ -53,6 +53,11 @@ noreturn static void pager_fallback(void) { _exit(EXIT_SUCCESS); } +static int stored_stdout = -1; +static int stored_stderr = -1; +static bool stdout_redirected = false; +static bool stderr_redirected = false; + int pager_open(bool no_pager, bool jump_to_end) { _cleanup_close_pair_ int fd[2] = { -1, -1 }; const char *pager; @@ -147,10 +152,19 @@ int pager_open(bool no_pager, bool jump_to_end) { } /* Return in the parent */ - if (dup2(fd[1], STDOUT_FILENO) < 0) + stored_stdout = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 3); + if (dup2(fd[1], STDOUT_FILENO) < 0) { + stored_stdout = safe_close(stored_stdout); return log_error_errno(errno, "Failed to duplicate pager pipe: %m"); - if (dup2(fd[1], STDERR_FILENO) < 0) + } + stdout_redirected = true; + + stored_stderr = fcntl(STDERR_FILENO, F_DUPFD_CLOEXEC, 3); + if (dup2(fd[1], STDERR_FILENO) < 0) { + stored_stderr = safe_close(stored_stderr); return log_error_errno(errno, "Failed to duplicate pager pipe: %m"); + } + stderr_redirected = true; return 1; } @@ -161,8 +175,17 @@ void pager_close(void) { return; /* Inform pager that we are done */ - stdout = safe_fclose(stdout); - stderr = safe_fclose(stderr); + (void) fflush(stdout); + if (stdout_redirected) + if (stored_stdout < 0 || dup2(stored_stdout, STDOUT_FILENO) < 0) + (void) close(STDOUT_FILENO); + stored_stdout = safe_close(stored_stdout); + (void) fflush(stderr); + if (stderr_redirected) + if (stored_stderr < 0 || dup2(stored_stderr, STDERR_FILENO) < 0) + (void) close(STDERR_FILENO); + stored_stderr = safe_close(stored_stderr); + stdout_redirected = stderr_redirected = false; (void) kill(pager_pid, SIGCONT); (void) wait_for_terminate(pager_pid, NULL); diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index 2631856563..36843d4bf5 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -792,37 +792,10 @@ 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: - case SCMP_ARCH_PPC64: - case SCMP_ARCH_PPC64LE: - 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 SECCOMP_RESTRICT_NAMESPACES_BROKEN -# 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; @@ -871,7 +844,8 @@ int seccomp_restrict_namespaces(unsigned long retain) { break; } - if (clone_reversed_order == 0) + /* On s390/s390x the first two parameters to clone are switched */ + if (!IN_SET(arch, SCMP_ARCH_S390, SCMP_ARCH_S390X)) r = seccomp_rule_add_exact( seccomp, SCMP_ACT_ERRNO(EPERM), @@ -966,16 +940,16 @@ int seccomp_restrict_address_families(Set *address_families, bool whitelist) { case SCMP_ARCH_X32: case SCMP_ARCH_ARM: case SCMP_ARCH_AARCH64: + case SCMP_ARCH_PPC64: + case SCMP_ARCH_PPC64LE: /* 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: + case SCMP_ARCH_X86: default: /* These we either know we don't support (i.e. are the ones that do use socketcall()), or we * don't know */ @@ -1186,6 +1160,37 @@ int seccomp_restrict_realtime(void) { return 0; } +static int add_seccomp_syscall_filter(scmp_filter_ctx seccomp, + uint32_t arch, + int nr, + unsigned int arg_cnt, + const struct scmp_arg_cmp arg) { + int r; + + r = seccomp_rule_add_exact(seccomp, SCMP_ACT_ERRNO(EPERM), nr, arg_cnt, arg); + if (r < 0) { + _cleanup_free_ char *n = NULL; + + n = seccomp_syscall_resolve_num_arch(arch, nr); + log_debug_errno(r, "Failed to add %s() rule for architecture %s, skipping: %m", + strna(n), + seccomp_arch_to_string(arch)); + } + + return r; +} + +/* For known architectures, check that syscalls are indeed defined or not. */ +#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) +assert_cc(SCMP_SYS(shmget) > 0); +assert_cc(SCMP_SYS(shmat) > 0); +assert_cc(SCMP_SYS(shmdt) > 0); +#elif defined(__i386__) || defined(__powerpc64__) +assert_cc(SCMP_SYS(shmget) < 0); +assert_cc(SCMP_SYS(shmat) < 0); +assert_cc(SCMP_SYS(shmdt) < 0); +#endif + int seccomp_memory_deny_write_execute(void) { uint32_t arch; @@ -1202,21 +1207,36 @@ int seccomp_memory_deny_write_execute(void) { case SCMP_ARCH_X86: filter_syscall = SCMP_SYS(mmap2); block_syscall = SCMP_SYS(mmap); + break; + + case SCMP_ARCH_PPC64: + case SCMP_ARCH_PPC64LE: + filter_syscall = SCMP_SYS(mmap); + + /* Note that shmat() isn't available, and 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. That's a pity, but no total loss. */ - /* 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_AARCH64: + block_syscall = SCMP_SYS(mmap); + /* fall through */ + + case SCMP_ARCH_ARM: + filter_syscall = SCMP_SYS(mmap2); /* arm has only mmap2 */ + shmat_syscall = SCMP_SYS(shmat); break; case SCMP_ARCH_X86_64: case SCMP_ARCH_X32: - filter_syscall = SCMP_SYS(mmap); + filter_syscall = SCMP_SYS(mmap); /* amd64 and x32 have only 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__) +#if !defined(__i386__) && !defined(__x86_64__) && !defined(__powerpc64__) && !defined(__arm__) && !defined(__aarch64__) #warning "Consider adding the right mmap() syscall definitions here!" #endif } @@ -1229,63 +1249,30 @@ int seccomp_memory_deny_write_execute(void) { if (r < 0) return r; - 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; - } - } + r = add_seccomp_syscall_filter(seccomp, arch, filter_syscall, + 1, + SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC|PROT_WRITE, PROT_EXEC|PROT_WRITE)); + if (r < 0) + 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)); + r = add_seccomp_syscall_filter(seccomp, arch, block_syscall, 0, (const struct scmp_arg_cmp){} ); + if (r < 0) continue; - } } - r = seccomp_rule_add_exact( - seccomp, - SCMP_ACT_ERRNO(EPERM), - SCMP_SYS(mprotect), - 1, - SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC, PROT_EXEC)); - if (r < 0) { - log_debug_errno(r, "Failed to add mprotect() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); + r = add_seccomp_syscall_filter(seccomp, arch, SCMP_SYS(mprotect), + 1, + SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC, PROT_EXEC)); + if (r < 0) 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)); + r = add_seccomp_syscall_filter(seccomp, arch, SCMP_SYS(shmat), + 1, + SCMP_A2(SCMP_CMP_MASKED_EQ, SHM_EXEC, SHM_EXEC)); + if (r < 0) continue; - } } r = seccomp_load(seccomp); diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h index b56ac3f763..4438e87fa6 100644 --- a/src/shared/seccomp-util.h +++ b/src/shared/seccomp-util.h @@ -76,28 +76,6 @@ 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__) || defined(__powerpc64__) -#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/shared/sleep-config.c b/src/shared/sleep-config.c index ed31a80c8d..8c1624ff46 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -59,9 +59,9 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states) { }; config_parse_many_nulstr(PKGSYSCONFDIR "/sleep.conf", - CONF_PATHS_NULSTR("systemd/sleep.conf.d"), - "Sleep\0", config_item_table_lookup, items, - false, NULL); + CONF_PATHS_NULSTR("systemd/sleep.conf.d"), + "Sleep\0", config_item_table_lookup, items, + false, NULL); if (streq(verb, "suspend")) { /* empty by default */ diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c new file mode 100644 index 0000000000..f708dcfa14 --- /dev/null +++ b/src/shared/udev-util.c @@ -0,0 +1,56 @@ +/*** + This file is part of systemd. + + Copyright 2017 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <string.h> + +#include "fileio.h" +#include "log.h" +#include "string-util.h" +#include "udev-util.h" + +int udev_parse_config(void) { + _cleanup_free_ char *val = NULL; + const char *log; + size_t n; + int r; + + r = parse_env_file("/etc/udev/udev.conf", NEWLINE, "udev_log", &val, NULL); + if (r == -ENOENT || !val) + return 0; + if (r < 0) + return r; + + /* unquote */ + n = strlen(val); + if (n >= 2 && + ((val[0] == '"' && val[n-1] == '"') || + (val[0] == '\'' && val[n-1] == '\''))) { + val[n - 1] = '\0'; + log = val + 1; + } else + log = val; + + /* we set the udev log level here explicitly, this is supposed + * to regulate the code in libudev/ and udev/. */ + r = log_set_max_level_from_string_realm(LOG_REALM_UDEV, log); + if (r < 0) + log_debug_errno(r, "/etc/udev/udev.conf: failed to set udev log level '%s', ignoring: %m", log); + + return 0; +} diff --git a/src/shared/udev-util.h b/src/shared/udev-util.h index ca0889f8a6..a415be249e 100644 --- a/src/shared/udev-util.h +++ b/src/shared/udev-util.h @@ -42,3 +42,5 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_monitor*, udev_monitor_unref); #define _cleanup_udev_ctrl_msg_unref_ _cleanup_(udev_ctrl_msg_unrefp) #define _cleanup_udev_monitor_unref_ _cleanup_(udev_monitor_unrefp) #define _cleanup_udev_list_cleanup_ _cleanup_(udev_list_cleanup) + +int udev_parse_config(void); diff --git a/src/shared/vlan-util.c b/src/shared/vlan-util.c index 78d66dd3d9..1edd96fbe7 100644 --- a/src/shared/vlan-util.c +++ b/src/shared/vlan-util.c @@ -17,9 +17,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "vlan-util.h" -#include "parse-util.h" #include "conf-parser.h" +#include "parse-util.h" +#include "string-util.h" +#include "vlan-util.h" int parse_vlanid(const char *p, uint16_t *ret) { uint16_t id; @@ -35,6 +36,32 @@ int parse_vlanid(const char *p, uint16_t *ret) { return 0; } +int config_parse_default_port_vlanid( + 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) { + uint16_t *id = data; + + assert(lvalue); + assert(rvalue); + assert(data); + + if (streq(rvalue, "none")) { + *id = 0; + return 0; + } + + return config_parse_vlanid(unit, filename, line, section, section_line, + lvalue, ltype, rvalue, data, userdata); +} + int config_parse_vlanid( const char *unit, const char *filename, diff --git a/src/shared/vlan-util.h b/src/shared/vlan-util.h index ce6763b3a3..365ed14d88 100644 --- a/src/shared/vlan-util.h +++ b/src/shared/vlan-util.h @@ -32,4 +32,5 @@ static inline bool vlanid_is_valid(uint16_t id) { int parse_vlanid(const char *p, uint16_t *ret); +int config_parse_default_port_vlanid(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_vlanid(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/stdio-bridge/stdio-bridge.c b/src/stdio-bridge/stdio-bridge.c index ce8efce3d5..097f8c1a4b 100644 --- a/src/stdio-bridge/stdio-bridge.c +++ b/src/stdio-bridge/stdio-bridge.c @@ -115,7 +115,7 @@ int main(int argc, char *argv[]) { in_fd = SD_LISTEN_FDS_START; out_fd = SD_LISTEN_FDS_START; } else { - log_error("Illegal number of file descriptors passed\n"); + log_error("Illegal number of file descriptors passed."); goto finish; } @@ -190,7 +190,7 @@ int main(int argc, char *argv[]) { } for (;;) { - _cleanup_(sd_bus_message_unrefp)sd_bus_message *m = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; int events_a, events_b, fd; uint64_t timeout_a, timeout_b, t; struct timespec _ts, *ts; @@ -292,7 +292,7 @@ int main(int argc, char *argv[]) { r = ppoll(p, ELEMENTSOF(p), ts, NULL); } if (r < 0) { - log_error("ppoll() failed: %m"); + log_error_errno(errno, "ppoll() failed: %m"); goto finish; } } diff --git a/src/sulogin-shell/.gitignore b/src/sulogin-shell/.gitignore new file mode 100644 index 0000000000..01a315524b --- /dev/null +++ b/src/sulogin-shell/.gitignore @@ -0,0 +1 @@ +systemd-sulogin-shell diff --git a/src/sulogin-shell/meson.build b/src/sulogin-shell/meson.build new file mode 100644 index 0000000000..4ec0d3da1a --- /dev/null +++ b/src/sulogin-shell/meson.build @@ -0,0 +1,7 @@ +gen = configure_file( + input : 'systemd-sulogin-shell.in', + output : 'systemd-sulogin-shell', + configuration : substs) + +install_data(gen, + install_dir : rootlibexecdir) diff --git a/src/sulogin-shell/systemd-sulogin-shell.in b/src/sulogin-shell/systemd-sulogin-shell.in new file mode 100755 index 0000000000..103f841a57 --- /dev/null +++ b/src/sulogin-shell/systemd-sulogin-shell.in @@ -0,0 +1,12 @@ +#!/bin/sh + +if [ -x /bin/plymouth ]; then + /bin/plymouth --wait quit +fi + +echo "You are in $1 mode. After logging in, type \"journalctl -xb\" to view" +echo "system logs, \"systemctl reboot\" to reboot, \"systemctl default\" or ^D to boot" +echo "into default mode." + +@SULOGIN@ +@SYSTEMCTL@ --job-mode=fail --no-block default diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index d78e56d777..83ed9ef9f7 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -47,6 +47,7 @@ #include "dropin.h" #include "efivars.h" #include "env-util.h" +#include "escape.h" #include "exit-status.h" #include "fd-util.h" #include "fileio.h" @@ -1780,6 +1781,7 @@ static int list_dependencies_one( STRV_FOREACH(c, deps) { if (strv_contains(*units, *c)) { if (!arg_plain) { + printf(" "); r = list_dependencies_print("...", level + 1, (branches << 1) | (c[1] == NULL ? 0 : 1), 1); if (r < 0) return r; @@ -3190,8 +3192,8 @@ static int start_unit(int argc, char *argv[], void *userdata) { return r; } +#ifdef ENABLE_LOGIND static int logind_set_wall_message(void) { -#ifdef HAVE_LOGIND _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus; _cleanup_free_ char *m = NULL; @@ -3219,15 +3221,14 @@ static int logind_set_wall_message(void) { if (r < 0) return log_warning_errno(r, "Failed to set wall message, ignoring: %s", bus_error_message(&error, r)); - -#endif return 0; } +#endif /* Ask systemd-logind, which might grant access to unprivileged users * through PolicyKit */ static int logind_reboot(enum action a) { -#ifdef HAVE_LOGIND +#ifdef ENABLE_LOGIND _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; const char *method, *description; sd_bus *bus; @@ -3290,7 +3291,7 @@ static int logind_reboot(enum action a) { } static int logind_check_inhibitors(enum action a) { -#ifdef HAVE_LOGIND +#ifdef ENABLE_LOGIND _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_strv_free_ char **sessions = NULL; const char *what, *who, *why, *mode; @@ -3409,7 +3410,7 @@ static int logind_check_inhibitors(enum action a) { } static int logind_prepare_firmware_setup(void) { -#ifdef HAVE_LOGIND +#ifdef ENABLE_LOGIND _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus; int r; @@ -3817,6 +3818,8 @@ typedef struct UnitStatusInfo { bool failed_assert_negate; const char *failed_assert; const char *failed_assert_parameter; + usec_t next_elapse_real; + usec_t next_elapse_monotonic; /* Socket */ unsigned n_accepted; @@ -3986,6 +3989,31 @@ static void print_status_info( else printf("\n"); + if (endswith(i->id, ".timer")) { + char tstamp1[FORMAT_TIMESTAMP_RELATIVE_MAX], + tstamp2[FORMAT_TIMESTAMP_MAX]; + char *next_rel_time, *next_time; + dual_timestamp nw, next = {i->next_elapse_real, + i->next_elapse_monotonic}; + usec_t next_elapse; + + printf(" Trigger: "); + + dual_timestamp_get(&nw); + next_elapse = calc_next_elapse(&nw, &next); + next_rel_time = format_timestamp_relative(tstamp1, + sizeof(tstamp1), + next_elapse); + next_time = format_timestamp(tstamp2, + sizeof(tstamp2), + next_elapse); + + if (next_time && next_rel_time) + printf("%s; %s\n", next_time, next_rel_time); + else + printf("n/a\n"); + } + if (!i->condition_result && i->condition_timestamp > 0) { UnitCondition *c; int n = 0; @@ -4423,6 +4451,10 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo * i->tasks_max = u; else if (streq(name, "CPUUsageNSec")) i->cpu_usage_nsec = u; + else if (streq(name, "NextElapseUSecMonotonic")) + i->next_elapse_monotonic = u; + else if (streq(name, "NextElapseUSecRealtime")) + i->next_elapse_real = u; break; } @@ -5573,6 +5605,24 @@ static int reset_failed(int argc, char *argv[], void *userdata) { return r; } +static int print_variable(const char *s) { + const char *sep; + _cleanup_free_ char *esc = NULL; + + sep = strchr(s, '='); + if (!sep) { + log_error("Invalid environment block"); + return -EUCLEAN; + } + + esc = shell_maybe_quote(sep + 1, ESCAPE_POSIX); + if (!esc) + return log_oom(); + + printf("%.*s=%s\n", (int)(sep-s), s, esc); + return 0; +} + static int show_environment(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; @@ -5602,8 +5652,11 @@ static int show_environment(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_parse_error(r); - while ((r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &text)) > 0) - puts(text); + while ((r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &text)) > 0) { + r = print_variable(text); + if (r < 0) + return r; + } if (r < 0) return bus_log_parse_error(r); @@ -5654,7 +5707,7 @@ static int switch_root(int argc, char *argv[], void *userdata) { /* If the passed init is actually the same as the * systemd binary, then let's suppress it. */ - if (files_same(root_init_path, root_systemd_path) > 0) + if (files_same(root_init_path, root_systemd_path, 0) > 0) init = NULL; } @@ -5890,7 +5943,8 @@ static int enable_sysv_units(const char *verb, char **args) { if (!l) return log_oom(); - log_info("Executing: %s", l); + if (!arg_quiet) + log_info("Executing: %s", l); pid = fork(); if (pid < 0) @@ -5979,6 +6033,34 @@ static int mangle_names(char **original_names, char ***mangled_names) { return 0; } +static int normalize_filenames(char **names) { + char **u; + int r; + + STRV_FOREACH(u, names) + if (!path_is_absolute(*u)) { + char* normalized_path; + + if (!isempty(arg_root)) { + log_error("Non-absolute paths are not allowed when --root is used: %s", *u); + return -EINVAL; + } + + if (!strchr(*u,'/')) { + log_error("Link argument does contain at least one directory separator: %s", *u); + return -EINVAL; + } + + r = path_make_absolute_cwd(*u, &normalized_path); + if (r < 0) + return r; + + free_and_replace(*u, normalized_path); + } + + return 0; +} + static int normalize_names(char **names, bool warn_if_path) { char **u; bool was_path = false; @@ -6075,6 +6157,12 @@ static int enable_unit(int argc, char *argv[], void *userdata) { return r; } + if (streq(verb, "link")) { + r = normalize_filenames(names); + if (r < 0) + return r; + } + if (install_client_side()) { UnitFileFlags flags; @@ -7001,7 +7089,8 @@ static void systemctl_help(void) { " --root=PATH Enable unit files in the specified root directory\n" " -n --lines=INTEGER Number of journal entries to show\n" " -o --output=STRING Change journal output mode (short, short-precise,\n" - " short-iso, short-full, short-monotonic, short-unix,\n" + " short-iso, short-iso-precise, short-full,\n" + " short-monotonic, short-unix,\n" " verbose, export, json, json-pretty, json-sse, cat)\n" " --firmware-setup Tell the firmware to show the setup menu on next boot\n" " --plain Print unit dependencies as a list instead of a tree\n\n" @@ -8245,12 +8334,14 @@ static int halt_now(enum action a) { switch (a) { case ACTION_HALT: - log_info("Halting."); + if (!arg_quiet) + log_info("Halting."); (void) reboot(RB_HALT_SYSTEM); return -errno; case ACTION_POWEROFF: - log_info("Powering off."); + if (!arg_quiet) + log_info("Powering off."); (void) reboot(RB_POWER_OFF); return -errno; @@ -8259,16 +8350,18 @@ static int halt_now(enum action a) { _cleanup_free_ char *param = NULL; r = read_one_line_file("/run/systemd/reboot-param", ¶m); - if (r < 0) + if (r < 0 && r != -ENOENT) log_warning_errno(r, "Failed to read reboot parameter file: %m"); if (!isempty(param)) { - log_info("Rebooting with argument '%s'.", param); + if (!arg_quiet) + log_info("Rebooting with argument '%s'.", param); (void) syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param); log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m"); } - log_info("Rebooting."); + if (!arg_quiet) + log_info("Rebooting."); (void) reboot(RB_AUTOBOOT); return -errno; } @@ -8280,7 +8373,7 @@ static int halt_now(enum action a) { static int logind_schedule_shutdown(void) { -#ifdef HAVE_LOGIND +#ifdef ENABLE_LOGIND _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; char date[FORMAT_TIMESTAMP_MAX]; const char *action; @@ -8329,7 +8422,8 @@ static int logind_schedule_shutdown(void) { if (r < 0) return log_warning_errno(r, "Failed to call ScheduleShutdown in logind, proceeding with immediate shutdown: %s", bus_error_message(&error, r)); - log_info("Shutdown scheduled for %s, use 'shutdown -c' to cancel.", format_timestamp(date, sizeof(date), arg_when)); + if (!arg_quiet) + log_info("Shutdown scheduled for %s, use 'shutdown -c' to cancel.", format_timestamp(date, sizeof(date), arg_when)); return 0; #else log_error("Cannot schedule shutdown without logind support, proceeding with immediate shutdown."); @@ -8408,7 +8502,7 @@ static int runlevel_main(void) { } static int logind_cancel_shutdown(void) { -#ifdef HAVE_LOGIND +#ifdef ENABLE_LOGIND _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus; int r; @@ -8457,7 +8551,9 @@ int main(int argc, char*argv[]) { goto finish; if (arg_action != ACTION_SYSTEMCTL && running_in_chroot() > 0) { - log_info("Running in chroot, ignoring request."); + + if (!arg_quiet) + log_info("Running in chroot, ignoring request."); r = 0; goto finish; } diff --git a/src/systemd/_sd-common.h b/src/systemd/_sd-common.h index 3bb886be75..97c3943861 100644 --- a/src/systemd/_sd-common.h +++ b/src/systemd/_sd-common.h @@ -22,8 +22,8 @@ /* This is a private header; never even think of including this directly! */ -#if __INCLUDE_LEVEL__ <= 1 -#error "Do not include _sd-common.h directly; it is a private header." +#if defined(__INCLUDE_LEVEL__) && __INCLUDE_LEVEL__ <= 1 +# error "Do not include _sd-common.h directly; it is a private header." #endif #ifndef _sd_printf_ diff --git a/src/systemd/meson.build b/src/systemd/meson.build new file mode 100644 index 0000000000..debbd46dff --- /dev/null +++ b/src/systemd/meson.build @@ -0,0 +1,59 @@ +_systemd_headers = ''' + sd-bus.h + sd-bus-protocol.h + sd-bus-vtable.h + sd-daemon.h + sd-event.h + sd-id128.h + sd-journal.h + sd-login.h + sd-messages.h +'''.split() + +# https://github.com/mesonbuild/meson/issues/1633 +systemd_headers = files(_systemd_headers) + +# sd-device.h +# sd-hwdb.h +# sd-dhcp6-client.h +# sd-dhcp6-lease.h +# sd-dhcp-client.h +# sd-dhcp-lease.h +# sd-dhcp-server.h +# sd-ipv4acd.h +# sd-ipv4ll.h +# sd-lldp.h +# sd-ndisc.h +# sd-netlink.h +# sd-network.h +# sd-path.h +# sd-resolve.h +# sd-utf8.h + +install_headers( + systemd_headers, + '_sd-common.h', + subdir : 'systemd') + + +############################################################ + +opts = [['c'], + ['c', '-ansi'], + ['c', '-std=iso9899:1990']] + +cxx = find_program('c++', required : false) +if cxx.found() + opts += [['c++']] +endif + +foreach header : _systemd_headers + foreach opt : opts + name = ''.join([header, ':'] + opt) + test('cc-' + name, + check_compilation_sh, + args : cc.cmd_array() + ['-c', '-x'] + opt + + ['-Werror', '-include', + join_paths(meson.current_source_dir(), header)]) + endforeach +endforeach diff --git a/src/systemd/sd-bus-vtable.h b/src/systemd/sd-bus-vtable.h index 3563a2b126..1e82cae038 100644 --- a/src/systemd/sd-bus-vtable.h +++ b/src/systemd/sd-bus-vtable.h @@ -131,6 +131,7 @@ struct sd_bus_vtable { .member = _member, \ .signature = _signature, \ .get = _get, \ + .set = NULL, \ .offset = _offset, \ }, \ }, \ @@ -154,6 +155,9 @@ struct sd_bus_vtable { #define SD_BUS_VTABLE_END \ { \ .type = _SD_BUS_VTABLE_END, \ + .flags = 0, \ + .x = { \ + }, \ } _SD_END_DECLARATIONS; diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h index c47459c9ad..2b6aeb7989 100644 --- a/src/systemd/sd-bus.h +++ b/src/systemd/sd-bus.h @@ -266,6 +266,7 @@ int sd_bus_message_set_destination(sd_bus_message *m, const char *destination); int sd_bus_message_set_priority(sd_bus_message *m, int64_t priority); int sd_bus_message_append(sd_bus_message *m, const char *types, ...); +int sd_bus_message_appendv(sd_bus_message *m, const char *types, va_list ap); int sd_bus_message_append_basic(sd_bus_message *m, char type, const void *p); int sd_bus_message_append_array(sd_bus_message *m, char type, const void *ptr, size_t size); int sd_bus_message_append_array_space(sd_bus_message *m, char type, size_t size, void **ptr); diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index ffe7f836de..f731fdcbd4 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -76,6 +76,7 @@ enum { SD_DHCP_OPTION_FQDN = 81, SD_DHCP_OPTION_NEW_POSIX_TIMEZONE = 100, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE = 101, + SD_DHCP_OPTION_DOMAIN_SEARCH_LIST = 119, SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121, SD_DHCP_OPTION_PRIVATE_BASE = 224, SD_DHCP_OPTION_PRIVATE_LAST = 254, diff --git a/src/systemd/sd-dhcp-lease.h b/src/systemd/sd-dhcp-lease.h index 2f565ca825..7ab99cccd1 100644 --- a/src/systemd/sd-dhcp-lease.h +++ b/src/systemd/sd-dhcp-lease.h @@ -49,6 +49,7 @@ int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr); int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr); int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu); int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname); +int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains); int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname); int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path); int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes); diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h index cc26b7df55..f8cb895660 100644 --- a/src/systemd/sd-event.h +++ b/src/systemd/sd-event.h @@ -69,7 +69,7 @@ typedef int (*sd_event_handler_t)(sd_event_source *s, void *userdata); typedef int (*sd_event_io_handler_t)(sd_event_source *s, int fd, uint32_t revents, void *userdata); typedef int (*sd_event_time_handler_t)(sd_event_source *s, uint64_t usec, void *userdata); typedef int (*sd_event_signal_handler_t)(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata); -#if defined __USE_POSIX199309 || defined __USE_XOPEN_EXTENDED +#if defined _GNU_SOURCE || _POSIX_C_SOURCE >= 199309L typedef int (*sd_event_child_handler_t)(sd_event_source *s, const siginfo_t *si, void *userdata); #else typedef void* sd_event_child_handler_t; diff --git a/src/systemd/sd-ipv4ll.h b/src/systemd/sd-ipv4ll.h index 1109ec52e0..5ba92083f4 100644 --- a/src/systemd/sd-ipv4ll.h +++ b/src/systemd/sd-ipv4ll.h @@ -47,6 +47,7 @@ int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int interface_index); int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address); int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed); int sd_ipv4ll_is_running(sd_ipv4ll *ll); +int sd_ipv4ll_restart(sd_ipv4ll *ll); int sd_ipv4ll_start(sd_ipv4ll *ll); int sd_ipv4ll_stop(sd_ipv4ll *ll); sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll); diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h index 7efa8ebe5a..3f5a6673cf 100644 --- a/src/systemd/sd-netlink.h +++ b/src/systemd/sd-netlink.h @@ -155,6 +155,10 @@ int sd_rtnl_message_neigh_get_ifindex(sd_netlink_message *m, int *family); int sd_rtnl_message_neigh_get_state(sd_netlink_message *m, uint16_t *state); int sd_rtnl_message_neigh_get_flags(sd_netlink_message *m, uint8_t *flags); +int sd_rtnl_message_new_addrlabel(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int ifindex, int ifal_family); +int sd_rtnl_message_addrlabel_set_prefixlen(sd_netlink_message *m, unsigned char prefixlen); +int sd_rtnl_message_addrlabel_get_prefixlen(sd_netlink_message *m, unsigned char *prefixlen); + _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink, sd_netlink_unref); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink_message, sd_netlink_message_unref); diff --git a/src/systemd/sd-radv.h b/src/systemd/sd-radv.h new file mode 100644 index 0000000000..4cbd80db68 --- /dev/null +++ b/src/systemd/sd-radv.h @@ -0,0 +1,81 @@ +#ifndef foosdradvfoo +#define foosdradvfoo + +/*** + This file is part of systemd. + + Copyright (C) 2017 Intel Corporation. All rights reserved. + + 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 <inttypes.h> +#include <net/ethernet.h> +#include <netinet/in.h> +#include <sys/types.h> + +#include "sd-ndisc.h" + +#include "sd-event.h" + +#include "_sd-common.h" + +_SD_BEGIN_DECLARATIONS; + +typedef struct sd_radv sd_radv; +typedef struct sd_radv_prefix sd_radv_prefix; + +/* Router Advertisment */ +int sd_radv_new(sd_radv **ret); +sd_radv *sd_radv_ref(sd_radv *ra); +sd_radv *sd_radv_unref(sd_radv *ra); + +int sd_radv_attach_event(sd_radv *ra, sd_event *event, int64_t priority); +int sd_radv_detach_event(sd_radv *nd); +sd_event *sd_radv_get_event(sd_radv *ra); + +int sd_radv_start(sd_radv *ra); +int sd_radv_stop(sd_radv *ra); + +int sd_radv_set_ifindex(sd_radv *ra, int interface_index); +int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr); +int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu); +int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit); +int sd_radv_set_router_lifetime(sd_radv *ra, uint32_t router_lifetime); +int sd_radv_set_managed_information(sd_radv *ra, int managed); +int sd_radv_set_other_information(sd_radv *ra, int other); +int sd_radv_set_preference(sd_radv *ra, unsigned preference); +int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p); + +/* Advertised prefixes */ +int sd_radv_prefix_new(sd_radv_prefix **ret); +sd_radv_prefix *sd_radv_prefix_ref(sd_radv_prefix *ra); +sd_radv_prefix *sd_radv_prefix_unref(sd_radv_prefix *ra); + +int sd_radv_prefix_set_prefix(sd_radv_prefix *p, struct in6_addr *in6_addr, + unsigned char prefixlen); +int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink); +int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p, + int address_autoconfiguration); +int sd_radv_prefix_set_valid_lifetime(sd_radv_prefix *p, + uint32_t valid_lifetime); +int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix *p, + uint32_t preferred_lifetime); + +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv, sd_radv_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv_prefix, sd_radv_prefix_unref); + +_SD_END_DECLARATIONS; + +#endif diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 4a0a49f2bb..fbe51a6ed5 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -29,6 +29,7 @@ #include "copy.h" #include "def.h" #include "fd-util.h" +#include "fs-util.h" #include "fileio-label.h" #include "format-util.h" #include "hashmap.h" @@ -292,6 +293,7 @@ static int putgrent_with_members(const struct group *gr, FILE *group) { return 0; } +#ifdef ENABLE_GSHADOW static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) { char **a; @@ -341,6 +343,7 @@ static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) { return 0; } +#endif static int sync_rights(FILE *from, FILE *to) { struct stat st; @@ -370,400 +373,424 @@ static int rename_and_apply_smack(const char *temp_path, const char *dest_path) return r; } -static int write_files(void) { - - _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL; - _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL; - const char *passwd_path = NULL, *group_path = NULL, *shadow_path = NULL, *gshadow_path = NULL; - bool group_changed = false; +static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char **tmpfile_path) { + _cleanup_fclose_ FILE *original = NULL, *passwd = NULL; + _cleanup_(unlink_and_freep) char *passwd_tmp = NULL; Iterator iterator; Item *i; int r; - if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) { - _cleanup_fclose_ FILE *original = NULL; + if (hashmap_size(todo_uids) == 0) + return 0; - /* First we update the actual group list file */ - group_path = prefix_roota(arg_root, "/etc/group"); - r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp); + r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp); + if (r < 0) + return r; + + original = fopen(passwd_path, "re"); + if (original) { + struct passwd *pw; + + r = sync_rights(original, passwd); if (r < 0) - goto finish; + return r; - original = fopen(group_path, "re"); - if (original) { - struct group *gr; + errno = 0; + while ((pw = fgetpwent(original))) { - r = sync_rights(original, group); - if (r < 0) - goto finish; + i = hashmap_get(users, pw->pw_name); + if (i && i->todo_user) { + log_error("%s: User \"%s\" already exists.", passwd_path, pw->pw_name); + return -EEXIST; + } + + if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) { + log_error("%s: Detected collision for UID " UID_FMT ".", passwd_path, pw->pw_uid); + return -EEXIST; + } errno = 0; - while ((gr = fgetgrent(original))) { - /* Safety checks against name and GID - * collisions. Normally, this should - * be unnecessary, but given that we - * look at the entries anyway here, - * let's make an extra verification - * step that we don't generate - * duplicate entries. */ - - i = hashmap_get(groups, gr->gr_name); - if (i && i->todo_group) { - log_error("%s: Group \"%s\" already exists.", group_path, gr->gr_name); - r = -EEXIST; - goto finish; - } + if (putpwent(pw, passwd) < 0) + return errno ? -errno : -EIO; - if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) { - log_error("%s: Detected collision for GID " GID_FMT ".", group_path, gr->gr_gid); - r = -EEXIST; - goto finish; - } + errno = 0; + } + if (!IN_SET(errno, 0, ENOENT)) + return -errno; - r = putgrent_with_members(gr, group); - if (r < 0) - goto finish; - if (r > 0) - group_changed = true; + } else { + if (errno != ENOENT) + return -errno; + if (fchmod(fileno(passwd), 0644) < 0) + return -errno; + } - errno = 0; - } - if (!IN_SET(errno, 0, ENOENT)) { - r = -errno; - goto finish; - } + HASHMAP_FOREACH(i, todo_uids, iterator) { + struct passwd n = { + .pw_name = i->name, + .pw_uid = i->uid, + .pw_gid = i->gid, + .pw_gecos = i->description, - } else if (errno != ENOENT) { - r = -errno; - goto finish; - } else if (fchmod(fileno(group), 0644) < 0) { - r = -errno; - goto finish; - } + /* "x" means the password is stored in the shadow file */ + .pw_passwd = (char*) "x", - HASHMAP_FOREACH(i, todo_gids, iterator) { - struct group n = { - .gr_name = i->name, - .gr_gid = i->gid, - .gr_passwd = (char*) "x", - }; + /* We default to the root directory as home */ + .pw_dir = i->home ? i->home : (char*) "/", - r = putgrent_with_members(&n, group); - if (r < 0) - goto finish; + /* Initialize the shell to nologin, with one exception: + * for root we patch in something special */ + .pw_shell = i->uid == 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin", + }; - group_changed = true; - } + errno = 0; + if (putpwent(&n, passwd) != 0) + return errno ? -errno : -EIO; + } - r = fflush_and_check(group); - if (r < 0) - goto finish; + r = fflush_and_check(passwd); + if (r < 0) + return r; - if (original) { - fclose(original); - original = NULL; - } + *tmpfile = passwd; + *tmpfile_path = passwd_tmp; + passwd = NULL; + passwd_tmp = NULL; + return 0; +} - /* OK, now also update the shadow file for the group list */ - gshadow_path = prefix_roota(arg_root, "/etc/gshadow"); - r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp); - if (r < 0) - goto finish; +static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char **tmpfile_path) { + _cleanup_fclose_ FILE *original = NULL, *shadow = NULL; + _cleanup_(unlink_and_freep) char *shadow_tmp = NULL; + Iterator iterator; + long lstchg; + Item *i; + int r; - original = fopen(gshadow_path, "re"); - if (original) { - struct sgrp *sg; + if (hashmap_size(todo_uids) == 0) + return 0; - r = sync_rights(original, gshadow); - if (r < 0) - goto finish; + r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp); + if (r < 0) + return r; - errno = 0; - while ((sg = fgetsgent(original))) { + lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); - i = hashmap_get(groups, sg->sg_namp); - if (i && i->todo_group) { - log_error("%s: Group \"%s\" already exists.", gshadow_path, sg->sg_namp); - r = -EEXIST; - goto finish; - } + original = fopen(shadow_path, "re"); + if (original) { + struct spwd *sp; - r = putsgent_with_members(sg, gshadow); - if (r < 0) - goto finish; - if (r > 0) - group_changed = true; + r = sync_rights(original, shadow); + if (r < 0) + return r; - errno = 0; - } - if (!IN_SET(errno, 0, ENOENT)) { - r = -errno; - goto finish; - } + errno = 0; + while ((sp = fgetspent(original))) { - } else if (errno != ENOENT) { - r = -errno; - goto finish; - } else if (fchmod(fileno(gshadow), 0000) < 0) { - r = -errno; - goto finish; - } + i = hashmap_get(users, sp->sp_namp); + if (i && i->todo_user) { + /* we will update the existing entry */ + sp->sp_lstchg = lstchg; - HASHMAP_FOREACH(i, todo_gids, iterator) { - struct sgrp n = { - .sg_namp = i->name, - .sg_passwd = (char*) "!!", - }; + /* only the /etc/shadow stage is left, so we can + * safely remove the item from the todo set */ + i->todo_user = false; + hashmap_remove(todo_uids, UID_TO_PTR(i->uid)); + } - r = putsgent_with_members(&n, gshadow); - if (r < 0) - goto finish; + errno = 0; + if (putspent(sp, shadow) < 0) + return errno ? -errno : -EIO; - group_changed = true; + errno = 0; } + if (!IN_SET(errno, 0, ENOENT)) + return -errno; - r = fflush_and_check(gshadow); - if (r < 0) - goto finish; + } else { + if (errno != ENOENT) + return -errno; + if (fchmod(fileno(shadow), 0000) < 0) + return -errno; } - if (hashmap_size(todo_uids) > 0) { - _cleanup_fclose_ FILE *original = NULL; - long lstchg; - - /* First we update the user database itself */ - passwd_path = prefix_roota(arg_root, "/etc/passwd"); - r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp); - if (r < 0) - goto finish; - - original = fopen(passwd_path, "re"); - if (original) { - struct passwd *pw; + HASHMAP_FOREACH(i, todo_uids, iterator) { + struct spwd n = { + .sp_namp = i->name, + .sp_pwdp = (char*) "!!", + .sp_lstchg = lstchg, + .sp_min = -1, + .sp_max = -1, + .sp_warn = -1, + .sp_inact = -1, + .sp_expire = -1, + .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */ + }; - r = sync_rights(original, passwd); - if (r < 0) - goto finish; + errno = 0; + if (putspent(&n, shadow) != 0) + return errno ? -errno : -EIO; + } - errno = 0; - while ((pw = fgetpwent(original))) { + r = fflush_and_check(shadow); + if (r < 0) + return r; - i = hashmap_get(users, pw->pw_name); - if (i && i->todo_user) { - log_error("%s: User \"%s\" already exists.", passwd_path, pw->pw_name); - r = -EEXIST; - goto finish; - } + *tmpfile = shadow; + *tmpfile_path = shadow_tmp; + shadow = NULL; + shadow_tmp = NULL; + return 0; +} - if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) { - log_error("%s: Detected collision for UID " UID_FMT ".", passwd_path, pw->pw_uid); - r = -EEXIST; - goto finish; - } +static int write_temporary_group(const char *group_path, FILE **tmpfile, char **tmpfile_path) { + _cleanup_fclose_ FILE *original = NULL, *group = NULL; + _cleanup_(unlink_and_freep) char *group_tmp = NULL; + bool group_changed = false; + Iterator iterator; + Item *i; + int r; - errno = 0; - if (putpwent(pw, passwd) < 0) { - r = errno ? -errno : -EIO; - goto finish; - } + if (hashmap_size(todo_gids) == 0 && hashmap_size(members) == 0) + return 0; - errno = 0; - } - if (!IN_SET(errno, 0, ENOENT)) { - r = -errno; - goto finish; - } + r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp); + if (r < 0) + return r; - } else if (errno != ENOENT) { - r = -errno; - goto finish; - } else if (fchmod(fileno(passwd), 0644) < 0) { - r = -errno; - goto finish; - } + original = fopen(group_path, "re"); + if (original) { + struct group *gr; - HASHMAP_FOREACH(i, todo_uids, iterator) { - struct passwd n = { - .pw_name = i->name, - .pw_uid = i->uid, - .pw_gid = i->gid, - .pw_gecos = i->description, + r = sync_rights(original, group); + if (r < 0) + return r; - /* "x" means the password is stored in - * the shadow file */ - .pw_passwd = (char*) "x", + errno = 0; + while ((gr = fgetgrent(original))) { + /* Safety checks against name and GID collisions. Normally, + * this should be unnecessary, but given that we look at the + * entries anyway here, let's make an extra verification + * step that we don't generate duplicate entries. */ + + i = hashmap_get(groups, gr->gr_name); + if (i && i->todo_group) { + log_error("%s: Group \"%s\" already exists.", group_path, gr->gr_name); + return -EEXIST; + } - /* We default to the root directory as home */ - .pw_dir = i->home ? i->home : (char*) "/", + if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) { + log_error("%s: Detected collision for GID " GID_FMT ".", group_path, gr->gr_gid); + return -EEXIST; + } - /* Initialize the shell to nologin, - * with one exception: for root we - * patch in something special */ - .pw_shell = i->uid == 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin", - }; + r = putgrent_with_members(gr, group); + if (r < 0) + return r; + if (r > 0) + group_changed = true; errno = 0; - if (putpwent(&n, passwd) != 0) { - r = errno ? -errno : -EIO; - goto finish; - } } + if (!IN_SET(errno, 0, ENOENT)) + return -errno; - r = fflush_and_check(passwd); - if (r < 0) - goto finish; + } else { + if (errno != ENOENT) + return -errno; + if (fchmod(fileno(group), 0644) < 0) + return -errno; + } - if (original) { - fclose(original); - original = NULL; - } + HASHMAP_FOREACH(i, todo_gids, iterator) { + struct group n = { + .gr_name = i->name, + .gr_gid = i->gid, + .gr_passwd = (char*) "x", + }; - /* The we update the shadow database */ - shadow_path = prefix_roota(arg_root, "/etc/shadow"); - r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp); + r = putgrent_with_members(&n, group); if (r < 0) - goto finish; + return r; - lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); + group_changed = true; + } - original = fopen(shadow_path, "re"); - if (original) { - struct spwd *sp; + r = fflush_and_check(group); + if (r < 0) + return r; - r = sync_rights(original, shadow); - if (r < 0) - goto finish; + if (group_changed) { + *tmpfile = group; + *tmpfile_path = group_tmp; + group = NULL; + group_tmp = NULL; + } + return 0; +} - errno = 0; - while ((sp = fgetspent(original))) { +static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, char **tmpfile_path) { +#ifdef ENABLE_GSHADOW + _cleanup_fclose_ FILE *original = NULL, *gshadow = NULL; + _cleanup_(unlink_and_freep) char *gshadow_tmp = NULL; + bool group_changed = false; + Iterator iterator; + Item *i; + int r; - i = hashmap_get(users, sp->sp_namp); - if (i && i->todo_user) { - /* we will update the existing entry */ - sp->sp_lstchg = lstchg; + if (hashmap_size(todo_gids) == 0 && hashmap_size(members) == 0) + return 0; - /* only the /etc/shadow stage is left, so we can - * safely remove the item from the todo set */ - i->todo_user = false; - hashmap_remove(todo_uids, UID_TO_PTR(i->uid)); - } + r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp); + if (r < 0) + return r; - errno = 0; - if (putspent(sp, shadow) < 0) { - r = errno ? -errno : -EIO; - goto finish; - } + original = fopen(gshadow_path, "re"); + if (original) { + struct sgrp *sg; - errno = 0; - } - if (!IN_SET(errno, 0, ENOENT)) { - r = -errno; - goto finish; + r = sync_rights(original, gshadow); + if (r < 0) + return r; + + errno = 0; + while ((sg = fgetsgent(original))) { + + i = hashmap_get(groups, sg->sg_namp); + if (i && i->todo_group) { + log_error("%s: Group \"%s\" already exists.", gshadow_path, sg->sg_namp); + return -EEXIST; } - } else if (errno != ENOENT) { - r = -errno; - goto finish; - } else if (fchmod(fileno(shadow), 0000) < 0) { - r = -errno; - goto finish; - } - HASHMAP_FOREACH(i, todo_uids, iterator) { - struct spwd n = { - .sp_namp = i->name, - .sp_pwdp = (char*) "!!", - .sp_lstchg = lstchg, - .sp_min = -1, - .sp_max = -1, - .sp_warn = -1, - .sp_inact = -1, - .sp_expire = -1, - .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */ - }; + r = putsgent_with_members(sg, gshadow); + if (r < 0) + return r; + if (r > 0) + group_changed = true; errno = 0; - if (putspent(&n, shadow) != 0) { - r = errno ? -errno : -EIO; - goto finish; - } } + if (!IN_SET(errno, 0, ENOENT)) + return -errno; + + } else { + if (errno != ENOENT) + return -errno; + if (fchmod(fileno(gshadow), 0000) < 0) + return -errno; + } + + HASHMAP_FOREACH(i, todo_gids, iterator) { + struct sgrp n = { + .sg_namp = i->name, + .sg_passwd = (char*) "!!", + }; - r = fflush_and_check(shadow); + r = putsgent_with_members(&n, gshadow); if (r < 0) - goto finish; + return r; + + group_changed = true; } - /* Make a backup of the old files */ + r = fflush_and_check(gshadow); + if (r < 0) + return r; + if (group_changed) { - if (group) { - r = make_backup("/etc/group", group_path); - if (r < 0) - goto finish; - } - if (gshadow) { - r = make_backup("/etc/gshadow", gshadow_path); - if (r < 0) - goto finish; - } + *tmpfile = gshadow; + *tmpfile_path = gshadow_tmp; + gshadow = NULL; + gshadow_tmp = NULL; + } + return 0; +#else + return 0; +#endif +} + +static int write_files(void) { + _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL; + _cleanup_(unlink_and_freep) char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL; + const char *passwd_path = NULL, *group_path = NULL, *shadow_path = NULL, *gshadow_path = NULL; + int r; + + passwd_path = prefix_roota(arg_root, "/etc/passwd"); + shadow_path = prefix_roota(arg_root, "/etc/shadow"); + group_path = prefix_roota(arg_root, "/etc/group"); + gshadow_path = prefix_roota(arg_root, "/etc/gshadow"); + + r = write_temporary_group(group_path, &group, &group_tmp); + if (r < 0) + return r; + + r = write_temporary_gshadow(gshadow_path, &gshadow, &gshadow_tmp); + if (r < 0) + return r; + + r = write_temporary_passwd(passwd_path, &passwd, &passwd_tmp); + if (r < 0) + return r; + + r = write_temporary_shadow(shadow_path, &shadow, &shadow_tmp); + if (r < 0) + return r; + + /* Make a backup of the old files */ + if (group) { + r = make_backup("/etc/group", group_path); + if (r < 0) + return r; + } + if (gshadow) { + r = make_backup("/etc/gshadow", gshadow_path); + if (r < 0) + return r; } if (passwd) { r = make_backup("/etc/passwd", passwd_path); if (r < 0) - goto finish; + return r; } if (shadow) { r = make_backup("/etc/shadow", shadow_path); if (r < 0) - goto finish; + return r; } /* And make the new files count */ - if (group_changed) { - if (group) { - r = rename_and_apply_smack(group_tmp, group_path); - if (r < 0) - goto finish; + if (group) { + r = rename_and_apply_smack(group_tmp, group_path); + if (r < 0) + return r; - group_tmp = mfree(group_tmp); - } - if (gshadow) { - r = rename_and_apply_smack(gshadow_tmp, gshadow_path); - if (r < 0) - goto finish; + group_tmp = mfree(group_tmp); + } + if (gshadow) { + r = rename_and_apply_smack(gshadow_tmp, gshadow_path); + if (r < 0) + return r; - gshadow_tmp = mfree(gshadow_tmp); - } + gshadow_tmp = mfree(gshadow_tmp); } if (passwd) { r = rename_and_apply_smack(passwd_tmp, passwd_path); if (r < 0) - goto finish; + return r; passwd_tmp = mfree(passwd_tmp); } if (shadow) { r = rename_and_apply_smack(shadow_tmp, shadow_path); if (r < 0) - goto finish; + return r; shadow_tmp = mfree(shadow_tmp); } - r = 0; - -finish: - if (passwd_tmp) - unlink(passwd_tmp); - if (shadow_tmp) - unlink(shadow_tmp); - if (group_tmp) - unlink(group_tmp); - if (gshadow_tmp) - unlink(gshadow_tmp); - - return r; + return 0; } static int uid_is_ok(uid_t uid, const char *name) { diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c index 9fde9b1884..9828078443 100644 --- a/src/sysv-generator/sysv-generator.c +++ b/src/sysv-generator/sysv-generator.c @@ -389,6 +389,9 @@ static int handle_provides(SysvStub *s, unsigned line, const char *full_text, co r = strv_extend(&s->before, SPECIAL_NETWORK_TARGET); if (r < 0) return log_oom(); + r = strv_extend(&s->wants, SPECIAL_NETWORK_TARGET); + if (r < 0) + return log_oom(); } break; diff --git a/src/test/generate-sym-test.py b/src/test/generate-sym-test.py new file mode 100755 index 0000000000..357cce8e44 --- /dev/null +++ b/src/test/generate-sym-test.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +import sys, re + +print('#include <stdio.h>') +for header in sys.argv[2:]: + print('#include "{}"'.format(header.split('/')[-1])) + +print(''' +void* functions[] = {''') + +for line in open(sys.argv[1]): + match = re.search('^ +([a-zA-Z0-9_]+);', line) + if match: + print(' {},'.format(match.group(1))) + +print('''}; + +int main(void) { + unsigned i; + for (i = 0; i < sizeof(functions)/sizeof(void*); i++) + printf("%p\\n", functions[i]); + return 0; +}''') diff --git a/src/test/meson.build b/src/test/meson.build new file mode 100644 index 0000000000..7b493a4d05 --- /dev/null +++ b/src/test/meson.build @@ -0,0 +1,906 @@ +awkscript = 'test-hashmap-ordered.awk' +test_hashmap_ordered_c = custom_target( + 'test-hashmap-ordered.c', + input : [awkscript, 'test-hashmap-plain.c'], + output : 'test-hashmap-ordered.c', + command : [awk, '-f', '@INPUT0@', '@INPUT1@'], + capture : true) + +test_include_dir = include_directories('.') + +path = run_command('sh', ['-c', 'echo "$PATH"']).stdout() +test_env = environment() +test_env.set('SYSTEMD_KBD_MODEL_MAP', kbd_model_map) +test_env.set('SYSTEMD_LANGUAGE_FALLBACK_MAP', language_fallback_map) +test_env.set('PATH', path) +test_env.prepend('PATH', meson.build_root()) + +############################################################ + +generate_sym_test_py = find_program('generate-sym-test.py') + +test_libsystemd_sym_c = custom_target( + 'test-libsystemd-sym.c', + input : [libsystemd_sym_path] + systemd_headers, + output : 'test-libsystemd-sym.c', + command : [generate_sym_test_py, libsystemd_sym_path] + systemd_headers, + capture : true) + +test_libudev_sym_c = custom_target( + 'test-libudev-sym.c', + input : [libudev_sym_path, libudev_h_path], + output : 'test-libudev-sym.c', + command : [generate_sym_test_py, '@INPUT0@', '@INPUT1@'], + capture : true) + +test_dlopen_c = files('test-dlopen.c') + +############################################################ + +tests += [ + [['src/test/test-device-nodes.c'], + [], + []], + + [['src/test/test-engine.c'], + [libcore, + libudev, + libsystemd_internal], + [threads, + librt, + libseccomp, + libselinux, + libmount, + libblkid]], + + [['src/test/test-job-type.c'], + [libcore, + libshared], + [threads, + librt, + libseccomp, + libselinux, + libmount, + libblkid]], + + [['src/test/test-ns.c'], + [libcore, + libshared], + [threads, + librt, + libseccomp, + libselinux, + libmount, + libblkid], + '', 'manual'], + + [['src/test/test-loopback.c'], + [libcore, + libshared], + [threads, + librt, + libseccomp, + libselinux, + libmount, + libblkid]], + + [['src/test/test-hostname.c'], + [libcore, + libshared], + [threads, + librt, + libseccomp, + libselinux, + libmount, + libblkid], + '', 'unsafe'], + + [['src/test/test-dns-domain.c'], + [libcore, + libsystemd_network], + []], + + [['src/test/test-boot-timestamps.c'], + [], + [], + 'ENABLE_EFI'], + + [['src/test/test-unit-name.c'], + [libcore, + libshared], + [threads, + librt, + libseccomp, + libselinux, + libmount, + libblkid]], + + [['src/test/test-unit-file.c'], + [libcore, + libshared], + [threads, + librt, + libseccomp, + libselinux, + libmount, + libblkid]], + + [['src/test/test-utf8.c'], + [], + []], + + [['src/test/test-capability.c'], + [], + [libcap]], + + [['src/test/test-async.c'], + [], + []], + + [['src/test/test-locale-util.c'], + [], + []], + + [['src/test/test-copy.c'], + [libshared_static], + []], + + [['src/test/test-sigbus.c'], + [], + []], + + [['src/test/test-condition.c'], + [], + []], + + [['src/test/test-fdset.c'], + [], + []], + + [['src/test/test-fstab-util.c'], + [], + []], + + [['src/test/test-random-util.c'], + [], + []], + + [['src/test/test-ratelimit.c'], + [], + []], + + [['src/test/test-util.c'], + [], + []], + + [['src/test/test-mount-util.c'], + [], + []], + + [['src/test/test-exec-util.c'], + [], + []], + + [['src/test/test-hexdecoct.c'], + [], + []], + + [['src/test/test-alloc-util.c'], + [], + []], + + [['src/test/test-xattr-util.c'], + [], + []], + + [['src/test/test-io-util.c'], + [], + []], + + [['src/test/test-glob-util.c'], + [], + []], + + [['src/test/test-fs-util.c'], + [], + []], + + [['src/test/test-proc-cmdline.c'], + [], + []], + + [['src/test/test-fd-util.c'], + [], + []], + + [['src/test/test-web-util.c'], + [], + []], + + [['src/test/test-cpu-set-util.c'], + [], + []], + + [['src/test/test-stat-util.c'], + [], + []], + + [['src/test/test-escape.c'], + [], + []], + + [['src/test/test-string-util.c'], + [], + []], + + [['src/test/test-extract-word.c'], + [], + []], + + [['src/test/test-parse-util.c'], + [], + []], + + [['src/test/test-user-util.c'], + [], + []], + + [['src/test/test-hostname-util.c'], + [], + []], + + [['src/test/test-process-util.c'], + [], + []], + + [['src/test/test-terminal-util.c'], + [], + []], + + [['src/test/test-path-lookup.c'], + [], + []], + + [['src/test/test-uid-range.c'], + [], + []], + + [['src/test/test-cap-list.c', + generated_gperf_headers], + [], + [libcap]], + + [['src/test/test-socket-util.c'], + [], + []], + + [['src/test/test-barrier.c'], + [], + []], + + [['src/test/test-tmpfiles.c'], + [], + []], + + [['src/test/test-namespace.c'], + [libcore, + libshared], + [threads, + libblkid]], + + [['src/test/test-verbs.c'], + [], + []], + + [['src/test/test-install-root.c'], + [], + []], + + [['src/test/test-acl-util.c'], + [], + [], + 'HAVE_ACL'], + + [['src/test/test-seccomp.c'], + [], + [libseccomp], + 'HAVE_SECCOMP'], + + [['src/test/test-rlimit-util.c'], + [], + []], + + [['src/test/test-ask-password-api.c'], + [], + [], + '', 'manual'], + + [['src/test/test-dissect-image.c'], + [], + [libblkid], + '', 'manual'], + + [['src/test/test-signal-util.c'], + [], + []], + + [['src/test/test-selinux.c'], + [], + []], + + [['src/test/test-sizeof.c'], + [libbasic], + []], + + [['src/test/test-hashmap.c', + 'src/test/test-hashmap-plain.c', + test_hashmap_ordered_c], + [], + [], + '', 'timeout=90'], + + [['src/test/test-set.c'], + [], + []], + + [['src/test/test-bitmap.c'], + [], + []], + + [['src/test/test-xml.c'], + [], + []], + + [['src/test/test-list.c'], + [], + []], + + [['src/test/test-unaligned.c'], + [], + []], + + [['src/test/test-tables.c', + 'src/shared/test-tables.h', + 'src/journal/journald-server.c', + 'src/journal/journald-server.h'], + [libcore, + libjournal_core, + libudev_core, + libudev_internal, + libsystemd_network, + libshared], + [threads, + libseccomp, + libmount, + libxz, + liblz4, + libblkid], + '', '', [], libudev_core_includes], + + [['src/test/test-prioq.c'], + [], + []], + + [['src/test/test-fileio.c'], + [], + []], + + [['src/test/test-time.c'], + [], + []], + + [['src/test/test-clock.c'], + [], + []], + + [['src/test/test-architecture.c'], + [], + []], + + [['src/test/test-log.c'], + [], + []], + + [['src/test/test-ipcrm.c'], + [], + [], + '', 'unsafe'], + + [['src/test/test-btrfs.c'], + [], + [], + '', 'manual'], + + + [['src/test/test-firewall-util.c'], + [libshared], + [], + 'HAVE_LIBIPTC'], + + [['src/test/test-netlink-manual.c'], + [], + [libkmod], + 'HAVE_KMOD', 'manual'], + + [['src/test/test-ellipsize.c'], + [], + []], + + [['src/test/test-date.c'], + [], + []], + + [['src/test/test-sleep.c'], + [], + []], + + [['src/test/test-replace-var.c'], + [], + []], + + [['src/test/test-calendarspec.c'], + [], + []], + + [['src/test/test-strip-tab-ansi.c'], + [], + []], + + [['src/test/test-daemon.c'], + [], + []], + + [['src/test/test-cgroup.c'], + [], + [], + '', 'manual'], + + + [['src/test/test-cgroup-mask.c'], + [libcore, + libshared], + [threads, + librt, + libseccomp, + libselinux, + libmount, + libblkid]], + + [['src/test/test-cgroup-util.c'], + [], + []], + + [['src/test/test-env-util.c'], + [], + []], + + [['src/test/test-strbuf.c'], + [], + []], + + [['src/test/test-strv.c'], + [], + []], + + [['src/test/test-path-util.c'], + [], + []], + + [['src/test/test-path.c'], + [libcore, + libshared], + [threads, + librt, + libseccomp, + libselinux, + libmount, + libblkid]], + + [['src/test/test-execute.c'], + [libcore, + libshared], + [threads, + librt, + libseccomp, + libselinux, + libmount, + libblkid]], + + [['src/test/test-siphash24.c'], + [], + []], + + [['src/test/test-strxcpyx.c'], + [], + []], + + [['src/test/test-install.c'], + [libcore, + libshared], + [], + '', 'manual'], + + [['src/test/test-watchdog.c'], + [], + []], + + [['src/test/test-sched-prio.c'], + [libcore, + libshared], + [threads, + librt, + libseccomp, + libselinux, + libmount, + libblkid]], + + [['src/test/test-conf-files.c'], + [], + []], + + [['src/test/test-conf-parser.c'], + [], + []], + + [['src/test/test-af-list.c', + generated_gperf_headers], + [], + []], + + [['src/test/test-arphrd-list.c', + generated_gperf_headers], + [], + []], + + [['src/test/test-journal-importer.c'], + [], + []], + + [['src/test/test-libudev.c'], + [libshared], + []], + + [['src/test/test-udev.c'], + [libudev_core, + libudev_internal, + libsystemd_network, + libshared], + [threads, + librt, + libblkid, + libkmod, + libacl], + '', 'manual'], + + [['src/test/test-id128.c'], + [], + []], + + [['src/test/test-hash.c'], + [], + []], + + [['src/test/test-nss.c'], + [], + [libdl], + '', 'manual'], +] + +############################################################ + +# define some tests here, because the link_with deps were not defined earlier + +tests += [ + [['src/journal/test-journal.c'], + [libjournal_core, + libshared], + [threads, + libxz, + liblz4]], + + [['src/journal/test-journal-send.c'], + [libjournal_core, + libshared], + [threads, + libxz, + liblz4]], + + [['src/journal/test-journal-syslog.c'], + [libjournal_core, + libshared], + [threads, + libxz, + liblz4, + libselinux]], + + [['src/journal/test-journal-match.c'], + [libjournal_core, + libshared], + [threads, + libxz, + liblz4]], + + [['src/journal/test-journal-enum.c'], + [libjournal_core, + libshared], + [threads, + libxz, + liblz4]], + + [['src/journal/test-journal-stream.c'], + [libjournal_core, + libshared], + [threads, + libxz, + liblz4]], + + [['src/journal/test-journal-flush.c'], + [libjournal_core, + libshared], + [threads, + libxz, + liblz4]], + + [['src/journal/test-journal-init.c'], + [libjournal_core, + libshared], + [threads, + libxz, + liblz4]], + + [['src/journal/test-journal-verify.c'], + [libjournal_core, + libshared], + [threads, + libxz, + liblz4]], + + [['src/journal/test-journal-interleaving.c'], + [libjournal_core, + libshared], + [threads, + libxz, + liblz4]], + + [['src/journal/test-mmap-cache.c'], + [libjournal_core, + libshared], + [threads, + libxz, + liblz4]], + + [['src/journal/test-catalog.c'], + [libjournal_core, + libshared], + [threads, + libxz, + liblz4], + '', '', '-DCATALOG_DIR="@0@"'.format(build_catalog_dir)], + + [['src/journal/test-compress.c'], + [libjournal_core, + libshared], + [liblz4, + libxz]], + + [['src/journal/test-compress-benchmark.c'], + [libjournal_core, + libshared], + [liblz4, + libxz], + '', 'timeout=90'], + + [['src/journal/test-audit-type.c'], + [libjournal_core, + libshared], + [liblz4, + libxz]], +] + +############################################################ + +tests += [ + [['src/libsystemd/sd-bus/test-bus-marshal.c'], + [], + [threads, + libglib, + libgobject, + libgio, + libdbus]], + + [['src/libsystemd/sd-bus/test-bus-signature.c'], + [], + [threads]], + + [['src/libsystemd/sd-bus/test-bus-chat.c'], + [], + [threads]], + + [['src/libsystemd/sd-bus/test-bus-cleanup.c'], + [], + [threads, + libseccomp]], + + [['src/libsystemd/sd-bus/test-bus-error.c'], + [libshared_static, + libsystemd_internal], + []], + + [['src/libsystemd/sd-bus/test-bus-track.c'], + [], + [libseccomp]], + + [['src/libsystemd/sd-bus/test-bus-server.c'], + [], + [threads]], + + [['src/libsystemd/sd-bus/test-bus-objects.c'], + [], + [threads]], + + [['src/libsystemd/sd-bus/test-bus-vtable.c'], + [], + []], + + [['src/libsystemd/sd-bus/test-bus-gvariant.c'], + [], + [libglib, + libgobject, + libgio]], + + [['src/libsystemd/sd-bus/test-bus-creds.c'], + [], + []], + + [['src/libsystemd/sd-bus/test-bus-match.c'], + [], + []], + + [['src/libsystemd/sd-bus/test-bus-kernel.c'], + [], + []], + + [['src/libsystemd/sd-bus/test-bus-kernel-bloom.c'], + [], + []], + + [['src/libsystemd/sd-bus/test-bus-benchmark.c'], + [], + [threads]], + + [['src/libsystemd/sd-bus/test-bus-zero-copy.c'], + [], + []], + + [['src/libsystemd/sd-bus/test-bus-introspect.c'], + [], + []], + + [['src/libsystemd/sd-event/test-event.c'], + [], + []], + + [['src/libsystemd/sd-netlink/test-netlink.c'], + [], + []], + + [['src/libsystemd/sd-netlink/test-local-addresses.c'], + [], + []], + + [['src/libsystemd/sd-resolve/test-resolve.c'], + [], + [threads]], + + [['src/libsystemd/sd-login/test-login.c'], + [], + []], +] + +if cxx.found() + tests += [ + [['src/libsystemd/sd-bus/test-bus-vtable-cc.cc'], + [], + []] + ] +endif + +############################################################ + +tests += [ + [['src/libsystemd-network/test-dhcp-option.c', + 'src/libsystemd-network/dhcp-protocol.h', + 'src/libsystemd-network/dhcp-internal.h'], + [libshared, + libsystemd_network], + []], + + [['src/libsystemd-network/test-sd-dhcp-lease.c', + 'src/libsystemd-network/dhcp-lease-internal.h'], + [libshared, + libsystemd_network], + []], + + [['src/libsystemd-network/test-dhcp-client.c', + 'src/libsystemd-network/dhcp-protocol.h', + 'src/libsystemd-network/dhcp-internal.h', + 'src/systemd/sd-dhcp-client.h'], + [libshared, + libsystemd_network], + []], + + [['src/libsystemd-network/test-dhcp-server.c'], + [libshared, + libsystemd_network], + []], + + [['src/libsystemd-network/test-ipv4ll.c', + 'src/libsystemd-network/arp-util.h', + 'src/systemd/sd-ipv4ll.h'], + [libshared, + libsystemd_network], + []], + + [['src/libsystemd-network/test-ipv4ll-manual.c', + 'src/systemd/sd-ipv4ll.h'], + [libshared, + libsystemd_network], + [], + '', 'manual'], + + [['src/libsystemd-network/test-acd.c', + 'src/systemd/sd-ipv4acd.h'], + [libshared, + libsystemd_network], + [], + '', 'manual'], + + [['src/libsystemd-network/test-ndisc-rs.c', + 'src/libsystemd-network/dhcp-identifier.h', + 'src/libsystemd-network/dhcp-identifier.c', + 'src/libsystemd-network/icmp6-util.h', + 'src/systemd/sd-dhcp6-client.h', + 'src/systemd/sd-ndisc.h'], + [libshared, + libsystemd_network], + []], + + [['src/libsystemd-network/test-ndisc-ra.c', + 'src/libsystemd-network/icmp6-util.h', + 'src/systemd/sd-ndisc.h'], + [libshared, + libsystemd_network], + []], + + [['src/libsystemd-network/test-dhcp6-client.c', + 'src/libsystemd-network/dhcp-identifier.h', + 'src/libsystemd-network/dhcp-identifier.c', + 'src/libsystemd-network/dhcp6-internal.h', + 'src/systemd/sd-dhcp6-client.h'], + [libshared, + libsystemd_network], + []], + + [['src/libsystemd-network/test-lldp.c'], + [libshared, + libsystemd_network], + []], +] + +############################################################ + +tests += [ + [['src/login/test-login-shared.c'], + [], + []], + + [['src/login/test-inhibit.c'], + [], + [], + '', 'manual'], + + [['src/login/test-login-tables.c'], + [liblogind_core, + libshared], + [threads]], +] diff --git a/src/test/test-af-list.c b/src/test/test-af-list.c index e5ca54c8e7..bbaf18b08a 100644 --- a/src/test/test-af-list.c +++ b/src/test/test-af-list.c @@ -24,7 +24,7 @@ #include "string-util.h" #include "util.h" -_unused_ \ +_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-calendarspec.c b/src/test/test-calendarspec.c index f90b73aeaf..a026ce4ef1 100644 --- a/src/test/test-calendarspec.c +++ b/src/test/test-calendarspec.c @@ -193,6 +193,11 @@ int main(int argc, char* argv[]) { 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"); + /* UNIX timestamps are always UTC */ + test_one("@1493187147", "2017-04-26 06:12:27 UTC"); + test_one("@1493187147 UTC", "2017-04-26 06:12:27 UTC"); + test_one("@0", "1970-01-01 00:00:00 UTC"); + test_one("@0 UTC", "1970-01-01 00:00:00 UTC"); 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-condition.c b/src/test/test-condition.c index dd985f5863..121345cfd1 100644 --- a/src/test/test-condition.c +++ b/src/test/test-condition.c @@ -17,6 +17,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> + #include "sd-id128.h" #include "alloc-util.h" @@ -34,6 +38,7 @@ #include "strv.h" #include "virt.h" #include "util.h" +#include "user-util.h" static void test_condition_test_path(void) { Condition *condition; @@ -243,7 +248,7 @@ static void test_condition_test_security(void) { condition = condition_new(CONDITION_SECURITY, "selinux", false, true); assert_se(condition); - assert_se(condition_test(condition) != mac_selinux_have()); + assert_se(condition_test(condition) != mac_selinux_use()); condition_free(condition); condition = condition_new(CONDITION_SECURITY, "ima", false, false); @@ -323,6 +328,150 @@ static void test_condition_test_virtualization(void) { } } +static void test_condition_test_user(void) { + Condition *condition; + char* uid; + char* username; + int r; + + condition = condition_new(CONDITION_USER, "garbage oifdsjfoidsjoj", false, false); + assert_se(condition); + r = condition_test(condition); + log_info("ConditionUser=garbage → %i", r); + assert_se(r == 0); + condition_free(condition); + + assert_se(asprintf(&uid, "%"PRIu32, UINT32_C(0xFFFF)) > 0); + condition = condition_new(CONDITION_USER, uid, false, false); + assert_se(condition); + r = condition_test(condition); + log_info("ConditionUser=%s → %i", uid, r); + assert_se(r == 0); + condition_free(condition); + free(uid); + + assert_se(asprintf(&uid, "%u", (unsigned)getuid()) > 0); + condition = condition_new(CONDITION_USER, uid, false, false); + assert_se(condition); + r = condition_test(condition); + log_info("ConditionUser=%s → %i", uid, r); + assert_se(r > 0); + condition_free(condition); + free(uid); + + assert_se(asprintf(&uid, "%u", (unsigned)getuid()+1) > 0); + condition = condition_new(CONDITION_USER, uid, false, false); + assert_se(condition); + r = condition_test(condition); + log_info("ConditionUser=%s → %i", uid, r); + assert_se(r == 0); + condition_free(condition); + free(uid); + + username = getusername_malloc(); + assert_se(username); + condition = condition_new(CONDITION_USER, username, false, false); + assert_se(condition); + r = condition_test(condition); + log_info("ConditionUser=%s → %i", username, r); + assert_se(r > 0); + condition_free(condition); + free(username); + + username = (char*)(geteuid() == 0 ? NOBODY_USER_NAME : "root"); + condition = condition_new(CONDITION_USER, username, false, false); + assert_se(condition); + r = condition_test(condition); + log_info("ConditionUser=%s → %i", username, r); + assert_se(r == 0); + condition_free(condition); + + condition = condition_new(CONDITION_USER, "@system", false, false); + assert_se(condition); + r = condition_test(condition); + log_info("ConditionUser=@system → %i", r); + if (geteuid() == 0) + assert_se(r > 0); + else + assert_se(r == 0); + condition_free(condition); +} + +static void test_condition_test_group(void) { + Condition *condition; + char* gid; + char* groupname; + gid_t *gids, max_gid; + int ngroups_max, r, i; + + assert_se(0 < asprintf(&gid, "%u", UINT32_C(0xFFFF))); + condition = condition_new(CONDITION_GROUP, gid, false, false); + assert_se(condition); + r = condition_test(condition); + log_info("ConditionGroup=%s → %i", gid, r); + assert_se(r == 0); + condition_free(condition); + free(gid); + + assert_se(0 < asprintf(&gid, "%u", getgid())); + condition = condition_new(CONDITION_GROUP, gid, false, false); + assert_se(condition); + r = condition_test(condition); + log_info("ConditionGroup=%s → %i", gid, r); + assert_se(r > 0); + condition_free(condition); + free(gid); + + ngroups_max = sysconf(_SC_NGROUPS_MAX); + assert(ngroups_max > 0); + + gids = alloca(sizeof(gid_t) * ngroups_max); + + r = getgroups(ngroups_max, gids); + assert(r >= 0); + + max_gid = getgid(); + for (i = 0; i < r; i++) { + assert_se(0 < asprintf(&gid, "%u", gids[i])); + condition = condition_new(CONDITION_GROUP, gid, false, false); + assert_se(condition); + r = condition_test(condition); + log_info("ConditionGroup=%s → %i", gid, r); + assert_se(r > 0); + condition_free(condition); + free(gid); + max_gid = gids[i] > max_gid ? gids[i] : max_gid; + + groupname = gid_to_name(gids[i]); + assert_se(groupname); + condition = condition_new(CONDITION_GROUP, groupname, false, false); + assert_se(condition); + r = condition_test(condition); + log_info("ConditionGroup=%s → %i", groupname, r); + assert_se(r > 0); + condition_free(condition); + free(groupname); + max_gid = gids[i] > max_gid ? gids[i] : max_gid; + } + + assert_se(0 < asprintf(&gid, "%u", max_gid+1)); + condition = condition_new(CONDITION_GROUP, gid, false, false); + assert_se(condition); + r = condition_test(condition); + log_info("ConditionGroup=%s → %i", gid, r); + assert_se(r == 0); + condition_free(condition); + free(gid); + + groupname = (char*)(geteuid() == 0 ? NOBODY_GROUP_NAME : "root"); + condition = condition_new(CONDITION_GROUP, groupname, false, false); + assert_se(condition); + r = condition_test(condition); + log_info("ConditionGroup=%s → %i", groupname, r); + assert_se(r == 0); + condition_free(condition); +} + int main(int argc, char *argv[]) { log_set_max_level(LOG_DEBUG); log_parse_environment(); @@ -336,6 +485,8 @@ int main(int argc, char *argv[]) { test_condition_test_null(); test_condition_test_security(); test_condition_test_virtualization(); + test_condition_test_user(); + test_condition_test_group(); return 0; } diff --git a/src/test/test-conf-parser.c b/src/test/test-conf-parser.c index be5d2611f8..77fcbc0dd3 100644 --- a/src/test/test-conf-parser.c +++ b/src/test/test-conf-parser.c @@ -109,8 +109,10 @@ static void test_config_parse_path(void) { test_config_parse_path_one("/path", "/path"); test_config_parse_path_one("/path//////////", "/path"); test_config_parse_path_one("///path/foo///bar////bar//", "/path/foo/bar/bar"); + test_config_parse_path_one("/path/\xc3\x80", "/path/\xc3\x80"); test_config_parse_path_one("not_absolute/path", NULL); + test_config_parse_path_one("/path/\xc3\x7f", NULL); } static void test_config_parse_log_level(void) { @@ -180,6 +182,8 @@ static void test_config_parse_strv(void) { test_config_parse_strv_one("foo", STRV_MAKE("foo")); test_config_parse_strv_one("foo bar foo", STRV_MAKE("foo", "bar", "foo")); test_config_parse_strv_one("\"foo bar\" foo", STRV_MAKE("foo bar", "foo")); + test_config_parse_strv_one("\xc3\x80", STRV_MAKE("\xc3\x80")); + test_config_parse_strv_one("\xc3\x7f", STRV_MAKE_EMPTY); } static void test_config_parse_mode(void) { diff --git a/src/test/test-date.c b/src/test/test-date.c index b77598c81d..0e7d44fade 100644 --- a/src/test/test-date.c +++ b/src/test/test-date.c @@ -25,41 +25,44 @@ static void test_should_pass(const char *p) { usec_t t, q; - char buf[FORMAT_TIMESTAMP_MAX], buf_relative[FORMAT_TIMESTAMP_RELATIVE_MAX], *sp; + char buf[FORMAT_TIMESTAMP_MAX], buf_relative[FORMAT_TIMESTAMP_RELATIVE_MAX]; + log_info("Test: %s", p); assert_se(parse_timestamp(p, &t) >= 0); - format_timestamp_us(buf, sizeof(buf), t); - log_info("%s", buf); - - /* Chop off timezone */ - sp = strrchr(buf, ' '); - assert_se(sp); - *sp = 0; + assert_se(format_timestamp_us(buf, sizeof(buf), t)); + log_info("\"%s\" → \"%s\"", p, buf); assert_se(parse_timestamp(buf, &q) >= 0); assert_se(q == t); - format_timestamp_relative(buf_relative, sizeof(buf_relative), t); + assert_se(format_timestamp_relative(buf_relative, sizeof(buf_relative), t)); log_info("%s", strna(buf_relative)); - assert_se(parse_timestamp(buf, &q) >= 0); } static void test_should_parse(const char *p) { usec_t t; + log_info("Test: %s", p); assert_se(parse_timestamp(p, &t) >= 0); + log_info("\"%s\" → \"@%" PRI_USEC "\"", p, t); } static void test_should_fail(const char *p) { usec_t t; + int r; - assert_se(parse_timestamp(p, &t) < 0); + log_info("Test: %s", p); + r = parse_timestamp(p, &t); + if (r >= 0) + log_info("\"%s\" → \"@%" PRI_USEC "\" (unexpected)", p, t); + else + log_info("parse_timestamp() returns %d (expected)", r); + assert_se(r < 0); } static void test_one(const char *p) { _cleanup_free_ char *with_utc; - log_info("Test: %s", p); with_utc = strjoin(p, " UTC"); test_should_pass(p); test_should_pass(with_utc); @@ -68,7 +71,6 @@ static void test_one(const char *p) { static void test_one_noutc(const char *p) { _cleanup_free_ char *with_utc; - log_info("Test: %s", p); with_utc = strjoin(p, " UTC"); test_should_pass(p); test_should_fail(with_utc); @@ -85,25 +87,26 @@ int main(int argc, char *argv[]) { test_one("2012-12-30 18:42"); test_one("2012-10-02"); test_one("Tue 2012-10-02"); - test_one_noutc("now"); test_one("yesterday"); test_one("today"); test_one("tomorrow"); + test_one_noutc("now"); test_one_noutc("+2d"); test_one_noutc("+2y 4d"); test_one_noutc("5months ago"); 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"); + test_should_pass("1970-1-1 00:00:01 UTC"); + test_should_fail("1969-12-31 UTC"); + test_should_fail("-100y"); + test_should_fail("today UTC UTC"); #if SIZEOF_TIME_T == 8 - test_should_parse("9999-12-30 23:59:59 UTC"); + test_should_pass("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"); + test_should_pass("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-dlopen.c b/src/test/test-dlopen.c new file mode 100644 index 0000000000..9f5343a7ea --- /dev/null +++ b/src/test/test-dlopen.c @@ -0,0 +1,32 @@ +/*** + This file is part of systemd. + + Copyright 2016 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <dlfcn.h> +#include <stdlib.h> + +#include "macro.h" + +int main(int argc, char **argv) { + void *handle; + + assert_se((handle = dlopen(argv[1], RTLD_NOW))); + assert_se(dlclose(handle) == 0); + + return EXIT_SUCCESS; +} diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index a7cd8e4b51..11cf0b1f0b 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -607,24 +607,53 @@ static void test_dns_name_common_suffix(void) { test_dns_name_common_suffix_one("FOO.BAR", "tEST.bAR", "BAR"); } -static void test_dns_name_apply_idna_one(const char *s, const char *result) { -#ifdef HAVE_LIBIDN +static void test_dns_name_apply_idna_one(const char *s, int expected, const char *result) { _cleanup_free_ char *buf = NULL; - assert_se(dns_name_apply_idna(s, &buf) >= 0); - assert_se(dns_name_equal(buf, result) > 0); -#endif + int r; + + r = dns_name_apply_idna(s, &buf); + log_debug("dns_name_apply_idna: \"%s\" → %d/\"%s\" (expected %d/\"%s\")", + s, r, strnull(buf), expected, strnull(result)); + + assert_se(r == expected); + if (expected == 1) + assert_se(dns_name_equal(buf, result) == 1); } static void test_dns_name_apply_idna(void) { - test_dns_name_apply_idna_one("", ""); - test_dns_name_apply_idna_one("foo", "foo"); - test_dns_name_apply_idna_one("foo.", "foo"); - test_dns_name_apply_idna_one("foo.bar", "foo.bar"); - test_dns_name_apply_idna_one("foo.bar.", "foo.bar"); - test_dns_name_apply_idna_one("föö", "xn--f-1gaa"); - test_dns_name_apply_idna_one("föö.", "xn--f-1gaa"); - test_dns_name_apply_idna_one("föö.bär", "xn--f-1gaa.xn--br-via"); - test_dns_name_apply_idna_one("föö.bär.", "xn--f-1gaa.xn--br-via"); +#if defined HAVE_LIBIDN2 || defined HAVE_LIBIDN + const int ret = 1; +#else + const int ret = 0; +#endif + + /* IDNA2008 forbids names with hyphens in third and fourth positions + * (https://tools.ietf.org/html/rfc5891#section-4.2.3.1). + * IDNA2003 does not have this restriction + * (https://tools.ietf.org/html/rfc3490#section-5). + * This means that when using libidn we will transform and test more + * labels. If registrars follow IDNA2008 we'll just be performing a + * useless lookup. + */ +#if defined HAVE_LIBIDN + const int ret2 = 1; +#else + const int ret2 = 0; +#endif + + test_dns_name_apply_idna_one("", ret, ""); + test_dns_name_apply_idna_one("foo", ret, "foo"); + test_dns_name_apply_idna_one("foo.", ret, "foo"); + test_dns_name_apply_idna_one("foo.bar", ret, "foo.bar"); + test_dns_name_apply_idna_one("foo.bar.", ret, "foo.bar"); + test_dns_name_apply_idna_one("föö", ret, "xn--f-1gaa"); + test_dns_name_apply_idna_one("föö.", ret, "xn--f-1gaa"); + test_dns_name_apply_idna_one("föö.bär", ret, "xn--f-1gaa.xn--br-via"); + test_dns_name_apply_idna_one("föö.bär.", ret, "xn--f-1gaa.xn--br-via"); + test_dns_name_apply_idna_one("xn--f-1gaa.xn--br-via", ret, "xn--f-1gaa.xn--br-via"); + + test_dns_name_apply_idna_one("r3---sn-ab5l6ne7.googlevideo.com", ret2, + ret2 ? "r3---sn-ab5l6ne7.googlevideo.com" : ""); } static void test_dns_name_is_valid_or_address(void) { @@ -640,6 +669,9 @@ static void test_dns_name_is_valid_or_address(void) { } int main(int argc, char *argv[]) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); test_dns_label_unescape(); test_dns_label_unescape_suffix(); diff --git a/src/test/test-env-util.c b/src/test/test-env-util.c index e5cc2a2df8..3a2492dc6f 100644 --- a/src/test/test-env-util.c +++ b/src/test/test-env-util.c @@ -21,6 +21,8 @@ #include <string.h> #include "env-util.h" +#include "fd-util.h" +#include "fileio.h" #include "string-util.h" #include "strv.h" #include "util.h" @@ -314,6 +316,57 @@ static void test_env_assignment_is_valid(void) { assert_se(!env_assignment_is_valid("głąb=printf \"\x1b]0;<mock-chroot>\x07<mock-chroot>\"")); } +static void test_deserialize_environment(void) { + _cleanup_strv_free_ char **env = strv_new("A=1", NULL); + + assert_se(deserialize_environment(&env, "env=test") < 0); + assert_se(deserialize_environment(&env, "env=B=2") >= 0); + + assert_se(strv_equal(env, STRV_MAKE("A=1", "B=2"))); +} + +static void test_serialize_environment(void) { + char fn[] = "/tmp/test-env-util.XXXXXXX"; + int fd, r; + _cleanup_fclose_ FILE *f = NULL; + + _cleanup_strv_free_ char **env = strv_new("A=1", + "B=2", + "C=ąęółń", + "D=D=a\\x0Ab", + NULL); + _cleanup_strv_free_ char **env2 = NULL; + + fd = mkostemp_safe(fn); + assert_se(fd >= 0); + + assert_se(f = fdopen(fd, "r+")); + + assert_se(serialize_environment(f, env) == 0); + assert_se(fflush_and_check(f) == 0); + + rewind(f); + + for (;;) { + char line[LINE_MAX]; + const char *l; + + if (!fgets(line, sizeof line, f)) + break; + + char_array_0(line); + l = strstrip(line); + + r = deserialize_environment(&env2, l); + assert_se(r == 1); + } + assert_se(feof(f)); + + assert_se(strv_equal(env, env2)); + + unlink(fn); +} + int main(int argc, char *argv[]) { test_strv_env_delete(); test_strv_env_get(); @@ -330,6 +383,8 @@ int main(int argc, char *argv[]) { test_env_name_is_valid(); test_env_value_is_valid(); test_env_assignment_is_valid(); + test_deserialize_environment(); + test_serialize_environment(); return 0; } diff --git a/src/test/test-escape.c b/src/test/test-escape.c index 6cbb8443fe..d060afaffa 100644 --- a/src/test/test-escape.c +++ b/src/test/test-escape.c @@ -69,6 +69,14 @@ static void test_cunescape(void) { assert_se(cunescape("\\073", 0, &unescaped) >= 0); assert_se(streq_ptr(unescaped, ";")); + unescaped = mfree(unescaped); + + assert_se(cunescape("A=A\\\\x0aB", 0, &unescaped) >= 0); + assert_se(streq_ptr(unescaped, "A=A\\x0aB")); + unescaped = mfree(unescaped); + + assert_se(cunescape("A=A\\\\x0aB", UNESCAPE_RELAX, &unescaped) >= 0); + assert_se(streq_ptr(unescaped, "A=A\\x0aB")); } static void test_shell_escape_one(const char *s, const char *bad, const char *expected) { @@ -86,25 +94,52 @@ static void test_shell_escape(void) { test_shell_escape_one("foo:bar,baz", ",:", "foo\\:bar\\,baz"); } -static void test_shell_maybe_quote_one(const char *s, const char *expected) { - _cleanup_free_ char *r; +static void test_shell_maybe_quote_one(const char *s, + EscapeStyle style, + const char *expected) { + _cleanup_free_ char *ret = NULL; - assert_se(r = shell_maybe_quote(s)); - assert_se(streq(r, expected)); + assert_se(ret = shell_maybe_quote(s, style)); + log_debug("[%s] → [%s] (%s)", s, ret, expected); + assert_se(streq(ret, expected)); } static void test_shell_maybe_quote(void) { - test_shell_maybe_quote_one("", ""); - test_shell_maybe_quote_one("\\", "\"\\\\\""); - test_shell_maybe_quote_one("\"", "\"\\\"\""); - test_shell_maybe_quote_one("foobar", "foobar"); - test_shell_maybe_quote_one("foo bar", "\"foo bar\""); - test_shell_maybe_quote_one("foo \"bar\" waldo", "\"foo \\\"bar\\\" waldo\""); - test_shell_maybe_quote_one("foo$bar", "\"foo\\$bar\""); + test_shell_maybe_quote_one("", ESCAPE_BACKSLASH, ""); + test_shell_maybe_quote_one("", ESCAPE_POSIX, ""); + test_shell_maybe_quote_one("\\", ESCAPE_BACKSLASH, "\"\\\\\""); + test_shell_maybe_quote_one("\\", ESCAPE_POSIX, "$'\\\\'"); + test_shell_maybe_quote_one("\"", ESCAPE_BACKSLASH, "\"\\\"\""); + test_shell_maybe_quote_one("\"", ESCAPE_POSIX, "$'\"'"); + test_shell_maybe_quote_one("foobar", ESCAPE_BACKSLASH, "foobar"); + test_shell_maybe_quote_one("foobar", ESCAPE_POSIX, "foobar"); + test_shell_maybe_quote_one("foo bar", ESCAPE_BACKSLASH, "\"foo bar\""); + test_shell_maybe_quote_one("foo bar", ESCAPE_POSIX, "$'foo bar'"); + test_shell_maybe_quote_one("foo\tbar", ESCAPE_BACKSLASH, "\"foo\tbar\""); + test_shell_maybe_quote_one("foo\tbar", ESCAPE_POSIX, "$'foo\\tbar'"); + test_shell_maybe_quote_one("foo\nbar", ESCAPE_BACKSLASH, "\"foo\nbar\""); + test_shell_maybe_quote_one("foo\nbar", ESCAPE_POSIX, "$'foo\\nbar'"); + test_shell_maybe_quote_one("foo \"bar\" waldo", ESCAPE_BACKSLASH, "\"foo \\\"bar\\\" waldo\""); + test_shell_maybe_quote_one("foo \"bar\" waldo", ESCAPE_POSIX, "$'foo \"bar\" waldo'"); + test_shell_maybe_quote_one("foo$bar", ESCAPE_BACKSLASH, "\"foo\\$bar\""); + test_shell_maybe_quote_one("foo$bar", ESCAPE_POSIX, "$'foo$bar'"); + + /* Note that current users disallow control characters, so this "test" + * is here merely to establish current behaviour. If control characters + * were allowed, they should be quoted, i.e. \001 should become \\001. */ + test_shell_maybe_quote_one("a\nb\001", ESCAPE_BACKSLASH, "\"a\nb\001\""); + test_shell_maybe_quote_one("a\nb\001", ESCAPE_POSIX, "$'a\\nb\001'"); + + test_shell_maybe_quote_one("foo!bar", ESCAPE_BACKSLASH, "\"foo!bar\""); + test_shell_maybe_quote_one("foo!bar", ESCAPE_POSIX, "$'foo!bar'"); } int main(int argc, char *argv[]) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + test_cescape(); test_cunescape(); test_shell_escape(); diff --git a/src/test/test-exec-util.c b/src/test/test-exec-util.c index 482b0751b9..30c92019d9 100644 --- a/src/test/test-exec-util.c +++ b/src/test/test-exec-util.c @@ -223,7 +223,7 @@ static int gather_stdout_three(int fd, void *arg) { return 0; } -const gather_stdout_callback_t const gather_stdout[] = { +const gather_stdout_callback_t gather_stdout[] = { gather_stdout_one, gather_stdout_two, gather_stdout_three, diff --git a/src/test/test-execute.c b/src/test/test-execute.c index 90540b884b..29c8fd613f 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -233,6 +233,15 @@ static void test_exec_inaccessiblepaths(Manager *m) { test(m, "exec-inaccessiblepaths-mount-propagation.service", 0, CLD_EXITED); } +static void test_exec_inaccessiblepaths_proc(Manager *m) { + if (!is_inaccessible_available()) { + log_notice("testing without inaccessible, skipping %s", __func__); + return; + } + + test(m, "exec-inaccessiblepaths-proc.service", 0, CLD_EXITED); +} + static void test_exec_systemcallfilter(Manager *m) { #ifdef HAVE_SECCOMP if (!is_seccomp_available()) @@ -479,6 +488,7 @@ int main(int argc, char *argv[]) { test_exec_readonlypaths, test_exec_readwritepaths, test_exec_inaccessiblepaths, + test_exec_inaccessiblepaths_proc, test_exec_privatenetwork, test_exec_systemcallfilter, test_exec_systemcallerrornumber, diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index e774f567e0..9e964a8bbb 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -317,8 +317,8 @@ static void test_dot_or_dot_dot(void) { int main(int argc, char *argv[]) { test_unlink_noerrno(); - test_readlink_and_make_absolute(); test_get_files_in_directory(); + test_readlink_and_make_absolute(); test_var_tmp(); test_chase_symlinks(); test_dot_or_dot_dot(); diff --git a/src/test/test-glob-util.c b/src/test/test-glob-util.c index 9eea3eb608..af866e004b 100644 --- a/src/test/test-glob-util.c +++ b/src/test/test-glob-util.c @@ -18,12 +18,17 @@ ***/ #include <fcntl.h> +#include <glob.h> +#include <sys/stat.h> #include <unistd.h> #include "alloc-util.h" +#include "dirent-util.h" #include "fileio.h" +#include "fs-util.h" #include "glob-util.h" #include "macro.h" +#include "rm-rf.h" static void test_glob_exists(void) { char name[] = "/tmp/test-glob_exists.XXXXXX"; @@ -43,8 +48,69 @@ static void test_glob_exists(void) { assert_se(r == 0); } +static void test_glob_no_dot(void) { + char template[] = "/tmp/test-glob-util.XXXXXXX"; + const char *fn; + + _cleanup_globfree_ glob_t g = { + .gl_closedir = (void (*)(void *)) closedir, + .gl_readdir = (struct dirent *(*)(void *)) readdir_no_dot, + .gl_opendir = (void *(*)(const char *)) opendir, + .gl_lstat = lstat, + .gl_stat = stat, + }; + + int r; + + assert_se(mkdtemp(template)); + + fn = strjoina(template, "/*"); + r = glob(fn, GLOB_NOSORT|GLOB_BRACE|GLOB_ALTDIRFUNC, NULL, &g); + assert_se(r == GLOB_NOMATCH); + + fn = strjoina(template, "/.*"); + r = glob(fn, GLOB_NOSORT|GLOB_BRACE|GLOB_ALTDIRFUNC, NULL, &g); + assert_se(r == GLOB_NOMATCH); + + (void) rm_rf(template, REMOVE_ROOT|REMOVE_PHYSICAL); +} + +static void test_safe_glob(void) { + char template[] = "/tmp/test-glob-util.XXXXXXX"; + const char *fn, *fn2, *fname; + + _cleanup_globfree_ glob_t g = {}; + int r; + + assert_se(mkdtemp(template)); + + fn = strjoina(template, "/*"); + r = safe_glob(fn, 0, &g); + assert_se(r == -ENOENT); + + fn2 = strjoina(template, "/.*"); + r = safe_glob(fn2, GLOB_NOSORT|GLOB_BRACE, &g); + assert_se(r == -ENOENT); + + fname = strjoina(template, "/.foobar"); + assert_se(touch(fname) == 0); + + r = safe_glob(fn, 0, &g); + assert_se(r == -ENOENT); + + r = safe_glob(fn2, GLOB_NOSORT|GLOB_BRACE, &g); + assert_se(r == 0); + assert_se(g.gl_pathc == 1); + assert_se(streq(g.gl_pathv[0], fname)); + assert_se(g.gl_pathv[1] == NULL); + + (void) rm_rf(template, REMOVE_ROOT|REMOVE_PHYSICAL); +} + int main(void) { test_glob_exists(); + test_glob_no_dot(); + test_safe_glob(); return 0; } diff --git a/src/test/test-hash.c b/src/test/test-hash.c index 1972b94cfe..02d1cfaee3 100644 --- a/src/test/test-hash.c +++ b/src/test/test-hash.c @@ -17,6 +17,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <stdio.h> + #include "alloc-util.h" #include "log.h" #include "string-util.h" @@ -25,12 +27,18 @@ int main(int argc, char *argv[]) { _cleanup_(khash_unrefp) khash *h = NULL, *copy = NULL; _cleanup_free_ char *s = NULL; + int r; log_set_max_level(LOG_DEBUG); assert_se(khash_new(&h, NULL) == -EINVAL); assert_se(khash_new(&h, "") == -EINVAL); - assert_se(khash_new(&h, "foobar") == -EOPNOTSUPP); + r = khash_new(&h, "foobar"); + if (r == -EAFNOSUPPORT) { + puts("khash not supported on this kernel, skipping"); + return EXIT_TEST_SKIP; + } + assert_se(r == -EOPNOTSUPP); assert_se(khash_new(&h, "sha256") >= 0); assert_se(khash_get_size(h) == 32); diff --git a/src/test/test-hashmap-ordered.awk b/src/test/test-hashmap-ordered.awk new file mode 100644 index 0000000000..10f4386fa4 --- /dev/null +++ b/src/test/test-hashmap-ordered.awk @@ -0,0 +1,11 @@ +BEGIN { + print "/* GENERATED FILE */"; + print "#define ORDERED" +} +{ + if (!match($0, "^#include")) + gsub(/hashmap/, "ordered_hashmap"); + gsub(/HASHMAP/, "ORDERED_HASHMAP"); + gsub(/Hashmap/, "OrderedHashmap"); + print +} diff --git a/src/test/test-id128.c b/src/test/test-id128.c index e8c4c3e550..e5f45206f1 100644 --- a/src/test/test-id128.c +++ b/src/test/test-id128.c @@ -154,11 +154,16 @@ int main(int argc, char *argv[]) { assert_se(id128_read_fd(fd, ID128_UUID, &id2) >= 0); assert_se(sd_id128_equal(id, id2)); - assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id) >= 0); - assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id2) >= 0); - assert_se(sd_id128_equal(id, id2)); - assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(51,df,0b,4b,c3,b0,4c,97,80,e2,99,b9,8c,a3,73,b8), &id2) >= 0); - assert_se(!sd_id128_equal(id, id2)); + r = sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id); + if (r == -EAFNOSUPPORT) { + log_info("khash not supported on this kernel, skipping sd_id128_get_machine_app_specific() checks"); + } else { + assert_se(r >= 0); + assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(f0,3d,aa,eb,1c,33,4b,43,a7,32,17,29,44,bf,77,2e), &id2) >= 0); + assert_se(sd_id128_equal(id, id2)); + assert_se(sd_id128_get_machine_app_specific(SD_ID128_MAKE(51,df,0b,4b,c3,b0,4c,97,80,e2,99,b9,8c,a3,73,b8), &id2) >= 0); + assert_se(!sd_id128_equal(id, id2)); + } /* Query the invocation ID */ r = sd_id128_get_invocation(&id); diff --git a/src/test/test-libudev.c b/src/test/test-libudev.c index e28de9b37b..0f71c18b65 100644 --- a/src/test/test-libudev.c +++ b/src/test/test-libudev.c @@ -392,7 +392,7 @@ int main(int argc, char *argv[]) { return EXIT_SUCCESS; case 'V': - printf("%s\n", VERSION); + printf("%s\n", PACKAGE_VERSION); return EXIT_SUCCESS; case 'm': diff --git a/src/test/test-log.c b/src/test/test-log.c index ae9e113efb..626d2c6135 100644 --- a/src/test/test-log.c +++ b/src/test/test-log.c @@ -24,6 +24,15 @@ #include "log.h" #include "util.h" +assert_cc(LOG_REALM_REMOVE_LEVEL(LOG_REALM_PLUS_LEVEL(LOG_REALM_SYSTEMD, LOG_FTP | LOG_DEBUG)) + == LOG_REALM_SYSTEMD); +assert_cc(LOG_REALM_REMOVE_LEVEL(LOG_REALM_PLUS_LEVEL(LOG_REALM_UDEV, LOG_LOCAL7 | LOG_DEBUG)) + == LOG_REALM_UDEV); +assert_cc((LOG_REALM_PLUS_LEVEL(LOG_REALM_SYSTEMD, LOG_LOCAL3 | LOG_DEBUG) & LOG_FACMASK) + == LOG_LOCAL3); +assert_cc((LOG_REALM_PLUS_LEVEL(LOG_REALM_UDEV, LOG_USER | LOG_INFO) & LOG_PRIMASK) + == LOG_INFO); + int main(int argc, char* argv[]) { log_set_target(LOG_TARGET_CONSOLE); diff --git a/src/test/test-loopback.c b/src/test/test-loopback.c index 7b67337331..c70633a0f3 100644 --- a/src/test/test-loopback.c +++ b/src/test/test-loopback.c @@ -27,11 +27,12 @@ int main(int argc, char* argv[]) { int r; log_open(); + log_set_max_level(LOG_DEBUG); log_parse_environment(); r = loopback_setup(); if (r < 0) - log_error("loopback: %m"); + log_error_errno(r, "loopback: %m"); return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/test/test-nss.c b/src/test/test-nss.c index b59cb7aa69..57eeb8e40c 100644 --- a/src/test/test-nss.c +++ b/src/test/test-nss.c @@ -71,9 +71,11 @@ static void* open_handle(const char* dir, const char* module, int flags) { const char *path; void *handle; - if (dir) - path = strjoina(dir, "/.libs/libnss_", module, ".so.2"); - else + if (dir) { + path = strjoina(dir, "/libnss_", module, ".so.2"); + if (access(path, F_OK) < 0) + path = strjoina(dir, "/.libs/libnss_", module, ".so.2"); + } else path = strjoina("libnss_", module, ".so.2"); handle = dlopen(path, flags); @@ -103,7 +105,7 @@ static int print_gaih_addrtuples(const struct gaih_addrtuple *tuples) { goto numerical_index; if (if_indextoname(it->scopeid, ifname) == NULL) { - log_warning("if_indextoname(%d) failed: %m", it->scopeid); + log_warning_errno(errno, "if_indextoname(%d) failed: %m", it->scopeid); numerical_index: xsprintf(ifname, "%i", it->scopeid); }; diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c index d08014100b..1b29b2ea87 100644 --- a/src/test/test-parse-util.c +++ b/src/test/test-parse-util.c @@ -395,6 +395,9 @@ static void test_safe_atou16(void) { r = safe_atou16("junk", &l); assert_se(r == -EINVAL); + + r = safe_atou16("123x", &l); + assert_se(r == -EINVAL); } static void test_safe_atoi16(void) { @@ -425,6 +428,70 @@ static void test_safe_atoi16(void) { r = safe_atoi16("junk", &l); assert_se(r == -EINVAL); + + r = safe_atoi16("123x", &l); + assert_se(r == -EINVAL); +} + +static void test_safe_atou64(void) { + int r; + uint64_t l; + + r = safe_atou64("12345", &l); + assert_se(r == 0); + assert_se(l == 12345); + + r = safe_atou64(" 12345", &l); + assert_se(r == 0); + assert_se(l == 12345); + + r = safe_atou64("18446744073709551617", &l); + assert_se(r == -ERANGE); + + r = safe_atou64("-1", &l); + assert_se(r == -ERANGE); + + r = safe_atou64(" -1", &l); + assert_se(r == -ERANGE); + + r = safe_atou64("junk", &l); + assert_se(r == -EINVAL); + + r = safe_atou64("123x", &l); + assert_se(r == -EINVAL); +} + +static void test_safe_atoi64(void) { + int r; + int64_t l; + + r = safe_atoi64("-12345", &l); + assert_se(r == 0); + assert_se(l == -12345); + + r = safe_atoi64(" -12345", &l); + assert_se(r == 0); + assert_se(l == -12345); + + r = safe_atoi64("32767", &l); + assert_se(r == 0); + assert_se(l == 32767); + + r = safe_atoi64(" 32767", &l); + assert_se(r == 0); + assert_se(l == 32767); + + r = safe_atoi64("9223372036854775813", &l); + assert_se(r == -ERANGE); + + r = safe_atoi64("-9223372036854775813", &l); + assert_se(r == -ERANGE); + + r = safe_atoi64("junk", &l); + assert_se(r == -EINVAL); + + r = safe_atoi64("123x", &l); + assert_se(r == -EINVAL); } static void test_safe_atod(void) { @@ -526,6 +593,19 @@ static void test_parse_nice(void) { assert_se(parse_nice("+20", &n) == -ERANGE); } +static void test_parse_dev(void) { + dev_t dev; + + assert_se(parse_dev("0", &dev) == -EINVAL); + assert_se(parse_dev("5", &dev) == -EINVAL); + assert_se(parse_dev("5:", &dev) == -EINVAL); + assert_se(parse_dev(":5", &dev) == -EINVAL); +#if SIZEOF_DEV_T < 8 + assert_se(parse_dev("4294967295:4294967295", &dev) == -EINVAL); +#endif + assert_se(parse_dev("8:11", &dev) >= 0 && major(dev) == 8 && minor(dev) == 11); +} + int main(int argc, char *argv[]) { log_parse_environment(); log_open(); @@ -538,10 +618,13 @@ int main(int argc, char *argv[]) { test_safe_atolli(); test_safe_atou16(); test_safe_atoi16(); + test_safe_atou64(); + test_safe_atoi64(); test_safe_atod(); test_parse_percent(); test_parse_percent_unbounded(); test_parse_nice(); + test_parse_dev(); return 0; } diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index 22df20a1eb..e5644246c2 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -27,6 +27,7 @@ #include "mount-util.h" #include "path-util.h" #include "rm-rf.h" +#include "stat-util.h" #include "string-util.h" #include "strv.h" #include "util.h" @@ -104,6 +105,48 @@ static void test_path(void) { assert_se(!path_equal_ptr(NULL, "/a")); } +static void test_path_equal_root(void) { + /* Nail down the details of how path_equal("/", ...) works. */ + + assert_se(path_equal("/", "/")); + assert_se(path_equal("/", "//")); + + assert_se(!path_equal("/", "/./")); + assert_se(!path_equal("/", "/../")); + + assert_se(!path_equal("/", "/.../")); + + /* Make sure that files_same works as expected. */ + + assert_se(files_same("/", "/", 0) > 0); + assert_se(files_same("/", "/", AT_SYMLINK_NOFOLLOW) > 0); + assert_se(files_same("/", "//", 0) > 0); + assert_se(files_same("/", "//", AT_SYMLINK_NOFOLLOW) > 0); + + assert_se(files_same("/", "/./", 0) > 0); + assert_se(files_same("/", "/./", AT_SYMLINK_NOFOLLOW) > 0); + assert_se(files_same("/", "/../", 0) > 0); + assert_se(files_same("/", "/../", AT_SYMLINK_NOFOLLOW) > 0); + + assert_se(files_same("/", "/.../", 0) == -ENOENT); + assert_se(files_same("/", "/.../", AT_SYMLINK_NOFOLLOW) == -ENOENT); + + /* The same for path_equal_or_files_same. */ + + assert_se(path_equal_or_files_same("/", "/", 0)); + assert_se(path_equal_or_files_same("/", "/", AT_SYMLINK_NOFOLLOW)); + assert_se(path_equal_or_files_same("/", "//", 0)); + assert_se(path_equal_or_files_same("/", "//", AT_SYMLINK_NOFOLLOW)); + + assert_se(path_equal_or_files_same("/", "/./", 0)); + assert_se(path_equal_or_files_same("/", "/./", AT_SYMLINK_NOFOLLOW)); + assert_se(path_equal_or_files_same("/", "/../", 0)); + assert_se(path_equal_or_files_same("/", "/../", AT_SYMLINK_NOFOLLOW)); + + assert_se(!path_equal_or_files_same("/", "/.../", 0)); + assert_se(!path_equal_or_files_same("/", "/.../", AT_SYMLINK_NOFOLLOW)); +} + static void test_find_binary(const char *self) { char *p; @@ -551,6 +594,7 @@ int main(int argc, char **argv) { log_open(); test_path(); + test_path_equal_root(); test_find_binary(argv[0]); test_prefixes(); test_path_join(); diff --git a/src/test/test-random-util.c b/src/test/test-random-util.c new file mode 100644 index 0000000000..50f4da270d --- /dev/null +++ b/src/test/test-random-util.c @@ -0,0 +1,65 @@ +/*** + This file is part of systemd. + + Copyright 2017 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "hexdecoct.h" +#include "random-util.h" +#include "log.h" + +static void test_acquire_random_bytes(bool high_quality_required) { + uint8_t buf[16] = {}; + unsigned i; + + log_info("/* %s */", __func__); + + for (i = 1; i < sizeof buf; i++) { + assert_se(acquire_random_bytes(buf, i, high_quality_required) == 0); + if (i + 1 < sizeof buf) + assert_se(buf[i] == 0); + + hexdump(stdout, buf, i); + } +} + +static void test_pseudorandom_bytes(void) { + uint8_t buf[16] = {}; + unsigned i; + + log_info("/* %s */", __func__); + + for (i = 1; i < sizeof buf; i++) { + pseudorandom_bytes(buf, i); + if (i + 1 < sizeof buf) + assert_se(buf[i] == 0); + + hexdump(stdout, buf, i); + } +} + +int main(int argc, char **argv) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + test_acquire_random_bytes(false); + test_acquire_random_bytes(true); + + test_pseudorandom_bytes(); + + return 0; +} diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c index 34a1275162..efd145e063 100644 --- a/src/test/test-seccomp.c +++ b/src/test/test-seccomp.c @@ -21,8 +21,10 @@ #include <stdlib.h> #include <sys/eventfd.h> #include <sys/mman.h> -#include <unistd.h> #include <sys/poll.h> +#include <sys/shm.h> +#include <sys/types.h> +#include <unistd.h> #include "alloc-util.h" #include "fd-util.h" @@ -37,6 +39,15 @@ #include "util.h" #include "virt.h" +#if SCMP_SYS(socket) < 0 || defined(__i386__) || defined(__s390x__) || defined(__s390__) +/* 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 + + static void test_seccomp_arch_to_string(void) { uint32_t a, b; const char *name; @@ -158,8 +169,6 @@ 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) @@ -218,7 +227,6 @@ 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) { @@ -286,12 +294,12 @@ 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); +#if SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN assert_se(fd >= 0); safe_close(fd); #else - assert_se(socket(AF_UNIX, SOCK_DGRAM, 0) < 0); + assert_se(fd < 0); assert_se(errno == EAFNOSUPPORT); #endif @@ -309,19 +317,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); +#if SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN assert_se(fd >= 0); safe_close(fd); +#else + assert_se(fd < 0); + assert_se(errno == EAFNOSUPPORT); +#endif fd = socket(AF_NETLINK, SOCK_DGRAM, 0); +#if SECCOMP_RESTRICT_ADDRESS_FAMILIES_BROKEN 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(fd < 0); assert_se(errno == EAFNOSUPPORT); #endif @@ -369,7 +379,7 @@ static void test_restrict_realtime(void) { assert_se(wait_for_terminate_and_warn("realtimeseccomp", pid, true) == EXIT_SUCCESS); } -static void test_memory_deny_write_execute(void) { +static void test_memory_deny_write_execute_mmap(void) { pid_t pid; if (!is_seccomp_available()) @@ -393,14 +403,13 @@ static void test_memory_deny_write_execute(void) { 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); +#if defined(__x86_64__) || defined(__i386__) || defined(__powerpc64__) || defined(__arm__) || defined(__aarch64__) assert_se(p == MAP_FAILED); assert_se(errno == EPERM); +#else /* unknown architectures */ + assert_se(p != MAP_FAILED); + assert_se(munmap(p, page_size()) >= 0); #endif p = mmap(NULL, page_size(), PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1,0); @@ -410,7 +419,54 @@ static void test_memory_deny_write_execute(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn("memoryseccomp", pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_warn("memoryseccomp-mmap", pid, true) == EXIT_SUCCESS); +} + +static void test_memory_deny_write_execute_shmat(void) { + int shmid; + pid_t pid; + + if (!is_seccomp_available()) + return; + if (geteuid() != 0) + return; + + shmid = shmget(IPC_PRIVATE, page_size(), 0); + assert_se(shmid >= 0); + + pid = fork(); + assert_se(pid >= 0); + + if (pid == 0) { + void *p; + + p = shmat(shmid, NULL, 0); + assert_se(p != MAP_FAILED); + assert_se(shmdt(p) == 0); + + p = shmat(shmid, NULL, SHM_EXEC); + assert_se(p != MAP_FAILED); + assert_se(shmdt(p) == 0); + + assert_se(seccomp_memory_deny_write_execute() >= 0); + + p = shmat(shmid, NULL, SHM_EXEC); +#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) + assert_se(p == MAP_FAILED); + assert_se(errno == EPERM); +#else /* __i386__, __powerpc64__, and "unknown" architectures */ + assert_se(p != MAP_FAILED); + assert_se(shmdt(p) == 0); +#endif + + p = shmat(shmid, NULL, 0); + assert_se(p != MAP_FAILED); + assert_se(shmdt(p) == 0); + + _exit(EXIT_SUCCESS); + } + + assert_se(wait_for_terminate_and_warn("memoryseccomp-shmat", pid, true) == EXIT_SUCCESS); } static void test_restrict_archs(void) { @@ -509,7 +565,8 @@ int main(int argc, char *argv[]) { test_protect_sysctl(); test_restrict_address_families(); test_restrict_realtime(); - test_memory_deny_write_execute(); + test_memory_deny_write_execute_mmap(); + test_memory_deny_write_execute_shmat(); test_restrict_archs(); test_load_syscall_filter_set_raw(); diff --git a/src/test/test-selinux.c b/src/test/test-selinux.c index b676c25913..190736aa47 100644 --- a/src/test/test-selinux.c +++ b/src/test/test-selinux.c @@ -35,16 +35,16 @@ static void test_testing(void) { b = mac_selinux_use(); log_info("mac_selinux_use → %s", yes_no(b)); - b = mac_selinux_have(); - log_info("mac_selinux_have → %s", yes_no(b)); + b = mac_selinux_use(); + log_info("mac_selinux_use → %s", yes_no(b)); mac_selinux_retest(); b = mac_selinux_use(); log_info("mac_selinux_use → %s", yes_no(b)); - b = mac_selinux_have(); - log_info("mac_selinux_have → %s", yes_no(b)); + b = mac_selinux_use(); + log_info("mac_selinux_use → %s", yes_no(b)); } static void test_loading(void) { diff --git a/src/test/test-sigbus.c b/src/test/test-sigbus.c index 02b8e24308..7a4a8a6636 100644 --- a/src/test/test-sigbus.c +++ b/src/test/test-sigbus.c @@ -22,6 +22,9 @@ #include "fd-util.h" #include "sigbus.h" #include "util.h" +#ifdef HAVE_VALGRIND_VALGRIND_H +#include <valgrind/valgrind.h> +#endif int main(int argc, char *argv[]) { _cleanup_close_ int fd = -1; @@ -29,6 +32,11 @@ int main(int argc, char *argv[]) { void *addr = NULL; uint8_t *p; +#ifdef HAVE_VALGRIND_VALGRIND_H + if (RUNNING_ON_VALGRIND) + return EXIT_TEST_SKIP; +#endif + #ifdef __SANITIZE_ADDRESS__ return EXIT_TEST_SKIP; #endif @@ -38,7 +46,7 @@ int main(int argc, char *argv[]) { assert_se((fd = mkostemp(template, O_RDWR|O_CREAT|O_EXCL)) >= 0); assert_se(unlink(template) >= 0); - assert_se(fallocate(fd, 0, 0, page_size() * 8) >= 0); + assert_se(posix_fallocate(fd, 0, page_size() * 8) >= 0); p = mmap(NULL, page_size() * 16, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); assert_se(p != MAP_FAILED); diff --git a/src/test/test-sizeof.c b/src/test/test-sizeof.c index 8f99a13772..269adfd18f 100644 --- a/src/test/test-sizeof.c +++ b/src/test/test-sizeof.c @@ -17,7 +17,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "log.h" +#include <stdio.h> + #include "time-util.h" /* Print information about various types. Useful when diagnosing @@ -26,10 +27,18 @@ #pragma GCC diagnostic ignored "-Wtype-limits" #define info(t) \ - log_info("%s → %zu bits%s", STRINGIFY(t), \ - sizeof(t)*CHAR_BIT, \ - strstr(STRINGIFY(t), "signed") ? "" : \ - ((t)-1 < (t)0 ? ", signed" : ", unsigned")); + printf("%s → %zu bits%s\n", STRINGIFY(t), \ + sizeof(t)*CHAR_BIT, \ + strstr(STRINGIFY(t), "signed") ? "" : \ + ((t)-1 < (t)0 ? ", signed" : ", unsigned")); + +enum Enum { + enum_value, +}; + +enum BigEnum { + big_enum_value = UINT64_C(-1), +}; int main(void) { info(char); @@ -39,6 +48,8 @@ int main(void) { info(unsigned); info(long unsigned); info(long long unsigned); + info(__syscall_ulong_t); + info(__syscall_slong_t); info(float); info(double); @@ -48,6 +59,10 @@ int main(void) { info(ssize_t); info(time_t); info(usec_t); + info(__time_t); + + info(enum Enum); + info(enum BigEnum); return 0; } diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c index 3ff2aadea4..8e027ff26c 100644 --- a/src/test/test-stat-util.c +++ b/src/test/test-stat-util.c @@ -38,8 +38,10 @@ static void test_files_same(void) { assert_se(fd >= 0); assert_se(symlink(name, name_alias) >= 0); - assert_se(files_same(name, name)); - assert_se(files_same(name, name_alias)); + assert_se(files_same(name, name, 0)); + assert_se(files_same(name, name, AT_SYMLINK_NOFOLLOW)); + assert_se(files_same(name, name_alias, 0)); + assert_se(!files_same(name, name_alias, AT_SYMLINK_NOFOLLOW)); unlink(name); unlink(name_alias); diff --git a/src/test/test-strxcpyx.c b/src/test/test-strxcpyx.c index 9bea770131..d95945f6b0 100644 --- a/src/test/test-strxcpyx.c +++ b/src/test/test-strxcpyx.c @@ -51,6 +51,13 @@ static void test_strpcpyf(void) { assert_se(streq(target, "space left: 25. foobar")); assert_se(space_left == 3); + + /* test overflow */ + s = target; + space_left = strpcpyf(&s, 12, "00 left: %i. ", 999); + assert_se(streq(target, "00 left: 99")); + assert_se(space_left == 0); + assert_se(target[12] == '2'); } static void test_strpcpyl(void) { diff --git a/src/test/test-time.c b/src/test/test-time.c index 911282bf0c..b7a06c7b19 100644 --- a/src/test/test-time.c +++ b/src/test/test-time.c @@ -61,6 +61,19 @@ static void test_parse_sec(void) { assert_se(parse_sec(".3 infinity", &u) < 0); } +static void test_parse_sec_fix_0(void) { + usec_t u; + + assert_se(parse_sec_fix_0("5s", &u) >= 0); + assert_se(u == 5 * USEC_PER_SEC); + assert_se(parse_sec_fix_0("0s", &u) >= 0); + assert_se(u == 0 * USEC_PER_SEC); + assert_se(parse_sec_fix_0("0", &u) >= 0); + assert_se(u == USEC_INFINITY); + assert_se(parse_sec_fix_0(" 0", &u) >= 0); + assert_se(u == USEC_INFINITY); +} + static void test_parse_time(void) { usec_t u; @@ -195,16 +208,37 @@ static void test_usec_add(void) { assert_se(usec_add(USEC_INFINITY, 2) == USEC_INFINITY); } -static void test_usec_sub(void) { - assert_se(usec_sub(0, 0) == 0); - assert_se(usec_sub(4, 1) == 3); - assert_se(usec_sub(4, 4) == 0); - assert_se(usec_sub(4, 5) == 0); - assert_se(usec_sub(USEC_INFINITY-3, -3) == USEC_INFINITY); - assert_se(usec_sub(USEC_INFINITY-3, -3) == USEC_INFINITY); - assert_se(usec_sub(USEC_INFINITY-3, -4) == USEC_INFINITY); - assert_se(usec_sub(USEC_INFINITY-3, -5) == USEC_INFINITY); - assert_se(usec_sub(USEC_INFINITY, 5) == USEC_INFINITY); +static void test_usec_sub_unsigned(void) { + assert_se(usec_sub_unsigned(0, 0) == 0); + assert_se(usec_sub_unsigned(0, 2) == 0); + assert_se(usec_sub_unsigned(0, USEC_INFINITY) == 0); + assert_se(usec_sub_unsigned(1, 0) == 1); + assert_se(usec_sub_unsigned(1, 1) == 0); + assert_se(usec_sub_unsigned(1, 2) == 0); + assert_se(usec_sub_unsigned(1, 3) == 0); + assert_se(usec_sub_unsigned(1, USEC_INFINITY) == 0); + assert_se(usec_sub_unsigned(USEC_INFINITY-1, 0) == USEC_INFINITY-1); + assert_se(usec_sub_unsigned(USEC_INFINITY-1, 1) == USEC_INFINITY-2); + assert_se(usec_sub_unsigned(USEC_INFINITY-1, 2) == USEC_INFINITY-3); + assert_se(usec_sub_unsigned(USEC_INFINITY-1, USEC_INFINITY-2) == 1); + assert_se(usec_sub_unsigned(USEC_INFINITY-1, USEC_INFINITY-1) == 0); + assert_se(usec_sub_unsigned(USEC_INFINITY-1, USEC_INFINITY) == 0); + assert_se(usec_sub_unsigned(USEC_INFINITY, 0) == USEC_INFINITY); + assert_se(usec_sub_unsigned(USEC_INFINITY, 1) == USEC_INFINITY); + assert_se(usec_sub_unsigned(USEC_INFINITY, 2) == USEC_INFINITY); + assert_se(usec_sub_unsigned(USEC_INFINITY, USEC_INFINITY) == USEC_INFINITY); +} + +static void test_usec_sub_signed(void) { + assert_se(usec_sub_signed(0, 0) == 0); + assert_se(usec_sub_signed(4, 1) == 3); + assert_se(usec_sub_signed(4, 4) == 0); + assert_se(usec_sub_signed(4, 5) == 0); + assert_se(usec_sub_signed(USEC_INFINITY-3, -3) == USEC_INFINITY); + assert_se(usec_sub_signed(USEC_INFINITY-3, -3) == USEC_INFINITY); + assert_se(usec_sub_signed(USEC_INFINITY-3, -4) == USEC_INFINITY); + assert_se(usec_sub_signed(USEC_INFINITY-3, -5) == USEC_INFINITY); + assert_se(usec_sub_signed(USEC_INFINITY, 5) == USEC_INFINITY); } static void test_format_timestamp(void) { @@ -273,10 +307,93 @@ static void test_format_timestamp_utc(void) { test_format_timestamp_utc_one(USEC_INFINITY, NULL); } +static void test_dual_timestamp_deserialize(void) { + int r; + dual_timestamp t; + + r = dual_timestamp_deserialize("1234 5678", &t); + assert_se(r == 0); + assert_se(t.realtime == 1234); + assert_se(t.monotonic == 5678); + + r = dual_timestamp_deserialize("1234x 5678", &t); + assert_se(r == -EINVAL); + + r = dual_timestamp_deserialize("1234 5678y", &t); + assert_se(r == -EINVAL); + + r = dual_timestamp_deserialize("-1234 5678", &t); + assert_se(r == -EINVAL); + + r = dual_timestamp_deserialize("1234 -5678", &t); + assert_se(r == -EINVAL); + + /* Check that output wasn't modified. */ + assert_se(t.realtime == 1234); + assert_se(t.monotonic == 5678); + + r = dual_timestamp_deserialize("+123 567", &t); + assert_se(r == 0); + assert_se(t.realtime == 123); + assert_se(t.monotonic == 567); + + /* Check that we get "infinity" on overflow. */ + r = dual_timestamp_deserialize("18446744073709551617 0", &t); + assert_se(r == 0); + assert_se(t.realtime == USEC_INFINITY); + assert_se(t.monotonic == 0); +} + +static void assert_similar(usec_t a, usec_t b) { + usec_t d; + + if (a > b) + d = a - b; + else + d = b - a; + + assert(d < 10*USEC_PER_SEC); +} + +static void test_usec_shift_clock(void) { + usec_t rt, mn, bt; + + rt = now(CLOCK_REALTIME); + mn = now(CLOCK_MONOTONIC); + bt = now(clock_boottime_or_monotonic()); + + assert_se(usec_shift_clock(USEC_INFINITY, CLOCK_REALTIME, CLOCK_MONOTONIC) == USEC_INFINITY); + + assert_similar(usec_shift_clock(rt + USEC_PER_HOUR, CLOCK_REALTIME, CLOCK_MONOTONIC), mn + USEC_PER_HOUR); + assert_similar(usec_shift_clock(rt + 2*USEC_PER_HOUR, CLOCK_REALTIME, clock_boottime_or_monotonic()), bt + 2*USEC_PER_HOUR); + assert_se(usec_shift_clock(rt + 3*USEC_PER_HOUR, CLOCK_REALTIME, CLOCK_REALTIME_ALARM) == rt + 3*USEC_PER_HOUR); + + assert_similar(usec_shift_clock(mn + 4*USEC_PER_HOUR, CLOCK_MONOTONIC, CLOCK_REALTIME_ALARM), rt + 4*USEC_PER_HOUR); + assert_similar(usec_shift_clock(mn + 5*USEC_PER_HOUR, CLOCK_MONOTONIC, clock_boottime_or_monotonic()), bt + 5*USEC_PER_HOUR); + assert_se(usec_shift_clock(mn + 6*USEC_PER_HOUR, CLOCK_MONOTONIC, CLOCK_MONOTONIC) == mn + 6*USEC_PER_HOUR); + + assert_similar(usec_shift_clock(bt + 7*USEC_PER_HOUR, clock_boottime_or_monotonic(), CLOCK_MONOTONIC), mn + 7*USEC_PER_HOUR); + assert_similar(usec_shift_clock(bt + 8*USEC_PER_HOUR, clock_boottime_or_monotonic(), CLOCK_REALTIME_ALARM), rt + 8*USEC_PER_HOUR); + assert_se(usec_shift_clock(bt + 9*USEC_PER_HOUR, clock_boottime_or_monotonic(), clock_boottime_or_monotonic()) == bt + 9*USEC_PER_HOUR); + + if (mn > USEC_PER_MINUTE) { + assert_similar(usec_shift_clock(rt - 30 * USEC_PER_SEC, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC), mn - 30 * USEC_PER_SEC); + assert_similar(usec_shift_clock(rt - 50 * USEC_PER_SEC, CLOCK_REALTIME, clock_boottime_or_monotonic()), bt - 50 * USEC_PER_SEC); + } +} + int main(int argc, char *argv[]) { uintmax_t x; + log_info("realtime=" USEC_FMT "\n" + "monotonic=" USEC_FMT "\n" + "boottime=" USEC_FMT "\n", + now(CLOCK_REALTIME), + now(CLOCK_MONOTONIC), + now(clock_boottime_or_monotonic())); + test_parse_sec(); + test_parse_sec_fix_0(); test_parse_time(); test_parse_nsec(); test_format_timespan(1); @@ -285,9 +402,12 @@ int main(int argc, char *argv[]) { test_timezone_is_valid(); test_get_timezones(); test_usec_add(); - test_usec_sub(); + test_usec_sub_signed(); + test_usec_sub_unsigned(); test_format_timestamp(); test_format_timestamp_utc(); + test_dual_timestamp_deserialize(); + test_usec_shift_clock(); /* Ensure time_t is signed */ assert_cc((time_t) -1 < (time_t) 1); diff --git a/src/test/test-udev.c b/src/test/test-udev.c index e965b4494a..c84bd8991e 100644 --- a/src/test/test-udev.c +++ b/src/test/test-udev.c @@ -88,7 +88,7 @@ int main(int argc, char *argv[]) { if (udev == NULL) return EXIT_FAILURE; - log_debug("version %s", VERSION); + log_debug("version %s", PACKAGE_VERSION); mac_selinux_init(); action = argv[1]; diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c index 12f48bf435..fd797b587e 100644 --- a/src/test/test-unit-file.c +++ b/src/test/test-unit-file.c @@ -146,7 +146,7 @@ static void test_config_parse_exec(void) { r = config_parse_exec(NULL, "fake", 4, "section", 1, "LValue", 0, "/RValue/ argv0 r1", &c, u); - assert_se(r == 0); + assert_se(r == -ENOEXEC); assert_se(c1->command_next == NULL); log_info("/* honour_argv0 */"); @@ -161,7 +161,7 @@ static void test_config_parse_exec(void) { r = config_parse_exec(NULL, "fake", 3, "section", 1, "LValue", 0, "@/RValue", &c, u); - assert_se(r == 0); + assert_se(r == -ENOEXEC); assert_se(c1->command_next == NULL); log_info("/* no command, whitespace only, reset */"); @@ -220,7 +220,7 @@ static void test_config_parse_exec(void) { "-@/RValue argv0 r1 ; ; " "/goo/goo boo", &c, u); - assert_se(r >= 0); + assert_se(r == -ENOEXEC); c1 = c1->command_next; check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true); @@ -374,7 +374,7 @@ static void test_config_parse_exec(void) { r = config_parse_exec(NULL, "fake", 4, "section", 1, "LValue", 0, path, &c, u); - assert_se(r == 0); + assert_se(r == -ENOEXEC); assert_se(c1->command_next == NULL); } @@ -401,21 +401,21 @@ static void test_config_parse_exec(void) { r = config_parse_exec(NULL, "fake", 4, "section", 1, "LValue", 0, "/path\\", &c, u); - assert_se(r == 0); + assert_se(r == -ENOEXEC); assert_se(c1->command_next == NULL); log_info("/* missing ending ' */"); r = config_parse_exec(NULL, "fake", 4, "section", 1, "LValue", 0, "/path 'foo", &c, u); - assert_se(r == 0); + assert_se(r == -ENOEXEC); assert_se(c1->command_next == NULL); log_info("/* missing ending ' with trailing backslash */"); r = config_parse_exec(NULL, "fake", 4, "section", 1, "LValue", 0, "/path 'foo\\", &c, u); - assert_se(r == 0); + assert_se(r == -ENOEXEC); assert_se(c1->command_next == NULL); log_info("/* invalid space between modifiers */"); diff --git a/src/timedate/meson.build b/src/timedate/meson.build new file mode 100644 index 0000000000..63124d665b --- /dev/null +++ b/src/timedate/meson.build @@ -0,0 +1,14 @@ +if conf.get('ENABLE_TIMEDATED', false) + install_data('org.freedesktop.timedate1.conf', + install_dir : dbuspolicydir) + install_data('org.freedesktop.timedate1.service', + install_dir : dbussystemservicedir) + + custom_target( + 'org.freedesktop.timedate1.policy', + input : 'org.freedesktop.timedate1.policy.in', + output : 'org.freedesktop.timedate1.policy', + command : intltool_command, + install : install_polkit, + install_dir : polkitpolicydir) +endif diff --git a/src/timesync/meson.build b/src/timesync/meson.build new file mode 100644 index 0000000000..4391afa93a --- /dev/null +++ b/src/timesync/meson.build @@ -0,0 +1,42 @@ +systemd_timesyncd_sources = files(''' + timesyncd.c + timesyncd-manager.c + timesyncd-manager.h + timesyncd-conf.c + timesyncd-conf.h + timesyncd-server.c + timesyncd-server.h +'''.split()) + +timesyncd_gperf_c = custom_target( + 'timesyncd-gperf.c', + input : 'timesyncd-gperf.gperf', + output : 'timesyncd-gperf.c', + command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@']) + +systemd_timesyncd_sources += [timesyncd_gperf_c] + +if conf.get('ENABLE_TIMESYNCD', false) + timesyncd_conf = configure_file( + input : 'timesyncd.conf.in', + output : 'timesyncd.conf', + configuration : substs) + install_data(timesyncd_conf, + install_dir : pkgsysconfdir) +endif + +############################################################ + +tests += [ + [['src/timesync/test-timesync.c', + 'src/timesync/timesyncd-manager.c', + 'src/timesync/timesyncd-manager.h', + 'src/timesync/timesyncd-conf.c', + 'src/timesync/timesyncd-conf.h', + 'src/timesync/timesyncd-server.c', + 'src/timesync/timesyncd-server.h', + timesyncd_gperf_c], + [libshared], + [libm], + 'ENABLE_TIMESYNCD'], +] diff --git a/src/timesync/test-timesync.c b/src/timesync/test-timesync.c new file mode 100644 index 0000000000..a5a3433022 --- /dev/null +++ b/src/timesync/test-timesync.c @@ -0,0 +1,51 @@ +/*** + This file is part of systemd. + + Copyright 2017 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +/* Some unit tests for the helper functions in timesyncd. */ + +#include "log.h" +#include "macro.h" +#include "timesyncd-conf.h" + +static void test_manager_parse_string(void) { + /* Make sure that NTP_SERVERS is configured to something + * that we can actually parse successfully. */ + + _cleanup_(manager_freep) Manager *m = NULL; + + assert_se(manager_new(&m) == 0); + + assert_se(!m->have_fallbacks); + assert_se(manager_parse_server_string(m, SERVER_FALLBACK, NTP_SERVERS) == 0); + assert_se(m->have_fallbacks); + assert_se(manager_parse_fallback_string(m, NTP_SERVERS) == 0); + + assert_se(manager_parse_server_string(m, SERVER_SYSTEM, "time1.foobar.com time2.foobar.com") == 0); + assert_se(manager_parse_server_string(m, SERVER_FALLBACK, "time1.foobar.com time2.foobar.com") == 0); + assert_se(manager_parse_server_string(m, SERVER_LINK, "time1.foobar.com time2.foobar.com") == 0); +} + +int main(int argc, char **argv) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + + test_manager_parse_string(); + + return 0; +} diff --git a/src/timesync/timesyncd-conf.c b/src/timesync/timesyncd-conf.c index bf25b112e1..f394d0a2af 100644 --- a/src/timesync/timesyncd-conf.c +++ b/src/timesync/timesyncd-conf.c @@ -34,6 +34,9 @@ int manager_parse_server_string(Manager *m, ServerType type, const char *string) first = type == SERVER_FALLBACK ? m->fallback_servers : m->system_servers; + if (type == SERVER_FALLBACK) + m->have_fallbacks = true; + for (;;) { _cleanup_free_ char *word = NULL; bool found = false; @@ -63,6 +66,13 @@ int manager_parse_server_string(Manager *m, ServerType type, const char *string) return 0; } +int manager_parse_fallback_string(Manager *m, const char *string) { + if (m->have_fallbacks) + return 0; + + return manager_parse_server_string(m, SERVER_FALLBACK, string); +} + int config_parse_servers( const char *unit, const char *filename, @@ -99,8 +109,8 @@ int manager_parse_config_file(Manager *m) { assert(m); return config_parse_many_nulstr(PKGSYSCONFDIR "/timesyncd.conf", - CONF_PATHS_NULSTR("systemd/timesyncd.conf.d"), - "Time\0", - config_item_perf_lookup, timesyncd_gperf_lookup, - false, m); + CONF_PATHS_NULSTR("systemd/timesyncd.conf.d"), + "Time\0", + config_item_perf_lookup, timesyncd_gperf_lookup, + false, m); } diff --git a/src/timesync/timesyncd-conf.h b/src/timesync/timesyncd-conf.h index 0280697e9c..0c4b44e707 100644 --- a/src/timesync/timesyncd-conf.h +++ b/src/timesync/timesyncd-conf.h @@ -29,3 +29,4 @@ int manager_parse_server_string(Manager *m, ServerType type, const char *string) int config_parse_servers(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 manager_parse_config_file(Manager *m); +int manager_parse_fallback_string(Manager *m, const char *string); diff --git a/src/timesync/timesyncd-manager.c b/src/timesync/timesyncd-manager.c index 6a6c1577c6..a24c821bdc 100644 --- a/src/timesync/timesyncd-manager.c +++ b/src/timesync/timesyncd-manager.c @@ -378,10 +378,10 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) { m->drift_ppm = tmx.freq / 65536; log_debug(" status : %04i %s\n" - " time now : %li.%03"PRI_USEC"\n" - " constant : %li\n" + " time now : %"PRI_TIME".%03"PRI_USEC"\n" + " constant : %"PRI_TIMEX"\n" " offset : %+.3f sec\n" - " freq offset : %+li (%i ppm)\n", + " freq offset : %+"PRI_TIMEX" (%i ppm)\n", tmx.status, tmx.status & STA_UNSYNC ? "unsync" : "sync", tmx.time.tv_sec, tmx.time.tv_usec / NSEC_PER_MSEC, tmx.constant, @@ -1124,10 +1124,6 @@ int manager_new(Manager **ret) { RATELIMIT_INIT(m->ratelimit, RATELIMIT_INTERVAL_USEC, RATELIMIT_BURST); - r = manager_parse_server_string(m, SERVER_FALLBACK, NTP_SERVERS); - if (r < 0) - return r; - r = sd_event_default(&m->event); if (r < 0) return r; diff --git a/src/timesync/timesyncd-manager.h b/src/timesync/timesyncd-manager.h index efe3e60d3e..cf681f6098 100644 --- a/src/timesync/timesyncd-manager.h +++ b/src/timesync/timesyncd-manager.h @@ -38,6 +38,8 @@ struct Manager { LIST_HEAD(ServerName, link_servers); LIST_HEAD(ServerName, fallback_servers); + bool have_fallbacks:1; + RateLimit ratelimit; bool exhausted_servers; diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c index b67d672a6a..ff90f04070 100644 --- a/src/timesync/timesyncd.c +++ b/src/timesync/timesyncd.c @@ -132,6 +132,12 @@ int main(int argc, char *argv[]) { if (r < 0) log_warning_errno(r, "Failed to parse configuration file: %m"); + r = manager_parse_fallback_string(m, NTP_SERVERS); + if (r < 0) { + log_error_errno(r, "Failed to parse fallback server strings: %m"); + goto finish; + } + log_debug("systemd-timesyncd running as pid " PID_FMT, getpid()); sd_notify(false, "READY=1\n" diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 7326597b8c..9419c99e28 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -724,10 +724,9 @@ static int path_set_xattrs(Item *i, const char *path) { n = strlen(*value); log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path); - if (lsetxattr(path, *name, *value, n, 0) < 0) { - log_error("Setting extended attribute %s=%s on %s failed: %m", *name, *value, path); - return -errno; - } + if (lsetxattr(path, *name, *value, n, 0) < 0) + return log_error_errno(errno, "Setting extended attribute %s=%s on %s failed: %m", + *name, *value, path); } return 0; } @@ -973,7 +972,7 @@ static int path_set_attribute(Item *item, const char *path) { r = chattr_fd(fd, f, item->attribute_mask); if (r < 0) - log_full_errno(r == -ENOTTY ? LOG_DEBUG : LOG_WARNING, + log_full_errno(r == -ENOTTY || r == -EOPNOTSUPP ? LOG_DEBUG : LOG_WARNING, r, "Cannot set file attribute for '%s', value=0x%08x, mask=0x%08x: %m", path, item->attribute_value, item->attribute_mask); @@ -1093,19 +1092,14 @@ static int item_do_children(Item *i, const char *path, action_t action) { static int glob_item(Item *i, action_t action, bool recursive) { _cleanup_globfree_ glob_t g = { - .gl_closedir = (void (*)(void *)) closedir, - .gl_readdir = (struct dirent *(*)(void *)) readdir, .gl_opendir = (void *(*)(const char *)) opendir_nomod, - .gl_lstat = lstat, - .gl_stat = stat, }; int r = 0, k; char **fn; - errno = 0; - k = glob(i->path, GLOB_NOSORT|GLOB_BRACE|GLOB_ALTDIRFUNC, NULL, &g); - if (k != 0 && k != GLOB_NOMATCH) - return log_error_errno(errno ?: EIO, "glob(%s) failed: %m", i->path); + k = safe_glob(i->path, GLOB_NOSORT|GLOB_BRACE, &g); + if (k < 0 && k != -ENOENT) + return log_error_errno(k, "glob(%s) failed: %m", i->path); STRV_FOREACH(fn, g.gl_pathv) { k = action(i, *fn); diff --git a/src/udev/ata_id/ata_id.c b/src/udev/ata_id/ata_id.c index 1e414664ce..ad152b9d31 100644 --- a/src/udev/ata_id/ata_id.c +++ b/src/udev/ata_id/ata_id.c @@ -427,6 +427,8 @@ int main(int argc, char *argv[]) {} }; + log_set_target(LOG_TARGET_AUTO); + udev_parse_config(); log_parse_environment(); log_open(); diff --git a/src/udev/cdrom_id/cdrom_id.c b/src/udev/cdrom_id/cdrom_id.c index 72f284f710..1f906a8525 100644 --- a/src/udev/cdrom_id/cdrom_id.c +++ b/src/udev/cdrom_id/cdrom_id.c @@ -38,6 +38,7 @@ #include "libudev-private.h" #include "random-util.h" +#include "udev-util.h" /* device info */ static unsigned int cd_cd_rom; @@ -843,8 +844,7 @@ static int cd_media_toc(struct udev *udev, int fd) return 0; } -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { struct udev *udev; static const struct option options[] = { { "lock-media", no_argument, NULL, 'l' }, @@ -862,6 +862,8 @@ int main(int argc, char *argv[]) int cnt; int rc = 0; + log_set_target(LOG_TARGET_AUTO); + udev_parse_config(); log_parse_environment(); log_open(); diff --git a/src/udev/collect/collect.c b/src/udev/collect/collect.c index 0e973cd521..57dfb016f9 100644 --- a/src/udev/collect/collect.c +++ b/src/udev/collect/collect.c @@ -29,6 +29,7 @@ #include "macro.h" #include "stdio-util.h" #include "string-util.h" +#include "udev-util.h" #define BUFSIZE 16 #define UDEV_ALARM_TIMEOUT 180 @@ -343,9 +344,7 @@ static void everybody(void) } } -int main(int argc, char **argv) -{ - struct udev *udev; +int main(int argc, char **argv) { static const struct option options[] = { { "add", no_argument, NULL, 'a' }, { "remove", no_argument, NULL, 'r' }, @@ -361,11 +360,10 @@ int main(int argc, char **argv) int prune = 0; char tmpdir[UTIL_PATH_SIZE]; - udev = udev_new(); - if (udev == NULL) { - ret = EXIT_FAILURE; - goto exit; - } + log_set_target(LOG_TARGET_AUTO); + udev_parse_config(); + log_parse_environment(); + log_open(); for (;;) { int option; @@ -386,26 +384,23 @@ int main(int argc, char **argv) break; case 'h': usage(); - goto exit; + return 0; default: - ret = 1; - goto exit; + return 1; } } argi = optind; if (argi + 2 > argc) { printf("Missing parameter(s)\n"); - ret = 1; - goto exit; + return 1; } checkpoint = argv[argi++]; us = argv[argi++]; if (signal(SIGALRM, sig_alrm) == SIG_ERR) { fprintf(stderr, "Cannot set SIGALRM: %m\n"); - ret = 2; - goto exit; + return 2; } udev_list_node_init(&bunch); @@ -485,7 +480,5 @@ out: everybody(); if (ret >= 0) printf("COLLECT_%s=%d\n", checkpoint, ret); -exit: - udev_unref(udev); return ret; } diff --git a/src/udev/generate-keyboard-keys-gperf.sh b/src/udev/generate-keyboard-keys-gperf.sh new file mode 100755 index 0000000000..5724e4e3dc --- /dev/null +++ b/src/udev/generate-keyboard-keys-gperf.sh @@ -0,0 +1,10 @@ +#!/bin/sh -eu +awk ' BEGIN { + print "struct key_name { const char* name; unsigned short id; };" + print "%null-strings" + print "%%" + } + + /^KEY_/ { print tolower(substr($1 ,5)) ", " $1 } + { print tolower($1) ", " $1 } +' < "$1" diff --git a/src/udev/generate-keyboard-keys-list.sh b/src/udev/generate-keyboard-keys-list.sh new file mode 100755 index 0000000000..7a74e0dae1 --- /dev/null +++ b/src/udev/generate-keyboard-keys-list.sh @@ -0,0 +1,6 @@ +#!/bin/sh -eu + +$1 -dM -include linux/input.h - </dev/null | awk ' + /\<(KEY_(MAX|MIN_INTERESTING))|(BTN_(MISC|MOUSE|JOYSTICK|GAMEPAD|DIGI|WHEEL|TRIGGER_HAPPY))\>/ { next } + /^#define[ \t]+(KEY|BTN)_[^ ]+[ \t]+[0-9BK]/ { print $2 } +' diff --git a/src/udev/meson.build b/src/udev/meson.build new file mode 100644 index 0000000000..eeb341f8d1 --- /dev/null +++ b/src/udev/meson.build @@ -0,0 +1,152 @@ +udevadm_sources = files(''' + udevadm.c + udevadm-info.c + udevadm-control.c + udevadm-monitor.c + udevadm-hwdb.c + udevadm-settle.c + udevadm-trigger.c + udevadm-test.c + udevadm-test-builtin.c + udevadm-util.c + udevadm-util.h +'''.split()) + +systemd_udevd_sources = files('udevd.c') + +libudev_core_sources = ''' + udev.h + udev-event.c + udev-watch.c + udev-node.c + udev-rules.c + udev-ctrl.c + udev-builtin.c + udev-builtin-btrfs.c + udev-builtin-hwdb.c + udev-builtin-input_id.c + udev-builtin-keyboard.c + udev-builtin-net_id.c + udev-builtin-net_setup_link.c + udev-builtin-path_id.c + udev-builtin-usb_id.c + net/link-config.c + net/link-config.h + net/ethtool-util.c + net/ethtool-util.h +'''.split() + +if conf.get('HAVE_KMOD', false) + libudev_core_sources += ['udev-builtin-kmod.c'] +endif + +if conf.get('HAVE_BLKID', false) + libudev_core_sources += ['udev-builtin-blkid.c'] +endif + +if conf.get('HAVE_ACL', false) + libudev_core_sources += ['udev-builtin-uaccess.c', + logind_acl_c, + sd_login_c] +endif + +############################################################ + +generate_keyboard_keys_list = find_program('generate-keyboard-keys-list.sh') +keyboard_keys_list_txt = custom_target( + 'keyboard-keys-list.txt', + output : 'keyboard-keys-list.txt', + command : [generate_keyboard_keys_list, cpp], + capture : true) + +generate_keyboard_keys_gperf = find_program('generate-keyboard-keys-gperf.sh') +fname = 'keyboard-keys-from-name.gperf' +gperf_file = custom_target( + fname, + input : keyboard_keys_list_txt, + output : fname, + command : [generate_keyboard_keys_gperf, '@INPUT@'], + capture : true) + +fname = 'keyboard-keys-from-name.h' +keyboard_keys_from_name_h = custom_target( + fname, + input : gperf_file, + output : fname, + command : [gperf, + '-L', 'ANSI-C', '-t', + '-N', 'keyboard_lookup_key', + '-H', 'hash_key_name', + '-p', '-C', + '@INPUT@'], + capture : true) + +############################################################ + +link_config_gperf_c = custom_target( + 'link-config-gperf.c', + input : 'net/link-config-gperf.gperf', + output : 'link-config-gperf.c', + command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@']) + +############################################################ + +if get_option('link-udev-shared') + udev_link_with = [libshared] + udev_rpath = rootlibexecdir +else + udev_link_with = [libshared_static, + libsystemd_internal] + udev_rpath = '' +endif + +libudev_internal = static_library( + 'udev', + libudev_sources, + include_directories : includes, + link_with : udev_link_with) + +libudev_core_includes = [includes, include_directories('net')] +libudev_core = static_library( + 'udev-core', + libudev_core_sources, + link_config_gperf_c, + keyboard_keys_from_name_h, + include_directories : libudev_core_includes, + link_with : udev_link_with, + dependencies : [libblkid, libkmod]) + +foreach prog : [['ata_id/ata_id.c'], + ['cdrom_id/cdrom_id.c'], + ['collect/collect.c'], + ['scsi_id/scsi_id.c', + 'scsi_id/scsi_id.h', + 'scsi_id/scsi_serial.c', + 'scsi_id/scsi.h'], + ['v4l_id/v4l_id.c'], + ['mtd_probe/mtd_probe.c', + 'mtd_probe/mtd_probe.h', + 'mtd_probe/probe_smartmedia.c']] + + executable(prog[0].split('/')[0], + prog, + include_directories : includes, + c_args : ['-DLOG_REALM=LOG_REALM_UDEV'], + link_with : [libudev_internal], + install_rpath : udev_rpath, + install : true, + install_dir : udevlibexecdir) +endforeach + +install_data('udev.conf', + install_dir : join_paths(sysconfdir, 'udev')) + +udev_pc = configure_file( + input : 'udev.pc.in', + output : 'udev.pc', + configuration : substs) +install_data(udev_pc, + install_dir : pkgconfigdatadir) + +meson.add_install_script('sh', '-c', + mkdir_p.format(join_paths(sysconfdir, 'udev/rules.d'))) diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c index d7edbb396b..201fc23437 100644 --- a/src/udev/net/ethtool-util.c +++ b/src/udev/net/ethtool-util.c @@ -25,6 +25,7 @@ #include "conf-parser.h" #include "ethtool-util.h" #include "log.h" +#include "link-config.h" #include "socket-util.h" #include "string-table.h" #include "strxcpyx.h" @@ -48,6 +49,17 @@ static const char* const wol_table[_WOL_MAX] = { DEFINE_STRING_TABLE_LOOKUP(wol, WakeOnLan); DEFINE_CONFIG_PARSE_ENUM(config_parse_wol, wol, WakeOnLan, "Failed to parse WakeOnLan setting"); +static const char* const port_table[_NET_DEV_PORT_MAX] = { + [NET_DEV_PORT_TP] = "tp", + [NET_DEV_PORT_AUI] = "aui", + [NET_DEV_PORT_MII] = "mii", + [NET_DEV_PORT_FIBRE] = "fibre", + [NET_DEV_PORT_BNC] = "bnc" +}; + +DEFINE_STRING_TABLE_LOOKUP(port, NetDevPort); +DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort, "Failed to parse Port setting"); + static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = { [NET_DEV_FEAT_GSO] = "tx-generic-segmentation", [NET_DEV_FEAT_GRO] = "rx-gro", @@ -488,12 +500,12 @@ static int set_sset(int *fd, struct ifreq *ifr, const struct ethtool_link_usetti * enabled speed and @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode. */ -int ethtool_set_glinksettings(int *fd, const char *ifname, unsigned int speed, Duplex duplex, int autonegotiation) { +int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *link) { _cleanup_free_ struct ethtool_link_usettings *u = NULL; struct ifreq ifr = {}; int r; - if (autonegotiation != 0) { + if (link->autonegotiation != 0) { log_info("link_config: autonegotiation is unset or enabled, the speed and duplex are not writable."); return 0; } @@ -514,13 +526,16 @@ int ethtool_set_glinksettings(int *fd, const char *ifname, unsigned int speed, D return log_warning_errno(r, "link_config: Cannot get device settings for %s : %m", ifname); } - if (speed) - u->base.speed = speed; + if (link->speed) + u->base.speed = DIV_ROUND_UP(link->speed, 1000000); + + if (link->duplex != _DUP_INVALID) + u->base.duplex = link->duplex; - if (duplex != _DUP_INVALID) - u->base.duplex = duplex; + if (link->port != _NET_DEV_PORT_INVALID) + u->base.port = link->port; - u->base.autoneg = autonegotiation; + u->base.autoneg = link->autonegotiation; if (u->base.cmd == ETHTOOL_GLINKSETTINGS) r = set_slinksettings(fd, &ifr, u); diff --git a/src/udev/net/ethtool-util.h b/src/udev/net/ethtool-util.h index 75d6af396b..27ce0e0aba 100644 --- a/src/udev/net/ethtool-util.h +++ b/src/udev/net/ethtool-util.h @@ -24,11 +24,13 @@ #include "missing.h" +struct link_config; + /* we can't use DUPLEX_ prefix, as it * clashes with <linux/ethtool.h> */ typedef enum Duplex { - DUP_FULL, - DUP_HALF, + DUP_HALF = DUPLEX_HALF, + DUP_FULL = DUPLEX_FULL, _DUP_MAX, _DUP_INVALID = -1 } Duplex; @@ -51,6 +53,18 @@ typedef enum NetDevFeature { _NET_DEV_FEAT_INVALID = -1 } NetDevFeature; +typedef enum NetDevPort { + NET_DEV_PORT_TP = 0x00, + NET_DEV_PORT_AUI = 0x01, + NET_DEV_PORT_MII = 0x02, + NET_DEV_PORT_FIBRE = 0x03, + NET_DEV_PORT_BNC = 0x04, + NET_DEV_PORT_DA = 0x05, + NET_DEV_PORT_NONE = 0xef, + NET_DEV_PORT_OTHER = 0xff, + _NET_DEV_PORT_MAX, + _NET_DEV_PORT_INVALID = -1 +} NetDevPort; #define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32 (SCHAR_MAX) @@ -71,7 +85,7 @@ int ethtool_get_driver(int *fd, const char *ifname, char **ret); int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex); int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol); int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features); -int ethtool_set_glinksettings(int *fd, const char *ifname, unsigned int speed, Duplex duplex, int autoneg); +int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *link); const char *duplex_to_string(Duplex d) _const_; Duplex duplex_from_string(const char *d) _pure_; @@ -79,5 +93,9 @@ Duplex duplex_from_string(const char *d) _pure_; const char *wol_to_string(WakeOnLan wol) _const_; WakeOnLan wol_from_string(const char *wol) _pure_; +const char *port_to_string(NetDevPort port) _const_; +NetDevPort port_from_string(const char *port) _pure_; + int config_parse_duplex(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_wol(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_port(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/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index 78e551df22..5488867ba7 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -36,6 +36,7 @@ Link.BitsPerSecond, config_parse_si_size, 0, Link.Duplex, config_parse_duplex, 0, offsetof(link_config, duplex) Link.AutoNegotiation, config_parse_tristate, 0, offsetof(link_config, autonegotiation) Link.WakeOnLan, config_parse_wol, 0, offsetof(link_config, wol) +Link.Port, config_parse_port, 0, offsetof(link_config, port) Link.GenericSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GSO]) Link.TCPSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_TSO]) Link.UDPSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_UFO]) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 3af87f1388..05a186357c 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -167,6 +167,7 @@ static int load_link(link_config_ctx *ctx, const char *filename) { link->mac_policy = _MACPOLICY_INVALID; link->wol = _WOL_INVALID; link->duplex = _DUP_INVALID; + link->port = _NET_DEV_PORT_INVALID; link->autonegotiation = -1; memset(&link->features, -1, sizeof(link->features)); @@ -377,12 +378,13 @@ int link_config_apply(link_config_ctx *ctx, link_config *config, if (!old_name) return -EINVAL; - - speed = DIV_ROUND_UP(config->speed, 1000000); - - r = ethtool_set_glinksettings(&ctx->ethtool_fd, old_name, speed, config->duplex, config->autonegotiation); + r = ethtool_set_glinksettings(&ctx->ethtool_fd, old_name, config); if (r < 0) { + if (config->port != _NET_DEV_PORT_INVALID) + log_warning_errno(r, "Could not set port (%s) of %s: %m", port_to_string(config->port), old_name); + + speed = DIV_ROUND_UP(config->speed, 1000000); if (r == -EOPNOTSUPP) r = ethtool_set_speed(&ctx->ethtool_fd, old_name, speed, config->duplex); diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index 5a25cec6fd..ff91a65135 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -71,6 +71,7 @@ struct link_config { Duplex duplex; int autonegotiation; WakeOnLan wol; + NetDevPort port; NetDevFeature features[_NET_DEV_FEAT_MAX]; LIST_FIELDS(link_config, links); diff --git a/src/udev/scsi_id/scsi_id.c b/src/udev/scsi_id/scsi_id.c index 4655691642..3c3d7a6b33 100644 --- a/src/udev/scsi_id/scsi_id.c +++ b/src/udev/scsi_id/scsi_id.c @@ -391,7 +391,7 @@ static int set_options(struct udev *udev, break; case 'V': - printf("%s\n", VERSION); + printf("%s\n", PACKAGE_VERSION); exit(0); case 'x': @@ -577,6 +577,8 @@ int main(int argc, char **argv) int newargc; char **newargv = NULL; + log_set_target(LOG_TARGET_AUTO); + udev_parse_config(); log_parse_environment(); log_open(); diff --git a/src/udev/udev-builtin-blkid.c b/src/udev/udev-builtin-blkid.c index 9037aa1304..11d7085153 100644 --- a/src/udev/udev-builtin-blkid.c +++ b/src/udev/udev-builtin-blkid.c @@ -18,7 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <blkid/blkid.h> +#include <blkid.h> #include <errno.h> #include <fcntl.h> #include <getopt.h> @@ -30,6 +30,7 @@ #include "sd-id128.h" #include "alloc-util.h" +#include "blkid-util.h" #include "efivars.h" #include "fd-util.h" #include "gpt.h" @@ -225,10 +226,9 @@ static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool t int64_t offset = 0; bool noraid = false; _cleanup_close_ int fd = -1; - blkid_probe pr; + _cleanup_blkid_free_probe_ blkid_probe pr = NULL; const char *data; const char *name; - const char *prtype = NULL; int nvals; int i; int err = 0; @@ -264,8 +264,7 @@ static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool t blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID | BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE | - BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION | - BLKID_SUBLKS_BADCSUM); + BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION); if (noraid) blkid_probe_filter_superblocks_usage(pr, BLKID_FLTR_NOTIN, BLKID_USAGE_RAID); @@ -287,15 +286,6 @@ static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool t err = probe_superblocks(pr); if (err < 0) goto out; - if (blkid_probe_has_value(pr, "SBBADCSUM")) { - if (!blkid_probe_lookup_value(pr, "TYPE", &prtype, NULL)) - log_warning("incorrect %s checksum on %s", - prtype, udev_device_get_devnode(dev)); - else - log_warning("incorrect checksum on %s", - udev_device_get_devnode(dev)); - goto out; - } /* If we are a partition then our parent passed on the root * partition UUID to us */ @@ -321,7 +311,6 @@ static int builtin_blkid(struct udev_device *dev, int argc, char *argv[], bool t if (is_gpt) find_gpt_root(dev, pr, test); - blkid_free_probe(pr); out: if (err < 0) return EXIT_FAILURE; diff --git a/src/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c index 51f364bf94..60f760ef77 100644 --- a/src/udev/udev-builtin-input_id.c +++ b/src/udev/udev-builtin-input_id.c @@ -31,6 +31,7 @@ #include <linux/input.h> #include "fd-util.h" +#include "missing.h" #include "stdio-util.h" #include "string-util.h" #include "udev.h" @@ -44,6 +45,17 @@ #define LONG(x) ((x)/BITS_PER_LONG) #define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1) +struct range { + unsigned start; + unsigned end; +}; + +/* key code ranges above BTN_MISC (start is inclusive, stop is exclusive)*/ +static const struct range high_key_blocks[] = { + { KEY_OK, BTN_DPAD_UP }, + { KEY_ALS_TOGGLE, BTN_TRIGGER_HAPPY } +}; + static inline int abs_size_mm(const struct input_absinfo *absinfo) { /* Resolution is defined to be in units/mm for ABS_X/Y */ return (absinfo->maximum - absinfo->minimum) / absinfo->resolution; @@ -137,6 +149,7 @@ static bool test_pointers(struct udev_device *dev, const unsigned long* bitmask_rel, const unsigned long* bitmask_props, bool test) { + int button, axis; bool has_abs_coordinates = false; bool has_rel_coordinates = false; bool has_mt_coordinates = false; @@ -172,7 +185,8 @@ static bool test_pointers(struct udev_device *dev, is_pointing_stick = test_bit(INPUT_PROP_POINTING_STICK, bitmask_props); stylus_or_pen = test_bit(BTN_STYLUS, bitmask_key) || test_bit(BTN_TOOL_PEN, bitmask_key); finger_but_no_pen = test_bit(BTN_TOOL_FINGER, bitmask_key) && !test_bit(BTN_TOOL_PEN, bitmask_key); - has_mouse_button = test_bit(BTN_LEFT, bitmask_key); + for (button = BTN_MOUSE; button < BTN_JOYSTICK && !has_mouse_button; button++) + has_mouse_button = test_bit(button, bitmask_key); has_rel_coordinates = test_bit(EV_REL, bitmask_ev) && test_bit(REL_X, bitmask_rel) && test_bit(REL_Y, bitmask_rel); has_mt_coordinates = test_bit(ABS_MT_POSITION_X, bitmask_abs) && test_bit(ABS_MT_POSITION_Y, bitmask_abs); @@ -183,18 +197,15 @@ static bool test_pointers(struct udev_device *dev, has_touch = test_bit(BTN_TOUCH, bitmask_key); /* joysticks don't necessarily have buttons; e. g. * rudders/pedals are joystick-like, but buttonless; they have - * other fancy axes */ - has_joystick_axes_or_buttons = test_bit(BTN_TRIGGER, bitmask_key) || - test_bit(BTN_A, bitmask_key) || - test_bit(BTN_1, bitmask_key) || - test_bit(ABS_RX, bitmask_abs) || - test_bit(ABS_RY, bitmask_abs) || - test_bit(ABS_RZ, bitmask_abs) || - test_bit(ABS_THROTTLE, bitmask_abs) || - test_bit(ABS_RUDDER, bitmask_abs) || - test_bit(ABS_WHEEL, bitmask_abs) || - test_bit(ABS_GAS, bitmask_abs) || - test_bit(ABS_BRAKE, bitmask_abs); + * other fancy axes. Others have buttons only but no axes. */ + for (button = BTN_JOYSTICK; button < BTN_DIGI && !has_joystick_axes_or_buttons; button++) + has_joystick_axes_or_buttons = test_bit(button, bitmask_key); + for (button = BTN_TRIGGER_HAPPY1; button <= BTN_TRIGGER_HAPPY40 && !has_joystick_axes_or_buttons; button++) + has_joystick_axes_or_buttons = test_bit(button, bitmask_key); + for (button = BTN_DPAD_UP; button <= BTN_DPAD_RIGHT && !has_joystick_axes_or_buttons; button++) + has_joystick_axes_or_buttons = test_bit(button, bitmask_key); + for (axis = ABS_RX; axis < ABS_PRESSURE && !has_joystick_axes_or_buttons; axis++) + has_joystick_axes_or_buttons = test_bit(axis, bitmask_abs); if (has_abs_coordinates) { if (stylus_or_pen) @@ -209,7 +220,10 @@ static bool test_pointers(struct udev_device *dev, is_touchscreen = true; else if (has_joystick_axes_or_buttons) is_joystick = true; + } else if (has_joystick_axes_or_buttons) { + is_joystick = true; } + if (has_mt_coordinates) { if (stylus_or_pen) is_tablet = true; @@ -219,7 +233,9 @@ static bool test_pointers(struct udev_device *dev, is_touchscreen = true; } - if (has_rel_coordinates && has_mouse_button) + if (has_mouse_button && + (has_rel_coordinates || + !has_abs_coordinates)) /* mouse buttons and no axis */ is_mouse = true; if (is_pointing_stick) @@ -260,13 +276,16 @@ static bool test_key(struct udev_device *dev, found |= bitmask_key[i]; log_debug("test_key: checking bit block %lu for any keys; found=%i", (unsigned long)i*BITS_PER_LONG, found > 0); } - /* If there are no keys in the lower block, check the higher block */ + /* If there are no keys in the lower block, check the higher blocks */ if (!found) { - for (i = KEY_OK; i < BTN_TRIGGER_HAPPY; ++i) { - if (test_bit(i, bitmask_key)) { - log_debug("test_key: Found key %x in high block", i); - found = 1; - break; + unsigned block; + for (block = 0; block < (sizeof(high_key_blocks) / sizeof(struct range)); ++block) { + for (i = high_key_blocks[block].start; i < high_key_blocks[block].end; ++i) { + if (test_bit(i, bitmask_key)) { + log_debug("test_key: Found key %x in high block", i); + found = 1; + break; + } } } } diff --git a/src/udev/udev-builtin-keyboard.c b/src/udev/udev-builtin-keyboard.c index 09024116f2..e316bb93ba 100644 --- a/src/udev/udev-builtin-keyboard.c +++ b/src/udev/udev-builtin-keyboard.c @@ -29,7 +29,7 @@ #include "string-util.h" #include "udev.h" -static const struct key *keyboard_lookup_key(const char *str, GPERF_LEN_TYPE len); +static const struct key_name *keyboard_lookup_key(const char *str, GPERF_LEN_TYPE len); #include "keyboard-keys-from-name.h" static int install_force_release(struct udev_device *dev, const unsigned *release, unsigned release_count) { @@ -76,7 +76,7 @@ static void map_keycode(int fd, const char *devnode, int scancode, const char *k unsigned key; } map; char *endptr; - const struct key *k; + const struct key_name *k; unsigned keycode_num; /* translate identifier to key code */ @@ -173,6 +173,9 @@ static void set_trackpoint_sensitivity(struct udev_device *dev, const char *valu if (r < 0) { log_error("Unable to parse POINTINGSTICK_SENSITIVITY '%s' for '%s'", value, udev_device_get_devnode(dev)); return; + } else if (val_i < 0 || val_i > 255) { + log_error("POINTINGSTICK_SENSITIVITY %d outside range [0..255] for '%s' ", val_i, udev_device_get_devnode(dev)); + return; } xsprintf(val_s, "%d", val_i); @@ -198,6 +201,7 @@ static int builtin_keyboard(struct udev_device *dev, int argc, char *argv[], boo unsigned release_count = 0; _cleanup_close_ int fd = -1; const char *node; + int has_abs = -1; node = udev_device_get_devnode(dev); if (!node) { @@ -258,6 +262,24 @@ static int builtin_keyboard(struct udev_device *dev, int argc, char *argv[], boo return EXIT_FAILURE; } + if (has_abs == -1) { + unsigned long bits; + int rc; + + rc = ioctl(fd, EVIOCGBIT(0, sizeof(bits)), &bits); + if (rc < 0) { + log_error_errno(errno, "Unable to EVIOCGBIT device \"%s\"", node); + return EXIT_FAILURE; + } + + has_abs = !!(bits & (1 << EV_ABS)); + if (!has_abs) + log_warning("EVDEV_ABS override set but no EV_ABS present on device \"%s\"", node); + } + + if (!has_abs) + continue; + override_abs(fd, node, evcode, udev_list_entry_get_value(entry)); } else if (streq(key, "POINTINGSTICK_SENSITIVITY")) set_trackpoint_sensitivity(dev, udev_list_entry_get_value(entry)); diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c index bd7b789cad..46eb6114ff 100644 --- a/src/udev/udev-builtin-net_id.c +++ b/src/udev/udev-builtin-net_id.c @@ -45,6 +45,8 @@ * — PCI geographical location * [P<domain>]p<bus>s<slot>[f<function>][u<port>][..][c<config>][i<interface>] * — USB port number chain + * v<slot> - VIO slot number (IBM PowerVM) + * a<vendor><model>i<instance> — Platform bus ACPI instance id * * All multi-function PCI devices will carry the [f<function>] number in the * device name, including the function 0 device. @@ -122,6 +124,8 @@ enum netname_type{ NET_BCMA, NET_VIRTIO, NET_CCW, + NET_VIO, + NET_PLATFORM, }; struct netnames { @@ -139,6 +143,8 @@ struct netnames { char usb_ports[IFNAMSIZ]; char bcma_core[IFNAMSIZ]; char ccw_busid[IFNAMSIZ]; + char vio_slot[IFNAMSIZ]; + char platform_path[IFNAMSIZ]; }; /* skip intermediate virtio devices */ @@ -319,6 +325,87 @@ out: return err; } +static int names_vio(struct udev_device *dev, struct netnames *names) { + struct udev_device *parent; + unsigned busid, slotid, ethid; + const char *syspath; + + /* check if our direct parent is a VIO device with no other bus in-between */ + parent = udev_device_get_parent(dev); + if (!parent) + return -ENOENT; + + if (!streq_ptr("vio", udev_device_get_subsystem(parent))) + return -ENOENT; + + /* The devices' $DEVPATH number is tied to (virtual) hardware (slot id + * selected in the HMC), thus this provides a reliable naming (e.g. + * "/devices/vio/30000002/net/eth1"); we ignore the bus number, as + * there should only ever be one bus, and then remove leading zeros. */ + syspath = udev_device_get_syspath(dev); + + if (sscanf(syspath, "/sys/devices/vio/%4x%4x/net/eth%u", &busid, &slotid, ðid) != 3) + return -EINVAL; + + xsprintf(names->vio_slot, "v%u", slotid); + names->type = NET_VIO; + return 0; +} + +#define _PLATFORM_TEST "/sys/devices/platform/vvvvPPPP" +#define _PLATFORM_PATTERN4 "/sys/devices/platform/%4s%4x:%2x/net/eth%u" +#define _PLATFORM_PATTERN3 "/sys/devices/platform/%3s%4x:%2x/net/eth%u" + +static int names_platform(struct udev_device *dev, struct netnames *names, bool test) { + struct udev_device *parent; + char vendor[5]; + unsigned model, instance, ethid; + const char *syspath, *pattern, *validchars; + + /* check if our direct parent is a platform device with no other bus in-between */ + parent = udev_device_get_parent(dev); + if (!parent) + return -ENOENT; + + if (!streq_ptr("platform", udev_device_get_subsystem(parent))) + return -ENOENT; + + syspath = udev_device_get_syspath(dev); + + /* syspath is too short, to have a valid ACPI instance */ + if (strlen(syspath) < sizeof _PLATFORM_TEST) + return -EINVAL; + + /* Vendor ID can be either PNP ID (3 chars A-Z) or ACPI ID (4 chars A-Z and numerals) */ + if (syspath[sizeof _PLATFORM_TEST - 1] == ':') { + pattern = _PLATFORM_PATTERN4; + validchars = UPPERCASE_LETTERS DIGITS; + } else { + pattern = _PLATFORM_PATTERN3; + validchars = UPPERCASE_LETTERS; + } + + /* Platform devices are named after ACPI table match, and instance id + * eg. "/sys/devices/platform/HISI00C2:00"); + * The Vendor (3 or 4 char), followed by hexdecimal model number : instance id. + */ + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + if (sscanf(syspath, pattern, vendor, &model, &instance, ðid) != 4) + return -EINVAL; +#pragma GCC diagnostic pop + + if (!in_charset(vendor, validchars)) + return -ENOENT; + + ascii_strlower(vendor); + + xsprintf(names->platform_path, "a%s%xi%u", vendor, model, instance); + names->type = NET_PLATFORM; + return 0; +} + static int names_pci(struct udev_device *dev, struct netnames *names) { struct udev_device *parent; @@ -591,6 +678,26 @@ static int builtin_net_id(struct udev_device *dev, int argc, char *argv[], bool goto out; } + /* get ibmveth/ibmvnic slot-based names. */ + err = names_vio(dev, &names); + if (err >= 0 && names.type == NET_VIO) { + char str[IFNAMSIZ]; + + if (snprintf(str, sizeof(str), "%s%s", prefix, names.vio_slot) < (int)sizeof(str)) + udev_builtin_add_property(dev, test, "ID_NET_NAME_SLOT", str); + goto out; + } + + /* get ACPI path names for ARM64 platform devices */ + err = names_platform(dev, &names, test); + if (err >= 0 && names.type == NET_PLATFORM) { + char str[IFNAMSIZ]; + + if (snprintf(str, sizeof(str), "%s%s", prefix, names.platform_path) < (int)sizeof(str)) + udev_builtin_add_property(dev, test, "ID_NET_NAME_PATH", str); + goto out; + } + /* get PCI based path names, we compose only PCI based paths */ err = names_pci(dev, &names); if (err < 0) diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c index dbefbbe175..92e4f8d9c0 100644 --- a/src/udev/udev-ctrl.c +++ b/src/udev/udev-ctrl.c @@ -239,7 +239,7 @@ static int ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int int err = 0; memzero(&ctrl_msg_wire, sizeof(struct udev_ctrl_msg_wire)); - strcpy(ctrl_msg_wire.version, "udev-" VERSION); + strcpy(ctrl_msg_wire.version, "udev-" PACKAGE_VERSION); ctrl_msg_wire.magic = UDEV_CTRL_MAGIC; ctrl_msg_wire.type = type; diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index 3f9c3ed0cf..601f0ee13d 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -58,7 +58,7 @@ struct udev_event *udev_event_new(struct udev_device *dev) { event->udev = udev; udev_list_init(udev, &event->run_list, false); udev_list_init(udev, &event->seclabel_list, false); - event->birth_usec = clock_boottime_or_monotonic(); + event->birth_usec = now(CLOCK_MONOTONIC); return event; } @@ -520,7 +520,7 @@ static void spawn_read(struct udev_event *event, if (timeout_usec > 0) { usec_t age_usec; - age_usec = clock_boottime_or_monotonic() - event->birth_usec; + age_usec = now(CLOCK_MONOTONIC) - event->birth_usec; if (age_usec >= timeout_usec) { log_error("timeout '%s'", cmd); return; @@ -671,13 +671,13 @@ static int spawn_wait(struct udev_event *event, if (timeout_usec > 0) { usec_t usec, age_usec; - usec = now(clock_boottime_or_monotonic()); + usec = now(CLOCK_MONOTONIC); age_usec = usec - event->birth_usec; if (age_usec < timeout_usec) { if (timeout_warn_usec > 0 && timeout_warn_usec < timeout_usec && age_usec < timeout_warn_usec) { spawn.timeout_warn = timeout_warn_usec - age_usec; - r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(), + r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC, usec + spawn.timeout_warn, USEC_PER_SEC, on_spawn_timeout_warning, &spawn); if (r < 0) @@ -686,7 +686,7 @@ static int spawn_wait(struct udev_event *event, spawn.timeout = timeout_usec - age_usec; - r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(), + r = sd_event_add_time(e, NULL, CLOCK_MONOTONIC, usec + spawn.timeout, USEC_PER_SEC, on_spawn_timeout, &spawn); if (r < 0) return r; diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index 4d07b8fce0..294a322547 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -474,6 +474,13 @@ static int add_token(struct udev_rules *rules, struct token *token) { return 0; } +static void log_unknown_owner(int error, const char *entity, const char *owner) { + if (IN_SET(abs(error), ENOENT, ESRCH)) + log_error("Specified %s '%s' unknown", entity, owner); + else + log_error_errno(error, "Error resolving %s '%s': %m", entity, owner); +} + static uid_t add_uid(struct udev_rules *rules, const char *owner) { unsigned int i; uid_t uid = 0; @@ -489,12 +496,8 @@ static uid_t add_uid(struct udev_rules *rules, const char *owner) { } } r = get_user_creds(&owner, &uid, NULL, NULL, NULL); - if (r < 0) { - if (r == -ENOENT || r == -ESRCH) - log_error("specified user '%s' unknown", owner); - else - log_error_errno(r, "error resolving user '%s': %m", owner); - } + if (r < 0) + log_unknown_owner(r, "user", owner); /* grow buffer if needed */ if (rules->uids_cur+1 >= rules->uids_max) { @@ -536,12 +539,8 @@ static gid_t add_gid(struct udev_rules *rules, const char *group) { } } r = get_group_creds(&group, &gid); - if (r < 0) { - if (r == -ENOENT || r == -ESRCH) - log_error("specified group '%s' unknown", group); - else - log_error_errno(r, "error resolving group '%s': %m", group); - } + if (r < 0) + log_unknown_owner(r, "group", group); /* grow buffer if needed */ if (rules->gids_cur+1 >= rules->gids_max) { @@ -814,7 +813,7 @@ static const char *get_key_attribute(struct udev *udev, char *str) { attr++; pos = strchr(attr, '}'); if (pos == NULL) { - log_error("missing closing brace for format"); + log_error("Missing closing brace for format"); return NULL; } pos[0] = '\0'; @@ -2119,11 +2118,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules, event->owner_set = true; r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL); if (r < 0) { - if (r == -ENOENT || r == -ESRCH) - log_error("specified user '%s' unknown", owner); - else - log_error_errno(r, "error resolving user '%s': %m", owner); - + log_unknown_owner(r, "user", owner); event->uid = 0; } log_debug("OWNER %u %s:%u", @@ -2145,11 +2140,7 @@ void udev_rules_apply_to_event(struct udev_rules *rules, event->group_set = true; r = get_group_creds(&gr, &event->gid); if (r < 0) { - if (r == -ENOENT || r == -ESRCH) - log_error("specified group '%s' unknown", group); - else - log_error_errno(r, "error resolving group '%s': %m", group); - + log_unknown_owner(r, "group", group); event->gid = 0; } log_debug("GROUP %u %s:%u", @@ -2536,19 +2527,19 @@ int udev_rules_apply_static_dev_perms(struct udev_rules *rules) { } if (mode != (stats.st_mode & 01777)) { r = chmod(device_node, mode); - if (r < 0) { - log_error("failed to chmod '%s' %#o", device_node, mode); - return -errno; - } else + if (r < 0) + return log_error_errno(errno, "Failed to chmod '%s' %#o: %m", + device_node, mode); + else log_debug("chmod '%s' %#o", device_node, mode); } if ((uid != 0 && uid != stats.st_uid) || (gid != 0 && gid != stats.st_gid)) { r = chown(device_node, uid, gid); - if (r < 0) { - log_error("failed to chown '%s' %u %u ", device_node, uid, gid); - return -errno; - } else + if (r < 0) + return log_error_errno(errno, "Failed to chown '%s' %u %u: %m", + device_node, uid, gid); + else log_debug("chown '%s' %u %u", device_node, uid, gid); } diff --git a/src/udev/udev.pc.in b/src/udev/udev.pc.in index a0c2e82d47..e384a6f7c9 100644 --- a/src/udev/udev.pc.in +++ b/src/udev/udev.pc.in @@ -1,5 +1,5 @@ Name: udev Description: udev -Version: @VERSION@ +Version: @PACKAGE_VERSION@ udevdir=@udevlibexecdir@ diff --git a/src/udev/udevadm-hwdb.c b/src/udev/udevadm-hwdb.c index 70a5fa4d7a..69b0b9025c 100644 --- a/src/udev/udevadm-hwdb.c +++ b/src/udev/udevadm-hwdb.c @@ -352,7 +352,7 @@ static int trie_store(struct trie *trie, const char *filename) { int64_t size; struct trie_header_f h = { .signature = HWDB_SIG, - .tool_version = htole64(atoi(VERSION)), + .tool_version = htole64(atoi(PACKAGE_VERSION)), .header_size = htole64(sizeof(struct trie_header_f)), .node_size = htole64(sizeof(struct trie_node_f)), .child_entry_size = htole64(sizeof(struct trie_child_entry_f)), diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c index 90cdfa16c7..16b2aab0a1 100644 --- a/src/udev/udevadm-info.c +++ b/src/udev/udevadm-info.c @@ -376,7 +376,7 @@ static int uinfo(struct udev *udev, int argc, char *argv[]) { export_prefix = optarg; break; case 'V': - printf("%s\n", VERSION); + printf("%s\n", PACKAGE_VERSION); return 0; case 'h': help(); diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c index f631834341..94a59186ed 100644 --- a/src/udev/udevadm-monitor.c +++ b/src/udev/udevadm-monitor.c @@ -41,9 +41,9 @@ static void print_device(struct udev_device *device, const char *source, int pro struct timespec ts; assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); - printf("%-6s[%"PRI_TIME".%06ld] %-8s %s (%s)\n", + printf("%-6s[%"PRI_TIME".%06"PRI_NSEC"] %-8s %s (%s)\n", source, - ts.tv_sec, ts.tv_nsec/1000, + ts.tv_sec, (nsec_t)ts.tv_nsec/1000, udev_device_get_action(device), udev_device_get_devpath(device), udev_device_get_subsystem(device)); diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c index 07b667f131..e8ffe2f309 100644 --- a/src/udev/udevadm-test.c +++ b/src/udev/udevadm-test.c @@ -59,7 +59,7 @@ static int adm_test(struct udev *udev, int argc, char *argv[]) { {} }; - log_debug("version %s", VERSION); + log_debug("version %s", PACKAGE_VERSION); while ((c = getopt_long(argc, argv, "a:N:h", options, NULL)) >= 0) switch (c) { diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c index a6a873e5de..befc3bad7b 100644 --- a/src/udev/udevadm.c +++ b/src/udev/udevadm.c @@ -23,9 +23,10 @@ #include "selinux-util.h" #include "string-util.h" #include "udev.h" +#include "udev-util.h" static int adm_version(struct udev *udev, int argc, char *argv[]) { - printf("%s\n", VERSION); + printf("%s\n", PACKAGE_VERSION); return 0; } @@ -87,14 +88,16 @@ int main(int argc, char *argv[]) { unsigned int i; int rc = 1, c; - udev = udev_new(); - if (udev == NULL) - goto out; - + udev_parse_config(); log_parse_environment(); log_open(); + mac_selinux_init(); + udev = udev_new(); + if (udev == NULL) + goto out; + while ((c = getopt_long(argc, argv, "+dhV", options, NULL)) >= 0) switch (c) { diff --git a/src/udev/udevd.c b/src/udev/udevd.c index ce2ff89b85..acbddd4180 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -284,12 +284,12 @@ static void worker_attach_event(struct worker *worker, struct event *event) { e = worker->manager->event; - assert_se(sd_event_now(e, clock_boottime_or_monotonic(), &usec) >= 0); + assert_se(sd_event_now(e, CLOCK_MONOTONIC, &usec) >= 0); - (void) sd_event_add_time(e, &event->timeout_warning, clock_boottime_or_monotonic(), + (void) sd_event_add_time(e, &event->timeout_warning, CLOCK_MONOTONIC, usec + arg_event_timeout_warn_usec, USEC_PER_SEC, on_event_timeout_warning, event); - (void) sd_event_add_time(e, &event->timeout, clock_boottime_or_monotonic(), + (void) sd_event_add_time(e, &event->timeout, CLOCK_MONOTONIC, usec + arg_event_timeout_usec, USEC_PER_SEC, on_event_timeout, event); } @@ -755,9 +755,9 @@ static void manager_exit(Manager *manager) { event_queue_cleanup(manager, EVENT_QUEUED); manager_kill_workers(manager); - assert_se(sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec) >= 0); + assert_se(sd_event_now(manager->event, CLOCK_MONOTONIC, &usec) >= 0); - r = sd_event_add_time(manager->event, NULL, clock_boottime_or_monotonic(), + r = sd_event_add_time(manager->event, NULL, CLOCK_MONOTONIC, usec + 30 * USEC_PER_SEC, USEC_PER_SEC, on_exit_timeout, manager); if (r < 0) return; @@ -791,7 +791,7 @@ static void event_queue_start(Manager *manager) { manager->exit || manager->stop_exec_queue) return; - assert_se(sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec) >= 0); + assert_se(sd_event_now(manager->event, CLOCK_MONOTONIC, &usec) >= 0); /* check for changed config, every 3 seconds at most */ if (manager->last_usec == 0 || (usec - manager->last_usec) > 3 * USEC_PER_SEC) { @@ -1492,7 +1492,7 @@ static int parse_argv(int argc, char *argv[]) { help(); return 0; case 'V': - printf("%s\n", VERSION); + printf("%s\n", PACKAGE_VERSION); return 0; case '?': return -EINVAL; @@ -1663,6 +1663,7 @@ int main(int argc, char *argv[]) { int r; log_set_target(LOG_TARGET_AUTO); + udev_parse_config(); log_parse_environment(); log_open(); @@ -1740,7 +1741,7 @@ int main(int argc, char *argv[]) { if (arg_daemonize) { pid_t pid; - log_info("starting version " VERSION); + log_info("starting version " PACKAGE_VERSION); /* connect /dev/null to stdin, stdout, stderr */ if (log_get_max_level() < LOG_DEBUG) { diff --git a/src/update-done/update-done.c b/src/update-done/update-done.c index d466e1b759..ec467f1953 100644 --- a/src/update-done/update-done.c +++ b/src/update-done/update-done.c @@ -17,9 +17,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "fd-util.h" -#include "fileio.h" -#include "io-util.h" +#include "alloc-util.h" +#include "fileio-label.h" #include "selinux-util.h" #include "util.h" @@ -29,55 +28,25 @@ "# was updated. See man:systemd-update-done.service(8).\n" static int apply_timestamp(const char *path, struct timespec *ts) { - struct timespec twice[2] = { - *ts, - *ts - }; - _cleanup_fclose_ FILE *f = NULL; - int fd = -1; + _cleanup_free_ char *message = NULL; int r; - assert(path); - assert(ts); - /* * We store the timestamp both as mtime of the file and in the file itself, * to support filesystems which cannot store nanosecond-precision timestamps. - * Hence, don't bother updating the file, let's just rewrite it. */ - r = mac_selinux_create_file_prepare(path, S_IFREG); - if (r < 0) - return log_error_errno(r, "Failed to set SELinux context for %s: %m", path); - - fd = open(path, O_CREAT|O_WRONLY|O_TRUNC|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644); - mac_selinux_create_file_clear(); - - if (fd < 0) { - if (errno == EROFS) - return log_debug("Can't create timestamp file %s, file system is read-only.", path); - - return log_error_errno(errno, "Failed to create/open timestamp file %s: %m", path); - } + if (asprintf(&message, + MESSAGE + "TIMESTAMP_NSEC=" NSEC_FMT "\n", + timespec_load_nsec(ts)) < 0) + return log_oom(); - f = fdopen(fd, "we"); - if (!f) { - safe_close(fd); - return log_error_errno(errno, "Failed to fdopen() timestamp file %s: %m", path); - } - - (void) fprintf(f, - MESSAGE - "TIMESTAMP_NSEC=" NSEC_FMT "\n", - timespec_load_nsec(ts)); - - r = fflush_and_check(f); + r = write_string_file_atomic_label_ts(path, message, ts); + if (r == -EROFS) + return log_debug("Cannot create \"%s\", file system is read-only.", path); if (r < 0) - return log_error_errno(r, "Failed to write timestamp file: %m"); - - if (futimens(fd, twice) < 0) - return log_error_errno(errno, "Failed to update timestamp on %s: %m", path); - + return log_error_errno(r, "Failed to write \"%s\": %m", path); return 0; } diff --git a/src/vconsole/meson.build b/src/vconsole/meson.build new file mode 100644 index 0000000000..1260b53537 --- /dev/null +++ b/src/vconsole/meson.build @@ -0,0 +1,8 @@ +if conf.get('ENABLE_VCONSOLE', false) + vconsole_rules = configure_file( + input : '90-vconsole.rules.in', + output : '90-vconsole.rules', + configuration : substs) + install_data(vconsole_rules, + install_dir : udevrulesdir) +endif diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index a0ab5990fc..f531aece7f 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -28,6 +28,7 @@ #include <stdio.h> #include <stdlib.h> #include <sys/ioctl.h> +#include <sysexits.h> #include <termios.h> #include <unistd.h> @@ -41,35 +42,38 @@ #include "signal-util.h" #include "stdio-util.h" #include "string-util.h" +#include "strv.h" #include "terminal-util.h" #include "util.h" #include "virt.h" -static bool is_vconsole(int fd) { +static int verify_vc_device(int fd) { unsigned char data[1]; + int r; data[0] = TIOCL_GETFGCONSOLE; - return ioctl(fd, TIOCLINUX, data) >= 0; + r = ioctl(fd, TIOCLINUX, data); + return r < 0 ? -errno : r; } -static bool is_allocated(unsigned int idx) { - char vcname[strlen("/dev/vcs") + DECIMAL_STR_MAX(int)]; +static int verify_vc_allocation(unsigned idx) { + char vcname[sizeof("/dev/vcs") + DECIMAL_STR_MAX(unsigned) - 2]; + int r; - xsprintf(vcname, "/dev/vcs%i", idx); - return access(vcname, F_OK) == 0; + xsprintf(vcname, "/dev/vcs%u", idx); + r = access(vcname, F_OK); + return r < 0 ? -errno : r; } -static bool is_allocated_byfd(int fd) { +static int verify_vc_allocation_byfd(int fd) { struct vt_stat vcs = {}; + int r; - if (ioctl(fd, VT_GETSTATE, &vcs) < 0) { - log_warning_errno(errno, "VT_GETSTATE failed: %m"); - return false; - } - return is_allocated(vcs.v_active); + r = ioctl(fd, VT_GETSTATE, &vcs); + return r < 0 ? -errno : verify_vc_allocation(vcs.v_active); } -static bool is_settable(int fd) { +static int verify_vc_kbmode(int fd) { int r, curr_mode; r = ioctl(fd, KDGKBMODE, &curr_mode); @@ -80,7 +84,10 @@ static bool is_settable(int fd) { * * http://lists.freedesktop.org/archives/systemd-devel/2013-February/008573.html */ - return r == 0 && IN_SET(curr_mode, K_XLATE, K_UNICODE); + if (r < 0) + return -errno; + + return IN_SET(curr_mode, K_XLATE, K_UNICODE) ? 0 : -EBUSY; } static int toggle_utf8(const char *name, int fd, bool utf8) { @@ -99,10 +106,7 @@ static int toggle_utf8(const char *name, int fd, bool utf8) { r = tcgetattr(fd, &tc); if (r >= 0) { - if (utf8) - tc.c_iflag |= IUTF8; - else - tc.c_iflag &= ~IUTF8; + SET_FLAG(tc.c_iflag, IUTF8, utf8); r = tcsetattr(fd, TCSANOW, &tc); } if (r < 0) @@ -124,8 +128,9 @@ static int toggle_utf8_sysfs(bool utf8) { } static int keyboard_load_and_wait(const char *vc, const char *map, const char *map_toggle, bool utf8) { + _cleanup_free_ char *cmd = NULL; const char *args[8]; - int i = 0; + unsigned i = 0; pid_t pid; /* An empty map means kernel map */ @@ -143,6 +148,9 @@ static int keyboard_load_and_wait(const char *vc, const char *map, const char *m args[i++] = map_toggle; args[i++] = NULL; + log_debug("Executing \"%s\"...", + strnull((cmd = strv_join((char**) args, " ")))); + pid = fork(); if (pid < 0) return log_error_errno(errno, "Failed to fork: %m"); @@ -159,8 +167,9 @@ static int keyboard_load_and_wait(const char *vc, const char *map, const char *m } static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) { + _cleanup_free_ char *cmd = NULL; const char *args[9]; - int i = 0; + unsigned i = 0; pid_t pid; /* Any part can be set independently */ @@ -182,6 +191,9 @@ static int font_load_and_wait(const char *vc, const char *font, const char *map, args[i++] = font; args[i++] = NULL; + log_debug("Executing \"%s\"...", + strnull((cmd = strv_join((char**) args, " ")))); + pid = fork(); if (pid < 0) return log_error_errno(errno, "Failed to fork: %m"); @@ -198,7 +210,7 @@ static int font_load_and_wait(const char *vc, const char *font, const char *map, } /* - * A newly allocated VT uses the font from the active VT. Here + * A newly allocated VT uses the font from the source VT. Here * we update all possibly already allocated VTs with the configured * font. It also allows to restart systemd-vconsole-setup.service, * to apply a new font to all VTs. @@ -206,18 +218,18 @@ static int font_load_and_wait(const char *vc, const char *font, const char *map, * We also setup per-console utf8 related stuff: kbdmode, term * processing, stty iutf8. */ -static void setup_remaining_vcs(int fd, bool utf8) { +static void setup_remaining_vcs(int src_fd, unsigned src_idx, bool utf8) { struct console_font_op cfo = { .op = KD_FONT_OP_GET, .width = UINT_MAX, .height = UINT_MAX, .charcount = UINT_MAX, }; - struct vt_stat vcs = {}; struct unimapinit adv = {}; struct unimapdesc unimapd; _cleanup_free_ struct unipair* unipairs = NULL; _cleanup_free_ void *fontbuf = NULL; - int i, r; + unsigned i; + int r; unipairs = new(struct unipair, USHRT_MAX); if (!unipairs) { @@ -225,15 +237,8 @@ static void setup_remaining_vcs(int fd, bool utf8) { return; } - /* get active, and 16 bit mask of used VT numbers */ - r = ioctl(fd, VT_GETSTATE, &vcs); - if (r < 0) { - log_warning_errno(errno, "VT_GETSTATE failed, ignoring remaining consoles: %m"); - return; - } - /* get metadata of the current font (width, height, count) */ - r = ioctl(fd, KDFONTOP, &cfo); + r = ioctl(src_fd, KDFONTOP, &cfo); if (r < 0) log_warning_errno(errno, "KD_FONT_OP_GET failed while trying to get the font metadata: %m"); else { @@ -253,15 +258,15 @@ static void setup_remaining_vcs(int fd, bool utf8) { log_oom(); return; } - /* get fonts from source console */ + /* get fonts from the source console */ cfo.data = fontbuf; - r = ioctl(fd, KDFONTOP, &cfo); + r = ioctl(src_fd, KDFONTOP, &cfo); if (r < 0) log_warning_errno(errno, "KD_FONT_OP_GET failed while trying to read the font data: %m"); else { unimapd.entries = unipairs; unimapd.entry_ct = USHRT_MAX; - r = ioctl(fd, GIO_UNIMAP, &unimapd); + r = ioctl(src_fd, GIO_UNIMAP, &unimapd); if (r < 0) log_warning_errno(errno, "GIO_UNIMAP failed while trying to read unicode mappings: %m"); else @@ -274,21 +279,21 @@ static void setup_remaining_vcs(int fd, bool utf8) { log_warning("Fonts will not be copied to remaining consoles"); for (i = 1; i <= 63; i++) { - char ttyname[strlen("/dev/tty") + DECIMAL_STR_MAX(int)]; + char ttyname[sizeof("/dev/tty63")]; _cleanup_close_ int fd_d = -1; - if (i == vcs.v_active || !is_allocated(i)) + if (i == src_idx || verify_vc_allocation(i) < 0) continue; /* try to open terminal */ - xsprintf(ttyname, "/dev/tty%i", i); - fd_d = open_terminal(ttyname, O_RDWR|O_CLOEXEC); + xsprintf(ttyname, "/dev/tty%u", i); + fd_d = open_terminal(ttyname, O_RDWR|O_CLOEXEC|O_NOCTTY); if (fd_d < 0) { - log_warning_errno(fd_d, "Unable to open tty%i, fonts will not be copied: %m", i); + log_warning_errno(fd_d, "Unable to open tty%u, fonts will not be copied: %m", i); continue; } - if (!is_settable(fd_d)) + if (verify_vc_kbmode(fd_d) < 0) continue; toggle_utf8(ttyname, fd_d, utf8); @@ -298,22 +303,24 @@ static void setup_remaining_vcs(int fd, bool utf8) { r = ioctl(fd_d, KDFONTOP, &cfo); if (r < 0) { - log_warning_errno(errno, "KD_FONT_OP_SET failed, fonts will not be copied to tty%i: %m", i); + log_warning_errno(errno, "KD_FONT_OP_SET failed, fonts will not be copied to tty%u: %m", i); continue; } - /* copy unicode translation table */ - /* unimapd is a ushort count and a pointer to an - array of struct unipair { ushort, ushort } */ + /* + * copy unicode translation table + * unimapd is a ushort count and a pointer to an + * array of struct unipair { ushort, ushort } + */ r = ioctl(fd_d, PIO_UNIMAPCLR, &adv); if (r < 0) { - log_warning_errno(errno, "PIO_UNIMAPCLR failed, unimaps might be incorrect for tty%i: %m", i); + log_warning_errno(errno, "PIO_UNIMAPCLR failed, unimaps might be incorrect for tty%u: %m", i); continue; } r = ioctl(fd_d, PIO_UNIMAP, &unimapd); if (r < 0) { - log_warning_errno(errno, "PIO_UNIMAP failed, unimaps might be incorrect for tty%i: %m", i); + log_warning_errno(errno, "PIO_UNIMAP failed, unimaps might be incorrect for tty%u: %m", i); continue; } @@ -321,14 +328,91 @@ static void setup_remaining_vcs(int fd, bool utf8) { } } +static int find_source_vc(char **ret_path, unsigned *ret_idx) { + _cleanup_free_ char *path = NULL; + unsigned i; + int ret_fd, r, err = 0; + + path = new(char, sizeof("/dev/tty63")); + if (path == NULL) + return log_oom(); + + for (i = 1; i <= 63; i++) { + _cleanup_close_ int fd = -1; + + r = verify_vc_allocation(i); + if (r < 0) { + if (!err) + err = -r; + continue; + } + + sprintf(path, "/dev/tty%u", i); + fd = open_terminal(path, O_RDWR|O_CLOEXEC|O_NOCTTY); + if (fd < 0) { + if (!err) + err = -fd; + continue; + } + r = verify_vc_kbmode(fd); + if (r < 0) { + if (!err) + err = -r; + continue; + } + + /* all checks passed, return this one as a source console */ + *ret_idx = i; + *ret_path = path; + path = NULL; + ret_fd = fd; + fd = -1; + return ret_fd; + } + + return log_error_errno(err, "No usable source console found: %m"); +} + +static int verify_source_vc(char **ret_path, const char *src_vc) { + char *path; + _cleanup_close_ int fd = -1; + int ret_fd, r; + + fd = open_terminal(src_vc, O_RDWR|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return log_error_errno(fd, "Failed to open %s: %m", src_vc); + + r = verify_vc_device(fd); + if (r < 0) + return log_error_errno(r, "Device %s is not a virtual console: %m", src_vc); + + r = verify_vc_allocation_byfd(fd); + if (r < 0) + return log_error_errno(r, "Virtual console %s is not allocated: %m", src_vc); + + r = verify_vc_kbmode(fd); + if (r < 0) + return log_error_errno(r, "Virtual console %s is not in K_XLATE or K_UNICODE: %m", src_vc); + + path = strdup(src_vc); + if (path == NULL) + return log_oom(); + + *ret_path = path; + ret_fd = fd; + fd = -1; + return ret_fd; +} + int main(int argc, char **argv) { - const char *vc; _cleanup_free_ char + *vc = NULL, *vc_keymap = NULL, *vc_keymap_toggle = NULL, *vc_font = NULL, *vc_font_map = NULL, *vc_font_unimap = NULL; _cleanup_close_ int fd = -1; - bool utf8, font_copy = false, font_ok, keyboard_ok; - int r = EXIT_FAILURE; + bool utf8, keyboard_ok; + unsigned idx = 0; + int r; log_set_target(LOG_TARGET_AUTO); log_parse_environment(); @@ -337,32 +421,12 @@ int main(int argc, char **argv) { umask(0022); if (argv[1]) - vc = argv[1]; - else { - vc = "/dev/tty0"; - font_copy = true; - } - - fd = open_terminal(vc, O_RDWR|O_CLOEXEC); - if (fd < 0) { - log_error_errno(fd, "Failed to open %s: %m", vc); - return EXIT_FAILURE; - } + fd = verify_source_vc(&vc, argv[1]); + else + fd = find_source_vc(&vc, &idx); - if (!is_vconsole(fd)) { - log_error("Device %s is not a virtual console.", vc); + if (fd < 0) return EXIT_FAILURE; - } - - if (!is_allocated_byfd(fd)) { - log_error("Virtual console %s is not allocated.", vc); - return EXIT_FAILURE; - } - - if (!is_settable(fd)) { - log_error("Virtual console %s is not in K_XLATE or K_UNICODE.", vc); - return EXIT_FAILURE; - } utf8 = is_locale_utf8(); @@ -373,7 +437,6 @@ int main(int argc, char **argv) { "FONT_MAP", &vc_font_map, "FONT_UNIMAP", &vc_font_unimap, NULL); - if (r < 0 && r != -ENOENT) log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m"); @@ -390,22 +453,27 @@ int main(int argc, char **argv) { "vconsole.font.map", &vc_font_map, "vconsole.font.unimap", &vc_font_unimap, NULL); - if (r < 0 && r != -ENOENT) log_warning_errno(r, "Failed to read /proc/cmdline: %m"); } toggle_utf8_sysfs(utf8); toggle_utf8(vc, fd, utf8); - font_ok = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap) == 0; + + r = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap); keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) == 0; - if (font_copy) { - if (font_ok) - setup_remaining_vcs(fd, utf8); + if (idx > 0) { + if (r == 0) + setup_remaining_vcs(fd, idx, utf8); + else if (r == EX_OSERR) + /* setfont returns EX_OSERR when ioctl(KDFONTOP/PIO_FONTX/PIO_FONTX) fails. + * This might mean various things, but in particular lack of a graphical + * console. Let's be generous and not treat this as an error. */ + log_notice("Setting fonts failed with a \"system error\", ignoring."); else log_warning("Setting source virtual console failed, ignoring remaining ones"); } - return font_ok && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE; + return IN_SET(r, 0, EX_OSERR) && keyboard_ok ? EXIT_SUCCESS : EXIT_FAILURE; } |