diff options
author | Martin Pitt <martin.pitt@ubuntu.com> | 2016-05-22 21:34:07 +0200 |
---|---|---|
committer | Martin Pitt <martin.pitt@ubuntu.com> | 2016-05-22 21:34:07 +0200 |
commit | aa27b158cd13271991aa126baf355288f6359487 (patch) | |
tree | 2b92dde5bd583dd521ec9b62810b5702f027ac85 /src | |
parent | 4c89c718b555be6ac0b3bfb47f73255b637e16e5 (diff) | |
download | systemd-aa27b158cd13271991aa126baf355288f6359487.tar.gz |
Imported Upstream version 230
Diffstat (limited to 'src')
626 files changed, 22837 insertions, 22980 deletions
diff --git a/src/activate/activate.c b/src/activate/activate.c index 0db4967edb..a0cfc22000 100644 --- a/src/activate/activate.c +++ b/src/activate/activate.c @@ -27,6 +27,7 @@ #include "sd-daemon.h" #include "alloc-util.h" +#include "escape.h" #include "fd-util.h" #include "log.h" #include "macro.h" @@ -40,7 +41,7 @@ static bool arg_accept = false; static int arg_socket_type = SOCK_STREAM; static char** arg_args = NULL; static char** arg_setenv = NULL; -static const char *arg_fdname = NULL; +static char **arg_fdnames = NULL; static bool arg_inetd = false; static int add_epoll(int epoll_fd, int fd) { @@ -76,7 +77,7 @@ static int open_sockets(int *epoll_fd, bool accept) { if (r < 0) return r; - count ++; + count++; } } @@ -104,7 +105,7 @@ static int open_sockets(int *epoll_fd, bool accept) { } assert(fd == SD_LISTEN_FDS_START + count); - count ++; + count++; } if (arg_listen) @@ -134,7 +135,6 @@ static int exec_process(const char* name, char **argv, char **env, int start_fd, _cleanup_free_ char *joined = NULL; unsigned n_env = 0, length; const char *tocopy; - unsigned i; char **s; int r; @@ -176,7 +176,7 @@ static int exec_process(const char* name, char **argv, char **env, int start_fd, if (!envp[n_env]) return log_oom(); - n_env ++; + n_env++; } } @@ -191,7 +191,7 @@ static int exec_process(const char* name, char **argv, char **env, int start_fd, if (!envp[n_env]) return log_oom(); - n_env ++; + n_env++; } if (arg_inetd) { @@ -224,25 +224,30 @@ static int exec_process(const char* name, char **argv, char **env, int start_fd, if (asprintf((char**)(envp + n_env++), "LISTEN_PID=" PID_FMT, getpid()) < 0) return log_oom(); - if (arg_fdname) { + if (arg_fdnames) { + _cleanup_free_ char *names = NULL; + size_t len; char *e; + int i; + + len = strv_length(arg_fdnames); + if (len == 1) + for (i = 1; i < n_fds; i++) { + r = strv_extend(&arg_fdnames, arg_fdnames[0]); + if (r < 0) + return log_error_errno(r, "Failed to extend strv: %m"); + } + else if (len != (unsigned) n_fds) + log_warning("The number of fd names is different than number of fds: %zu vs %d", + len, n_fds); - e = strappend("LISTEN_FDNAMES=", arg_fdname); - if (!e) + names = strv_join(arg_fdnames, ":"); + if (!names) return log_oom(); - for (i = 1; i < (unsigned) n_fds; i++) { - char *c; - - c = strjoin(e, ":", arg_fdname, NULL); - if (!c) { - free(e); - return log_oom(); - } - - free(e); - e = c; - } + e = strappend("LISTEN_FDNAMES=", names); + if (!e) + return log_oom(); envp[n_env++] = e; } @@ -311,19 +316,31 @@ static int do_accept(const char* name, char **argv, char **envp, int fd) { } /* SIGCHLD handler. */ -static void sigchld_hdl(int sig, siginfo_t *t, void *data) { +static void sigchld_hdl(int sig) { PROTECT_ERRNO; - log_info("Child %d died with code %d", t->si_pid, t->si_status); + for (;;) { + siginfo_t si; + int r; + + si.si_pid = 0; + r = waitid(P_ALL, 0, &si, WEXITED|WNOHANG); + if (r < 0) { + if (errno != ECHILD) + log_error_errno(errno, "Failed to reap children: %m"); + return; + } + if (si.si_pid == 0) + return; - /* Wait for a dead child. */ - (void) waitpid(t->si_pid, NULL, 0); + log_info("Child %d died with code %d", si.si_pid, si.si_status); + } } static int install_chld_handler(void) { static const struct sigaction act = { - .sa_flags = SA_SIGINFO, - .sa_sigaction = sigchld_hdl, + .sa_flags = SA_NOCLDSTOP, + .sa_handler = sigchld_hdl, }; int r; @@ -339,14 +356,15 @@ static void help(void) { printf("%s [OPTIONS...]\n\n" "Listen on sockets and launch child on connection.\n\n" "Options:\n" - " -h --help Show this help and exit\n" - " --version Print version string and exit\n" - " -l --listen=ADDR Listen for raw connections at ADDR\n" - " -d --datagram Listen on datagram instead of stream socket\n" - " --seqpacket Listen on SOCK_SEQPACKET instead of stream socket\n" - " -a --accept Spawn separate child for each connection\n" - " -E --setenv=NAME[=VALUE] Pass an environment variable to children\n" - " --inetd Enable inetd file descriptor passing protocol\n" + " -h --help Show this help and exit\n" + " --version Print version string and exit\n" + " -l --listen=ADDR Listen for raw connections at ADDR\n" + " -d --datagram Listen on datagram instead of stream socket\n" + " --seqpacket Listen on SOCK_SEQPACKET instead of stream socket\n" + " -a --accept Spawn separate child for each connection\n" + " -E --setenv=NAME[=VALUE] Pass an environment variable to children\n" + " --fdname=NAME[:NAME...] Specify names for file descriptors\n" + " --inetd Enable inetd file descriptor passing protocol\n" "\n" "Note: file descriptors from sd_listen_fds() will be passed through.\n" , program_invocation_short_name); @@ -379,7 +397,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "+hl:aEd", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "+hl:aE:d", options, NULL)) >= 0) switch(c) { case 'h': help(); @@ -424,14 +442,30 @@ static int parse_argv(int argc, char *argv[]) { break; - case ARG_FDNAME: - if (!fdname_is_valid(optarg)) { - log_error("File descriptor name %s is not valid, refusing.", optarg); - return -EINVAL; - } + case ARG_FDNAME: { + _cleanup_strv_free_ char **names; + char **s; + + names = strv_split(optarg, ":"); + if (!names) + return log_oom(); - arg_fdname = optarg; + STRV_FOREACH(s, names) + if (!fdname_is_valid(*s)) { + _cleanup_free_ char *esc; + + esc = cescape(*s); + log_warning("File descriptor name \"%s\" is not valid.", esc); + } + + /* Empty optargs means one empty name */ + r = strv_extend_strv(&arg_fdnames, + strv_isempty(names) ? STRV_MAKE("") : names, + false); + if (r < 0) + return log_error_errno(r, "strv_extend_strv: %m"); break; + } case ARG_INETD: arg_inetd = true; diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c index d36c8db3d4..5fd3ee49eb 100644 --- a/src/analyze/analyze-verify.c +++ b/src/analyze/analyze-verify.c @@ -231,14 +231,12 @@ static int verify_unit(Unit *u, bool check_man) { return r; } -int verify_units(char **filenames, ManagerRunningAs running_as, bool check_man) { +int verify_units(char **filenames, UnitFileScope scope, bool check_man) { _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; + _cleanup_free_ char *var = NULL; Manager *m = NULL; FILE *serial = NULL; FDSet *fdset = NULL; - - _cleanup_free_ char *var = NULL; - char **filename; int r = 0, k; @@ -255,7 +253,7 @@ int verify_units(char **filenames, ManagerRunningAs running_as, bool check_man) assert_se(set_unit_path(var) >= 0); - r = manager_new(running_as, true, &m); + r = manager_new(scope, true, &m); if (r < 0) return log_error_errno(r, "Failed to initialize manager: %m"); @@ -290,7 +288,7 @@ int verify_units(char **filenames, ManagerRunningAs running_as, bool check_man) if (r == 0) r = k; } else - count ++; + count++; } for (i = 0; i < count; i++) { diff --git a/src/analyze/analyze-verify.h b/src/analyze/analyze-verify.h index 54adad93e1..d8204dc69c 100644 --- a/src/analyze/analyze-verify.h +++ b/src/analyze/analyze-verify.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,10 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - #include <stdbool.h> #include "path-lookup.h" -int verify_units(char **filenames, ManagerRunningAs running_as, bool check_man); +int verify_units(char **filenames, UnitFileScope scope, bool check_man); diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index a847084781..d621f66aec 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -28,6 +28,7 @@ #include "alloc-util.h" #include "analyze-verify.h" #include "bus-error.h" +#include "bus-unit-util.h" #include "bus-util.h" #include "glob-util.h" #include "hashmap.h" @@ -60,7 +61,7 @@ svg(" <text class=\"%s\" x=\"%.03f\" y=\"%.03f\">", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \ svg(format, ## __VA_ARGS__); \ svg("</text>\n"); \ - } while(false) + } while (false) static enum dot { DEP_ALL, @@ -123,14 +124,6 @@ struct host_info { char *architecture; }; -static void pager_open_if_enabled(void) { - - if (arg_no_pager) - return; - - pager_open(false); -} - static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; @@ -759,9 +752,9 @@ static int list_dependencies_print(const char *name, unsigned int level, unsigne char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX]; for (i = level; i != 0; i--) - printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE)); + printf("%s", special_glyph(branches & (1 << (i-1)) ? TREE_VERTICAL : TREE_SPACE)); - printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH)); + printf("%s", special_glyph(last ? TREE_RIGHT : TREE_BRANCH)); if (times) { if (times->time) @@ -965,7 +958,7 @@ static int analyze_critical_chain(sd_bus *bus, char *names[]) { } unit_times_hashmap = h; - pager_open_if_enabled(); + pager_open(arg_no_pager, false); puts("The time after the unit is active or started is printed after the \"@\" character.\n" "The time the unit takes to start is printed after the \"+\" character.\n"); @@ -993,7 +986,7 @@ static int analyze_blame(sd_bus *bus) { qsort(times, n, sizeof(struct unit_times), compare_unit_time); - pager_open_if_enabled(); + pager_open(arg_no_pager, false); for (i = 0; i < (unsigned) n; i++) { char ts[FORMAT_TIMESPAN_MAX]; @@ -1206,7 +1199,7 @@ static int dump(sd_bus *bus, char **args) { return -E2BIG; } - pager_open_if_enabled(); + pager_open(arg_no_pager, false); r = sd_bus_call_method( bus, @@ -1284,7 +1277,7 @@ static int set_log_target(sd_bus *bus, char **args) { static void help(void) { - pager_open_if_enabled(); + pager_open(arg_no_pager, false); printf("%s [OPTIONS...] {COMMAND} ...\n\n" "Profile systemd, show unit dependencies, check unit files.\n\n" @@ -1451,7 +1444,7 @@ int main(int argc, char *argv[]) { if (streq_ptr(argv[optind], "verify")) r = verify_units(argv+optind+1, - arg_user ? MANAGER_USER : MANAGER_SYSTEM, + arg_user ? UNIT_FILE_USER : UNIT_FILE_SYSTEM, arg_man); else { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c index adc9286612..6d53dd982c 100644 --- a/src/ask-password/ask-password.c +++ b/src/ask-password/ask-password.c @@ -34,6 +34,7 @@ static const char *arg_keyname = NULL; static char *arg_message = NULL; static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC; static bool arg_multiple = false; +static bool arg_no_output = false; static AskPasswordFlags arg_flags = ASK_PASSWORD_PUSH_CACHE; static void help(void) { @@ -48,6 +49,7 @@ static void help(void) { " --no-tty Ask question via agent even on TTY\n" " --accept-cached Accept cached passwords\n" " --multiple List multiple passwords if available\n" + " --no-output Do not print password to standard output\n" , program_invocation_short_name); } @@ -62,6 +64,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_MULTIPLE, ARG_ID, ARG_KEYNAME, + ARG_NO_OUTPUT, }; static const struct option options[] = { @@ -74,6 +77,7 @@ static int parse_argv(int argc, char *argv[]) { { "multiple", no_argument, NULL, ARG_MULTIPLE }, { "id", required_argument, NULL, ARG_ID }, { "keyname", required_argument, NULL, ARG_KEYNAME }, + { "no-output", no_argument, NULL, ARG_NO_OUTPUT }, {} }; @@ -125,6 +129,10 @@ static int parse_argv(int argc, char *argv[]) { arg_keyname = optarg; break; + case ARG_NO_OUTPUT: + arg_no_output = true; + break; + case '?': return -EINVAL; @@ -166,7 +174,8 @@ int main(int argc, char *argv[]) { } STRV_FOREACH(p, l) { - puts(*p); + if (!arg_no_output) + puts(*p); if (!arg_multiple) break; diff --git a/src/basic/MurmurHash2.c b/src/basic/MurmurHash2.c index 2f4149dbe9..9020793930 100644 --- a/src/basic/MurmurHash2.c +++ b/src/basic/MurmurHash2.c @@ -50,7 +50,7 @@ uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed ) const unsigned char * data = (const unsigned char *)key; - while(len >= 4) + while (len >= 4) { uint32_t k = *(uint32_t*)data; diff --git a/src/basic/af-list.h b/src/basic/af-list.h index 135248dc64..6a4cc03839 100644 --- a/src/basic/af-list.h +++ b/src/basic/af-list.h @@ -19,7 +19,23 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "string-util.h" + const char *af_to_name(int id); int af_from_name(const char *name); +static inline const char* af_to_name_short(int id) { + const char *f; + + if (id == AF_UNSPEC) + return "*"; + + f = af_to_name(id); + if (!f) + return "unknown"; + + assert(startswith(f, "AF_")); + return f + 3; +} + int af_max(void); diff --git a/src/basic/alloc-util.h b/src/basic/alloc-util.h index 679ba7f398..ceeee519b7 100644 --- a/src/basic/alloc-util.h +++ b/src/basic/alloc-util.h @@ -51,25 +51,29 @@ static inline void freep(void *p) { #define _cleanup_free_ _cleanup_(freep) -_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t a, size_t b) { - if (_unlikely_(b != 0 && a > ((size_t) -1) / b)) +static inline bool size_multiply_overflow(size_t size, size_t need) { + return _unlikely_(need != 0 && size > (SIZE_MAX / need)); +} + +_malloc_ _alloc_(1, 2) static inline void *malloc_multiply(size_t size, size_t need) { + if (size_multiply_overflow(size, need)) return NULL; - return malloc(a * b); + return malloc(size * need); } -_alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t a, size_t b) { - if (_unlikely_(b != 0 && a > ((size_t) -1) / b)) +_alloc_(2, 3) static inline void *realloc_multiply(void *p, size_t size, size_t need) { + if (size_multiply_overflow(size, need)) return NULL; - return realloc(p, a * b); + return realloc(p, size * need); } -_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t a, size_t b) { - if (_unlikely_(b != 0 && a > ((size_t) -1) / b)) +_alloc_(2, 3) static inline void *memdup_multiply(const void *p, size_t size, size_t need) { + if (size_multiply_overflow(size, need)) return NULL; - return memdup(p, a * b); + return memdup(p, size * need); } void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size); diff --git a/src/shared/architecture.c b/src/basic/architecture.c index a9ecfc1cd6..b1c8e91f50 100644 --- a/src/shared/architecture.c +++ b/src/basic/architecture.c @@ -63,7 +63,7 @@ int uname_architecture(void) { #elif defined(__s390__) || defined(__s390x__) { "s390x", ARCHITECTURE_S390X }, { "s390", ARCHITECTURE_S390 }, -#elif defined(__sparc__) || defined(__sparc64__) +#elif defined(__sparc__) { "sparc64", ARCHITECTURE_SPARC64 }, { "sparc", ARCHITECTURE_SPARC }, #elif defined(__mips__) || defined(__mips64__) @@ -121,6 +121,8 @@ int uname_architecture(void) { { "tilegx", ARCHITECTURE_TILEGX }, #elif defined(__cris__) { "crisv32", ARCHITECTURE_CRIS }, +#elif defined(__nios2__) + { "nios2", ARCHITECTURE_NIOS2 }, #else #error "Please register your architecture here!" #endif @@ -171,6 +173,7 @@ static const char *const architecture_table[_ARCHITECTURE_MAX] = { [ARCHITECTURE_M68K] = "m68k", [ARCHITECTURE_TILEGX] = "tilegx", [ARCHITECTURE_CRIS] = "cris", + [ARCHITECTURE_NIOS2] = "nios2", }; DEFINE_STRING_TABLE_LOOKUP(architecture, int); diff --git a/src/shared/architecture.h b/src/basic/architecture.h index 26679e28c6..b3e4d85906 100644 --- a/src/shared/architecture.h +++ b/src/basic/architecture.h @@ -57,6 +57,7 @@ enum { ARCHITECTURE_M68K, ARCHITECTURE_TILEGX, ARCHITECTURE_CRIS, + ARCHITECTURE_NIOS2, _ARCHITECTURE_MAX, _ARCHITECTURE_INVALID = -1 }; @@ -77,20 +78,20 @@ int uname_architecture(void); #if defined(__x86_64__) # define native_architecture() ARCHITECTURE_X86_64 # define LIB_ARCH_TUPLE "x86_64-linux-gnu" -# define PROC_CPUINFO_MODEL "model name" +# define SECONDARY_ARCHITECTURE ARCHITECTURE_X86 #elif defined(__i386__) # define native_architecture() ARCHITECTURE_X86 # define LIB_ARCH_TUPLE "i386-linux-gnu" -# define PROC_CPUINFO_MODEL "model name" #elif defined(__powerpc64__) # if __BYTE_ORDER == __BIG_ENDIAN # define native_architecture() ARCHITECTURE_PPC64 # define LIB_ARCH_TUPLE "ppc64-linux-gnu" +# define SECONDARY_ARCHITECTURE ARCHITECTURE_PPC # else # define native_architecture() ARCHITECTURE_PPC64_LE # define LIB_ARCH_TUPLE "powerpc64le-linux-gnu" +# define SECONDARY_ARCHITECTURE ARCHITECTURE_PPC_LE # endif -# define PROC_CPUINFO_MODEL "cpu" #elif defined(__powerpc__) # if __BYTE_ORDER == __BIG_ENDIAN # define native_architecture() ARCHITECTURE_PPC @@ -99,32 +100,28 @@ int uname_architecture(void); # define native_architecture() ARCHITECTURE_PPC_LE # error "Missing LIB_ARCH_TUPLE for PPCLE" # endif -# define PROC_CPUINFO_MODEL "cpu" #elif defined(__ia64__) # define native_architecture() ARCHITECTURE_IA64 # define LIB_ARCH_TUPLE "ia64-linux-gnu" #elif defined(__hppa64__) # define native_architecture() ARCHITECTURE_PARISC64 # error "Missing LIB_ARCH_TUPLE for HPPA64" -# define PROC_CPUINFO_MODEL "cpu" #elif defined(__hppa__) # define native_architecture() ARCHITECTURE_PARISC # define LIB_ARCH_TUPLE "hppa‑linux‑gnu" -# define PROC_CPUINFO_MODEL "cpu" #elif defined(__s390x__) # define native_architecture() ARCHITECTURE_S390X # define LIB_ARCH_TUPLE "s390x-linux-gnu" +# define SECONDARY_ARCHITECTURE ARCHITECTURE_S390 #elif defined(__s390__) # define native_architecture() ARCHITECTURE_S390 # define LIB_ARCH_TUPLE "s390-linux-gnu" -#elif defined(__sparc64__) +#elif defined(__sparc__) && defined (__arch64__) # define native_architecture() ARCHITECTURE_SPARC64 # define LIB_ARCH_TUPLE "sparc64-linux-gnu" -# define PROC_CPUINFO_MODEL "cpu" #elif defined(__sparc__) # define native_architecture() ARCHITECTURE_SPARC # define LIB_ARCH_TUPLE "sparc-linux-gnu" -# define PROC_CPUINFO_MODEL "cpu" #elif defined(__mips64__) # if __BYTE_ORDER == __BIG_ENDIAN # define native_architecture() ARCHITECTURE_MIPS64 @@ -133,7 +130,6 @@ int uname_architecture(void); # define native_architecture() ARCHITECTURE_MIPS64_LE # error "Missing LIB_ARCH_TUPLE for MIPS64_LE" # endif -# define PROC_CPUINFO_MODEL "cpu model" #elif defined(__mips__) # if __BYTE_ORDER == __BIG_ENDIAN # define native_architecture() ARCHITECTURE_MIPS @@ -142,7 +138,6 @@ int uname_architecture(void); # define native_architecture() ARCHITECTURE_MIPS_LE # define LIB_ARCH_TUPLE "mipsel-linux-gnu" # endif -# define PROC_CPUINFO_MODEL "cpu model" #elif defined(__alpha__) # define native_architecture() ARCHITECTURE_ALPHA # define LIB_ARCH_TUPLE "alpha-linux-gnu" @@ -178,7 +173,6 @@ int uname_architecture(void); # define LIB_ARCH_TUPLE "arm-linux-gnu" # endif # endif -# define PROC_CPUINFO_MODEL "model name" #elif defined(__sh64__) # define native_architecture() ARCHITECTURE_SH64 # error "Missing LIB_ARCH_TUPLE for SH64" @@ -194,14 +188,12 @@ int uname_architecture(void); #elif defined(__cris__) # define native_architecture() ARCHITECTURE_CRIS # error "Missing LIB_ARCH_TUPLE for CRIS" +#elif defined(__nios2__) +# define native_architecture() ARCHITECTURE_NIOS2 +# define LIB_ARCH_TUPLE "nios2-linux-gnu" #else # error "Please register your architecture here!" #endif -#ifndef PROC_CPUINFO_MODEL -#warning "PROC_CPUINFO_MODEL not defined for your architecture" -#define PROC_CPUINFO_MODEL "model name" -#endif - const char *architecture_to_string(int a) _const_; int architecture_from_string(const char *s) _pure_; diff --git a/src/basic/btrfs-util.h b/src/basic/btrfs-util.h index 37802c2565..1d852d502c 100644 --- a/src/basic/btrfs-util.h +++ b/src/basic/btrfs-util.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - #include <stdbool.h> #include <stdint.h> #include <sys/types.h> diff --git a/src/basic/c-rbtree.c b/src/basic/c-rbtree.c deleted file mode 100644 index 914d7e5229..0000000000 --- a/src/basic/c-rbtree.c +++ /dev/null @@ -1,679 +0,0 @@ -/*** - This file is part of systemd. See COPYING for details. - - 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/>. -***/ - -/* - * RB-Tree Implementation - * This implements the insertion/removal of elements in RB-Trees. You're highly - * recommended to have an RB-Tree documentation at hand when reading this. Both - * insertion and removal can be split into a handful of situations that can - * occur. Those situations are enumerated as "Case 1" to "Case n" here, and - * follow closely the cases described in most RB-Tree documentations. This file - * does not explain why it is enough to handle just those cases, nor does it - * provide a proof of correctness. Dig out your algorithm 101 handbook if - * you're interested. - * - * This implementation is *not* straightforward. Usually, a handful of - * rotation, reparent, swap and link helpers can be used to implement the - * rebalance operations. However, those often perform unnecessary writes. - * Therefore, this implementation hard-codes all the operations. You're highly - * recommended to look at the two basic helpers before reading the code: - * c_rbtree_swap_child() - * c_rbtree_set_parent_and_color() - * Those are the only helpers used, hence, you should really know what they do - * before digging into the code. - * - * For a highlevel documentation of the API, see the header file and docbook - * comments. - */ - -#include <assert.h> -#include <stddef.h> -#include "c-rbtree.h" - -enum { - C_RBNODE_RED = 0, - C_RBNODE_BLACK = 1, -}; - -static inline unsigned long c_rbnode_color(CRBNode *n) { - return (unsigned long)n->__parent_and_color & 1UL; -} - -static inline _Bool c_rbnode_is_red(CRBNode *n) { - return c_rbnode_color(n) == C_RBNODE_RED; -} - -static inline _Bool c_rbnode_is_black(CRBNode *n) { - return c_rbnode_color(n) == C_RBNODE_BLACK; -} - -/** - * c_rbnode_leftmost() - return leftmost child - * @n: current node, or NULL - * - * This returns the leftmost child of @n. If @n is NULL, this will return NULL. - * In all other cases, this function returns a valid pointer. That is, if @n - * does not have any left children, this returns @n. - * - * Worst case runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to leftmost child, or NULL. - */ -CRBNode *c_rbnode_leftmost(CRBNode *n) { - if (n) - while (n->left) - n = n->left; - return n; -} - -/** - * c_rbnode_rightmost() - return rightmost child - * @n: current node, or NULL - * - * This returns the rightmost child of @n. If @n is NULL, this will return - * NULL. In all other cases, this function returns a valid pointer. That is, if - * @n does not have any right children, this returns @n. - * - * Worst case runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to rightmost child, or NULL. - */ -CRBNode *c_rbnode_rightmost(CRBNode *n) { - if (n) - while (n->right) - n = n->right; - return n; -} - -/** - * c_rbnode_next() - return next node - * @n: current node, or NULL - * - * An RB-Tree always defines a linear order of its elements. This function - * returns the logically next node to @n. If @n is NULL, the last node or - * unlinked, this returns NULL. - * - * Worst case runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to next node, or NULL. - */ -CRBNode *c_rbnode_next(CRBNode *n) { - CRBNode *p; - - if (!c_rbnode_is_linked(n)) - return NULL; - if (n->right) - return c_rbnode_leftmost(n->right); - - while ((p = c_rbnode_parent(n)) && n == p->right) - n = p; - - return p; -} - -/** - * c_rbnode_prev() - return previous node - * @n: current node, or NULL - * - * An RB-Tree always defines a linear order of its elements. This function - * returns the logically previous node to @n. If @n is NULL, the first node or - * unlinked, this returns NULL. - * - * Worst case runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to previous node, or NULL. - */ -CRBNode *c_rbnode_prev(CRBNode *n) { - CRBNode *p; - - if (!c_rbnode_is_linked(n)) - return NULL; - if (n->left) - return c_rbnode_rightmost(n->left); - - while ((p = c_rbnode_parent(n)) && n == p->left) - n = p; - - return p; -} - -/** - * c_rbtree_first() - return first node - * @t: tree to operate on - * - * An RB-Tree always defines a linear order of its elements. This function - * returns the logically first node in @t. If @t is empty, NULL is returned. - * - * Fixed runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to first node, or NULL. - */ -CRBNode *c_rbtree_first(CRBTree *t) { - assert(t); - return c_rbnode_leftmost(t->root); -} - -/** - * c_rbtree_last() - return last node - * @t: tree to operate on - * - * An RB-Tree always defines a linear order of its elements. This function - * returns the logically last node in @t. If @t is empty, NULL is returned. - * - * Fixed runtime (n: number of elements in tree): O(log(n)) - * - * Return: Pointer to last node, or NULL. - */ -CRBNode *c_rbtree_last(CRBTree *t) { - assert(t); - return c_rbnode_rightmost(t->root); -} - -/* - * Set the color and parent of a node. This should be treated as a simple - * assignment of the 'color' and 'parent' fields of the node. No other magic is - * applied. But since both fields share its backing memory, this helper - * function is provided. - */ -static inline void c_rbnode_set_parent_and_color(CRBNode *n, CRBNode *p, unsigned long c) { - assert(!((unsigned long)p & 1)); - assert(c < 2); - n->__parent_and_color = (CRBNode*)((unsigned long)p | c); -} - -/* same as c_rbnode_set_parent_and_color(), but keeps the current parent */ -static inline void c_rbnode_set_color(CRBNode *n, unsigned long c) { - c_rbnode_set_parent_and_color(n, c_rbnode_parent(n), c); -} - -/* same as c_rbnode_set_parent_and_color(), but keeps the current color */ -static inline void c_rbnode_set_parent(CRBNode *n, CRBNode *p) { - c_rbnode_set_parent_and_color(n, p, c_rbnode_color(n)); -} - -/* - * This function partially replaces an existing child pointer to a new one. The - * existing child must be given as @old, the new child as @new. @p must be the - * parent of @old (or NULL if it has no parent). - * This function ensures that the parent of @old now points to @new. However, - * it does *NOT* change the parent pointer of @new. The caller must ensure - * this. - * If @p is NULL, this function ensures that the root-pointer is adjusted - * instead (given as @t). - */ -static inline void c_rbtree_swap_child(CRBTree *t, CRBNode *p, CRBNode *old, CRBNode *new) { - if (p) { - if (p->left == old) - p->left = new; - else - p->right = new; - } else { - t->root = new; - } -} - -static inline CRBNode *c_rbtree_paint_one(CRBTree *t, CRBNode *n) { - CRBNode *p, *g, *gg, *u, *x; - - /* - * Paint a single node according to RB-Tree rules. The node must - * already be linked into the tree and painted red. - * We repaint the node or rotate the tree, if required. In case a - * recursive repaint is required, the next node to be re-painted - * is returned. - * p: parent - * g: grandparent - * gg: grandgrandparent - * u: uncle - * x: temporary - */ - - /* node is red, so we can access the parent directly */ - p = n->__parent_and_color; - - if (!p) { - /* Case 1: - * We reached the root. Mark it black and be done. As all - * leaf-paths share the root, the ratio of black nodes on each - * path stays the same. */ - c_rbnode_set_parent_and_color(n, p, C_RBNODE_BLACK); - n = NULL; - } else if (c_rbnode_is_black(p)) { - /* Case 2: - * The parent is already black. As our node is red, we did not - * change the number of black nodes on any path, nor do we have - * multiple consecutive red nodes. */ - n = NULL; - } else if (p == p->__parent_and_color->left) { /* parent is red, so grandparent exists */ - g = p->__parent_and_color; - gg = c_rbnode_parent(g); - u = g->right; - - if (u && c_rbnode_is_red(u)) { - /* Case 3: - * Parent and uncle are both red. We know the - * grandparent must be black then. Repaint parent and - * uncle black, the grandparent red and recurse into - * the grandparent. */ - c_rbnode_set_parent_and_color(p, g, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(u, g, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(g, gg, C_RBNODE_RED); - n = g; - } else { - /* parent is red, uncle is black */ - - if (n == p->right) { - /* Case 4: - * We're the right child. Rotate on parent to - * become left child, so we can handle it the - * same as case 5. */ - x = n->left; - p->right = n->left; - n->left = p; - if (x) - c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(p, n, C_RBNODE_RED); - p = n; - } - - /* 'n' is invalid from here on! */ - n = NULL; - - /* Case 5: - * We're the red left child or a red parent, black - * grandparent and uncle. Rotate on grandparent and - * switch color with parent. Number of black nodes on - * each path stays the same, but we got rid of the - * double red path. As the grandparent is still black, - * we're done. */ - x = p->right; - g->left = x; - p->right = g; - if (x) - c_rbnode_set_parent_and_color(x, g, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(p, gg, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(g, p, C_RBNODE_RED); - c_rbtree_swap_child(t, gg, g, p); - } - } else /* if (p == p->__parent_and_color->left) */ { /* same as above, but mirrored */ - g = p->__parent_and_color; - gg = c_rbnode_parent(g); - u = g->left; - - if (u && c_rbnode_is_red(u)) { - c_rbnode_set_parent_and_color(p, g, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(u, g, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(g, gg, C_RBNODE_RED); - n = g; - } else { - if (n == p->left) { - x = n->right; - p->left = n->right; - n->right = p; - if (x) - c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(p, n, C_RBNODE_RED); - p = n; - } - - n = NULL; - - x = p->left; - g->right = x; - p->left = g; - if (x) - c_rbnode_set_parent_and_color(x, g, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(p, gg, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(g, p, C_RBNODE_RED); - c_rbtree_swap_child(t, gg, g, p); - } - } - - return n; -} - -static inline void c_rbtree_paint(CRBTree *t, CRBNode *n) { - assert(t); - assert(n); - - while (n) - n = c_rbtree_paint_one(t, n); -} - -/** - * c_rbtree_add() - add node to tree - * @t: tree to operate one - * @p: parent node to link under, or NULL - * @l: left/right slot of @p (or root) to link at - * @n: node to add - * - * This links @n into the tree given as @t. The caller must provide the exact - * spot where to link the node. That is, the caller must traverse the tree - * based on their search order. Once they hit a leaf where to insert the node, - * call this function to link it and rebalance the tree. - * - * A typical insertion would look like this (@t is your tree, @n is your node): - * - * CRBNode **i, *p; - * - * i = &t->root; - * p = NULL; - * while (*i) { - * p = *i; - * if (compare(n, *i) < 0) - * i = &(*i)->left; - * else - * i = &(*i)->right; - * } - * - * c_rbtree_add(t, p, i, n); - * - * Once the node is linked into the tree, a simple lookup on the same tree can - * be coded like this: - * - * CRBNode *i; - * - * i = t->root; - * while (i) { - * int v = compare(n, i); - * if (v < 0) - * i = (*i)->left; - * else if (v > 0) - * i = (*i)->right; - * else - * break; - * } - * - * When you add nodes to a tree, the memory contents of the node do not matter. - * That is, there is no need to initialize the node via c_rbnode_init(). - * However, if you relink nodes multiple times during their lifetime, it is - * usually very convenient to use c_rbnode_init() and c_rbtree_remove_init(). - * In those cases, you should validate that a node is unlinked before you call - * c_rbtree_add(). - */ -void c_rbtree_add(CRBTree *t, CRBNode *p, CRBNode **l, CRBNode *n) { - assert(t); - assert(l); - assert(n); - assert(!p || l == &p->left || l == &p->right); - assert(p || l == &t->root); - - c_rbnode_set_parent_and_color(n, p, C_RBNODE_RED); - n->left = n->right = NULL; - *l = n; - - c_rbtree_paint(t, n); -} - -static inline CRBNode *c_rbtree_rebalance_one(CRBTree *t, CRBNode *p, CRBNode *n) { - CRBNode *s, *x, *y, *g; - - /* - * Rebalance tree after a node was removed. This happens only if you - * remove a black node and one path is now left with an unbalanced - * number or black nodes. - * This function assumes all paths through p and n have one black node - * less than all other paths. If recursive fixup is required, the - * current node is returned. - */ - - if (n == p->left) { - s = p->right; - if (c_rbnode_is_red(s)) { - /* Case 3: - * We have a red node as sibling. Rotate it onto our - * side so we can later on turn it black. This way, we - * gain the additional black node in our path. */ - g = c_rbnode_parent(p); - x = s->left; - p->right = x; - s->left = p; - c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(s, g, c_rbnode_color(p)); - c_rbnode_set_parent_and_color(p, s, C_RBNODE_RED); - c_rbtree_swap_child(t, g, p, s); - s = x; - } - - x = s->right; - if (!x || c_rbnode_is_black(x)) { - y = s->left; - if (!y || c_rbnode_is_black(y)) { - /* Case 4: - * Our sibling is black and has only black - * children. Flip it red and turn parent black. - * This way we gained a black node in our path, - * or we fix it recursively one layer up, which - * will rotate the red sibling as parent. */ - c_rbnode_set_parent_and_color(s, p, C_RBNODE_RED); - if (c_rbnode_is_black(p)) - return p; - - c_rbnode_set_parent_and_color(p, c_rbnode_parent(p), C_RBNODE_BLACK); - return NULL; - } - - /* Case 5: - * Left child of our sibling is red, right one is black. - * Rotate on parent so the right child of our sibling is - * now red, and we can fall through to case 6. */ - x = y->right; - s->left = y->right; - y->right = s; - p->right = y; - if (x) - c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK); - x = s; - s = y; - } - - /* Case 6: - * The right child of our sibling is red. Rotate left and flip - * colors, which gains us an additional black node in our path, - * that was previously on our sibling. */ - g = c_rbnode_parent(p); - y = s->left; - p->right = y; - s->left = p; - c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK); - if (y) - c_rbnode_set_parent_and_color(y, p, c_rbnode_color(y)); - c_rbnode_set_parent_and_color(s, g, c_rbnode_color(p)); - c_rbnode_set_parent_and_color(p, s, C_RBNODE_BLACK); - c_rbtree_swap_child(t, g, p, s); - } else /* if (!n || n == p->right) */ { /* same as above, but mirrored */ - s = p->left; - if (c_rbnode_is_red(s)) { - g = c_rbnode_parent(p); - x = s->right; - p->left = x; - s->right = p; - c_rbnode_set_parent_and_color(x, p, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(s, g, C_RBNODE_BLACK); - c_rbnode_set_parent_and_color(p, s, C_RBNODE_RED); - c_rbtree_swap_child(t, g, p, s); - s = x; - } - - x = s->left; - if (!x || c_rbnode_is_black(x)) { - y = s->right; - if (!y || c_rbnode_is_black(y)) { - c_rbnode_set_parent_and_color(s, p, C_RBNODE_RED); - if (c_rbnode_is_black(p)) - return p; - - c_rbnode_set_parent_and_color(p, c_rbnode_parent(p), C_RBNODE_BLACK); - return NULL; - } - - x = y->left; - s->right = y->left; - y->left = s; - p->left = y; - if (x) - c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK); - x = s; - s = y; - } - - g = c_rbnode_parent(p); - y = s->right; - p->left = y; - s->right = p; - c_rbnode_set_parent_and_color(x, s, C_RBNODE_BLACK); - if (y) - c_rbnode_set_parent_and_color(y, p, c_rbnode_color(y)); - c_rbnode_set_parent_and_color(s, g, c_rbnode_color(p)); - c_rbnode_set_parent_and_color(p, s, C_RBNODE_BLACK); - c_rbtree_swap_child(t, g, p, s); - } - - return NULL; -} - -static inline void c_rbtree_rebalance(CRBTree *t, CRBNode *p) { - CRBNode *n = NULL; - - assert(t); - assert(p); - - do { - n = c_rbtree_rebalance_one(t, p, n); - p = n ? c_rbnode_parent(n) : NULL; - } while (p); -} - -/** - * c_rbtree_remove() - remove node from tree - * @t: tree to operate one - * @n: node to remove - * - * This removes the given node from its tree. Once unlinked, the tree is - * rebalanced. - * The caller *must* ensure that the given tree is actually the tree it is - * linked on. Otherwise, behavior is undefined. - * - * This does *NOT* reset @n to being unlinked (for performance reason, this - * function *never* modifies @n at all). If you need this, use - * c_rbtree_remove_init(). - */ -void c_rbtree_remove(CRBTree *t, CRBNode *n) { - CRBNode *p, *s, *gc, *x, *next = NULL; - unsigned long c; - - assert(t); - assert(n); - assert(c_rbnode_is_linked(n)); - - /* - * There are three distinct cases during node removal of a tree: - * * The node has no children, in which case it can simply be removed. - * * The node has exactly one child, in which case the child displaces - * its parent. - * * The node has two children, in which case there is guaranteed to - * be a successor to the node (successor being the node ordered - * directly after it). This successor cannot have two children by - * itself (two interior nodes can never be successive). Therefore, - * we can simply swap the node with its successor (including color) - * and have reduced this case to either of the first two. - * - * Whenever the node we removed was black, we have to rebalance the - * tree. Note that this affects the actual node we _remove_, not @n (in - * case we swap it). - * - * p: parent - * s: successor - * gc: grand-...-child - * x: temporary - * next: next node to rebalance on - */ - - if (!n->left) { - /* - * Case 1: - * The node has no left child. If it neither has a right child, - * it is a leaf-node and we can simply unlink it. If it also - * was black, we have to rebalance, as always if we remove a - * black node. - * But if the node has a right child, the child *must* be red - * (otherwise, the right path has more black nodes as the - * non-existing left path), and the node to be removed must - * hence be black. We simply replace the node with its child, - * turning the red child black, and thus no rebalancing is - * required. - */ - p = c_rbnode_parent(n); - c = c_rbnode_color(n); - c_rbtree_swap_child(t, p, n, n->right); - if (n->right) - c_rbnode_set_parent_and_color(n->right, p, c); - else - next = (c == C_RBNODE_BLACK) ? p : NULL; - } else if (!n->right) { - /* - * Case 1.1: - * The node has exactly one child, and it is on the left. Treat - * it as mirrored case of Case 1 (i.e., replace the node by its - * child). - */ - p = c_rbnode_parent(n); - c = c_rbnode_color(n); - c_rbtree_swap_child(t, p, n, n->left); - c_rbnode_set_parent_and_color(n->left, p, c); - } else { - /* - * Case 2: - * We are dealing with a full interior node with a child not on - * both sides. Find its successor and swap it. Then remove the - * node similar to Case 1. For performance reasons we don't - * perform the full swap, but skip links that are about to be - * removed, anyway. - */ - s = n->right; - if (!s->left) { - /* right child is next, no need to touch grandchild */ - p = s; - gc = s->right; - } else { - /* find successor and swap partially */ - s = c_rbnode_leftmost(s); - p = c_rbnode_parent(s); - - gc = s->right; - p->left = s->right; - s->right = n->right; - c_rbnode_set_parent(n->right, s); - } - - /* node is partially swapped, now remove as in Case 1 */ - s->left = n->left; - c_rbnode_set_parent(n->left, s); - - x = c_rbnode_parent(n); - c = c_rbnode_color(n); - c_rbtree_swap_child(t, x, n, s); - if (gc) - c_rbnode_set_parent_and_color(gc, p, C_RBNODE_BLACK); - else - next = c_rbnode_is_black(s) ? p : NULL; - c_rbnode_set_parent_and_color(s, x, c); - } - - if (next) - c_rbtree_rebalance(t, next); -} diff --git a/src/basic/c-rbtree.h b/src/basic/c-rbtree.h deleted file mode 100644 index 20c5515ca1..0000000000 --- a/src/basic/c-rbtree.h +++ /dev/null @@ -1,297 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. See COPYING for details. - - 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/>. -***/ - -/* - * Standalone Red-Black-Tree Implementation in Standard ISO-C11 - * - * This header provides an RB-Tree API, that is fully implemented in ISO-C11 - * and has no external dependencies. Furthermore, tree traversal, memory - * allocations, and key comparisons a fully in control of the API user. The - * implementation only provides the RB-Tree specific rebalancing and coloring. - * - * A tree is represented by the "CRBTree" structure. It contains a *singly* - * field, which is a pointer to the root node. If NULL, the tree is empty. If - * non-NULL, there is at least a single element in the tree. - * - * Each node of the tree is represented by the "CRBNode" structure. It has - * three fields. The @left and @right members can be accessed by the API user - * directly to traverse the tree. The third member is an implementation detail - * and encodes the parent pointer and color of the node. - * API users are required to embed the CRBNode object into their own objects - * and then use offsetof() (i.e., container_of() and friends) to turn CRBNode - * pointers into pointers to their own structure. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct CRBNode CRBNode; -typedef struct CRBTree CRBTree; - -/** - * struct CRBNode - Node of a Red-Black Tree - * @__parent_and_color: internal state - * @left: left child, or NULL - * @right: right child, or NULL - * - * Each node in an RB-Tree must embed an CRBNode object. This object contains - * pointers to its left and right child, which can be freely accessed by the - * API user at any time. They are NULL, if the node does not have a left/right - * child. - * - * The @__parent_and_color field must never be accessed directly. It encodes - * the pointer to the parent node, and the color of the node. Use the accessor - * functions instead. - * - * There is no reason to initialize a CRBNode object before linking it. - * However, if you need a boolean state that tells you whether the node is - * linked or not, you should initialize the node via c_rbnode_init() or - * C_RBNODE_INIT. - */ -struct CRBNode { - CRBNode *__parent_and_color; - CRBNode *left; - CRBNode *right; -}; - -#define C_RBNODE_INIT(_var) { .__parent_and_color = &(_var) } - -CRBNode *c_rbnode_leftmost(CRBNode *n); -CRBNode *c_rbnode_rightmost(CRBNode *n); -CRBNode *c_rbnode_next(CRBNode *n); -CRBNode *c_rbnode_prev(CRBNode *n); - -/** - * struct CRBTree - Red-Black Tree - * @root: pointer to the root node, or NULL - * - * Each Red-Black Tree is rooted in an CRBTree object. This object contains a - * pointer to the root node of the tree. The API user is free to access the - * @root member at any time, and use it to traverse the tree. - * - * To initialize an RB-Tree, set it to NULL / all zero. - */ -struct CRBTree { - CRBNode *root; -}; - -CRBNode *c_rbtree_first(CRBTree *t); -CRBNode *c_rbtree_last(CRBTree *t); - -void c_rbtree_add(CRBTree *t, CRBNode *p, CRBNode **l, CRBNode *n); -void c_rbtree_remove(CRBTree *t, CRBNode *n); - -/** - * c_rbnode_init() - mark a node as unlinked - * @n: node to operate on - * - * This marks the node @n as unlinked. The node will be set to a valid state - * that can never happen if the node is linked in a tree. Furthermore, this - * state is fully known to the implementation, and as such handled gracefully - * in all cases. - * - * You are *NOT* required to call this on your node. c_rbtree_add() can handle - * uninitialized nodes just fine. However, calling this allows to use - * c_rbnode_is_linked() to check for the state of a node. Furthermore, - * iterators and accessors can be called on initialized (yet unlinked) nodes. - * - * Use the C_RBNODE_INIT macro if you want to initialize static variables. - */ -static inline void c_rbnode_init(CRBNode *n) { - *n = (CRBNode)C_RBNODE_INIT(*n); -} - -/** - * c_rbnode_is_linked() - check whether a node is linked - * @n: node to check, or NULL - * - * This checks whether the passed node is linked. If you pass NULL, or if the - * node is not linked into a tree, this will return false. Otherwise, this - * returns true. - * - * Note that you must have either linked the node or initialized it, before - * calling this function. Never call this function on uninitialized nodes. - * Furthermore, removing a node via c_rbtree_remove() does *NOT* mark the node - * as unlinked. You have to call c_rbnode_init() yourself after removal, or use - * the c_rbtree_remove_init() helper. - * - * Return: true if the node is linked, false if not. - */ -static inline _Bool c_rbnode_is_linked(CRBNode *n) { - return n && n->__parent_and_color != n; -} - -/** - * c_rbnode_parent() - return parent pointer - * @n node to access - * - * This returns a pointer to the parent of the given node @n. If @n does not - * have a parent, NULL is returned. If @n is not linked, @n itself is returned. - * - * You should not call this on unlinked or uninitialized nodes! If you do, you - * better know how its semantics. - * - * Return: Pointer to parent. - */ -static inline CRBNode *c_rbnode_parent(CRBNode *n) { - return (CRBNode*)((unsigned long)n->__parent_and_color & ~1UL); -} - -/** - * c_rbtree_remove_init() - safely remove node from tree and reinitialize it - * @t: tree to operate on - * @n: node to remove, or NULL - * - * This is almost the same as c_rbtree_remove(), but extends it slightly, to be - * more convenient to use in many cases: - * - if @n is unlinked or NULL, this is a no-op - * - @n is reinitialized after being removed - */ -static inline void c_rbtree_remove_init(CRBTree *t, CRBNode *n) { - if (c_rbnode_is_linked(n)) { - c_rbtree_remove(t, n); - c_rbnode_init(n); - } -} - -/** - * CRBCompareFunc - compare a node to a key - * @t: tree where the node is linked to - * @k: key to compare - * @n: node to compare - * - * If you use the tree-traversal helpers (which are optional), you need to - * provide this callback so they can compare nodes in a tree to the key you - * look for. - * - * The tree @t is provided as optional context to this callback. The key you - * look for is provided as @k, the current node that should be compared to is - * provided as @n. This function should work like strcmp(), that is, return -1 - * if @key orders before @n, 0 if both compare equal, and 1 if it orders after - * @n. - */ -typedef int (*CRBCompareFunc) (CRBTree *t, void *k, CRBNode *n); - -/** - * c_rbtree_find_node() - find node - * @t: tree to search through - * @f: comparison function - * @k: key to search for - * - * This searches through @t for a node that compares equal to @k. The function - * @f must be provided by the caller, which is used to compare nodes to @k. See - * the documentation of CRBCompareFunc for details. - * - * If there are multiple entries that compare equal to @k, this will return a - * pseudo-randomly picked node. If you need stable lookup functions for trees - * where duplicate entries are allowed, you better code your own lookup. - * - * Return: Pointer to matching node, or NULL. - */ -static inline CRBNode *c_rbtree_find_node(CRBTree *t, CRBCompareFunc f, const void *k) { - CRBNode *i; - - assert(t); - assert(f); - - i = t->root; - while (i) { - int v = f(t, (void *)k, i); - if (v < 0) - i = i->left; - else if (v > 0) - i = i->right; - else - return i; - } - - return NULL; -} - -/** - * c_rbtree_find_entry() - find entry - * @_t: tree to search through - * @_f: comparison function - * @_k: key to search for - * @_t: type of the structure that embeds the nodes - * @_o: name of the node-member in type @_t - * - * This is very similar to c_rbtree_find_node(), but instead of returning a - * pointer to the CRBNode, it returns a pointer to the surrounding object. This - * object must embed the CRBNode object. The type of the surrounding object - * must be given as @_t, and the name of the embedded CRBNode member as @_o. - * - * See c_rbtree_find_node() for more details. - * - * Return: Pointer to found entry, NULL if not found. - */ -#define c_rbtree_find_entry(_m, _f, _k, _t, _o) \ - ((_t *)(((char *)c_rbtree_find_node((_m), (_f), (_k)) ?: \ - (char *)NULL + offsetof(_t, _o)) - offsetof(_t, _o))) - -/** - * c_rbtree_find_slot() - find slot to insert new node - * @t: tree to search through - * @f: comparison function - * @k: key to search for - * @p: output storage for parent pointer - * - * This searches through @t just like c_rbtree_find_node() does. However, - * instead of returning a pointer to a node that compares equal to @k, this - * searches for a slot to insert a node with key @k. A pointer to the slot is - * returned, and a pointer to the parent of the slot is stored in @p. Both - * can be passed directly to c_rbtree_add(), together with your node to insert. - * - * If there already is a node in the tree, that compares equal to @k, this will - * return NULL and store the conflicting node in @p. In all other cases, - * this will return a pointer (non-NULL) to the empty slot to insert the node - * at. @p will point to the parent node of that slot. - * - * If you want trees that allow duplicate nodes, you better code your own - * insertion function. - * - * Return: Pointer to slot to insert node, or NULL on conflicts. - */ -static inline CRBNode **c_rbtree_find_slot(CRBTree *t, CRBCompareFunc f, const void *k, CRBNode **p) { - CRBNode **i; - - assert(t); - assert(f); - assert(p); - - i = &t->root; - *p = NULL; - while (*i) { - int v = f(t, (void *)k, *i); - *p = *i; - if (v < 0) - i = &(*i)->left; - else if (v > 0) - i = &(*i)->right; - else - return NULL; - } - - return i; -} - -#ifdef __cplusplus -} -#endif diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c index 775879076d..6e0bab9b94 100644 --- a/src/basic/calendarspec.c +++ b/src/basic/calendarspec.c @@ -114,7 +114,7 @@ static void sort_chain(CalendarComponent **c) { static void fix_year(CalendarComponent *c) { /* Turns 12 → 2012, 89 → 1989 */ - while(c) { + while (c) { CalendarComponent *n = c->next; if (c->value >= 0 && c->value < 70) @@ -978,7 +978,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { for (;;) { /* Normalize the current date */ - mktime_or_timegm(&c, spec->utc); + (void) mktime_or_timegm(&c, spec->utc); c.tm_isdst = -1; c.tm_year += 1900; @@ -990,8 +990,10 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { c.tm_mday = 1; c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; } - if (r < 0 || tm_out_of_bounds(&c, spec->utc)) + if (r < 0) return r; + if (tm_out_of_bounds(&c, spec->utc)) + return -ENOENT; c.tm_mon += 1; r = find_matching_component(spec->month, &c.tm_mon); @@ -1002,7 +1004,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; } if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { - c.tm_year ++; + c.tm_year++; c.tm_mon = 0; c.tm_mday = 1; c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; @@ -1013,7 +1015,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { if (r > 0) c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { - c.tm_mon ++; + c.tm_mon++; c.tm_mday = 1; c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; continue; @@ -1027,18 +1029,18 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { r = find_matching_component(spec->hour, &c.tm_hour); if (r > 0) - c.tm_min = c.tm_sec = 0; + c.tm_min = c.tm_sec = tm_usec = 0; if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { - c.tm_mday ++; + c.tm_mday++; c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0; continue; } r = find_matching_component(spec->minute, &c.tm_min); if (r > 0) - c.tm_sec = 0; + c.tm_sec = tm_usec = 0; if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { - c.tm_hour ++; + c.tm_hour++; c.tm_min = c.tm_sec = tm_usec = 0; continue; } @@ -1049,7 +1051,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { c.tm_sec /= USEC_PER_SEC; if (r < 0 || tm_out_of_bounds(&c, spec->utc)) { - c.tm_min ++; + c.tm_min++; c.tm_sec = tm_usec = 0; continue; } diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 6ef00d51df..7cdc97ee3c 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -101,6 +101,39 @@ int cg_read_pid(FILE *f, pid_t *_pid) { return 1; } +int cg_read_event(const char *controller, const char *path, const char *event, + char **val) +{ + _cleanup_free_ char *events = NULL, *content = NULL; + char *p, *line; + int r; + + r = cg_get_path(controller, path, "cgroup.events", &events); + if (r < 0) + return r; + + r = read_full_file(events, &content, NULL); + if (r < 0) + return r; + + p = content; + while ((line = strsep(&p, "\n"))) { + char *key; + + key = strsep(&line, " "); + if (!key || !line) + return -EINVAL; + + if (strcmp(key, event)) + continue; + + *val = strdup(line); + return 0; + } + + return -ENOENT; +} + int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) { _cleanup_free_ char *fs = NULL; int r; @@ -1007,18 +1040,12 @@ int cg_is_empty_recursive(const char *controller, const char *path) { return unified; if (unified > 0) { - _cleanup_free_ char *populated = NULL, *t = NULL; + _cleanup_free_ char *t = NULL; /* On the unified hierarchy we can check empty state - * via the "cgroup.populated" attribute. */ + * via the "populated" attribute of "cgroup.events". */ - r = cg_get_path(controller, path, "cgroup.populated", &populated); - if (r < 0) - return r; - - r = read_one_line_file(populated, &t); - if (r == -ENOENT) - return 1; + r = cg_read_event(controller, path, "populated", &t); if (r < 0) return r; @@ -1248,7 +1275,7 @@ int cg_pid_get_path_shifted(pid_t pid, const char *root, char **cgroup) { return 0; } -int cg_path_decode_unit(const char *cgroup, char **unit){ +int cg_path_decode_unit(const char *cgroup, char **unit) { char *c, *s; size_t n; @@ -2033,10 +2060,10 @@ int cg_mask_supported(CGroupMask *ret) { mask |= CGROUP_CONTROLLER_TO_MASK(v); } - /* Currently, we only support the memory and pids + /* Currently, we only support the memory, io and pids * controller in the unified hierarchy, mask * everything else off. */ - mask &= CGROUP_MASK_MEMORY | CGROUP_MASK_PIDS; + mask &= CGROUP_MASK_MEMORY | CGROUP_MASK_IO | CGROUP_MASK_PIDS; } else { CGroupController c; @@ -2129,7 +2156,7 @@ int cg_unified(void) { if (statfs("/sys/fs/cgroup/", &fs) < 0) return -errno; - if (F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC)) + if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) unified_cache = true; else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) unified_cache = false; @@ -2222,6 +2249,42 @@ bool cg_is_legacy_wanted(void) { return !cg_is_unified_wanted(); } +int cg_weight_parse(const char *s, uint64_t *ret) { + uint64_t u; + int r; + + if (isempty(s)) { + *ret = CGROUP_WEIGHT_INVALID; + return 0; + } + + r = safe_atou64(s, &u); + if (r < 0) + return r; + + if (u < CGROUP_WEIGHT_MIN || u > CGROUP_WEIGHT_MAX) + return -ERANGE; + + *ret = u; + return 0; +} + +const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX] = { + [CGROUP_IO_RBPS_MAX] = CGROUP_LIMIT_MAX, + [CGROUP_IO_WBPS_MAX] = CGROUP_LIMIT_MAX, + [CGROUP_IO_RIOPS_MAX] = CGROUP_LIMIT_MAX, + [CGROUP_IO_WIOPS_MAX] = CGROUP_LIMIT_MAX, +}; + +static const char* const cgroup_io_limit_type_table[_CGROUP_IO_LIMIT_TYPE_MAX] = { + [CGROUP_IO_RBPS_MAX] = "IOReadBandwidthMax", + [CGROUP_IO_WBPS_MAX] = "IOWriteBandwidthMax", + [CGROUP_IO_RIOPS_MAX] = "IOReadIOPSMax", + [CGROUP_IO_WIOPS_MAX] = "IOWriteIOPSMax", +}; + +DEFINE_STRING_TABLE_LOOKUP(cgroup_io_limit_type, CGroupIOLimitType); + int cg_cpu_shares_parse(const char *s, uint64_t *ret) { uint64_t u; int r; @@ -2265,6 +2328,7 @@ int cg_blkio_weight_parse(const char *s, uint64_t *ret) { static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = { [CGROUP_CONTROLLER_CPU] = "cpu", [CGROUP_CONTROLLER_CPUACCT] = "cpuacct", + [CGROUP_CONTROLLER_IO] = "io", [CGROUP_CONTROLLER_BLKIO] = "blkio", [CGROUP_CONTROLLER_MEMORY] = "memory", [CGROUP_CONTROLLER_DEVICES] = "devices", diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h index ad1edd9cdb..4bb5291296 100644 --- a/src/basic/cgroup-util.h +++ b/src/basic/cgroup-util.h @@ -34,6 +34,7 @@ typedef enum CGroupController { CGROUP_CONTROLLER_CPU, CGROUP_CONTROLLER_CPUACCT, + CGROUP_CONTROLLER_IO, CGROUP_CONTROLLER_BLKIO, CGROUP_CONTROLLER_MEMORY, CGROUP_CONTROLLER_DEVICES, @@ -48,6 +49,7 @@ typedef enum CGroupController { typedef enum CGroupMask { CGROUP_MASK_CPU = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPU), CGROUP_MASK_CPUACCT = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPUACCT), + CGROUP_MASK_IO = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_IO), CGROUP_MASK_BLKIO = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BLKIO), CGROUP_MASK_MEMORY = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_MEMORY), CGROUP_MASK_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_DEVICES), @@ -55,6 +57,37 @@ typedef enum CGroupMask { _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1 } CGroupMask; +/* Special values for all weight knobs on unified hierarchy */ +#define CGROUP_WEIGHT_INVALID ((uint64_t) -1) +#define CGROUP_WEIGHT_MIN UINT64_C(1) +#define CGROUP_WEIGHT_MAX UINT64_C(10000) +#define CGROUP_WEIGHT_DEFAULT UINT64_C(100) + +#define CGROUP_LIMIT_MIN UINT64_C(0) +#define CGROUP_LIMIT_MAX ((uint64_t) -1) + +static inline bool CGROUP_WEIGHT_IS_OK(uint64_t x) { + return + x == CGROUP_WEIGHT_INVALID || + (x >= CGROUP_WEIGHT_MIN && x <= CGROUP_WEIGHT_MAX); +} + +/* IO limits on unified hierarchy */ +typedef enum CGroupIOLimitType { + CGROUP_IO_RBPS_MAX, + CGROUP_IO_WBPS_MAX, + CGROUP_IO_RIOPS_MAX, + CGROUP_IO_WIOPS_MAX, + + _CGROUP_IO_LIMIT_TYPE_MAX, + _CGROUP_IO_LIMIT_TYPE_INVALID = -1 +} CGroupIOLimitType; + +extern const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX]; + +const char* cgroup_io_limit_type_to_string(CGroupIOLimitType t) _const_; +CGroupIOLimitType cgroup_io_limit_type_from_string(const char *s) _pure_; + /* Special values for the cpu.shares attribute */ #define CGROUP_CPU_SHARES_INVALID ((uint64_t) -1) #define CGROUP_CPU_SHARES_MIN UINT64_C(2) @@ -96,6 +129,8 @@ static inline bool CGROUP_BLKIO_WEIGHT_IS_OK(uint64_t x) { int cg_enumerate_processes(const char *controller, const char *path, FILE **_f); int cg_read_pid(FILE *f, pid_t *_pid); +int cg_read_event(const char *controller, const char *path, const char *event, + char **val); int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d); int cg_read_subgroup(DIR *d, char **fn); @@ -188,5 +223,6 @@ bool cg_is_legacy_wanted(void); const char* cgroup_controller_to_string(CGroupController c) _const_; CGroupController cgroup_controller_from_string(const char *s) _pure_; +int cg_weight_parse(const char *s, uint64_t *ret); int cg_cpu_shares_parse(const char *s, uint64_t *ret); int cg_blkio_weight_parse(const char *s, uint64_t *ret); diff --git a/src/basic/clock-util.c b/src/basic/clock-util.c index 507e757ff0..7fe8d35ea5 100644 --- a/src/basic/clock-util.c +++ b/src/basic/clock-util.c @@ -69,9 +69,12 @@ int clock_set_hwclock(const struct tm *tm) { return 0; } -int clock_is_localtime(void) { +int clock_is_localtime(const char* adjtime_path) { _cleanup_fclose_ FILE *f; + if (adjtime_path == NULL) + adjtime_path = "/etc/adjtime"; + /* * The third line of adjtime is "UTC" or "LOCAL" or nothing. * # /etc/adjtime @@ -79,7 +82,7 @@ int clock_is_localtime(void) { * 0 * UTC */ - f = fopen("/etc/adjtime", "re"); + f = fopen(adjtime_path, "re"); if (f) { char line[LINE_MAX]; bool b; @@ -88,7 +91,8 @@ int clock_is_localtime(void) { fgets(line, sizeof(line), f) && fgets(line, sizeof(line), f); if (!b) - return -EIO; + /* less than three lines -> default to UTC */ + return 0; truncate_nl(line); return streq(line, "LOCAL"); @@ -96,6 +100,7 @@ int clock_is_localtime(void) { } else if (errno != ENOENT) return -errno; + /* adjtime not present -> default to UTC */ return 0; } diff --git a/src/basic/clock-util.h b/src/basic/clock-util.h index f471f2abcf..8830cd2f38 100644 --- a/src/basic/clock-util.h +++ b/src/basic/clock-util.h @@ -21,7 +21,7 @@ #include <time.h> -int clock_is_localtime(void); +int clock_is_localtime(const char* adjtime_path); int clock_set_timezone(int *min); int clock_reset_timewarp(void); int clock_get_hwclock(struct tm *tm); diff --git a/src/basic/copy.c b/src/basic/copy.c index 519b412941..c3586728d0 100644 --- a/src/basic/copy.c +++ b/src/basic/copy.c @@ -40,17 +40,38 @@ #include "fs-util.h" #include "io-util.h" #include "macro.h" +#include "missing.h" #include "string-util.h" #include "strv.h" #include "time-util.h" #include "umask-util.h" #include "xattr-util.h" -#define COPY_BUFFER_SIZE (16*1024) +#define COPY_BUFFER_SIZE (16*1024u) + +static ssize_t try_copy_file_range(int fd_in, loff_t *off_in, + int fd_out, loff_t *off_out, + size_t len, + unsigned int flags) { + static int have = -1; + ssize_t r; + + if (have == false) + return -ENOSYS; + + r = copy_file_range(fd_in, off_in, fd_out, off_out, len, flags); + if (_unlikely_(have < 0)) + have = r >= 0 || errno != ENOSYS; + if (r >= 0) + return r; + else + return -errno; +} int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { - bool try_sendfile = true, try_splice = true; + bool try_cfr = true, try_sendfile = true, try_splice = true; int r; + size_t m = SSIZE_MAX; /* that is the maximum that sendfile and c_f_r accept */ assert(fdf >= 0); assert(fdt >= 0); @@ -67,56 +88,69 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { } for (;;) { - size_t m = COPY_BUFFER_SIZE; ssize_t n; if (max_bytes != (uint64_t) -1) { - if (max_bytes <= 0) return 1; /* return > 0 if we hit the max_bytes limit */ - if ((uint64_t) m > max_bytes) - m = (size_t) max_bytes; + if (m > max_bytes) + m = max_bytes; + } + + /* First try copy_file_range(), unless we already tried */ + if (try_cfr) { + n = try_copy_file_range(fdf, NULL, fdt, NULL, m, 0u); + if (n < 0) { + if (!IN_SET(n, -EINVAL, -ENOSYS, -EXDEV, -EBADF)) + return n; + + try_cfr = false; + /* use fallback below */ + } else if (n == 0) /* EOF */ + break; + else + /* Success! */ + goto next; } /* First try sendfile(), unless we already tried */ if (try_sendfile) { - n = sendfile(fdt, fdf, NULL, m); if (n < 0) { - if (errno != EINVAL && errno != ENOSYS) + if (!IN_SET(errno, EINVAL, ENOSYS)) return -errno; try_sendfile = false; /* use fallback below */ } else if (n == 0) /* EOF */ break; - else if (n > 0) + else /* Success! */ goto next; } - /* The try splice, unless we already tried */ + /* Then try splice, unless we already tried */ if (try_splice) { n = splice(fdf, NULL, fdt, NULL, m, 0); if (n < 0) { - if (errno != EINVAL && errno != ENOSYS) + if (!IN_SET(errno, EINVAL, ENOSYS)) return -errno; try_splice = false; /* use fallback below */ } else if (n == 0) /* EOF */ break; - else if (n > 0) + else /* Success! */ goto next; } /* As a fallback just copy bits by hand */ { - uint8_t buf[m]; + uint8_t buf[MIN(m, COPY_BUFFER_SIZE)]; - n = read(fdf, buf, m); + n = read(fdf, buf, sizeof buf); if (n < 0) return -errno; if (n == 0) /* EOF */ @@ -132,6 +166,11 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) { assert(max_bytes >= (uint64_t) n); max_bytes -= n; } + /* sendfile accepts at most SSIZE_MAX-offset bytes to copy, + * so reduce our maximum by the amount we already copied, + * but don't go below our copy buffer size, unless we are + * close the the limit of bytes we are allowed to copy. */ + m = MAX(MIN(COPY_BUFFER_SIZE, max_bytes), m - n); } return 0; /* return 0 if we hit EOF earlier than the size limit */ @@ -266,6 +305,8 @@ static int fd_copy_directory( fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); else fdf = fcntl(df, F_DUPFD_CLOEXEC, 3); + if (fdf < 0) + return -errno; d = fdopendir(fdf); if (!d) @@ -286,22 +327,6 @@ static int fd_copy_directory( r = 0; - if (created) { - struct timespec ut[2] = { - st->st_atim, - st->st_mtim - }; - - if (fchown(fdt, st->st_uid, st->st_gid) < 0) - r = -errno; - - if (fchmod(fdt, st->st_mode & 07777) < 0) - r = -errno; - - (void) futimens(fdt, ut); - (void) copy_xattr(dirfd(d), fdt); - } - FOREACH_DIRENT_ALL(de, d, return -errno) { struct stat buf; int q; @@ -325,7 +350,7 @@ static int fd_copy_directory( q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name); else if (S_ISFIFO(buf.st_mode)) q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name); - else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode)) + else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode) || S_ISSOCK(buf.st_mode)) q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name); else q = -EOPNOTSUPP; @@ -337,6 +362,22 @@ static int fd_copy_directory( r = q; } + if (created) { + struct timespec ut[2] = { + st->st_atim, + st->st_mtim + }; + + if (fchown(fdt, st->st_uid, st->st_gid) < 0) + r = -errno; + + if (fchmod(fdt, st->st_mode & 07777) < 0) + r = -errno; + + (void) copy_xattr(dirfd(d), fdt); + (void) futimens(fdt, ut); + } + return r; } @@ -357,7 +398,7 @@ int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) return fd_copy_symlink(fdf, from, &st, fdt, to); else if (S_ISFIFO(st.st_mode)) return fd_copy_fifo(fdf, from, &st, fdt, to); - else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) + else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode)) return fd_copy_node(fdf, from, &st, fdt, to); else return -EOPNOTSUPP; @@ -368,7 +409,6 @@ int copy_tree(const char *from, const char *to, bool merge) { } int copy_directory_fd(int dirfd, const char *to, bool merge) { - struct stat st; assert(dirfd >= 0); @@ -383,6 +423,21 @@ int copy_directory_fd(int dirfd, const char *to, bool merge) { return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge); } +int copy_directory(const char *from, const char *to, bool merge) { + struct stat st; + + assert(from); + assert(to); + + if (lstat(from, &st) < 0) + return -errno; + + if (!S_ISDIR(st.st_mode)) + return -ENOTDIR; + + return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge); +} + int copy_file_fd(const char *from, int fdt, bool try_reflink) { _cleanup_close_ int fdf = -1; int r; diff --git a/src/basic/copy.h b/src/basic/copy.h index 3e5eb52506..b5d08ebafe 100644 --- a/src/basic/copy.h +++ b/src/basic/copy.h @@ -30,6 +30,7 @@ int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace int copy_tree(const char *from, const char *to, bool merge); int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge); int copy_directory_fd(int dirfd, const char *to, bool merge); +int copy_directory(const char *from, const char *to, bool merge); int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink); int copy_times(int fdf, int fdt); int copy_xattr(int fdf, int fdt); diff --git a/src/basic/def.h b/src/basic/def.h index 963343eb7d..1a7a0f4928 100644 --- a/src/basic/def.h +++ b/src/basic/def.h @@ -41,8 +41,6 @@ #define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT #define SIGNALS_IGNORE SIGPIPE -#define REBOOT_PARAM_FILE "/run/systemd/reboot-param" - #ifdef HAVE_SPLIT_USR #define KBD_KEYMAP_DIRS \ "/usr/share/keymaps/\0" \ diff --git a/src/basic/dirent-util.c b/src/basic/dirent-util.c index 5fb535cb13..59067121b7 100644 --- a/src/basic/dirent-util.c +++ b/src/basic/dirent-util.c @@ -52,12 +52,10 @@ int dirent_ensure_type(DIR *d, struct dirent *de) { bool dirent_is_file(const struct dirent *de) { assert(de); - if (hidden_file(de->d_name)) + if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN)) return false; - if (de->d_type != DT_REG && - de->d_type != DT_LNK && - de->d_type != DT_UNKNOWN) + if (hidden_or_backup_file(de->d_name)) return false; return true; @@ -66,12 +64,10 @@ bool dirent_is_file(const struct dirent *de) { bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) { assert(de); - if (de->d_type != DT_REG && - de->d_type != DT_LNK && - de->d_type != DT_UNKNOWN) + if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN)) return false; - if (hidden_file_allow_backup(de->d_name)) + if (de->d_name[0] == '.') return false; return endswith(de->d_name, suffix); diff --git a/src/basic/dirent-util.h b/src/basic/dirent-util.h index 6bf099b46c..b91d04908f 100644 --- a/src/basic/dirent-util.h +++ b/src/basic/dirent-util.h @@ -38,7 +38,7 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pu on_error; \ } \ break; \ - } else if (hidden_file((de)->d_name)) \ + } else if (hidden_or_backup_file((de)->d_name)) \ continue; \ else diff --git a/src/basic/escape.c b/src/basic/escape.c index 2e483880c8..01daf11ce7 100644 --- a/src/basic/escape.c +++ b/src/basic/escape.c @@ -413,6 +413,34 @@ char *xescape(const char *s, const char *bad) { return r; } +char *octescape(const char *s, size_t len) { + char *r, *t; + const char *f; + + /* Escapes all chars in bad, in addition to \ and " chars, + * in \nnn style escaping. */ + + r = new(char, len * 4 + 1); + if (!r) + return NULL; + + for (f = s, t = r; f < s + len; f++) { + + if (*f < ' ' || *f >= 127 || *f == '\\' || *f == '"') { + *(t++) = '\\'; + *(t++) = '0' + (*f >> 6); + *(t++) = '0' + ((*f >> 3) & 8); + *(t++) = '0' + (*f & 8); + } else + *(t++) = *f; + } + + *t = 0; + + return r; + +} + static char *strcpy_backslash_escaped(char *t, const char *s, const char *bad) { assert(bad); diff --git a/src/basic/escape.h b/src/basic/escape.h index 1b28bd10af..deaa4def28 100644 --- a/src/basic/escape.h +++ b/src/basic/escape.h @@ -48,6 +48,7 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit); 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); diff --git a/src/basic/ether-addr-util.c b/src/basic/ether-addr-util.c index ded6d31f4b..5697e8d132 100644 --- a/src/basic/ether-addr-util.c +++ b/src/basic/ether-addr-util.c @@ -23,6 +23,7 @@ #include "ether-addr-util.h" #include "macro.h" +#include "string-util.h" char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]) { assert(addr); @@ -42,3 +43,83 @@ char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR return buffer; } + +bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b) { + assert(a); + assert(b); + + return a->ether_addr_octet[0] == b->ether_addr_octet[0] && + a->ether_addr_octet[1] == b->ether_addr_octet[1] && + a->ether_addr_octet[2] == b->ether_addr_octet[2] && + a->ether_addr_octet[3] == b->ether_addr_octet[3] && + a->ether_addr_octet[4] == b->ether_addr_octet[4] && + a->ether_addr_octet[5] == b->ether_addr_octet[5]; +} + +int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset) { + size_t pos = 0, n, field; + char sep = '\0'; + const char *hex = HEXDIGITS, *hexoff; + size_t x; + bool touched; + +#define parse_fields(v) \ + for (field = 0; field < ELEMENTSOF(v); field++) { \ + touched = false; \ + for (n = 0; n < (2 * sizeof(v[0])); n++) { \ + if (s[pos] == '\0') \ + break; \ + hexoff = strchr(hex, s[pos]); \ + if (hexoff == NULL) \ + break; \ + assert(hexoff >= hex); \ + x = hexoff - hex; \ + if (x >= 16) \ + x -= 6; /* A-F */ \ + assert(x < 16); \ + touched = true; \ + v[field] <<= 4; \ + v[field] += x; \ + pos++; \ + } \ + if (!touched) \ + return -EINVAL; \ + if (field < (ELEMENTSOF(v)-1)) { \ + if (s[pos] != sep) \ + return -EINVAL; \ + else \ + pos++; \ + } \ + } + + assert(s); + assert(ret); + + sep = s[strspn(s, hex)]; + if (sep == '\n') + return -EINVAL; + if (strchr(":.-", sep) == NULL) + return -EINVAL; + + if (sep == '.') { + uint16_t shorts[3] = { 0 }; + + parse_fields(shorts); + + for (n = 0; n < ELEMENTSOF(shorts); n++) { + ret->ether_addr_octet[2*n] = ((shorts[n] & (uint16_t)0xff00) >> 8); + ret->ether_addr_octet[2*n + 1] = (shorts[n] & (uint16_t)0x00ff); + } + } else { + struct ether_addr out = { .ether_addr_octet = { 0 } }; + + parse_fields(out.ether_addr_octet); + + for (n = 0; n < ELEMENTSOF(out.ether_addr_octet); n++) + ret->ether_addr_octet[n] = out.ether_addr_octet[n]; + } + + if (offset) + *offset = pos; + return 0; +} diff --git a/src/basic/ether-addr-util.h b/src/basic/ether-addr-util.h index 4487149efd..74e125a95f 100644 --- a/src/basic/ether-addr-util.h +++ b/src/basic/ether-addr-util.h @@ -20,10 +20,20 @@ ***/ #include <net/ethernet.h> +#include <stdbool.h> #define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X" #define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5] #define ETHER_ADDR_TO_STRING_MAX (3*6) - char* ether_addr_to_string(const struct ether_addr *addr, char buffer[ETHER_ADDR_TO_STRING_MAX]); + +bool ether_addr_equal(const struct ether_addr *a, const struct ether_addr *b); + +#define ETHER_ADDR_NULL ((const struct ether_addr){}) + +static inline bool ether_addr_is_null(const struct ether_addr *addr) { + return ether_addr_equal(addr, ÐER_ADDR_NULL); +} + +int ether_addr_from_string(const char *s, struct ether_addr *ret, size_t *offset); diff --git a/src/basic/exit-status.c b/src/basic/exit-status.c index 5e0bc415c8..92fa5ace61 100644 --- a/src/basic/exit-status.c +++ b/src/basic/exit-status.c @@ -147,9 +147,6 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) { case EXIT_MAKE_STARTER: return "MAKE_STARTER"; - case EXIT_BUS_ENDPOINT: - return "BUS_ENDPOINT"; - case EXIT_SMACK_PROCESS_LABEL: return "SMACK_PROCESS_LABEL"; } diff --git a/src/basic/exit-status.h b/src/basic/exit-status.h index 79525d30ee..1208c8feed 100644 --- a/src/basic/exit-status.h +++ b/src/basic/exit-status.h @@ -77,7 +77,6 @@ typedef enum ExitStatus { EXIT_RUNTIME_DIRECTORY, EXIT_MAKE_STARTER, EXIT_CHOWN, - EXIT_BUS_ENDPOINT, EXIT_SMACK_PROCESS_LABEL, } ExitStatus; diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c index ee35d2a0ec..d6c1228463 100644 --- a/src/basic/extract-word.c +++ b/src/basic/extract-word.c @@ -63,12 +63,12 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra if (!GREEDY_REALLOC(s, allocated, sz+1)) return -ENOMEM; - for (;; (*p) ++, c = **p) { + for (;; (*p)++, c = **p) { if (c == 0) goto finish_force_terminate; else if (strchr(separators, c)) { if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) { - (*p) ++; + (*p)++; goto finish_force_next; } } else { @@ -81,7 +81,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra } } - for (;; (*p) ++, c = **p) { + for (;; (*p)++, c = **p) { if (backslash) { if (!GREEDY_REALLOC(s, allocated, sz+7)) return -ENOMEM; @@ -129,7 +129,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra backslash = false; } else if (quote) { /* inside either single or double quotes */ - for (;; (*p) ++, c = **p) { + for (;; (*p)++, c = **p) { if (c == 0) { if (flags & EXTRACT_RELAX) goto finish_force_terminate; @@ -149,7 +149,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra } } else { - for (;; (*p) ++, c = **p) { + for (;; (*p)++, c = **p) { if (c == 0) goto finish_force_terminate; else if ((c == '\'' || c == '"') && (flags & EXTRACT_QUOTES)) { @@ -160,11 +160,11 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra break; } else if (strchr(separators, c)) { if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) { - (*p) ++; + (*p)++; goto finish_force_next; } /* Skip additional coalesced separators. */ - for (;; (*p) ++, c = **p) { + for (;; (*p)++, c = **p) { if (c == 0) goto finish_force_terminate; if (!strchr(separators, c)) diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index ec9560cd07..8b466cff15 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -25,11 +25,13 @@ #include <unistd.h> #include "fd-util.h" +#include "fs-util.h" #include "macro.h" #include "missing.h" #include "parse-util.h" #include "path-util.h" #include "socket-util.h" +#include "stdio-util.h" #include "util.h" int close_nointr(int fd) { @@ -229,7 +231,7 @@ int close_all_fds(const int except[], unsigned n_except) { while ((de = readdir(d))) { int fd = -1; - if (hidden_file(de->d_name)) + if (hidden_or_backup_file(de->d_name)) continue; if (safe_atoi(de->d_name, &fd) < 0) @@ -356,3 +358,17 @@ bool fdname_is_valid(const char *s) { return p - s < 256; } + +int fd_get_path(int fd, char **ret) { + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + int r; + + xsprintf(procfs_path, "/proc/self/fd/%i", fd); + + r = readlink_malloc(procfs_path, ret); + + if (r == -ENOENT) /* If the file doesn't exist the fd is invalid */ + return -EBADF; + + return r; +} diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index 44528c6e35..b86e41698a 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -72,6 +72,8 @@ void cmsg_close_all(struct msghdr *mh); bool fdname_is_valid(const char *s); +int fd_get_path(int fd, char **ret); + /* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */ #define ERRNO_IS_DISCONNECT(r) \ IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH) diff --git a/src/basic/fdset.c b/src/basic/fdset.c index 3674d3ed9d..527f27bc67 100644 --- a/src/basic/fdset.c +++ b/src/basic/fdset.c @@ -94,19 +94,6 @@ int fdset_put(FDSet *s, int fd) { return set_put(MAKE_SET(s), FD_TO_PTR(fd)); } -int fdset_consume(FDSet *s, int fd) { - int r; - - assert(s); - assert(fd >= 0); - - r = fdset_put(s, fd); - if (r <= 0) - safe_close(fd); - - return r; -} - int fdset_put_dup(FDSet *s, int fd) { int copy, r; @@ -164,7 +151,7 @@ int fdset_new_fill(FDSet **_s) { while ((de = readdir(d))) { int fd = -1; - if (hidden_file(de->d_name)) + if (hidden_or_backup_file(de->d_name)) continue; r = safe_atoi(de->d_name, &fd); diff --git a/src/basic/fdset.h b/src/basic/fdset.h index 12d0cef761..16efe5bdf2 100644 --- a/src/basic/fdset.h +++ b/src/basic/fdset.h @@ -32,7 +32,6 @@ FDSet* fdset_free(FDSet *s); int fdset_put(FDSet *s, int fd); int fdset_put_dup(FDSet *s, int fd); -int fdset_consume(FDSet *s, int fd); bool fdset_contains(FDSet *s, int fd); int fdset_remove(FDSet *s, int fd); diff --git a/src/basic/fileio.c b/src/basic/fileio.c index e43ca6d29e..29f5374222 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -352,7 +352,7 @@ static int parse_env_file_internal( case KEY: if (strchr(newline, c)) { state = PRE_KEY; - line ++; + line++; n_key = 0; } else if (c == '=') { state = PRE_VALUE; @@ -376,7 +376,7 @@ static int parse_env_file_internal( case PRE_VALUE: if (strchr(newline, c)) { state = PRE_KEY; - line ++; + line++; key[n_key] = 0; if (value) @@ -416,7 +416,7 @@ static int parse_env_file_internal( case VALUE: if (strchr(newline, c)) { state = PRE_KEY; - line ++; + line++; key[n_key] = 0; @@ -535,7 +535,7 @@ static int parse_env_file_internal( state = COMMENT_ESCAPE; else if (strchr(newline, c)) { state = PRE_KEY; - line ++; + line++; } break; @@ -588,7 +588,7 @@ static int parse_env_file_push( va_list aq, *ap = userdata; if (!utf8_is_valid(key)) { - _cleanup_free_ char *p; + _cleanup_free_ char *p = NULL; p = utf8_escape_invalid(key); log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p); @@ -596,7 +596,7 @@ static int parse_env_file_push( } if (value && !utf8_is_valid(value)) { - _cleanup_free_ char *p; + _cleanup_free_ char *p = NULL; p = utf8_escape_invalid(value); log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p); @@ -908,7 +908,7 @@ int get_proc_field(const char *filename, const char *pattern, const char *termin /* Back off one char if there's nothing but whitespace and zeros */ if (!*t || isspace(*t)) - t --; + t--; } len = strcspn(t, terminator); @@ -1069,7 +1069,7 @@ int fflush_and_check(FILE *f) { /* This is much like like mkostemp() but is subject to umask(). */ int mkostemp_safe(char *pattern, int flags) { - _cleanup_umask_ mode_t u; + _cleanup_umask_ mode_t u = 0; int fd; assert(pattern); @@ -1083,30 +1083,6 @@ int mkostemp_safe(char *pattern, int flags) { return fd; } -int open_tmpfile(const char *path, int flags) { - char *p; - int fd; - - assert(path); - -#ifdef O_TMPFILE - /* Try O_TMPFILE first, if it is supported */ - fd = open(path, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR); - if (fd >= 0) - return fd; -#endif - - /* Fall back to unguessable name + unlinking */ - p = strjoina(path, "/systemd-tmp-XXXXXX"); - - fd = mkostemp_safe(p, flags); - if (fd < 0) - return fd; - - unlink(p); - return fd; -} - int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { const char *fn; char *t; @@ -1278,3 +1254,103 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) return fputs(s, f); } + +int open_tmpfile_unlinkable(const char *directory, int flags) { + char *p; + int fd; + + assert(directory); + + /* Returns an unlinked temporary file that cannot be linked into the file system anymore */ + +#ifdef O_TMPFILE + /* Try O_TMPFILE first, if it is supported */ + fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR); + if (fd >= 0) + return fd; +#endif + + /* Fall back to unguessable name + unlinking */ + p = strjoina(directory, "/systemd-tmp-XXXXXX"); + + fd = mkostemp_safe(p, flags); + if (fd < 0) + return fd; + + (void) unlink(p); + + return fd; +} + +int open_tmpfile_linkable(const char *target, int flags, char **ret_path) { + _cleanup_free_ char *tmp = NULL; + int r, fd; + + assert(target); + assert(ret_path); + + /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */ + assert((flags & O_EXCL) == 0); + + /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in + * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in + * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */ + +#ifdef O_TMPFILE + { + _cleanup_free_ char *dn = NULL; + + dn = dirname_malloc(target); + if (!dn) + return -ENOMEM; + + fd = open(dn, O_TMPFILE|flags, 0640); + if (fd >= 0) { + *ret_path = NULL; + return fd; + } + + log_debug_errno(errno, "Failed to use O_TMPFILE on %s: %m", dn); + } +#endif + + r = tempfn_random(target, NULL, &tmp); + if (r < 0) + return r; + + fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640); + if (fd < 0) + return -errno; + + *ret_path = tmp; + tmp = NULL; + + return fd; +} + +int link_tmpfile(int fd, const char *path, const char *target) { + + assert(fd >= 0); + assert(target); + + /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd + * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported + * on the directory, and renameat2() is used instead. + * + * Note that in both cases we will not replace existing files. This is because linkat() does not support this + * operation currently (renameat2() does), and there is no nice way to emulate this. */ + + if (path) { + if (rename_noreplace(AT_FDCWD, path, AT_FDCWD, target) < 0) + return -errno; + } else { + char proc_fd_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; + + xsprintf(proc_fd_path, "/proc/self/fd/%i", fd); + + if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0) + return -errno; + } + + return 0; +} diff --git a/src/basic/fileio.h b/src/basic/fileio.h index 8084895ff3..58dbc80c24 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -72,7 +72,6 @@ int fflush_and_check(FILE *f); int fopen_temporary(const char *path, FILE **_f, char **_temp_path); int mkostemp_safe(char *pattern, int flags); -int open_tmpfile(const char *path, int flags); int tempfn_xxxxxx(const char *p, const char *extra, char **ret); int tempfn_random(const char *p, const char *extra, char **ret); @@ -82,3 +81,8 @@ int write_timestamp_file_atomic(const char *fn, usec_t n); int read_timestamp_file(const char *fn, usec_t *ret); int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space); + +int open_tmpfile_unlinkable(const char *directory, int flags); +int open_tmpfile_linkable(const char *target, int flags, char **ret_path); + +int link_tmpfile(int fd, const char *path, const char *target); diff --git a/src/basic/formats-util.h b/src/basic/formats-util.h index ce516b117d..9b4e8e98fa 100644 --- a/src/basic/formats-util.h +++ b/src/basic/formats-util.h @@ -49,7 +49,7 @@ #if SIZEOF_TIME_T == 8 # define PRI_TIME PRIi64 #elif SIZEOF_TIME_T == 4 -# define PRI_TIME PRIu32 +# define PRI_TIME "li" #else # error Unknown time_t size #endif diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 3ef1b90edd..e24e7036f7 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -38,6 +38,7 @@ #include "mkdir.h" #include "parse-util.h" #include "path-util.h" +#include "stdio-util.h" #include "string-util.h" #include "strv.h" #include "time-util.h" @@ -283,24 +284,6 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { return 0; } -int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) { - assert(fd >= 0); - - /* Under the assumption that we are running privileged we - * first change the access mode and only then hand out - * ownership to avoid a window where access is too open. */ - - if (mode != MODE_INVALID) - if (fchmod(fd, mode) < 0) - return -errno; - - if (uid != UID_INVALID || gid != GID_INVALID) - if (fchown(fd, uid, gid) < 0) - return -errno; - - return 0; -} - int fchmod_umask(int fd, mode_t m) { mode_t u; int r; @@ -511,3 +494,17 @@ int get_files_in_directory(const char *path, char ***list) { return n; } + +int inotify_add_watch_fd(int fd, int what, uint32_t mask) { + char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; + int r; + + /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ + xsprintf(path, "/proc/self/fd/%i", what); + + r = inotify_add_watch(fd, path, mask); + if (r < 0) + return -errno; + + return r; +} diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 0e2fcb21b9..517b599d6f 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -43,7 +43,6 @@ int readlink_and_canonicalize(const char *p, char **r); int readlink_and_make_absolute_root(const char *root, const char *path, char **ret); int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); -int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid); int fchmod_umask(int fd, mode_t mode); @@ -73,3 +72,5 @@ union inotify_event_buffer { struct inotify_event ev; uint8_t raw[INOTIFY_EVENT_MAX]; }; + +int inotify_add_watch_fd(int fd, int what, uint32_t mask); diff --git a/src/basic/gunicode.h b/src/basic/gunicode.h index b03aa43160..5975bc8fc9 100644 --- a/src/basic/gunicode.h +++ b/src/basic/gunicode.h @@ -1,11 +1,11 @@ +#pragma once + /* gunicode.h - Unicode manipulation functions * * Copyright (C) 1999, 2000 Tom Tromey * Copyright 2000, 2005 Red Hat, Inc. */ -#pragma once - #include <stdbool.h> #include <stdint.h> #include <stdlib.h> diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c index 6f1a049d47..49a0479592 100644 --- a/src/basic/hashmap.c +++ b/src/basic/hashmap.c @@ -176,7 +176,7 @@ enum HashmapType { }; struct _packed_ indirect_storage { - char *storage; /* where buckets and DIBs are stored */ + void *storage; /* where buckets and DIBs are stored */ uint8_t hash_key[HASH_KEY_SIZE]; /* hash key; changes during resize */ unsigned n_entries; /* number of stored entries */ @@ -193,7 +193,7 @@ struct direct_storage { /* This gives us 39 bytes on 64bit, or 35 bytes on 32bit. * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit, * or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */ - char storage[sizeof(struct indirect_storage)]; + uint8_t storage[sizeof(struct indirect_storage)]; }; #define DIRECT_BUCKETS(entry_t) \ @@ -302,7 +302,7 @@ static void n_entries_dec(HashmapBase *h) { h->n_direct_entries--; } -static char *storage_ptr(HashmapBase *h) { +static void *storage_ptr(HashmapBase *h) { return h->has_indirect ? h->indirect.storage : h->direct.storage; } @@ -347,7 +347,7 @@ static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) { static struct hashmap_base_entry *bucket_at(HashmapBase *h, unsigned idx) { return (struct hashmap_base_entry*) - (storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size); + ((uint8_t*) storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size); } static struct plain_hashmap_entry *plain_bucket_at(Hashmap *h, unsigned idx) { @@ -381,7 +381,7 @@ static struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_ static dib_raw_t *dib_raw_ptr(HashmapBase *h) { return (dib_raw_t*) - (storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h)); + ((uint8_t*) storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h)); } static unsigned bucket_distance(HashmapBase *h, unsigned idx, unsigned from) { @@ -1028,7 +1028,7 @@ static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx, */ static int resize_buckets(HashmapBase *h, unsigned entries_add) { struct swap_entries swap; - char *new_storage; + void *new_storage; dib_raw_t *old_dibs, *new_dibs; const struct hashmap_type_info *hi; unsigned idx, optimal_idx; @@ -1095,7 +1095,7 @@ static int resize_buckets(HashmapBase *h, unsigned entries_add) { h->indirect.n_buckets = (1U << new_shift) / (hi->entry_size + sizeof(dib_raw_t)); - old_dibs = (dib_raw_t*)(new_storage + hi->entry_size * old_n_buckets); + old_dibs = (dib_raw_t*)((uint8_t*) new_storage + hi->entry_size * old_n_buckets); new_dibs = dib_raw_ptr(h); /* @@ -1773,20 +1773,18 @@ int set_consume(Set *s, void *value) { int set_put_strdup(Set *s, const char *p) { char *c; - int r; assert(s); assert(p); + if (set_contains(s, (char*) p)) + return 0; + c = strdup(p); if (!c) return -ENOMEM; - r = set_consume(s, c); - if (r == -EEXIST) - return 0; - - return r; + return set_consume(s, c); } int set_put_strdupv(Set *s, char **l) { diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c index 592df53cb5..c5bda6c4d6 100644 --- a/src/basic/hexdecoct.c +++ b/src/basic/hexdecoct.c @@ -25,6 +25,7 @@ #include "alloc-util.h" #include "hexdecoct.h" #include "macro.h" +#include "util.h" char octchar(int x) { return '0' + (x & 7); @@ -275,8 +276,8 @@ int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_l if (padding) { /* strip the padding */ while (l > 0 && p[l - 1] == '=' && pad < 7) { - pad ++; - l --; + pad++; + l--; } } @@ -504,7 +505,7 @@ int unbase64char(char c) { if (c == '+') return offset; - offset ++; + offset++; if (c == '/') return offset; @@ -572,7 +573,7 @@ static int base64_append_width(char **prefix, int plen, if (!t) return -ENOMEM; - memcpy(t + plen, sep, slen); + memcpy_safe(t + plen, sep, slen); for (line = 0, s = t + plen + slen, avail = len; line < lines; line++) { int act = MIN(width, avail); @@ -620,9 +621,9 @@ int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) { /* strip the padding */ if (l > 0 && p[l - 1] == '=') - l --; + l--; if (l > 0 && p[l - 1] == '=') - l --; + l--; /* a group of four input bytes needs three output bytes, in case of padding we need to add two or three extra bytes */ diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c index 7bb23448ed..13c3bb6446 100644 --- a/src/basic/hostname-util.c +++ b/src/basic/hostname-util.c @@ -17,7 +17,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <bits/local_lim.h> #include <errno.h> #include <limits.h> #include <stdio.h> @@ -49,6 +48,10 @@ bool hostname_is_set(void) { char* gethostname_malloc(void) { struct utsname u; + /* This call tries to return something useful, either the actual hostname + * or it makes something up. The only reason it might fail is OOM. + * It might even return "localhost" if that's set. */ + assert_se(uname(&u) >= 0); if (isempty(u.nodename) || streq(u.nodename, "(none)")) @@ -57,6 +60,31 @@ char* gethostname_malloc(void) { return strdup(u.nodename); } +int gethostname_strict(char **ret) { + struct utsname u; + char *k; + + /* This call will rather fail than make up a name. It will not return "localhost" either. */ + + assert_se(uname(&u) >= 0); + + if (isempty(u.nodename)) + return -ENXIO; + + if (streq(u.nodename, "(none)")) + return -ENXIO; + + if (is_localhost(u.nodename)) + return -ENXIO; + + k = strdup(u.nodename); + if (!k) + return -ENOMEM; + + *ret = k; + return 0; +} + static bool hostname_valid_char(char c) { return (c >= 'a' && c <= 'z') || @@ -96,7 +124,7 @@ bool hostname_is_valid(const char *s, bool allow_trailing_dot) { return false; dot = true; - n_dots ++; + n_dots++; } else { if (!hostname_valid_char(*p)) return false; @@ -122,6 +150,8 @@ char* hostname_cleanup(char *s) { assert(s); + strshorten(s, HOST_NAME_MAX); + for (p = s, d = s, dot = true; *p; p++) { if (*p == '.') { if (dot) @@ -141,8 +171,6 @@ char* hostname_cleanup(char *s) { else *d = 0; - strshorten(s, HOST_NAME_MAX); - return s; } @@ -150,16 +178,16 @@ bool is_localhost(const char *hostname) { assert(hostname); /* This tries to identify local host and domain names - * described in RFC6761 plus the redhatism of .localdomain */ + * described in RFC6761 plus the redhatism of localdomain */ return strcaseeq(hostname, "localhost") || strcaseeq(hostname, "localhost.") || - strcaseeq(hostname, "localdomain.") || - strcaseeq(hostname, "localdomain") || + strcaseeq(hostname, "localhost.localdomain") || + strcaseeq(hostname, "localhost.localdomain.") || endswith_no_case(hostname, ".localhost") || endswith_no_case(hostname, ".localhost.") || - endswith_no_case(hostname, ".localdomain") || - endswith_no_case(hostname, ".localdomain."); + endswith_no_case(hostname, ".localhost.localdomain") || + endswith_no_case(hostname, ".localhost.localdomain."); } bool is_gateway_hostname(const char *hostname) { diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h index d062eddea1..7af4e6c7ec 100644 --- a/src/basic/hostname-util.h +++ b/src/basic/hostname-util.h @@ -26,6 +26,7 @@ bool hostname_is_set(void); char* gethostname_malloc(void); +int gethostname_strict(char **ret); bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_; char* hostname_cleanup(char *s); diff --git a/src/basic/io-util.c b/src/basic/io-util.c index 3ec8d61236..cc6dfa8c1b 100644 --- a/src/basic/io-util.c +++ b/src/basic/io-util.c @@ -33,6 +33,11 @@ int flush_fd(int fd) { .events = POLLIN, }; + /* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything + * read. Note that some file descriptors (notable IP sockets) will trigger POLLIN even when no data can be read + * (due to IP packet checksum mismatches), hence this function is only safe to be non-blocking if the fd used + * was set to non-blocking too. */ + for (;;) { char buf[LINE_MAX]; ssize_t l; @@ -249,7 +254,7 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length) { } else if (n > 0) q += n; else - q ++; + q++; } if (q > w) { diff --git a/src/basic/io-util.h b/src/basic/io-util.h index 142c940d92..4684ed3bfc 100644 --- a/src/basic/io-util.h +++ b/src/basic/io-util.h @@ -46,7 +46,7 @@ ssize_t sparse_write(int fd, const void *p, size_t sz, size_t run_length); char *_s = (char *)(s); \ _i->iov_base = _s; \ _i->iov_len = strlen(_s); \ - } while(false) + } while (false) static inline size_t IOVEC_TOTAL_SIZE(const struct iovec *i, unsigned n) { unsigned j; diff --git a/src/basic/json.c b/src/basic/json.c deleted file mode 100644 index daa98fc815..0000000000 --- a/src/basic/json.c +++ /dev/null @@ -1,871 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <errno.h> -#include <math.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> - -#include "alloc-util.h" -#include "hexdecoct.h" -#include "json.h" -#include "macro.h" -#include "string-util.h" -#include "utf8.h" - -int json_variant_new(JsonVariant **ret, JsonVariantType type) { - JsonVariant *v; - - v = new0(JsonVariant, 1); - if (!v) - return -ENOMEM; - v->type = type; - *ret = v; - return 0; -} - -static int json_variant_deep_copy(JsonVariant *ret, JsonVariant *variant) { - int r; - - assert(ret); - assert(variant); - - ret->type = variant->type; - ret->size = variant->size; - - if (variant->type == JSON_VARIANT_STRING) { - ret->string = memdup(variant->string, variant->size+1); - if (!ret->string) - return -ENOMEM; - } else if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT) { - size_t i; - - ret->objects = new0(JsonVariant, variant->size); - if (!ret->objects) - return -ENOMEM; - - for (i = 0; i < variant->size; ++i) { - r = json_variant_deep_copy(&ret->objects[i], &variant->objects[i]); - if (r < 0) - return r; - } - } else - ret->value = variant->value; - - return 0; -} - -static JsonVariant *json_object_unref(JsonVariant *variant); - -static JsonVariant *json_variant_unref_inner(JsonVariant *variant) { - if (!variant) - return NULL; - - if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT) - return json_object_unref(variant); - else if (variant->type == JSON_VARIANT_STRING) - free(variant->string); - - return NULL; -} - -static JsonVariant *json_raw_unref(JsonVariant *variant, size_t size) { - if (!variant) - return NULL; - - for (size_t i = 0; i < size; ++i) - json_variant_unref_inner(&variant[i]); - - free(variant); - return NULL; -} - -static JsonVariant *json_object_unref(JsonVariant *variant) { - size_t i; - - assert(variant); - - if (!variant->objects) - return NULL; - - for (i = 0; i < variant->size; ++i) - json_variant_unref_inner(&variant->objects[i]); - - free(variant->objects); - return NULL; -} - -static JsonVariant **json_variant_array_unref(JsonVariant **variant) { - size_t i = 0; - JsonVariant *p = NULL; - - if (!variant) - return NULL; - - while((p = (variant[i++])) != NULL) { - if (p->type == JSON_VARIANT_STRING) - free(p->string); - free(p); - } - - free(variant); - - return NULL; -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant **, json_variant_array_unref); - -JsonVariant *json_variant_unref(JsonVariant *variant) { - if (!variant) - return NULL; - - if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT) - json_object_unref(variant); - else if (variant->type == JSON_VARIANT_STRING) - free(variant->string); - - free(variant); - - return NULL; -} - -char *json_variant_string(JsonVariant *variant){ - assert(variant); - assert(variant->type == JSON_VARIANT_STRING); - - return variant->string; -} - -bool json_variant_bool(JsonVariant *variant) { - assert(variant); - assert(variant->type == JSON_VARIANT_BOOLEAN); - - return variant->value.boolean; -} - -intmax_t json_variant_integer(JsonVariant *variant) { - assert(variant); - assert(variant->type == JSON_VARIANT_INTEGER); - - return variant->value.integer; -} - -double json_variant_real(JsonVariant *variant) { - assert(variant); - assert(variant->type == JSON_VARIANT_REAL); - - return variant->value.real; -} - -JsonVariant *json_variant_element(JsonVariant *variant, unsigned index) { - assert(variant); - assert(variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT); - assert(index < variant->size); - assert(variant->objects); - - return &variant->objects[index]; -} - -JsonVariant *json_variant_value(JsonVariant *variant, const char *key) { - size_t i; - - assert(variant); - assert(variant->type == JSON_VARIANT_OBJECT); - assert(variant->objects); - - for (i = 0; i < variant->size; i += 2) { - JsonVariant *p = &variant->objects[i]; - if (p->type == JSON_VARIANT_STRING && streq(key, p->string)) - return &variant->objects[i + 1]; - } - - return NULL; -} - -static void inc_lines(unsigned *line, const char *s, size_t n) { - const char *p = s; - - if (!line) - return; - - for (;;) { - const char *f; - - f = memchr(p, '\n', n); - if (!f) - return; - - n -= (f - p) + 1; - p = f + 1; - (*line)++; - } -} - -static int unhex_ucs2(const char *c, uint16_t *ret) { - int aa, bb, cc, dd; - uint16_t x; - - assert(c); - assert(ret); - - aa = unhexchar(c[0]); - if (aa < 0) - return -EINVAL; - - bb = unhexchar(c[1]); - if (bb < 0) - return -EINVAL; - - cc = unhexchar(c[2]); - if (cc < 0) - return -EINVAL; - - dd = unhexchar(c[3]); - if (dd < 0) - return -EINVAL; - - x = ((uint16_t) aa << 12) | - ((uint16_t) bb << 8) | - ((uint16_t) cc << 4) | - ((uint16_t) dd); - - if (x <= 0) - return -EINVAL; - - *ret = x; - - return 0; -} - -static int json_parse_string(const char **p, char **ret) { - _cleanup_free_ char *s = NULL; - size_t n = 0, allocated = 0; - const char *c; - - assert(p); - assert(*p); - assert(ret); - - c = *p; - - if (*c != '"') - return -EINVAL; - - c++; - - for (;;) { - int len; - - /* Check for EOF */ - if (*c == 0) - return -EINVAL; - - /* Check for control characters 0x00..0x1f */ - if (*c > 0 && *c < ' ') - return -EINVAL; - - /* Check for control character 0x7f */ - if (*c == 0x7f) - return -EINVAL; - - if (*c == '"') { - if (!s) { - s = strdup(""); - if (!s) - return -ENOMEM; - } else - s[n] = 0; - - *p = c + 1; - - *ret = s; - s = NULL; - return JSON_STRING; - } - - if (*c == '\\') { - char ch = 0; - c++; - - if (*c == 0) - return -EINVAL; - - if (IN_SET(*c, '"', '\\', '/')) - ch = *c; - else if (*c == 'b') - ch = '\b'; - else if (*c == 'f') - ch = '\f'; - else if (*c == 'n') - ch = '\n'; - else if (*c == 'r') - ch = '\r'; - else if (*c == 't') - ch = '\t'; - else if (*c == 'u') { - char16_t x; - int r; - - r = unhex_ucs2(c + 1, &x); - if (r < 0) - return r; - - c += 5; - - if (!GREEDY_REALLOC(s, allocated, n + 4)) - return -ENOMEM; - - if (!utf16_is_surrogate(x)) - n += utf8_encode_unichar(s + n, (char32_t) x); - else if (utf16_is_trailing_surrogate(x)) - return -EINVAL; - else { - char16_t y; - - if (c[0] != '\\' || c[1] != 'u') - return -EINVAL; - - r = unhex_ucs2(c + 2, &y); - if (r < 0) - return r; - - c += 6; - - if (!utf16_is_trailing_surrogate(y)) - return -EINVAL; - - n += utf8_encode_unichar(s + n, utf16_surrogate_pair_to_unichar(x, y)); - } - - continue; - } else - return -EINVAL; - - if (!GREEDY_REALLOC(s, allocated, n + 2)) - return -ENOMEM; - - s[n++] = ch; - c ++; - continue; - } - - len = utf8_encoded_valid_unichar(c); - if (len < 0) - return len; - - if (!GREEDY_REALLOC(s, allocated, n + len + 1)) - return -ENOMEM; - - memcpy(s + n, c, len); - n += len; - c += len; - } -} - -static int json_parse_number(const char **p, union json_value *ret) { - bool negative = false, exponent_negative = false, is_double = false; - double x = 0.0, y = 0.0, exponent = 0.0, shift = 1.0; - intmax_t i = 0; - const char *c; - - assert(p); - assert(*p); - assert(ret); - - c = *p; - - if (*c == '-') { - negative = true; - c++; - } - - if (*c == '0') - c++; - else { - if (!strchr("123456789", *c) || *c == 0) - return -EINVAL; - - do { - if (!is_double) { - int64_t t; - - t = 10 * i + (*c - '0'); - if (t < i) /* overflow */ - is_double = false; - else - i = t; - } - - x = 10.0 * x + (*c - '0'); - c++; - } while (strchr("0123456789", *c) && *c != 0); - } - - if (*c == '.') { - is_double = true; - c++; - - if (!strchr("0123456789", *c) || *c == 0) - return -EINVAL; - - do { - y = 10.0 * y + (*c - '0'); - shift = 10.0 * shift; - c++; - } while (strchr("0123456789", *c) && *c != 0); - } - - if (*c == 'e' || *c == 'E') { - is_double = true; - c++; - - if (*c == '-') { - exponent_negative = true; - c++; - } else if (*c == '+') - c++; - - if (!strchr("0123456789", *c) || *c == 0) - return -EINVAL; - - do { - exponent = 10.0 * exponent + (*c - '0'); - c++; - } while (strchr("0123456789", *c) && *c != 0); - } - - *p = c; - - if (is_double) { - ret->real = ((negative ? -1.0 : 1.0) * (x + (y / shift))) * exp10((exponent_negative ? -1.0 : 1.0) * exponent); - return JSON_REAL; - } else { - ret->integer = negative ? -i : i; - return JSON_INTEGER; - } -} - -int json_tokenize( - const char **p, - char **ret_string, - union json_value *ret_value, - void **state, - unsigned *line) { - - const char *c; - int t; - int r; - - enum { - STATE_NULL, - STATE_VALUE, - STATE_VALUE_POST, - }; - - assert(p); - assert(*p); - assert(ret_string); - assert(ret_value); - assert(state); - - t = PTR_TO_INT(*state); - c = *p; - - if (t == STATE_NULL) { - if (line) - *line = 1; - t = STATE_VALUE; - } - - for (;;) { - const char *b; - - b = c + strspn(c, WHITESPACE); - if (*b == 0) - return JSON_END; - - inc_lines(line, c, b - c); - c = b; - - switch (t) { - - case STATE_VALUE: - - if (*c == '{') { - *ret_string = NULL; - *ret_value = JSON_VALUE_NULL; - *p = c + 1; - *state = INT_TO_PTR(STATE_VALUE); - return JSON_OBJECT_OPEN; - - } else if (*c == '}') { - *ret_string = NULL; - *ret_value = JSON_VALUE_NULL; - *p = c + 1; - *state = INT_TO_PTR(STATE_VALUE_POST); - return JSON_OBJECT_CLOSE; - - } else if (*c == '[') { - *ret_string = NULL; - *ret_value = JSON_VALUE_NULL; - *p = c + 1; - *state = INT_TO_PTR(STATE_VALUE); - return JSON_ARRAY_OPEN; - - } else if (*c == ']') { - *ret_string = NULL; - *ret_value = JSON_VALUE_NULL; - *p = c + 1; - *state = INT_TO_PTR(STATE_VALUE_POST); - return JSON_ARRAY_CLOSE; - - } else if (*c == '"') { - r = json_parse_string(&c, ret_string); - if (r < 0) - return r; - - *ret_value = JSON_VALUE_NULL; - *p = c; - *state = INT_TO_PTR(STATE_VALUE_POST); - return r; - - } else if (strchr("-0123456789", *c)) { - r = json_parse_number(&c, ret_value); - if (r < 0) - return r; - - *ret_string = NULL; - *p = c; - *state = INT_TO_PTR(STATE_VALUE_POST); - return r; - - } else if (startswith(c, "true")) { - *ret_string = NULL; - ret_value->boolean = true; - *p = c + 4; - *state = INT_TO_PTR(STATE_VALUE_POST); - return JSON_BOOLEAN; - - } else if (startswith(c, "false")) { - *ret_string = NULL; - ret_value->boolean = false; - *p = c + 5; - *state = INT_TO_PTR(STATE_VALUE_POST); - return JSON_BOOLEAN; - - } else if (startswith(c, "null")) { - *ret_string = NULL; - *ret_value = JSON_VALUE_NULL; - *p = c + 4; - *state = INT_TO_PTR(STATE_VALUE_POST); - return JSON_NULL; - - } else - return -EINVAL; - - case STATE_VALUE_POST: - - if (*c == ':') { - *ret_string = NULL; - *ret_value = JSON_VALUE_NULL; - *p = c + 1; - *state = INT_TO_PTR(STATE_VALUE); - return JSON_COLON; - } else if (*c == ',') { - *ret_string = NULL; - *ret_value = JSON_VALUE_NULL; - *p = c + 1; - *state = INT_TO_PTR(STATE_VALUE); - return JSON_COMMA; - } else if (*c == '}') { - *ret_string = NULL; - *ret_value = JSON_VALUE_NULL; - *p = c + 1; - *state = INT_TO_PTR(STATE_VALUE_POST); - return JSON_OBJECT_CLOSE; - } else if (*c == ']') { - *ret_string = NULL; - *ret_value = JSON_VALUE_NULL; - *p = c + 1; - *state = INT_TO_PTR(STATE_VALUE_POST); - return JSON_ARRAY_CLOSE; - } else - return -EINVAL; - } - - } -} - -static bool json_is_value(JsonVariant *var) { - assert(var); - - return var->type != JSON_VARIANT_CONTROL; -} - -static int json_scoped_parse(JsonVariant **tokens, size_t *i, size_t n, JsonVariant *scope) { - bool arr = scope->type == JSON_VARIANT_ARRAY; - int terminator = arr ? JSON_ARRAY_CLOSE : JSON_OBJECT_CLOSE; - size_t allocated = 0, size = 0; - JsonVariant *key = NULL, *value = NULL, *var = NULL, *items = NULL; - enum { - STATE_KEY, - STATE_COLON, - STATE_COMMA, - STATE_VALUE - } state = arr ? STATE_VALUE : STATE_KEY; - - assert(tokens); - assert(i); - assert(scope); - - while((var = *i < n ? tokens[(*i)++] : NULL) != NULL) { - bool stopper; - int r; - - stopper = !json_is_value(var) && var->value.integer == terminator; - - if (stopper) { - if (state != STATE_COMMA && size > 0) - goto error; - - goto out; - } - - if (state == STATE_KEY) { - if (var->type != JSON_VARIANT_STRING) - goto error; - else { - key = var; - state = STATE_COLON; - } - } - else if (state == STATE_COLON) { - if (key == NULL) - goto error; - - if (json_is_value(var)) - goto error; - - if (var->value.integer != JSON_COLON) - goto error; - - state = STATE_VALUE; - } - else if (state == STATE_VALUE) { - _cleanup_json_variant_unref_ JsonVariant *v = NULL; - size_t toadd = arr ? 1 : 2; - - if (!json_is_value(var)) { - int type = (var->value.integer == JSON_ARRAY_OPEN) ? JSON_VARIANT_ARRAY : JSON_VARIANT_OBJECT; - - r = json_variant_new(&v, type); - if (r < 0) - goto error; - - r = json_scoped_parse(tokens, i, n, v); - if (r < 0) - goto error; - - value = v; - } - else - value = var; - - if(!GREEDY_REALLOC(items, allocated, size + toadd)) - goto error; - - if (arr) { - r = json_variant_deep_copy(&items[size], value); - if (r < 0) - goto error; - } else { - r = json_variant_deep_copy(&items[size], key); - if (r < 0) - goto error; - - r = json_variant_deep_copy(&items[size+1], value); - if (r < 0) - goto error; - } - - size += toadd; - state = STATE_COMMA; - } - else if (state == STATE_COMMA) { - if (json_is_value(var)) - goto error; - - if (var->value.integer != JSON_COMMA) - goto error; - - key = NULL; - value = NULL; - - state = arr ? STATE_VALUE : STATE_KEY; - } - } - -error: - json_raw_unref(items, size); - return -EBADMSG; - -out: - scope->size = size; - scope->objects = items; - - return scope->type; -} - -static int json_parse_tokens(JsonVariant **tokens, size_t ntokens, JsonVariant **rv) { - size_t it = 0; - int r; - JsonVariant *e; - _cleanup_json_variant_unref_ JsonVariant *p = NULL; - - assert(tokens); - assert(ntokens); - - e = tokens[it++]; - r = json_variant_new(&p, JSON_VARIANT_OBJECT); - if (r < 0) - return r; - - if (e->type != JSON_VARIANT_CONTROL && e->value.integer != JSON_OBJECT_OPEN) - return -EBADMSG; - - r = json_scoped_parse(tokens, &it, ntokens, p); - if (r < 0) - return r; - - *rv = p; - p = NULL; - - return 0; -} - -static int json_tokens(const char *string, size_t size, JsonVariant ***tokens, size_t *n) { - _cleanup_free_ char *buf = NULL; - _cleanup_(json_variant_array_unrefp) JsonVariant **items = NULL; - union json_value v = {}; - void *json_state = NULL; - const char *p; - int t, r; - size_t allocated = 0, s = 0; - - assert(string); - assert(n); - - if (size <= 0) - return -EBADMSG; - - buf = strndup(string, size); - if (!buf) - return -ENOMEM; - - p = buf; - for (;;) { - _cleanup_json_variant_unref_ JsonVariant *var = NULL; - _cleanup_free_ char *rstr = NULL; - - t = json_tokenize(&p, &rstr, &v, &json_state, NULL); - - if (t < 0) - return t; - else if (t == JSON_END) - break; - - if (t <= JSON_ARRAY_CLOSE) { - r = json_variant_new(&var, JSON_VARIANT_CONTROL); - if (r < 0) - return r; - var->value.integer = t; - } else { - switch (t) { - case JSON_STRING: - r = json_variant_new(&var, JSON_VARIANT_STRING); - if (r < 0) - return r; - var->size = strlen(rstr); - var->string = strdup(rstr); - if (!var->string) { - return -ENOMEM; - } - break; - case JSON_INTEGER: - r = json_variant_new(&var, JSON_VARIANT_INTEGER); - if (r < 0) - return r; - var->value = v; - break; - case JSON_REAL: - r = json_variant_new(&var, JSON_VARIANT_REAL); - if (r < 0) - return r; - var->value = v; - break; - case JSON_BOOLEAN: - r = json_variant_new(&var, JSON_VARIANT_BOOLEAN); - if (r < 0) - return r; - var->value = v; - break; - case JSON_NULL: - r = json_variant_new(&var, JSON_VARIANT_NULL); - if (r < 0) - return r; - break; - } - } - - if (!GREEDY_REALLOC(items, allocated, s+2)) - return -ENOMEM; - - items[s++] = var; - items[s] = NULL; - var = NULL; - } - - *n = s; - *tokens = items; - items = NULL; - - return 0; -} - -int json_parse(const char *string, JsonVariant **rv) { - _cleanup_(json_variant_array_unrefp) JsonVariant **s = NULL; - JsonVariant *v = NULL; - size_t n = 0; - int r; - - assert(string); - assert(rv); - - r = json_tokens(string, strlen(string), &s, &n); - if (r < 0) - return r; - - r = json_parse_tokens(s, n, &v); - if (r < 0) - return r; - - *rv = v; - return 0; -} diff --git a/src/basic/json.h b/src/basic/json.h deleted file mode 100644 index a4509f680f..0000000000 --- a/src/basic/json.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> - -#include "macro.h" -#include "util.h" - -enum { - JSON_END, - JSON_COLON, - JSON_COMMA, - JSON_OBJECT_OPEN, - JSON_OBJECT_CLOSE, - JSON_ARRAY_OPEN, - JSON_ARRAY_CLOSE, - JSON_STRING, - JSON_REAL, - JSON_INTEGER, - JSON_BOOLEAN, - JSON_NULL, -}; - -typedef enum { - JSON_VARIANT_CONTROL, - JSON_VARIANT_STRING, - JSON_VARIANT_INTEGER, - JSON_VARIANT_BOOLEAN, - JSON_VARIANT_REAL, - JSON_VARIANT_ARRAY, - JSON_VARIANT_OBJECT, - JSON_VARIANT_NULL -} JsonVariantType; - -union json_value { - bool boolean; - double real; - intmax_t integer; -}; - -typedef struct JsonVariant { - JsonVariantType type; - size_t size; - union { - char *string; - struct JsonVariant *objects; - union json_value value; - }; -} JsonVariant; - -int json_variant_new(JsonVariant **ret, JsonVariantType type); -JsonVariant *json_variant_unref(JsonVariant *v); - -DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant *, json_variant_unref); -#define _cleanup_json_variant_unref_ _cleanup_(json_variant_unrefp) - -char *json_variant_string(JsonVariant *v); -bool json_variant_bool(JsonVariant *v); -intmax_t json_variant_integer(JsonVariant *v); -double json_variant_real(JsonVariant *v); - -JsonVariant *json_variant_element(JsonVariant *v, unsigned index); -JsonVariant *json_variant_value(JsonVariant *v, const char *key); - -#define JSON_VALUE_NULL ((union json_value) {}) - -int json_tokenize(const char **p, char **ret_string, union json_value *ret_value, void **state, unsigned *line); - -int json_parse(const char *string, JsonVariant **rv); -int json_parse_measure(const char *string, size_t *size); diff --git a/src/basic/linux/Makefile b/src/basic/linux/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/basic/linux/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile
\ No newline at end of file diff --git a/src/basic/list.h b/src/basic/list.h index c68185f587..5962aa4211 100644 --- a/src/basic/list.h +++ b/src/basic/list.h @@ -32,7 +32,7 @@ #define LIST_HEAD_INIT(head) \ do { \ (head) = NULL; } \ - while(false) + while (false) /* Initialize a list item */ #define LIST_INIT(name,item) \ @@ -40,7 +40,7 @@ typeof(*(item)) *_item = (item); \ assert(_item); \ _item->name##_prev = _item->name##_next = NULL; \ - } while(false) + } while (false) /* Prepend an item to the list */ #define LIST_PREPEND(name,head,item) \ @@ -51,7 +51,7 @@ _item->name##_next->name##_prev = _item; \ _item->name##_prev = NULL; \ *_head = _item; \ - } while(false) + } while (false) /* Append an item to the list */ #define LIST_APPEND(name,head,item) \ @@ -59,7 +59,7 @@ typeof(*(head)) *_tail; \ LIST_FIND_TAIL(name,head,_tail); \ LIST_INSERT_AFTER(name,head,_tail,item); \ - } while(false) + } while (false) /* Remove an item from the list */ #define LIST_REMOVE(name,head,item) \ @@ -75,7 +75,7 @@ *_head = _item->name##_next; \ } \ _item->name##_next = _item->name##_prev = NULL; \ - } while(false) + } while (false) /* Find the head of the list */ #define LIST_FIND_HEAD(name,item,head) \ @@ -119,7 +119,7 @@ _b->name##_prev = _a; \ _a->name##_next = _b; \ } \ - } while(false) + } while (false) /* Insert an item before another one (a = where, b = what) */ #define LIST_INSERT_BEFORE(name,head,a,b) \ @@ -145,7 +145,7 @@ _b->name##_next = _a; \ _a->name##_prev = _b; \ } \ - } while(false) + } while (false) #define LIST_JUST_US(name,item) \ (!(item)->name##_prev && !(item)->name##_next) \ diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c index cda6b2895d..ada0a28cd8 100644 --- a/src/basic/locale-util.c +++ b/src/basic/locale-util.c @@ -153,6 +153,8 @@ static int add_locales_from_libdir (Set *locales) { FOREACH_DIRENT(entry, dir, return -errno) { char *z; + dirent_ensure_type(dir, entry); + if (entry->d_type != DT_DIR) continue; @@ -269,34 +271,35 @@ out: } -const char *draw_special_char(DrawSpecialChar ch) { - - static const char *draw_table[2][_DRAW_SPECIAL_CHAR_MAX] = { +const char *special_glyph(SpecialGlyph code) { - /* UTF-8 */ { - [DRAW_TREE_VERTICAL] = "\342\224\202 ", /* │ */ - [DRAW_TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */ - [DRAW_TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */ - [DRAW_TREE_SPACE] = " ", /* */ - [DRAW_TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */ - [DRAW_BLACK_CIRCLE] = "\342\227\217", /* ● */ - [DRAW_ARROW] = "\342\206\222", /* → */ - [DRAW_DASH] = "\342\200\223", /* – */ + static const char* const draw_table[2][_SPECIAL_GLYPH_MAX] = { + /* ASCII fallback */ + [false] = { + [TREE_VERTICAL] = "| ", + [TREE_BRANCH] = "|-", + [TREE_RIGHT] = "`-", + [TREE_SPACE] = " ", + [TRIANGULAR_BULLET] = ">", + [BLACK_CIRCLE] = "*", + [ARROW] = "->", + [MDASH] = "-", }, - /* ASCII fallback */ { - [DRAW_TREE_VERTICAL] = "| ", - [DRAW_TREE_BRANCH] = "|-", - [DRAW_TREE_RIGHT] = "`-", - [DRAW_TREE_SPACE] = " ", - [DRAW_TRIANGULAR_BULLET] = ">", - [DRAW_BLACK_CIRCLE] = "*", - [DRAW_ARROW] = "->", - [DRAW_DASH] = "-", - } + /* UTF-8 */ + [ true ] = { + [TREE_VERTICAL] = "\342\224\202 ", /* │ */ + [TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */ + [TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */ + [TREE_SPACE] = " ", /* */ + [TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */ + [BLACK_CIRCLE] = "\342\227\217", /* ● */ + [ARROW] = "\342\206\222", /* → */ + [MDASH] = "\342\200\223", /* – */ + }, }; - return draw_table[!is_locale_utf8()][ch]; + return draw_table[is_locale_utf8()][code]; } static const char * const locale_variable_table[_VARIABLE_LC_MAX] = { diff --git a/src/basic/locale-util.h b/src/basic/locale-util.h index b0f9679286..0630a034ab 100644 --- a/src/basic/locale-util.h +++ b/src/basic/locale-util.h @@ -55,19 +55,19 @@ void init_gettext(void); bool is_locale_utf8(void); -typedef enum DrawSpecialChar { - DRAW_TREE_VERTICAL, - DRAW_TREE_BRANCH, - DRAW_TREE_RIGHT, - DRAW_TREE_SPACE, - DRAW_TRIANGULAR_BULLET, - DRAW_BLACK_CIRCLE, - DRAW_ARROW, - DRAW_DASH, - _DRAW_SPECIAL_CHAR_MAX -} DrawSpecialChar; +typedef enum { + TREE_VERTICAL, + TREE_BRANCH, + TREE_RIGHT, + TREE_SPACE, + TRIANGULAR_BULLET, + BLACK_CIRCLE, + ARROW, + MDASH, + _SPECIAL_GLYPH_MAX +} SpecialGlyph; -const char *draw_special_char(DrawSpecialChar ch); +const char *special_glyph(SpecialGlyph code) _const_; const char* locale_variable_to_string(LocaleVariable i) _const_; LocaleVariable locale_variable_from_string(const char *s) _pure_; diff --git a/src/basic/log.c b/src/basic/log.c index d89e6f7274..3ea643b6e6 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -165,7 +165,7 @@ static int log_open_syslog(void) { goto fail; } - if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) { + if (connect(syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { safe_close(syslog_fd); /* Some legacy syslog systems still use stream @@ -177,7 +177,7 @@ static int log_open_syslog(void) { goto fail; } - if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) { + if (connect(syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { r = -errno; goto fail; } @@ -215,7 +215,7 @@ static int log_open_journal(void) { goto fail; } - if (connect(journal_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) { + if (connect(journal_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { r = -errno; goto fail; } diff --git a/src/basic/log.h b/src/basic/log.h index 60ddead74c..b6356228d9 100644 --- a/src/basic/log.h +++ b/src/basic/log.h @@ -193,7 +193,7 @@ void log_assert_failed_return( #ifdef LOG_TRACE # define log_trace(...) log_debug(__VA_ARGS__) #else -# define log_trace(...) do {} while(0) +# define log_trace(...) do {} while (0) #endif /* Structured logging */ @@ -246,5 +246,4 @@ int log_syntax_internal( log_syntax_internal(unit, _level, config_file, config_line, 0, __FILE__, __LINE__, __func__, \ "String is not UTF-8 clean, ignoring assignment: %s", strna(_p)); \ } \ - -EINVAL; \ }) diff --git a/src/basic/login-util.h b/src/basic/login-util.h index 89a337d7c1..b01ee25c88 100644 --- a/src/basic/login-util.h +++ b/src/basic/login-util.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - #include <stdbool.h> #include <unistd.h> diff --git a/src/basic/macro.h b/src/basic/macro.h index 2695d0edb7..e41aa4260f 100644 --- a/src/basic/macro.h +++ b/src/basic/macro.h @@ -23,10 +23,15 @@ #include <inttypes.h> #include <stdbool.h> #include <sys/param.h> +#include <sys/sysmacros.h> #include <sys/types.h> #define _printf_(a,b) __attribute__ ((format (printf, a, b))) -#define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__))) +#ifdef __clang__ +# define _alloc_(...) +#else +# define _alloc_(...) __attribute__ ((alloc_size(__VA_ARGS__))) +#endif #define _sentinel_ __attribute__ ((sentinel)) #define _unused_ __attribute__ ((unused)) #define _destructor_ __attribute__ ((destructor)) @@ -224,7 +229,7 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { /* We override the glibc assert() here. */ #undef assert #ifdef NDEBUG -#define assert(expr) do {} while(false) +#define assert(expr) do {} while (false) #else #define assert(expr) assert_message_se(expr, #expr) #endif @@ -361,6 +366,12 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { _found; \ }) +#define SWAP_TWO(x, y) do { \ + typeof(x) _t = (x); \ + (x) = (y); \ + (y) = (_t); \ + } while (false) + /* Define C11 thread_local attribute even on older gcc compiler * version */ #ifndef thread_local diff --git a/src/basic/mempool.h b/src/basic/mempool.h index fea7841bcf..0618b8dd22 100644 --- a/src/basic/mempool.h +++ b/src/basic/mempool.h @@ -36,7 +36,7 @@ void* mempool_alloc0_tile(struct mempool *mp); void mempool_free_tile(struct mempool *mp, void *p); #define DEFINE_MEMPOOL(pool_name, tile_type, alloc_at_least) \ -struct mempool pool_name = { \ +static struct mempool pool_name = { \ .tile_size = sizeof(tile_type), \ .at_least = alloc_at_least, \ } diff --git a/src/basic/missing.h b/src/basic/missing.h index 36b060496a..651e414395 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -31,6 +31,7 @@ #include <linux/neighbour.h> #include <linux/oom.h> #include <linux/rtnetlink.h> +#include <net/ethernet.h> #include <stdlib.h> #include <sys/resource.h> #include <sys/syscall.h> @@ -134,84 +135,6 @@ #define SOL_SCTP 132 #endif -#if !HAVE_DECL_PIVOT_ROOT -static inline int pivot_root(const char *new_root, const char *put_old) { - return syscall(SYS_pivot_root, new_root, put_old); -} -#endif - -#ifndef __NR_memfd_create -# if defined __x86_64__ -# define __NR_memfd_create 319 -# elif defined __arm__ -# define __NR_memfd_create 385 -# elif defined __aarch64__ -# define __NR_memfd_create 279 -# elif defined __s390__ -# define __NR_memfd_create 350 -# elif defined _MIPS_SIM -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define __NR_memfd_create 4354 -# endif -# if _MIPS_SIM == _MIPS_SIM_NABI32 -# define __NR_memfd_create 6318 -# endif -# if _MIPS_SIM == _MIPS_SIM_ABI64 -# define __NR_memfd_create 5314 -# endif -# elif defined __i386__ -# define __NR_memfd_create 356 -# else -# warning "__NR_memfd_create unknown for your architecture" -# define __NR_memfd_create 0xffffffff -# endif -#endif - -#ifndef HAVE_MEMFD_CREATE -static inline int memfd_create(const char *name, unsigned int flags) { - return syscall(__NR_memfd_create, name, flags); -} -#endif - -#ifndef __NR_getrandom -# if defined __x86_64__ -# define __NR_getrandom 318 -# elif defined(__i386__) -# define __NR_getrandom 355 -# elif defined(__arm__) -# define __NR_getrandom 384 -# elif defined(__aarch64__) -# define __NR_getrandom 278 -# elif defined(__ia64__) -# define __NR_getrandom 1339 -# elif defined(__m68k__) -# define __NR_getrandom 352 -# elif defined(__s390x__) -# define __NR_getrandom 349 -# elif defined(__powerpc__) -# define __NR_getrandom 359 -# elif defined _MIPS_SIM -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define __NR_getrandom 4353 -# endif -# if _MIPS_SIM == _MIPS_SIM_NABI32 -# define __NR_getrandom 6317 -# endif -# if _MIPS_SIM == _MIPS_SIM_ABI64 -# define __NR_getrandom 5313 -# endif -# else -# warning "__NR_getrandom unknown for your architecture" -# define __NR_getrandom 0xffffffff -# endif -#endif - -#if !HAVE_DECL_GETRANDOM -static inline int getrandom(void *buffer, size_t count, unsigned flags) { - return syscall(__NR_getrandom, buffer, count, flags); -} -#endif - #ifndef GRND_NONBLOCK #define GRND_NONBLOCK 0x0001 #endif @@ -466,6 +389,10 @@ struct btrfs_ioctl_quota_ctl_args { struct btrfs_ioctl_qgroup_limit_args) #endif +#ifndef BTRFS_IOC_QUOTA_RESCAN_WAIT +#define BTRFS_IOC_QUOTA_RESCAN_WAIT _IO(BTRFS_IOCTL_MAGIC, 46) +#endif + #ifndef BTRFS_FIRST_FREE_OBJECTID #define BTRFS_FIRST_FREE_OBJECTID 256 #endif @@ -514,10 +441,18 @@ struct btrfs_ioctl_quota_ctl_args { #define CGROUP_SUPER_MAGIC 0x27e0eb #endif +#ifndef CGROUP2_SUPER_MAGIC +#define CGROUP2_SUPER_MAGIC 0x63677270 +#endif + #ifndef TMPFS_MAGIC #define TMPFS_MAGIC 0x01021994 #endif +#ifndef MQUEUE_MAGIC +#define MQUEUE_MAGIC 0x19800202 +#endif + #ifndef MS_MOVE #define MS_MOVE 8192 #endif @@ -526,12 +461,6 @@ struct btrfs_ioctl_quota_ctl_args { #define MS_PRIVATE (1 << 18) #endif -#if !HAVE_DECL_GETTID -static inline pid_t gettid(void) { - return (pid_t) syscall(SYS_gettid); -} -#endif - #ifndef SCM_SECURITY #define SCM_SECURITY 0x03 #endif @@ -560,32 +489,6 @@ static inline pid_t gettid(void) { #define MAX_HANDLE_SZ 128 #endif -#ifndef __NR_name_to_handle_at -# if defined(__x86_64__) -# define __NR_name_to_handle_at 303 -# elif defined(__i386__) -# define __NR_name_to_handle_at 341 -# elif defined(__arm__) -# define __NR_name_to_handle_at 370 -# elif defined(__powerpc__) -# define __NR_name_to_handle_at 345 -# else -# error "__NR_name_to_handle_at is not defined" -# endif -#endif - -#if !HAVE_DECL_NAME_TO_HANDLE_AT -struct file_handle { - unsigned int handle_bytes; - int handle_type; - unsigned char f_handle[0]; -}; - -static inline int name_to_handle_at(int fd, const char *name, struct file_handle *handle, int *mnt_id, int flags) { - return syscall(__NR_name_to_handle_at, fd, name, handle, mnt_id, flags); -} -#endif - #ifndef HAVE_SECURE_GETENV # ifdef HAVE___SECURE_GETENV # define secure_getenv __secure_getenv @@ -634,22 +537,6 @@ static inline int name_to_handle_at(int fd, const char *name, struct file_handle #endif -#ifndef __NR_setns -# if defined(__x86_64__) -# define __NR_setns 308 -# elif defined(__i386__) -# define __NR_setns 346 -# else -# error "__NR_setns is not defined" -# endif -#endif - -#if !HAVE_DECL_SETNS -static inline int setns(int fd, int nstype) { - return syscall(__NR_setns, fd, nstype); -} -#endif - #if !HAVE_DECL_LO_FLAGS_PARTSCAN #define LO_FLAGS_PARTSCAN 8 #endif @@ -674,7 +561,7 @@ static inline int setns(int fd, int nstype) { #define IFLA_INET6_ADDR_GEN_MODE 8 #define __IFLA_INET6_MAX 9 -#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1) +#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1) #define IN6_ADDR_GEN_MODE_EUI64 0 #define IN6_ADDR_GEN_MODE_NONE 1 @@ -714,6 +601,7 @@ static inline int setns(int fd, int nstype) { #endif #if !HAVE_DECL_IFLA_PHYS_PORT_ID +#define IFLA_EXT_MASK 29 #undef IFLA_PROMISCUITY #define IFLA_PROMISCUITY 30 #define IFLA_NUM_TX_QUEUES 31 @@ -858,7 +746,7 @@ static inline int setns(int fd, int nstype) { #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1) #endif -#if !HAVE_DECL_IFLA_BR_PRIORITY +#if !HAVE_DECL_IFLA_BR_VLAN_DEFAULT_PVID #define IFLA_BR_UNSPEC 0 #define IFLA_BR_FORWARD_DELAY 1 #define IFLA_BR_HELLO_TIME 2 @@ -866,7 +754,40 @@ static inline int setns(int fd, int nstype) { #define IFLA_BR_AGEING_TIME 4 #define IFLA_BR_STP_STATE 5 #define IFLA_BR_PRIORITY 6 -#define __IFLA_BR_MAX 7 +#define IFLA_BR_VLAN_FILTERING 7 +#define IFLA_BR_VLAN_PROTOCOL 8 +#define IFLA_BR_GROUP_FWD_MASK 9 +#define IFLA_BR_ROOT_ID 10 +#define IFLA_BR_BRIDGE_ID 11 +#define IFLA_BR_ROOT_PORT 12 +#define IFLA_BR_ROOT_PATH_COST 13 +#define IFLA_BR_TOPOLOGY_CHANGE 14 +#define IFLA_BR_TOPOLOGY_CHANGE_DETECTED 15 +#define IFLA_BR_HELLO_TIMER 16 +#define IFLA_BR_TCN_TIMER 17 +#define IFLA_BR_TOPOLOGY_CHANGE_TIMER 18 +#define IFLA_BR_GC_TIMER 19 +#define IFLA_BR_GROUP_ADDR 20 +#define IFLA_BR_FDB_FLUSH 21 +#define IFLA_BR_MCAST_ROUTER 22 +#define IFLA_BR_MCAST_SNOOPING 23 +#define IFLA_BR_MCAST_QUERY_USE_IFADDR 24 +#define IFLA_BR_MCAST_QUERIER 25 +#define IFLA_BR_MCAST_HASH_ELASTICITY 26 +#define IFLA_BR_MCAST_HASH_MAX 27 +#define IFLA_BR_MCAST_LAST_MEMBER_CNT 28 +#define IFLA_BR_MCAST_STARTUP_QUERY_CNT 29 +#define IFLA_BR_MCAST_LAST_MEMBER_INTVL 30 +#define IFLA_BR_MCAST_MEMBERSHIP_INTVL 31 +#define IFLA_BR_MCAST_QUERIER_INTVL 32 +#define IFLA_BR_MCAST_QUERY_INTVL 33 +#define IFLA_BR_MCAST_QUERY_RESPONSE_INTVL 34 +#define IFLA_BR_MCAST_STARTUP_QUERY_INTVL 35 +#define IFLA_BR_NF_CALL_IPTABLES 36 +#define IFLA_BR_NF_CALL_IP6TABLES 37 +#define IFLA_BR_NF_CALL_ARPTABLES 38 +#define IFLA_BR_VLAN_DEFAULT_PVID 39 +#define __IFLA_BR_MAX 40 #define IFLA_BR_MAX (__IFLA_BR_MAX - 1) #endif @@ -882,13 +803,16 @@ static inline int setns(int fd, int nstype) { #define IFLA_BRPORT_FAST_LEAVE 7 #define IFLA_BRPORT_LEARNING 8 #define IFLA_BRPORT_UNICAST_FLOOD 9 -#define IFLA_BRPORT_PROXYARP 10 #define IFLA_BRPORT_LEARNING_SYNC 11 #define __IFLA_BRPORT_MAX 12 #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) #endif +#if !HAVE_DECL_IFLA_BRPORT_PROXYARP +#define IFLA_BRPORT_PROXYARP 10 +#endif + #if !HAVE_DECL_NDA_IFINDEX #define NDA_UNSPEC 0 #define NDA_DST 1 @@ -912,6 +836,10 @@ static inline int setns(int fd, int nstype) { #define IPV6_UNICAST_IF 76 #endif +#ifndef IPV6_MIN_MTU +#define IPV6_MIN_MTU 1280 +#endif + #ifndef IFF_MULTI_QUEUE #define IFF_MULTI_QUEUE 0x100 #endif @@ -1013,69 +941,10 @@ static inline int setns(int fd, int nstype) { #define CAP_AUDIT_READ 37 #endif -static inline int raw_clone(unsigned long flags, void *child_stack) { -#if defined(__s390__) || defined(__CRIS__) - /* On s390 and cris the order of the first and second arguments - * of the raw clone() system call is reversed. */ - return (int) syscall(__NR_clone, child_stack, flags); -#else - return (int) syscall(__NR_clone, flags, child_stack); -#endif -} - -static inline pid_t raw_getpid(void) { -#if defined(__alpha__) - return (pid_t) syscall(__NR_getxpid); -#else - return (pid_t) syscall(__NR_getpid); -#endif -} - -#if !HAVE_DECL_RENAMEAT2 - -#ifndef __NR_renameat2 -# if defined __x86_64__ -# define __NR_renameat2 316 -# elif defined __arm__ -# define __NR_renameat2 382 -# elif defined _MIPS_SIM -# if _MIPS_SIM == _MIPS_SIM_ABI32 -# define __NR_renameat2 4351 -# endif -# if _MIPS_SIM == _MIPS_SIM_NABI32 -# define __NR_renameat2 6315 -# endif -# if _MIPS_SIM == _MIPS_SIM_ABI64 -# define __NR_renameat2 5311 -# endif -# elif defined __i386__ -# define __NR_renameat2 353 -# else -# warning "__NR_renameat2 unknown for your architecture" -# define __NR_renameat2 0xffffffff -# endif -#endif - -static inline int renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) { - return syscall(__NR_renameat2, oldfd, oldname, newfd, newname, flags); -} -#endif - #ifndef RENAME_NOREPLACE #define RENAME_NOREPLACE (1 << 0) #endif -#if !HAVE_DECL_KCMP -static inline int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) { -#if defined(__NR_kcmp) - return syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2); -#else - errno = ENOSYS; - return -1; -#endif -} -#endif - #ifndef KCMP_FILE #define KCMP_FILE 0 #endif @@ -1088,39 +957,10 @@ static inline int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, uns #define INPUT_PROP_ACCELEROMETER 0x06 #endif -#if !HAVE_DECL_KEY_SERIAL_T +#ifndef HAVE_KEY_SERIAL_T typedef int32_t key_serial_t; #endif -#if !HAVE_DECL_KEYCTL -static inline long keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4,unsigned long arg5) { -#if defined(__NR_keyctl) - return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5); -#else - errno = ENOSYS; - return -1; -#endif -} - -static inline key_serial_t add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) { -#if defined (__NR_add_key) - return syscall(__NR_add_key, type, description, payload, plen, ringid); -#else - errno = ENOSYS; - return -1; -#endif -} - -static inline key_serial_t request_key(const char *type, const char *description, const char * callout_info, key_serial_t destringid) { -#if defined (__NR_request_key) - return syscall(__NR_request_key, type, description, callout_info, destringid); -#else - errno = ENOSYS; - return -1; -#endif -} -#endif - #ifndef KEYCTL_READ #define KEYCTL_READ 11 #endif @@ -1159,12 +999,18 @@ static inline key_serial_t request_key(const char *type, const char *description #ifndef IF_OPER_UP #define IF_OPER_UP 6 -#ifndef HAVE_DECL_CHAR32_T +#ifndef HAVE_CHAR32_T #define char32_t uint32_t #endif -#ifndef HAVE_DECL_CHAR16_T +#ifndef HAVE_CHAR16_T #define char16_t uint16_t #endif +#ifndef ETHERTYPE_LLDP +#define ETHERTYPE_LLDP 0x88cc #endif + +#endif + +#include "missing_syscall.h" diff --git a/src/basic/missing_syscall.h b/src/basic/missing_syscall.h new file mode 100644 index 0000000000..d502d3b9ca --- /dev/null +++ b/src/basic/missing_syscall.h @@ -0,0 +1,310 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + 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/>. +***/ + +/* Missing glibc definitions to access certain kernel APIs */ + +#if !HAVE_DECL_PIVOT_ROOT +static inline int pivot_root(const char *new_root, const char *put_old) { + return syscall(SYS_pivot_root, new_root, put_old); +} +#endif + +/* ======================================================================= */ + +#if !HAVE_DECL_MEMFD_CREATE +# ifndef __NR_memfd_create +# if defined __x86_64__ +# define __NR_memfd_create 319 +# elif defined __arm__ +# define __NR_memfd_create 385 +# elif defined __aarch64__ +# define __NR_memfd_create 279 +# elif defined __s390__ +# define __NR_memfd_create 350 +# elif defined _MIPS_SIM +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define __NR_memfd_create 4354 +# endif +# if _MIPS_SIM == _MIPS_SIM_NABI32 +# define __NR_memfd_create 6318 +# endif +# if _MIPS_SIM == _MIPS_SIM_ABI64 +# define __NR_memfd_create 5314 +# endif +# elif defined __i386__ +# define __NR_memfd_create 356 +# else +# warning "__NR_memfd_create unknown for your architecture" +# endif +# endif + +static inline int memfd_create(const char *name, unsigned int flags) { +# ifdef __NR_memfd_create + return syscall(__NR_memfd_create, name, flags); +# else + errno = ENOSYS; + return -1; +# endif +} +#endif + +/* ======================================================================= */ + +#if !HAVE_DECL_GETRANDOM +# ifndef __NR_getrandom +# if defined __x86_64__ +# define __NR_getrandom 318 +# elif defined(__i386__) +# define __NR_getrandom 355 +# elif defined(__arm__) +# define __NR_getrandom 384 +# elif defined(__aarch64__) +# define __NR_getrandom 278 +# elif defined(__ia64__) +# define __NR_getrandom 1339 +# elif defined(__m68k__) +# define __NR_getrandom 352 +# elif defined(__s390x__) +# define __NR_getrandom 349 +# elif defined(__powerpc__) +# define __NR_getrandom 359 +# elif defined _MIPS_SIM +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define __NR_getrandom 4353 +# endif +# if _MIPS_SIM == _MIPS_SIM_NABI32 +# define __NR_getrandom 6317 +# endif +# if _MIPS_SIM == _MIPS_SIM_ABI64 +# define __NR_getrandom 5313 +# endif +# else +# warning "__NR_getrandom unknown for your architecture" +# endif +# endif + +static inline int getrandom(void *buffer, size_t count, unsigned flags) { +# ifdef __NR_getrandom + return syscall(__NR_getrandom, buffer, count, flags); +# else + errno = ENOSYS; + return -1; +# endif +} +#endif + +/* ======================================================================= */ + +#if !HAVE_DECL_GETTID +static inline pid_t gettid(void) { + return (pid_t) syscall(SYS_gettid); +} +#endif + +/* ======================================================================= */ + +#if !HAVE_DECL_NAME_TO_HANDLE_AT +# ifndef __NR_name_to_handle_at +# if defined(__x86_64__) +# define __NR_name_to_handle_at 303 +# elif defined(__i386__) +# define __NR_name_to_handle_at 341 +# elif defined(__arm__) +# define __NR_name_to_handle_at 370 +# elif defined(__powerpc__) +# define __NR_name_to_handle_at 345 +# else +# error "__NR_name_to_handle_at is not defined" +# endif +# endif + +struct file_handle { + unsigned int handle_bytes; + int handle_type; + unsigned char f_handle[0]; +}; + +static inline int name_to_handle_at(int fd, const char *name, struct file_handle *handle, int *mnt_id, int flags) { +# ifdef __NR_name_to_handle_at + return syscall(__NR_name_to_handle_at, fd, name, handle, mnt_id, flags); +# else + errno = ENOSYS; + return -1; +# endif +} +#endif + +/* ======================================================================= */ + +#if !HAVE_DECL_SETNS +# ifndef __NR_setns +# if defined(__x86_64__) +# define __NR_setns 308 +# elif defined(__i386__) +# define __NR_setns 346 +# else +# error "__NR_setns is not defined" +# endif +# endif + +static inline int setns(int fd, int nstype) { +# ifdef __NR_setns + return syscall(__NR_setns, fd, nstype); +# else + errno = ENOSYS; + return -1; +# endif +} +#endif + +/* ======================================================================= */ + +static inline int raw_clone(unsigned long flags, void *child_stack) { +#if defined(__s390__) || defined(__CRIS__) + /* On s390 and cris the order of the first and second arguments + * of the raw clone() system call is reversed. */ + return (int) syscall(__NR_clone, child_stack, flags); +#else + return (int) syscall(__NR_clone, flags, child_stack); +#endif +} + +/* ======================================================================= */ + +static inline pid_t raw_getpid(void) { +#if defined(__alpha__) + return (pid_t) syscall(__NR_getxpid); +#else + return (pid_t) syscall(__NR_getpid); +#endif +} + +/* ======================================================================= */ + +#if !HAVE_DECL_RENAMEAT2 +# ifndef __NR_renameat2 +# if defined __x86_64__ +# define __NR_renameat2 316 +# elif defined __arm__ +# define __NR_renameat2 382 +# elif defined _MIPS_SIM +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define __NR_renameat2 4351 +# endif +# if _MIPS_SIM == _MIPS_SIM_NABI32 +# define __NR_renameat2 6315 +# endif +# if _MIPS_SIM == _MIPS_SIM_ABI64 +# define __NR_renameat2 5311 +# endif +# elif defined __i386__ +# define __NR_renameat2 353 +# else +# warning "__NR_renameat2 unknown for your architecture" +# endif +# endif + +static inline int renameat2(int oldfd, const char *oldname, int newfd, const char *newname, unsigned flags) { +# ifdef __NR_renameat2 + return syscall(__NR_renameat2, oldfd, oldname, newfd, newname, flags); +# else + errno = ENOSYS; + return -1; +# endif +} +#endif + +/* ======================================================================= */ + +#if !HAVE_DECL_KCMP +static inline int kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) { +# ifdef __NR_kcmp + return syscall(__NR_kcmp, pid1, pid2, type, idx1, idx2); +# else + errno = ENOSYS; + return -1; +# endif +} +#endif + +/* ======================================================================= */ + +#if !HAVE_DECL_KEYCTL +static inline long keyctl(int cmd, unsigned long arg2, unsigned long arg3, unsigned long arg4,unsigned long arg5) { +# ifdef __NR_keyctl + return syscall(__NR_keyctl, cmd, arg2, arg3, arg4, arg5); +# else + errno = ENOSYS; + return -1; +# endif +} + +static inline key_serial_t add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid) { +# ifdef __NR_add_key + return syscall(__NR_add_key, type, description, payload, plen, ringid); +# else + errno = ENOSYS; + return -1; +# endif +} + +static inline key_serial_t request_key(const char *type, const char *description, const char * callout_info, key_serial_t destringid) { +# ifdef __NR_request_key + return syscall(__NR_request_key, type, description, callout_info, destringid); +# else + errno = ENOSYS; + return -1; +# endif +} +#endif + +/* ======================================================================= */ + +#if !HAVE_DECL_COPY_FILE_RANGE +# ifndef __NR_copy_file_range +# if defined(__x86_64__) +# define __NR_copy_file_range 326 +# elif defined(__i386__) +# define __NR_copy_file_range 377 +# elif defined __s390__ +# define __NR_copy_file_range 375 +# elif defined __arm__ +# define __NR_copy_file_range 391 +# elif defined __aarch64__ +# define __NR_copy_file_range 285 +# else +# warning "__NR_copy_file_range not defined for your architecture" +# endif +# endif + +static inline ssize_t copy_file_range(int fd_in, loff_t *off_in, + int fd_out, loff_t *off_out, + size_t len, + unsigned int flags) { +# ifdef __NR_copy_file_range + return syscall(__NR_copy_file_range, fd_in, off_in, fd_out, off_out, len, flags); +# else + errno = ENOSYS; + return -1; +# endif +} +#endif diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c index 33f2ee96d8..ba698959b7 100644 --- a/src/basic/mount-util.c +++ b/src/basic/mount-util.c @@ -47,7 +47,7 @@ static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id if ((flags & AT_EMPTY_PATH) && isempty(filename)) xsprintf(path, "/proc/self/fdinfo/%i", fd); else { - subfd = openat(fd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH); + subfd = openat(fd, filename, O_CLOEXEC|O_PATH); if (subfd < 0) return -errno; @@ -230,7 +230,7 @@ int path_is_mount_point(const char *t, int flags) { if (!parent) return -ENOMEM; - fd = openat(AT_FDCWD, parent, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_PATH); + fd = openat(AT_FDCWD, parent, O_DIRECTORY|O_CLOEXEC|O_PATH); if (fd < 0) return -errno; @@ -498,7 +498,9 @@ bool fstype_is_network(const char *fstype) { "nfs4\0" "gfs\0" "gfs2\0" - "glusterfs\0"; + "glusterfs\0" + "pvfs2\0" /* OrangeFS */ + ; const char *x; diff --git a/src/basic/nss-util.h b/src/basic/nss-util.h index df565a3593..bf7c4854fc 100644 --- a/src/basic/nss-util.h +++ b/src/basic/nss-util.h @@ -154,3 +154,46 @@ enum nss_status _nss_##module##_getgrgid_r( \ struct group *gr, \ char *buffer, size_t buflen, \ int *errnop) _public_ + +typedef enum nss_status (*_nss_gethostbyname4_r_t)( + const char *name, + struct gaih_addrtuple **pat, + char *buffer, size_t buflen, + int *errnop, int *h_errnop, + int32_t *ttlp); + +typedef enum nss_status (*_nss_gethostbyname3_r_t)( + const char *name, + int af, + struct hostent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop, + int32_t *ttlp, + char **canonp); + +typedef enum nss_status (*_nss_gethostbyname2_r_t)( + const char *name, + int af, + struct hostent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop); + +typedef enum nss_status (*_nss_gethostbyname_r_t)( + const char *name, + struct hostent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop); + +typedef enum nss_status (*_nss_gethostbyaddr2_r_t)( + const void* addr, socklen_t len, + int af, + struct hostent *result, + char *buffer, size_t buflen, + int *errnop, int *h_errnop, + int32_t *ttlp); +typedef enum nss_status (*_nss_gethostbyaddr_r_t)( + const void* addr, socklen_t len, + int af, + struct hostent *host, + char *buffer, size_t buflen, + int *errnop, int *h_errnop); diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index a3cb81b040..6c11b605a9 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -505,7 +505,7 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) { s = *p; /* accept any number of digits, strtoull is limted to 19 */ - for(i=0; i < digits; i++,s++) { + for (i=0; i < digits; i++,s++) { if (*s < '0' || *s > '9') { if (i == 0) return -EINVAL; diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h index d8dc26a36e..7dc579a159 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -90,6 +90,18 @@ static inline int safe_atoli(const char *s, long int *ret_u) { } #endif +#if SIZE_MAX == UINT_MAX +static inline int safe_atozu(const char *s, size_t *ret_u) { + assert_cc(sizeof(size_t) == sizeof(unsigned)); + return safe_atou(s, (unsigned *) ret_u); +} +#else +static inline int safe_atozu(const char *s, size_t *ret_u) { + assert_cc(sizeof(size_t) == sizeof(long unsigned)); + return safe_atolu(s, ret_u); +} +#endif + int safe_atod(const char *s, double *ret_d); int parse_fractional_part_u(const char **s, size_t digits, unsigned *res); diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 822c09bfba..b2fa81a294 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -569,10 +569,10 @@ static int binary_is_good(const char *binary) { if (r < 0) return r; - return !path_equal(d, "true") && - !path_equal(d, "/bin/true") && - !path_equal(d, "/usr/bin/true") && - !path_equal(d, "/dev/null"); + return !PATH_IN_SET(d, "true" + "/bin/true", + "/usr/bin/true", + "/dev/null"); } int fsck_exists(const char *fstype) { @@ -756,34 +756,53 @@ char *file_in_same_dir(const char *path, const char *filename) { return ret; } -bool hidden_file_allow_backup(const char *filename) { - assert(filename); - - return - filename[0] == '.' || - streq(filename, "lost+found") || - streq(filename, "aquota.user") || - streq(filename, "aquota.group") || - endswith(filename, ".rpmnew") || - endswith(filename, ".rpmsave") || - endswith(filename, ".rpmorig") || - endswith(filename, ".dpkg-old") || - endswith(filename, ".dpkg-new") || - endswith(filename, ".dpkg-tmp") || - endswith(filename, ".dpkg-dist") || - endswith(filename, ".dpkg-bak") || - endswith(filename, ".dpkg-backup") || - endswith(filename, ".dpkg-remove") || - endswith(filename, ".swp"); -} +bool hidden_or_backup_file(const char *filename) { + const char *p; -bool hidden_file(const char *filename) { assert(filename); - if (endswith(filename, "~")) + if (filename[0] == '.' || + streq(filename, "lost+found") || + streq(filename, "aquota.user") || + streq(filename, "aquota.group") || + endswith(filename, "~")) return true; - return hidden_file_allow_backup(filename); + p = strrchr(filename, '.'); + if (!p) + return false; + + /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up + * with always new suffixes and that everybody else should just adjust to that, then it really should be on + * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt + * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional + * string. Specifically: there's now: + * + * The generic suffixes "~" and ".bak" for backup files + * The generic prefix "." for hidden files + * + * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist" + * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead. + */ + + return STR_IN_SET(p + 1, + "rpmnew", + "rpmsave", + "rpmorig", + "dpkg-old", + "dpkg-new", + "dpkg-tmp", + "dpkg-dist", + "dpkg-bak", + "dpkg-backup", + "dpkg-remove", + "ucf-new", + "ucf-old", + "ucf-dist", + "swp", + "bak", + "old", + "new"); } bool is_device_path(const char *path) { diff --git a/src/basic/path-util.h b/src/basic/path-util.h index 2c2f87a9f2..a27c13fcc3 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -48,6 +48,23 @@ bool path_equal(const char *a, const char *b) _pure_; bool path_equal_or_files_same(const char *a, const char *b); char* path_join(const char *root, const char *path, const char *rest); +static inline bool path_equal_ptr(const char *a, const char *b) { + return !!a == !!b && (!a || path_equal(a, b)); +} + +/* Note: the search terminates on the first NULL item. */ +#define PATH_IN_SET(p, ...) \ + ({ \ + char **s; \ + bool _found = false; \ + STRV_FOREACH(s, STRV_MAKE(__VA_ARGS__)) \ + if (path_equal(p, *s)) { \ + _found = true; \ + break; \ + } \ + _found; \ + }) + int path_strv_make_absolute_cwd(char **l); char** path_strv_resolve(char **l, const char *prefix); char** path_strv_resolve_uniq(char **l, const char *prefix); @@ -105,7 +122,6 @@ bool path_is_safe(const char *p) _pure_; char *file_in_same_dir(const char *path, const char *filename); -bool hidden_file_allow_backup(const char *filename); -bool hidden_file(const char *filename) _pure_; +bool hidden_or_backup_file(const char *filename) _pure_; bool is_device_path(const char *path); diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 189ef9ab60..1ad8816206 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -38,6 +38,7 @@ #endif #include "alloc-util.h" +#include "architecture.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" @@ -205,7 +206,7 @@ void rename_process(const char name[8]) { * "systemd"). If you pass a longer string it will be * truncated */ - prctl(PR_SET_NAME, name); + (void) prctl(PR_SET_NAME, name); if (program_invocation_name) strncpy(program_invocation_name, name, strlen(program_invocation_name)); @@ -527,14 +528,20 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_cod return -EPROTO; } -void sigkill_wait(pid_t *pid) { +void sigkill_wait(pid_t pid) { + assert(pid > 1); + + if (kill(pid, SIGKILL) > 0) + (void) wait_for_terminate(pid, NULL); +} + +void sigkill_waitp(pid_t *pid) { if (!pid) return; if (*pid <= 1) return; - if (kill(*pid, SIGKILL) > 0) - (void) wait_for_terminate(*pid, NULL); + sigkill_wait(*pid); } int kill_and_sigcont(pid_t pid, int sig) { @@ -660,6 +667,8 @@ bool is_main_thread(void) { noreturn void freeze(void) { + log_close(); + /* Make sure nobody waits for us on a socket anymore */ close_all_fds(NULL, 0); @@ -674,75 +683,43 @@ bool oom_score_adjust_is_valid(int oa) { } unsigned long personality_from_string(const char *p) { + int architecture; - /* Parse a personality specifier. We introduce our own - * identifiers that indicate specific ABIs, rather than just - * hints regarding the register size, since we want to keep - * things open for multiple locally supported ABIs for the - * same register size. We try to reuse the ABI identifiers - * used by libseccomp. */ - -#if defined(__x86_64__) - - if (streq(p, "x86")) - return PER_LINUX32; + if (!p) + return PERSONALITY_INVALID; - if (streq(p, "x86-64")) - return PER_LINUX; + /* Parse a personality specifier. We use our own identifiers that indicate specific ABIs, rather than just + * hints regarding the register size, since we want to keep things open for multiple locally supported ABIs for + * the same register size. */ -#elif defined(__i386__) + architecture = architecture_from_string(p); + if (architecture < 0) + return PERSONALITY_INVALID; - if (streq(p, "x86")) + if (architecture == native_architecture()) return PER_LINUX; - -#elif defined(__s390x__) - - if (streq(p, "s390")) +#ifdef SECONDARY_ARCHITECTURE + if (architecture == SECONDARY_ARCHITECTURE) return PER_LINUX32; - - if (streq(p, "s390x")) - return PER_LINUX; - -#elif defined(__s390__) - - if (streq(p, "s390")) - return PER_LINUX; #endif return PERSONALITY_INVALID; } const char* personality_to_string(unsigned long p) { - -#if defined(__x86_64__) - - if (p == PER_LINUX32) - return "x86"; - - if (p == PER_LINUX) - return "x86-64"; - -#elif defined(__i386__) - - if (p == PER_LINUX) - return "x86"; - -#elif defined(__s390x__) - - if (p == PER_LINUX) - return "s390x"; - - if (p == PER_LINUX32) - return "s390"; - -#elif defined(__s390__) + int architecture = _ARCHITECTURE_INVALID; if (p == PER_LINUX) - return "s390"; - + architecture = native_architecture(); +#ifdef SECONDARY_ARCHITECTURE + else if (p == PER_LINUX32) + architecture = SECONDARY_ARCHITECTURE; #endif - return NULL; + if (architecture < 0) + return NULL; + + return architecture_to_string(architecture); } void valgrind_summary_hack(void) { @@ -762,6 +739,18 @@ void valgrind_summary_hack(void) { #endif } +int pid_compare_func(const void *a, const void *b) { + const pid_t *p = a, *q = b; + + /* Suitable for usage in qsort() */ + + if (*p < *q) + return -1; + if (*p > *q) + return 1; + 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 f5d193e762..9f75088796 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -58,8 +58,8 @@ int get_process_ppid(pid_t pid, pid_t *ppid); int wait_for_terminate(pid_t pid, siginfo_t *status); int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code); -void sigkill_wait(pid_t *pid); -#define _cleanup_sigkill_wait_ _cleanup_(sigkill_wait) +void sigkill_wait(pid_t pid); +void sigkill_waitp(pid_t *pid); int kill_and_sigcont(pid_t pid, int sig); @@ -101,3 +101,5 @@ int sched_policy_from_string(const char *s); #define PID_TO_PTR(p) ((void*) ((uintptr_t) p)) void valgrind_summary_hack(void); + +int pid_compare_func(const void *a, const void *b); diff --git a/src/basic/rlimit-util.c b/src/basic/rlimit-util.c index 7540b43215..ee063720ed 100644 --- a/src/basic/rlimit-util.c +++ b/src/basic/rlimit-util.c @@ -153,6 +153,56 @@ static int rlimit_parse_usec(const char *val, rlim_t *ret) { return 0; } +static int rlimit_parse_nice(const char *val, rlim_t *ret) { + uint64_t rl; + int r; + + /* So, Linux is weird. The range for RLIMIT_NICE is 40..1, mapping to the nice levels -20..19. However, the + * RLIMIT_NICE limit defaults to 0 by the kernel, i.e. a value that maps to nice level 20, which of course is + * bogus and does not exist. In order to permit parsing the RLIMIT_NICE of 0 here we hence implement a slight + * asymmetry: when parsing as positive nice level we permit 0..19. When parsing as negative nice level, we + * permit -20..0. But when parsing as raw resource limit value then we also allow the special value 0. + * + * Yeah, Linux is quality engineering sometimes... */ + + if (val[0] == '+') { + + /* Prefixed with "+": Parse as positive user-friendly nice value */ + r = safe_atou64(val + 1, &rl); + if (r < 0) + return r; + + if (rl >= PRIO_MAX) + return -ERANGE; + + rl = 20 - rl; + + } else if (val[0] == '-') { + + /* Prefixed with "-": Parse as negative user-friendly nice value */ + r = safe_atou64(val + 1, &rl); + if (r < 0) + return r; + + if (rl > (uint64_t) (-PRIO_MIN)) + return -ERANGE; + + rl = 20 + rl; + } else { + + /* Not prefixed: parse as raw resource limit value */ + r = safe_atou64(val, &rl); + if (r < 0) + return r; + + if (rl > (uint64_t) (20 - PRIO_MIN)) + return -ERANGE; + } + + *ret = (rlim_t) rl; + return 0; +} + static int (*const rlimit_parse_table[_RLIMIT_MAX])(const char *val, rlim_t *ret) = { [RLIMIT_CPU] = rlimit_parse_sec, [RLIMIT_FSIZE] = rlimit_parse_size, @@ -167,7 +217,7 @@ static int (*const rlimit_parse_table[_RLIMIT_MAX])(const char *val, rlim_t *ret [RLIMIT_LOCKS] = rlimit_parse_u64, [RLIMIT_SIGPENDING] = rlimit_parse_u64, [RLIMIT_MSGQUEUE] = rlimit_parse_size, - [RLIMIT_NICE] = rlimit_parse_u64, + [RLIMIT_NICE] = rlimit_parse_nice, [RLIMIT_RTPRIO] = rlimit_parse_u64, [RLIMIT_RTTIME] = rlimit_parse_usec, }; diff --git a/src/basic/rm-rf.h b/src/basic/rm-rf.h index 6d03268919..f693a5bb7c 100644 --- a/src/basic/rm-rf.h +++ b/src/basic/rm-rf.h @@ -30,3 +30,12 @@ typedef enum RemoveFlags { int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev); int rm_rf(const char *path, RemoveFlags flags); + +/* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */ +static inline void rm_rf_physical_and_free(char *p) { + if (!p) + return; + (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL); + free(p); +} +DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rm_rf_physical_and_free); diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c index 6c63b9d652..10c2f39369 100644 --- a/src/basic/selinux-util.c +++ b/src/basic/selinux-util.c @@ -80,31 +80,23 @@ void mac_selinux_retest(void) { #endif } -int mac_selinux_init(const char *prefix) { +int mac_selinux_init(void) { int r = 0; #ifdef HAVE_SELINUX usec_t before_timestamp, after_timestamp; struct mallinfo before_mallinfo, after_mallinfo; - if (!mac_selinux_use()) + if (label_hnd) return 0; - if (label_hnd) + if (!mac_selinux_use()) return 0; before_mallinfo = mallinfo(); before_timestamp = now(CLOCK_MONOTONIC); - if (prefix) { - struct selinux_opt options[] = { - { .type = SELABEL_OPT_SUBSET, .value = prefix }, - }; - - label_hnd = selabel_open(SELABEL_CTX_FILE, options, ELEMENTSOF(options)); - } else - label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0); - + label_hnd = selabel_open(SELABEL_CTX_FILE, NULL, 0); if (!label_hnd) { log_enforcing("Failed to initialize SELinux context: %m"); r = security_getenforce() == 1 ? -errno : 0; @@ -160,7 +152,7 @@ int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { return 0; if (r >= 0) { - r = lsetfilecon(path, fcon); + r = lsetfilecon_raw(path, fcon); /* If the FS doesn't support labels, then exit without warning */ if (r < 0 && errno == EOPNOTSUPP) @@ -225,7 +217,7 @@ int mac_selinux_get_create_label_from_exe(const char *exe, char **label) { return -errno; sclass = string_to_security_class("process"); - r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label); + r = security_compute_create_raw(mycon, fcon, sclass, (security_context_t *) label); if (r < 0) return -errno; #endif @@ -270,7 +262,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char * if (r < 0) return -errno; - r = getpeercon(socket_fd, &peercon); + r = getpeercon_raw(socket_fd, &peercon); if (r < 0) return -errno; @@ -304,7 +296,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char * return -ENOMEM; sclass = string_to_security_class("process"); - r = security_compute_create(mycon, fcon, sclass, (security_context_t *) label); + r = security_compute_create_raw(mycon, fcon, sclass, (security_context_t *) label); if (r < 0) return -errno; #endif @@ -358,7 +350,7 @@ int mac_selinux_create_file_prepare(const char *path, mode_t mode) { log_enforcing("Failed to determine SELinux security context for %s: %m", path); } else { - if (setfscreatecon(filecon) >= 0) + if (setfscreatecon_raw(filecon) >= 0) return 0; /* Success! */ log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path); @@ -379,7 +371,7 @@ void mac_selinux_create_file_clear(void) { if (!mac_selinux_use()) return; - setfscreatecon(NULL); + setfscreatecon_raw(NULL); #endif } @@ -410,7 +402,7 @@ void mac_selinux_create_socket_clear(void) { if (!mac_selinux_use()) return; - setsockcreatecon(NULL); + setsockcreatecon_raw(NULL); #endif } @@ -469,7 +461,7 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { return -errno; } else { - if (setfscreatecon(fcon) < 0) { + if (setfscreatecon_raw(fcon) < 0) { log_enforcing("Failed to set SELinux security context %s for %s: %m", fcon, path); if (security_getenforce() > 0) return -errno; @@ -480,7 +472,7 @@ int mac_selinux_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) { r = bind(fd, addr, addrlen) < 0 ? -errno : 0; if (context_changed) - setfscreatecon(NULL); + setfscreatecon_raw(NULL); return r; diff --git a/src/basic/selinux-util.h b/src/basic/selinux-util.h index 27e8edb41b..ce6bc8e44c 100644 --- a/src/basic/selinux-util.h +++ b/src/basic/selinux-util.h @@ -29,7 +29,7 @@ bool mac_selinux_use(void); bool mac_selinux_have(void); void mac_selinux_retest(void); -int mac_selinux_init(const char *prefix); +int mac_selinux_init(void); void mac_selinux_finish(void); int mac_selinux_fix(const char *path, bool ignore_enoent, bool ignore_erofs); diff --git a/src/basic/set.h b/src/basic/set.h index 2bff5062da..e0d9dd001c 100644 --- a/src/basic/set.h +++ b/src/basic/set.h @@ -126,6 +126,9 @@ int set_put_strdupv(Set *s, char **l); #define SET_FOREACH(e, s, i) \ for ((i) = ITERATOR_FIRST; set_iterate((s), &(i), (void**)&(e)); ) +#define SET_FOREACH_MOVE(e, d, s) \ + for (; ({ e = set_first(s); assert_se(!e || set_move_one(d, s, e) >= 0); e; }); ) + DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free); DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free); diff --git a/src/basic/sigbus.h b/src/basic/sigbus.h index cce9eb201b..980243d9ce 100644 --- a/src/basic/sigbus.h +++ b/src/basic/sigbus.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - void sigbus_install(void); void sigbus_reset(void); diff --git a/src/basic/signal-util.c b/src/basic/signal-util.c index e3047b209b..280b5c3251 100644 --- a/src/basic/signal-util.c +++ b/src/basic/signal-util.c @@ -255,7 +255,7 @@ int signal_from_string(const char *s) { } if (safe_atou(s, &u) >= 0) { signo = (int) u + offset; - if (signo > 0 && signo < _NSIG) + if (SIGNAL_VALID(signo)) return signo; } return -EINVAL; diff --git a/src/basic/signal-util.h b/src/basic/signal-util.h index 72b10e8712..dfd6eb564d 100644 --- a/src/basic/signal-util.h +++ b/src/basic/signal-util.h @@ -44,9 +44,13 @@ static inline void block_signals_reset(sigset_t *ss) { assert_se(sigprocmask(SIG_SETMASK, ss, NULL) >= 0); } -#define BLOCK_SIGNALS(...) \ - _cleanup_(block_signals_reset) sigset_t _saved_sigset = ({ \ - sigset_t t; \ +#define BLOCK_SIGNALS(...) \ + _cleanup_(block_signals_reset) _unused_ sigset_t _saved_sigset = ({ \ + sigset_t t; \ assert_se(sigprocmask_many(SIG_BLOCK, &t, __VA_ARGS__, -1) >= 0); \ - t; \ + t; \ }) + +static inline bool SIGNAL_VALID(int signo) { + return signo > 0 && signo < _NSIG; +} diff --git a/src/basic/socket-label.c b/src/basic/socket-label.c index 35e9573aa4..6d1dc83874 100644 --- a/src/basic/socket-label.c +++ b/src/basic/socket-label.c @@ -23,7 +23,6 @@ #include <stddef.h> #include <string.h> #include <sys/socket.h> -#include <sys/stat.h> #include <sys/un.h> #include <unistd.h> @@ -35,6 +34,7 @@ #include "mkdir.h" #include "selinux-util.h" #include "socket-util.h" +#include "umask-util.h" int socket_address_listen( const SocketAddress *a, @@ -112,28 +112,24 @@ int socket_address_listen( return -errno; if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) { - mode_t old_mask; - /* Create parents */ - mkdir_parents_label(a->sockaddr.un.sun_path, directory_mode); + (void) mkdir_parents_label(a->sockaddr.un.sun_path, directory_mode); /* Enforce the right access mode for the socket */ - old_mask = umask(~ socket_mode); - - r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size); - - if (r < 0 && errno == EADDRINUSE) { - /* Unlink and try again */ - unlink(a->sockaddr.un.sun_path); - r = bind(fd, &a->sockaddr.sa, a->size); + RUN_WITH_UMASK(~socket_mode) { + r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size); + if (r == -EADDRINUSE) { + /* Unlink and try again */ + unlink(a->sockaddr.un.sun_path); + if (bind(fd, &a->sockaddr.sa, a->size) < 0) + return -errno; + } else if (r < 0) + return r; } - - umask(old_mask); - } else - r = bind(fd, &a->sockaddr.sa, a->size); - - if (r < 0) - return -errno; + } else { + if (bind(fd, &a->sockaddr.sa, a->size) < 0) + return -errno; + } if (socket_address_can_accept(a)) if (listen(fd, backlog) < 0) diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index 49e5f5b125..c8769a54f4 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -23,6 +23,7 @@ #include <net/if.h> #include <netdb.h> #include <netinet/ip.h> +#include <poll.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> @@ -42,7 +43,9 @@ #include "socket-util.h" #include "string-table.h" #include "string-util.h" +#include "strv.h" #include "user-util.h" +#include "utf8.h" #include "util.h" int socket_address_parse(SocketAddress *a, const char *s) { @@ -794,6 +797,42 @@ static const char* const ip_tos_table[] = { DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff); +bool ifname_valid(const char *p) { + bool numeric = true; + + /* Checks whether a network interface name is valid. This is inspired by dev_valid_name() in the kernel sources + * but slightly stricter, as we only allow non-control, non-space ASCII characters in the interface name. We + * also don't permit names that only container numbers, to avoid confusion with numeric interface indexes. */ + + if (isempty(p)) + return false; + + if (strlen(p) >= IFNAMSIZ) + return false; + + if (STR_IN_SET(p, ".", "..")) + return false; + + while (*p) { + if ((unsigned char) *p >= 127U) + return false; + + if ((unsigned char) *p <= 32U) + return false; + + if (*p == ':' || *p == '/') + return false; + + numeric = numeric && (*p >= '0' && *p <= '9'); + p++; + } + + if (numeric) + return false; + + return true; +} + int getpeercred(int fd, struct ucred *ucred) { socklen_t n = sizeof(struct ucred); struct ucred u; @@ -936,3 +975,76 @@ int receive_one_fd(int transport_fd, int flags) { return *(int*) CMSG_DATA(found); } + +ssize_t next_datagram_size_fd(int fd) { + ssize_t l; + int k; + + /* This is a bit like FIONREAD/SIOCINQ, however a bit more powerful. The difference being: recv(MSG_PEEK) will + * actually cause the next datagram in the queue to be validated regarding checksums, which FIONREAD doesn't + * do. This difference is actually of major importance as we need to be sure that the size returned here + * actually matches what we will read with recvmsg() next, as otherwise we might end up allocating a buffer of + * the wrong size. */ + + l = recv(fd, NULL, 0, MSG_PEEK|MSG_TRUNC); + if (l < 0) { + if (errno == EOPNOTSUPP) + goto fallback; + + return -errno; + } + if (l == 0) + goto fallback; + + return l; + +fallback: + k = 0; + + /* Some sockets (AF_PACKET) do not support null-sized recv() with MSG_TRUNC set, let's fall back to FIONREAD + * for them. Checksums don't matter for raw sockets anyway, hence this should be fine. */ + + if (ioctl(fd, FIONREAD, &k) < 0) + return -errno; + + return (ssize_t) k; +} + +int flush_accept(int fd) { + + struct pollfd pollfd = { + .fd = fd, + .events = POLLIN, + }; + int r; + + + /* Similar to flush_fd() but flushes all incoming connection by accepting them and immediately closing them. */ + + for (;;) { + int cfd; + + r = poll(&pollfd, 1, 0); + if (r < 0) { + if (errno == EINTR) + continue; + + return -errno; + + } else if (r == 0) + return 0; + + cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); + if (cfd < 0) { + if (errno == EINTR) + continue; + + if (errno == EAGAIN) + return 0; + + return -errno; + } + + close(cfd); + } +} diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index 92edc1dc22..e9230e4a9f 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -123,6 +123,8 @@ int fd_inc_rcvbuf(int fd, size_t n); int ip_tos_to_string_alloc(int i, char **s); int ip_tos_from_string(const char *s); +bool ifname_valid(const char *p); + int getpeercred(int fd, struct ucred *ucred); int getpeersec(int fd, char **ret); @@ -133,5 +135,20 @@ int send_one_fd_sa(int transport_fd, #define send_one_fd(transport_fd, fd, flags) send_one_fd_sa(transport_fd, fd, NULL, 0, flags) int receive_one_fd(int transport_fd, int flags); +ssize_t next_datagram_size_fd(int fd); + +int flush_accept(int fd); + #define CMSG_FOREACH(cmsg, mh) \ for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg))) + +/* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */ +#define SOCKADDR_UN_LEN(sa) \ + ({ \ + const struct sockaddr_un *_sa = &(sa); \ + assert(_sa->sun_family == AF_UNIX); \ + offsetof(struct sockaddr_un, sun_path) + \ + (_sa->sun_path[0] == 0 ? \ + 1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \ + strnlen(_sa->sun_path, sizeof(_sa->sun_path))); \ + }) diff --git a/src/basic/special.h b/src/basic/special.h index 2fd03d9f75..084d3dfa23 100644 --- a/src/basic/special.h +++ b/src/basic/special.h @@ -52,6 +52,7 @@ #define SPECIAL_LOCAL_FS_TARGET "local-fs.target" #define SPECIAL_LOCAL_FS_PRE_TARGET "local-fs-pre.target" #define SPECIAL_INITRD_FS_TARGET "initrd-fs.target" +#define SPECIAL_INITRD_ROOT_DEVICE_TARGET "initrd-root-device.target" #define SPECIAL_INITRD_ROOT_FS_TARGET "initrd-root-fs.target" #define SPECIAL_REMOTE_FS_TARGET "remote-fs.target" /* LSB's $remote_fs */ #define SPECIAL_REMOTE_FS_PRE_TARGET "remote-fs-pre.target" diff --git a/src/basic/stdio-util.h b/src/basic/stdio-util.h index 0a675571ff..bd1144b4c9 100644 --- a/src/basic/stdio-util.h +++ b/src/basic/stdio-util.h @@ -73,4 +73,4 @@ do { \ assert_not_reached("Unknown format string argument."); \ } \ } \ -} while(false) +} while (false) diff --git a/src/basic/strbuf.c b/src/basic/strbuf.c index 77220c0251..4bef87d3c2 100644 --- a/src/basic/strbuf.c +++ b/src/basic/strbuf.c @@ -121,7 +121,7 @@ static void bubbleinsert(struct strbuf_node *node, sizeof(struct strbuf_child_entry) * (node->children_count - left)); node->children[left] = new; - node->children_count ++; + node->children_count++; } /* add string, return the index/offset into the buffer */ @@ -156,8 +156,13 @@ ssize_t strbuf_add_string(struct strbuf *str, const char *s, size_t len) { return off; } - /* lookup child node */ c = s[len - 1 - depth]; + + /* bsearch is not allowed on a NULL sequence */ + if (node->children_count == 0) + break; + + /* lookup child node */ search.c = c; child = bsearch(&search, node->children, node->children_count, sizeof(struct strbuf_child_entry), diff --git a/src/basic/string-table.h b/src/basic/string-table.h index b180488fe8..d88625fca7 100644 --- a/src/basic/string-table.h +++ b/src/basic/string-table.h @@ -56,26 +56,8 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k return (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ } -#define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \ - struct __useless_struct_to_allow_trailing_semicolon__ - -#define _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ - _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \ - struct __useless_struct_to_allow_trailing_semicolon__ - -#define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,) -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static) -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static) -#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static) - -#define DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,) - -/* For string conversions where numbers are also acceptable */ -#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \ - int name##_to_string_alloc(type i, char **str) { \ +#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,scope) \ + scope int name##_to_string_alloc(type i, char **str) { \ char *s; \ if (i < 0 || i > max) \ return -ERANGE; \ @@ -89,7 +71,9 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k } \ *str = s; \ return 0; \ - } \ + } + +#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,scope) \ type name##_from_string(const char *s) { \ type i; \ unsigned u = 0; \ @@ -102,4 +86,32 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k return (type) u; \ return (type) -1; \ } \ + + +#define _DEFINE_STRING_TABLE_LOOKUP(name,type,scope) \ + _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ + _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,scope) \ + struct __useless_struct_to_allow_trailing_semicolon__ + +#define _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,scope) \ + _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \ + _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_BOOLEAN(name,type,yes,scope) \ + struct __useless_struct_to_allow_trailing_semicolon__ + +#define DEFINE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,) +#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name,type) _DEFINE_STRING_TABLE_LOOKUP(name,type,static) +#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static) +#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name,type,static) + +#define DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes) _DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(name,type,yes,) + +/* For string conversions where numbers are also acceptable */ +#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \ + _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,) \ + _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,) \ struct __useless_struct_to_allow_trailing_semicolon__ + +#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max) \ + _DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,static) +#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max) \ + _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,static) diff --git a/src/basic/string-util.c b/src/basic/string-util.c index 0bde55f9d5..293a15f9c0 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -477,7 +477,7 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne } if (k > x) /* last character was wide and went over quota */ - x ++; + x++; for (j = s + old_length; k < new_length && j > i; ) { char32_t c; diff --git a/src/basic/string-util.h b/src/basic/string-util.h index ad0c813761..139cc8c91b 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -37,6 +37,7 @@ #define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" #define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS #define ALPHANUMERICAL LETTERS DIGITS +#define HEXDIGITS DIGITS "abcdefABCDEF" #define streq(a,b) (strcmp((a),(b)) == 0) #define strneq(a, b, n) (strncmp((a), (b), (n)) == 0) diff --git a/src/basic/strv.c b/src/basic/strv.c index b5d4d8191b..97a96e5762 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -371,7 +371,7 @@ char *strv_join(char **l, const char *separator) { n = 0; STRV_FOREACH(s, l) { - if (n != 0) + if (s != l) n += k; n += strlen(*s); } @@ -382,7 +382,7 @@ char *strv_join(char **l, const char *separator) { e = r; STRV_FOREACH(s, l) { - if (e != r) + if (s != l) e = stpcpy(e, separator); e = stpcpy(e, *s); @@ -558,6 +558,42 @@ int strv_extend(char ***l, const char *value) { return strv_consume(l, v); } +int strv_extend_front(char ***l, const char *value) { + size_t n, m; + char *v, **c; + + assert(l); + + /* Like strv_extend(), but prepends rather than appends the new entry */ + + if (!value) + return 0; + + n = strv_length(*l); + + /* Increase and overflow check. */ + m = n + 2; + if (m < n) + return -ENOMEM; + + v = strdup(value); + if (!v) + return -ENOMEM; + + c = realloc_multiply(*l, sizeof(char*), m); + if (!c) { + free(v); + return -ENOMEM; + } + + memmove(c+1, c, n * sizeof(char*)); + c[0] = v; + c[n+1] = NULL; + + *l = c; + return 0; +} + char **strv_uniq(char **l) { char **i; diff --git a/src/basic/strv.h b/src/basic/strv.h index 7bfa54408d..f61bbb5386 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -50,6 +50,7 @@ int strv_extend_strv(char ***a, char **b, bool filter_duplicates); int strv_extend_strv_concat(char ***a, char **b, const char *suffix); int strv_extend(char ***l, const char *value); int strv_extendf(char ***l, const char *format, ...) _printf_(2,0); +int strv_extend_front(char ***l, const char *value); int strv_push(char ***l, char *value); int strv_push_pair(char ***l, char *a, char *b); int strv_push_prepend(char ***l, char *value); diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index 0a9d2bbdef..9521b79daa 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -1135,14 +1135,19 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) { } bool colors_enabled(void) { - const char *colors; + static int enabled = -1; - colors = getenv("SYSTEMD_COLORS"); - if (!colors) { - if (streq_ptr(getenv("TERM"), "dumb")) - return false; - return on_tty(); + if (_unlikely_(enabled < 0)) { + const char *colors; + + colors = getenv("SYSTEMD_COLORS"); + if (colors) + enabled = parse_boolean(colors) != 0; + else if (streq_ptr(getenv("TERM"), "dumb")) + enabled = false; + else + enabled = on_tty(); } - return parse_boolean(colors) != 0; + return enabled; } diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 3973850b44..edd9179cb8 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -42,10 +42,30 @@ static nsec_t timespec_load_nsec(const struct timespec *ts); +static clockid_t map_clock_id(clockid_t c) { + + /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will + * fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is + * when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on + * those archs. */ + + switch (c) { + + case CLOCK_BOOTTIME_ALARM: + return CLOCK_BOOTTIME; + + case CLOCK_REALTIME_ALARM: + return CLOCK_REALTIME; + + default: + return c; + } +} + usec_t now(clockid_t clock_id) { struct timespec ts; - assert_se(clock_gettime(clock_id, &ts) == 0); + assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0); return timespec_load(&ts); } @@ -53,7 +73,7 @@ usec_t now(clockid_t clock_id) { nsec_t now_nsec(clockid_t clock_id) { struct timespec ts; - assert_se(clock_gettime(clock_id, &ts) == 0); + assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0); return timespec_load_nsec(&ts); } @@ -119,8 +139,7 @@ dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, us usec_t timespec_load(const struct timespec *ts) { assert(ts); - if (ts->tv_sec == (time_t) -1 && - ts->tv_nsec == (long) -1) + if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1) return USEC_INFINITY; if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC) @@ -134,13 +153,13 @@ usec_t timespec_load(const struct timespec *ts) { static nsec_t timespec_load_nsec(const struct timespec *ts) { assert(ts); - if (ts->tv_sec == (time_t) -1 && - ts->tv_nsec == (long) -1) + if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1) return NSEC_INFINITY; - return - (nsec_t) ts->tv_sec * NSEC_PER_SEC + - (nsec_t) ts->tv_nsec; + if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC) + return NSEC_INFINITY; + + return (nsec_t) ts->tv_sec * NSEC_PER_SEC + (nsec_t) ts->tv_nsec; } struct timespec *timespec_store(struct timespec *ts, usec_t u) { @@ -429,7 +448,7 @@ int dual_timestamp_deserialize(const char *value, dual_timestamp *t) { assert(t); if (sscanf(value, "%llu %llu", &a, &b) != 2) { - log_debug("Failed to parse finish timestamp value %s.", value); + log_debug("Failed to parse dual timestamp value \"%s\": %m", value); return -EINVAL; } @@ -439,6 +458,18 @@ int dual_timestamp_deserialize(const char *value, dual_timestamp *t) { return 0; } +int timestamp_deserialize(const char *value, usec_t *timestamp) { + int r; + + assert(value); + + r = safe_atou64(value, timestamp); + if (r < 0) + return log_debug_errno(r, "Failed to parse timestamp value \"%s\": %m", value); + + return r; +} + int parse_timestamp(const char *t, usec_t *usec) { static const struct { const char *name; @@ -545,12 +576,12 @@ int parse_timestamp(const char *t, usec_t *usec) { goto from_tm; } else if (streq(t, "yesterday")) { - tm.tm_mday --; + tm.tm_mday--; tm.tm_sec = tm.tm_min = tm.tm_hour = 0; goto from_tm; } else if (streq(t, "tomorrow")) { - tm.tm_mday ++; + tm.tm_mday++; tm.tm_sec = tm.tm_min = tm.tm_hour = 0; goto from_tm; } @@ -673,8 +704,7 @@ finish: return 0; } -int parse_time(const char *t, usec_t *usec, usec_t default_unit) { - +static char* extract_multiplier(char *p, usec_t *multiplier) { static const struct { const char *suffix; usec_t usec; @@ -708,7 +738,22 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) { { "usec", 1ULL }, { "us", 1ULL }, }; + unsigned i; + + for (i = 0; i < ELEMENTSOF(table); i++) { + char *e; + e = startswith(p, table[i].suffix); + if (e) { + *multiplier = table[i].usec; + return e; + } + } + + return p; +} + +int parse_time(const char *t, usec_t *usec, usec_t default_unit) { const char *p, *s; usec_t r = 0; bool something = false; @@ -733,8 +778,8 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) { for (;;) { long long l, z = 0; char *e; - unsigned i, n = 0; - usec_t multiplier, k; + unsigned n = 0; + usec_t multiplier = default_unit, k; p += strspn(p, WHITESPACE); @@ -747,10 +792,8 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) { errno = 0; l = strtoll(p, &e, 10); - if (errno > 0) return -errno; - if (l < 0) return -ERANGE; @@ -774,18 +817,7 @@ int parse_time(const char *t, usec_t *usec, usec_t default_unit) { return -EINVAL; e += strspn(e, WHITESPACE); - - for (i = 0; i < ELEMENTSOF(table); i++) - if (startswith(e, table[i].suffix)) { - multiplier = table[i].usec; - p = e + strlen(table[i].suffix); - break; - } - - if (i >= ELEMENTSOF(table)) { - multiplier = default_unit; - p = e; - } + p = extract_multiplier(e, &multiplier); something = true; @@ -1048,22 +1080,31 @@ bool timezone_is_valid(const char *name) { return true; } -clockid_t clock_boottime_or_monotonic(void) { - static clockid_t clock = -1; - int fd; - - if (clock != -1) - return clock; - - fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC); - if (fd < 0) - clock = CLOCK_MONOTONIC; - else { - safe_close(fd); - clock = CLOCK_BOOTTIME; +bool clock_boottime_supported(void) { + static int supported = -1; + + /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */ + + if (supported < 0) { + int fd; + + fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC); + if (fd < 0) + supported = false; + else { + safe_close(fd); + supported = true; + } } - return clock; + return supported; +} + +clockid_t clock_boottime_or_monotonic(void) { + if (clock_boottime_supported()) + return CLOCK_BOOTTIME; + else + return CLOCK_MONOTONIC; } int get_timezone(char **tz) { diff --git a/src/basic/time-util.h b/src/basic/time-util.h index 9894e626c5..a5e3f567ec 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -99,6 +99,7 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy); void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t); int dual_timestamp_deserialize(const char *value, dual_timestamp *t); +int timestamp_deserialize(const char *value, usec_t *timestamp); int parse_timestamp(const char *t, usec_t *usec); @@ -111,6 +112,7 @@ bool ntp_synced(void); int get_timezones(char ***l); bool timezone_is_valid(const char *name); +bool clock_boottime_supported(void); clockid_t clock_boottime_or_monotonic(void); #define xstrftime(buf, fmt, tm) \ diff --git a/src/basic/user-util.c b/src/basic/user-util.c index 19155bce53..f65ca3edaa 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -30,6 +30,7 @@ #include <sys/stat.h> #include <unistd.h> +#include "missing.h" #include "alloc-util.h" #include "fd-util.h" #include "formats-util.h" diff --git a/src/basic/user-util.h b/src/basic/user-util.h index c23f1d485d..8026eca3f4 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -21,6 +21,7 @@ #include <stdbool.h> #include <sys/types.h> +#include <unistd.h> bool uid_is_valid(uid_t uid); @@ -63,3 +64,7 @@ int take_etc_passwd_lock(const char *root); #define PTR_TO_GID(p) ((gid_t) (((uintptr_t) (p))-1)) #define GID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1)) + +static inline bool userns_supported(void) { + return access("/proc/self/uid_map", F_OK) >= 0; +} diff --git a/src/basic/utf8.c b/src/basic/utf8.c index 629db123cd..6eae2b983d 100644 --- a/src/basic/utf8.c +++ b/src/basic/utf8.c @@ -241,7 +241,7 @@ char *utf8_escape_non_printable(const char *str) { *(s++) = hexchar((int) *str); str += 1; - len --; + len--; } } } else { diff --git a/src/basic/utf8.h b/src/basic/utf8.h index 12c272d66e..f9b9c9468b 100644 --- a/src/basic/utf8.h +++ b/src/basic/utf8.h @@ -28,6 +28,7 @@ #include "missing.h" #define UTF8_REPLACEMENT_CHARACTER "\xef\xbf\xbd" +#define UTF8_BYTE_ORDER_MARK "\xef\xbb\xbf" bool unichar_is_valid(char32_t c); diff --git a/src/basic/util.c b/src/basic/util.c index ea1bed7ceb..756c663be4 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -55,6 +55,7 @@ #include "string-util.h" #include "strv.h" #include "time-util.h" +#include "umask-util.h" #include "user-util.h" #include "util.h" @@ -419,13 +420,17 @@ int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *pa _exit(EXIT_FAILURE); } - if (!stdout_is_tty) - dup2(fd, STDOUT_FILENO); + if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) { + log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); + _exit(EXIT_FAILURE); + } - if (!stderr_is_tty) - dup2(fd, STDERR_FILENO); + if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) { + log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); + _exit(EXIT_FAILURE); + } - if (fd > 2) + if (fd > STDERR_FILENO) close(fd); } @@ -517,7 +522,7 @@ int on_ac_power(void) { if (!de) break; - if (hidden_file(de->d_name)) + if (hidden_or_backup_file(de->d_name)) continue; device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY); @@ -773,15 +778,25 @@ uint64_t physical_memory(void) { return (uint64_t) mem * (uint64_t) page_size(); } -int update_reboot_param_file(const char *param) { - int r = 0; +int update_reboot_parameter_and_warn(const char *param) { + int r; + + if (isempty(param)) { + if (unlink("/run/systemd/reboot-param") < 0) { + if (errno == ENOENT) + return 0; + + return log_warning_errno(errno, "Failed to unlink reboot parameter file: %m"); + } - if (param) { - r = write_string_file(REBOOT_PARAM_FILE, param, WRITE_STRING_FILE_CREATE); + return 0; + } + + RUN_WITH_UMASK(0022) { + r = write_string_file("/run/systemd/reboot-param", param, WRITE_STRING_FILE_CREATE); if (r < 0) - return log_error_errno(r, "Failed to write reboot param to "REBOOT_PARAM_FILE": %m"); - } else - (void) unlink(REBOOT_PARAM_FILE); + return log_warning_errno(r, "Failed to write reboot parameter file: %m"); + } return 0; } diff --git a/src/basic/util.h b/src/basic/util.h index 6f42c85a33..1c032c15c9 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -36,6 +36,7 @@ #include <sys/socket.h> #include <sys/stat.h> #include <sys/statfs.h> +#include <sys/sysmacros.h> #include <sys/types.h> #include <time.h> #include <unistd.h> @@ -102,6 +103,16 @@ static inline void qsort_safe(void *base, size_t nmemb, size_t size, comparison_ qsort(base, nmemb, size, compar); } +/** + * Normal memcpy requires src to be nonnull. We do nothing if n is 0. + */ +static inline void memcpy_safe(void *dst, const void *src, size_t n) { + if (n == 0) + return; + assert(src); + memcpy(dst, src, n); +} + int on_ac_power(void); #define memzero(x,l) (memset((x), 0, (l))) @@ -173,6 +184,6 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int uint64_t physical_memory(void); -int update_reboot_param_file(const char *param); +int update_reboot_parameter_and_warn(const char *param); int version(void); diff --git a/src/basic/virt.c b/src/basic/virt.c index 19b6318e3d..dace1f4328 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -98,6 +98,8 @@ static int detect_vm_cpuid(void) { : "0" (eax) ); + log_debug("Virtualization found, CPUID=%s", sig.text); + for (j = 0; j < ELEMENTSOF(cpuid_vendor_table); j ++) if (streq(sig.text, cpuid_vendor_table[j].cpuid)) return cpuid_vendor_table[j].id; @@ -105,6 +107,7 @@ static int detect_vm_cpuid(void) { return VIRTUALIZATION_VM_OTHER; } #endif + log_debug("No virtualization found in CPUID"); return VIRTUALIZATION_NONE; } @@ -121,19 +124,25 @@ static int detect_vm_device_tree(void) { dir = opendir("/proc/device-tree"); if (!dir) { - if (errno == ENOENT) + if (errno == ENOENT) { + log_debug_errno(errno, "/proc/device-tree: %m"); return VIRTUALIZATION_NONE; + } return -errno; } FOREACH_DIRENT(dent, dir, return -errno) - if (strstr(dent->d_name, "fw-cfg")) + if (strstr(dent->d_name, "fw-cfg")) { + log_debug("Virtualization QEMU: \"fw-cfg\" present in /proc/device-tree/%s", dent->d_name); return VIRTUALIZATION_QEMU; + } + log_debug("No virtualization found in /proc/device-tree/*"); return VIRTUALIZATION_NONE; } else if (r < 0) return r; + log_debug("Virtualization %s found in /proc/device-tree/hypervisor/compatible", hvtype); if (streq(hvtype, "linux,kvm")) return VIRTUALIZATION_KVM; else if (strstr(hvtype, "xen")) @@ -141,6 +150,7 @@ static int detect_vm_device_tree(void) { else return VIRTUALIZATION_VM_OTHER; #else + log_debug("This platform does not support /proc/device-tree"); return VIRTUALIZATION_NONE; #endif } @@ -184,30 +194,58 @@ static int detect_vm_dmi(void) { return r; } + + for (j = 0; j < ELEMENTSOF(dmi_vendor_table); j++) - if (startswith(s, dmi_vendor_table[j].vendor)) + if (startswith(s, dmi_vendor_table[j].vendor)) { + log_debug("Virtualization %s found in DMI (%s)", s, dmi_vendors[i]); return dmi_vendor_table[j].id; + } } #endif + log_debug("No virtualization found in DMI"); + return VIRTUALIZATION_NONE; } static int detect_vm_xen(void) { + /* Check for Dom0 will be executed later in detect_vm_xen_dom0 + Thats why we dont check the content of /proc/xen/capabilities here. */ + if (access("/proc/xen/capabilities", F_OK) < 0) { + log_debug("Virtualization XEN not found, /proc/xen/capabilities does not exist"); + return VIRTUALIZATION_NONE; + } + + log_debug("Virtualization XEN found (/proc/xen/capabilities exists)"); + return VIRTUALIZATION_XEN; + +} + +static bool detect_vm_xen_dom0(void) { _cleanup_free_ char *domcap = NULL; char *cap, *i; int r; r = read_one_line_file("/proc/xen/capabilities", &domcap); - if (r == -ENOENT) - return VIRTUALIZATION_NONE; + if (r == -ENOENT) { + log_debug("Virtualization XEN not found, /proc/xen/capabilities does not exist"); + return false; + } + if (r < 0) + return r; i = domcap; while ((cap = strsep(&i, ","))) if (streq(cap, "control_d")) break; + if (!cap) { + log_debug("Virtualization XEN DomU found (/proc/xen/capabilites)"); + return false; + } - return cap ? VIRTUALIZATION_NONE : VIRTUALIZATION_XEN; + log_debug("Virtualization XEN Dom0 ignored (/proc/xen/capabilities)"); + return true; } static int detect_vm_hypervisor(void) { @@ -220,6 +258,8 @@ static int detect_vm_hypervisor(void) { if (r < 0) return r; + log_debug("Virtualization %s found in /sys/hypervisor/type", hvtype); + if (streq(hvtype, "xen")) return VIRTUALIZATION_XEN; else @@ -234,9 +274,13 @@ static int detect_vm_uml(void) { r = read_full_file("/proc/cpuinfo", &cpuinfo_contents, NULL); if (r < 0) return r; - if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) + + if (strstr(cpuinfo_contents, "\nvendor_id\t: User Mode Linux\n")) { + log_debug("UML virtualization found in /proc/cpuinfo"); return VIRTUALIZATION_UML; + } + log_debug("No virtualization found in /proc/cpuinfo."); return VIRTUALIZATION_NONE; } @@ -252,11 +296,13 @@ static int detect_vm_zvm(void) { if (r < 0) return r; + log_debug("Virtualization %s found in /proc/sysinfo", t); if (streq(t, "z/VM")) return VIRTUALIZATION_ZVM; else return VIRTUALIZATION_KVM; #else + log_debug("This platform does not support /proc/sysinfo"); return VIRTUALIZATION_NONE; #endif } @@ -324,7 +370,14 @@ int detect_vm(void) { return r; finish: + /* x86 xen Dom0 is detected as XEN in hypervisor and maybe others. + * In order to detect the Dom0 as not virtualization we need to + * double-check it */ + if (r == VIRTUALIZATION_XEN && detect_vm_xen_dom0()) + r = VIRTUALIZATION_NONE; + cached_found = r; + log_debug("Found VM virtualization %s", virtualization_to_string(r)); return r; } @@ -412,6 +465,7 @@ int detect_container(void) { r = VIRTUALIZATION_CONTAINER_OTHER; finish: + log_debug("Found container virtualization %s", virtualization_to_string(r)); cached_found = r; return r; } @@ -420,10 +474,10 @@ int detect_virtualization(void) { int r; r = detect_container(); - if (r != 0) - return r; + if (r == 0) + r = detect_vm(); - return detect_vm(); + return r; } int running_in_chroot(void) { diff --git a/src/basic/xattr-util.c b/src/basic/xattr-util.c index 8d7f14f382..8256899eda 100644 --- a/src/basic/xattr-util.c +++ b/src/basic/xattr-util.c @@ -110,7 +110,7 @@ ssize_t fgetxattrat_fake(int dirfd, const char *filename, const char *attribute, /* The kernel doesn't have a fgetxattrat() command, hence let's emulate one */ - fd = openat(dirfd, filename, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0)); + fd = openat(dirfd, filename, O_CLOEXEC|O_PATH|(flags & AT_SYMLINK_NOFOLLOW ? O_NOFOLLOW : 0)); if (fd < 0) return -errno; diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index e9baf69f6a..d0af41498f 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -266,9 +266,9 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char if (r < 0) return r; if (r > 0) - printf(" File: %s/%s/%s (%s)\n", draw_special_char(DRAW_TREE_RIGHT), path, de->d_name, v); + printf(" File: %s/%s/%s (%s)\n", special_glyph(TREE_RIGHT), path, de->d_name, v); else - printf(" File: %s/%s/%s\n", draw_special_char(DRAW_TREE_RIGHT), path, de->d_name); + printf(" File: %s/%s/%s\n", special_glyph(TREE_RIGHT), path, de->d_name); c++; } @@ -320,7 +320,7 @@ static int print_efi_option(uint16_t id, bool in_order) { printf(" ID: 0x%04X\n", id); printf(" Status: %sactive%s\n", active ? "" : "in", in_order ? ", boot-order" : ""); printf(" Partition: /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)); - printf(" File: %s%s\n", draw_special_char(DRAW_TREE_RIGHT), path); + printf(" File: %s%s\n", special_glyph(TREE_RIGHT), path); printf("\n"); return 0; @@ -1077,7 +1077,7 @@ static int bootctl_main(int argc, char*argv[]) { SD_ID128_FORMAT_VAL(loader_part_uuid)); else printf(" Partition: n/a\n"); - printf(" File: %s%s\n", draw_special_char(DRAW_TREE_RIGHT), strna(loader_path)); + printf(" File: %s%s\n", special_glyph(TREE_RIGHT), strna(loader_path)); printf("\n"); } else printf("System:\n Not booted with EFI\n"); diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 893980071f..30c1ead1aa 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -22,6 +22,7 @@ #include "linux.h" #include "pefile.h" #include "util.h" +#include "measure.h" #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL @@ -1644,6 +1645,18 @@ static EFI_STATUS image_start(EFI_HANDLE parent_image, const Config *config, con } loaded_image->LoadOptions = options; loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16); + +#ifdef SD_BOOT_LOG_TPM + /* Try to log any options to the TPM, escpecially to catch manually edited options */ + err = tpm_log_event(SD_TPM_PCR, + (EFI_PHYSICAL_ADDRESS) loaded_image->LoadOptions, + loaded_image->LoadOptionsSize, loaded_image->LoadOptions); + if (EFI_ERROR(err)) { + Print(L"Unable to add image options measurement: %r", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + return err; + } +#endif } efivar_set_time_usec(L"LoaderTimeExecUSec", 0); diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c new file mode 100644 index 0000000000..7c016387c1 --- /dev/null +++ b/src/boot/efi/measure.c @@ -0,0 +1,312 @@ +/* + * 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. + * + */ + +#ifdef SD_BOOT_LOG_TPM + +#include <efi.h> +#include <efilib.h> +#include "measure.h" + +#define EFI_TCG_PROTOCOL_GUID { 0xf541796d, 0xa62e, 0x4954, {0xa7, 0x75, 0x95, 0x84, 0xf6, 0x1b, 0x9c, 0xdd} } + +typedef struct _TCG_VERSION { + UINT8 Major; + UINT8 Minor; + UINT8 RevMajor; + UINT8 RevMinor; +} TCG_VERSION; + +typedef struct _TCG_BOOT_SERVICE_CAPABILITY { + UINT8 Size; + struct _TCG_VERSION StructureVersion; + struct _TCG_VERSION ProtocolSpecVersion; + UINT8 HashAlgorithmBitmap; + BOOLEAN TPMPresentFlag; + BOOLEAN TPMDeactivatedFlag; +} TCG_BOOT_SERVICE_CAPABILITY; + +typedef UINT32 TCG_ALGORITHM_ID; +#define TCG_ALG_SHA 0x00000004 // The SHA1 algorithm + +#define SHA1_DIGEST_SIZE 20 + +typedef struct _TCG_DIGEST { + UINT8 Digest[SHA1_DIGEST_SIZE]; +} TCG_DIGEST; + +#define EV_IPL 13 + +typedef struct _TCG_PCR_EVENT { + UINT32 PCRIndex; + UINT32 EventType; + struct _TCG_DIGEST digest; + UINT32 EventSize; + UINT8 Event[1]; +} TCG_PCR_EVENT; + +INTERFACE_DECL(_EFI_TCG); + +typedef EFI_STATUS(EFIAPI * EFI_TCG_STATUS_CHECK) (IN struct _EFI_TCG * This, + OUT struct _TCG_BOOT_SERVICE_CAPABILITY * ProtocolCapability, + OUT UINT32 * TCGFeatureFlags, + OUT EFI_PHYSICAL_ADDRESS * EventLogLocation, + OUT EFI_PHYSICAL_ADDRESS * EventLogLastEntry); + +typedef EFI_STATUS(EFIAPI * EFI_TCG_HASH_ALL) (IN struct _EFI_TCG * This, + IN UINT8 * HashData, + IN UINT64 HashDataLen, + IN TCG_ALGORITHM_ID AlgorithmId, + IN OUT UINT64 * HashedDataLen, IN OUT UINT8 ** HashedDataResult); + +typedef EFI_STATUS(EFIAPI * EFI_TCG_LOG_EVENT) (IN struct _EFI_TCG * This, + IN struct _TCG_PCR_EVENT * TCGLogData, + IN OUT UINT32 * EventNumber, IN UINT32 Flags); + +typedef EFI_STATUS(EFIAPI * EFI_TCG_PASS_THROUGH_TO_TPM) (IN struct _EFI_TCG * This, + IN UINT32 TpmInputParameterBlockSize, + IN UINT8 * TpmInputParameterBlock, + IN UINT32 TpmOutputParameterBlockSize, + IN UINT8 * TpmOutputParameterBlock); + +typedef EFI_STATUS(EFIAPI * EFI_TCG_HASH_LOG_EXTEND_EVENT) (IN struct _EFI_TCG * This, + IN EFI_PHYSICAL_ADDRESS HashData, + IN UINT64 HashDataLen, + IN TCG_ALGORITHM_ID AlgorithmId, + IN struct _TCG_PCR_EVENT * TCGLogData, + IN OUT UINT32 * EventNumber, + OUT EFI_PHYSICAL_ADDRESS * EventLogLastEntry); + +typedef struct _EFI_TCG { + EFI_TCG_STATUS_CHECK StatusCheck; + EFI_TCG_HASH_ALL HashAll; + EFI_TCG_LOG_EVENT LogEvent; + EFI_TCG_PASS_THROUGH_TO_TPM PassThroughToTPM; + EFI_TCG_HASH_LOG_EXTEND_EVENT HashLogExtendEvent; +} EFI_TCG; + +#define EFI_TCG2_PROTOCOL_GUID {0x607f766c, 0x7455, 0x42be, { 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f }} + +typedef struct tdEFI_TCG2_PROTOCOL EFI_TCG2_PROTOCOL; + +typedef struct tdEFI_TCG2_VERSION { + UINT8 Major; + UINT8 Minor; +} EFI_TCG2_VERSION; + +typedef UINT32 EFI_TCG2_EVENT_LOG_BITMAP; +typedef UINT32 EFI_TCG2_EVENT_LOG_FORMAT; +typedef UINT32 EFI_TCG2_EVENT_ALGORITHM_BITMAP; + +#define EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2 0x00000001 +#define EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 0x00000002 + +typedef struct tdEFI_TCG2_BOOT_SERVICE_CAPABILITY { + UINT8 Size; + EFI_TCG2_VERSION StructureVersion; + EFI_TCG2_VERSION ProtocolVersion; + EFI_TCG2_EVENT_ALGORITHM_BITMAP HashAlgorithmBitmap; + EFI_TCG2_EVENT_LOG_BITMAP SupportedEventLogs; + BOOLEAN TPMPresentFlag; + UINT16 MaxCommandSize; + UINT16 MaxResponseSize; + UINT32 ManufacturerID; + UINT32 NumberOfPCRBanks; + EFI_TCG2_EVENT_ALGORITHM_BITMAP ActivePcrBanks; +} EFI_TCG2_BOOT_SERVICE_CAPABILITY; + +#define EFI_TCG2_EVENT_HEADER_VERSION 1 + +typedef struct { + UINT32 HeaderSize; + UINT16 HeaderVersion; + UINT32 PCRIndex; + UINT32 EventType; +} EFI_TCG2_EVENT_HEADER; + +typedef struct tdEFI_TCG2_EVENT { + UINT32 Size; + EFI_TCG2_EVENT_HEADER Header; + UINT8 Event[1]; +} EFI_TCG2_EVENT; + +typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_CAPABILITY) (IN EFI_TCG2_PROTOCOL * This, + IN OUT EFI_TCG2_BOOT_SERVICE_CAPABILITY * ProtocolCapability); + +typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_EVENT_LOG) (IN EFI_TCG2_PROTOCOL * This, + IN EFI_TCG2_EVENT_LOG_FORMAT EventLogFormat, + OUT EFI_PHYSICAL_ADDRESS * EventLogLocation, + OUT EFI_PHYSICAL_ADDRESS * EventLogLastEntry, + OUT BOOLEAN * EventLogTruncated); + +typedef EFI_STATUS(EFIAPI * EFI_TCG2_HASH_LOG_EXTEND_EVENT) (IN EFI_TCG2_PROTOCOL * This, + IN UINT64 Flags, + IN EFI_PHYSICAL_ADDRESS DataToHash, + IN UINT64 DataToHashLen, IN EFI_TCG2_EVENT * EfiTcgEvent); + +typedef EFI_STATUS(EFIAPI * EFI_TCG2_SUBMIT_COMMAND) (IN EFI_TCG2_PROTOCOL * This, + IN UINT32 InputParameterBlockSize, + IN UINT8 * InputParameterBlock, + IN UINT32 OutputParameterBlockSize, IN UINT8 * OutputParameterBlock); + +typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_ACTIVE_PCR_BANKS) (IN EFI_TCG2_PROTOCOL * This, OUT UINT32 * ActivePcrBanks); + +typedef EFI_STATUS(EFIAPI * EFI_TCG2_SET_ACTIVE_PCR_BANKS) (IN EFI_TCG2_PROTOCOL * This, IN UINT32 ActivePcrBanks); + +typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_RESULT_OF_SET_ACTIVE_PCR_BANKS) (IN EFI_TCG2_PROTOCOL * This, + OUT UINT32 * OperationPresent, OUT UINT32 * Response); + +typedef struct tdEFI_TCG2_PROTOCOL { + EFI_TCG2_GET_CAPABILITY GetCapability; + EFI_TCG2_GET_EVENT_LOG GetEventLog; + EFI_TCG2_HASH_LOG_EXTEND_EVENT HashLogExtendEvent; + EFI_TCG2_SUBMIT_COMMAND SubmitCommand; + EFI_TCG2_GET_ACTIVE_PCR_BANKS GetActivePcrBanks; + EFI_TCG2_SET_ACTIVE_PCR_BANKS SetActivePcrBanks; + EFI_TCG2_GET_RESULT_OF_SET_ACTIVE_PCR_BANKS GetResultOfSetActivePcrBanks; +} EFI_TCG2; + + +static EFI_STATUS tpm1_measure_to_pcr_and_event_log(const EFI_TCG *tcg, UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, + UINTN buffer_size, const CHAR16 *description) { + EFI_STATUS status; + TCG_PCR_EVENT *tcg_event; + UINT32 event_number; + EFI_PHYSICAL_ADDRESS event_log_last; + UINTN desc_len; + + desc_len = (StrLen(description) + 1) * sizeof(CHAR16); + + tcg_event = AllocateZeroPool(desc_len + sizeof(TCG_PCR_EVENT)); + + if (tcg_event == NULL) + return EFI_OUT_OF_RESOURCES; + + tcg_event->EventSize = desc_len; + CopyMem((VOID *) & tcg_event->Event[0], (VOID *) description, desc_len); + + tcg_event->PCRIndex = pcrindex; + tcg_event->EventType = EV_IPL; + + event_number = 1; + status = uefi_call_wrapper(tcg->HashLogExtendEvent, 7, + tcg, buffer, buffer_size, TCG_ALG_SHA, tcg_event, &event_number, &event_log_last); + + if (EFI_ERROR(status)) + return status; + + uefi_call_wrapper(BS->FreePool, 1, tcg_event); + + return EFI_SUCCESS; +} + + +static EFI_STATUS tpm2_measure_to_pcr_and_event_log(const EFI_TCG2 *tcg, UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, + UINT64 buffer_size, const CHAR16 *description) { + EFI_STATUS status; + EFI_TCG2_EVENT *tcg_event; + UINTN desc_len; + + desc_len = StrLen(description) * sizeof(CHAR16); + + tcg_event = AllocateZeroPool(sizeof(*tcg_event) - sizeof(tcg_event->Event) + desc_len + 1); + + if (tcg_event == NULL) + return EFI_OUT_OF_RESOURCES; + + tcg_event->Size = sizeof(EFI_TCG2_EVENT) - sizeof(tcg_event->Event) + desc_len + 1; + tcg_event->Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER); + tcg_event->Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION; + tcg_event->Header.PCRIndex = pcrindex; + tcg_event->Header.EventType = EV_IPL; + + CopyMem((VOID *) tcg_event->Event, (VOID *) description, desc_len); + + status = uefi_call_wrapper(tcg->HashLogExtendEvent, 5, tcg, 0, buffer, buffer_size, tcg_event); + + uefi_call_wrapper(BS->FreePool, 1, tcg_event); + + if (EFI_ERROR(status)) + return status; + + return EFI_SUCCESS; +} + +static EFI_TCG * tcg1_interface_check(void) { + EFI_GUID tpm_guid = EFI_TCG_PROTOCOL_GUID; + EFI_STATUS status; + EFI_TCG *tcg; + TCG_BOOT_SERVICE_CAPABILITY capability; + UINT32 features; + EFI_PHYSICAL_ADDRESS event_log_location; + EFI_PHYSICAL_ADDRESS event_log_last_entry; + + status = LibLocateProtocol(&tpm_guid, (void **) &tcg); + + if (EFI_ERROR(status)) + return NULL; + + capability.Size = (UINT8) sizeof(capability); + status = uefi_call_wrapper(tcg->StatusCheck, 5, tcg, &capability, &features, &event_log_location, &event_log_last_entry); + + if (EFI_ERROR(status)) + return NULL; + + if (capability.TPMDeactivatedFlag) + return NULL; + + if (!capability.TPMPresentFlag) + return NULL; + + return tcg; +} + +static EFI_TCG2 * tcg2_interface_check(void) { + EFI_GUID tpm2_guid = EFI_TCG2_PROTOCOL_GUID; + EFI_STATUS status; + EFI_TCG2 *tcg; + EFI_TCG2_BOOT_SERVICE_CAPABILITY capability; + + status = LibLocateProtocol(&tpm2_guid, (void **) &tcg); + + if (EFI_ERROR(status)) + return NULL; + + capability.Size = (UINT8) sizeof(capability); + status = uefi_call_wrapper(tcg->GetCapability, 2, tcg, &capability); + + if (EFI_ERROR(status)) + return NULL; + + if (!capability.TPMPresentFlag) + return NULL; + + return tcg; +} + +EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description) { + EFI_TCG *tpm1; + EFI_TCG2 *tpm2; + + tpm2 = tcg2_interface_check(); + if (tpm2) + return tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description); + + tpm1 = tcg1_interface_check(); + if (tpm1) + return tpm1_measure_to_pcr_and_event_log(tpm1, pcrindex, buffer, buffer_size, description); + + /* No active TPM found, so don't return an error */ + return EFI_SUCCESS; +} + +#endif diff --git a/src/boot/efi/measure.h b/src/boot/efi/measure.h new file mode 100644 index 0000000000..a2cfe817d0 --- /dev/null +++ b/src/boot/efi/measure.h @@ -0,0 +1,21 @@ +/* + * 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. + * + */ +#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/splash.c b/src/boot/efi/splash.c index b1cc2c0b72..c0ef7f64fe 100644 --- a/src/boot/efi/splash.c +++ b/src/boot/efi/splash.c @@ -281,9 +281,9 @@ EFI_STATUS graphics_splash(UINT8 *content, UINTN len, const EFI_GRAPHICS_OUTPUT_ if (EFI_ERROR(err)) goto err; - if(dib->x < GraphicsOutput->Mode->Info->HorizontalResolution) + if (dib->x < GraphicsOutput->Mode->Info->HorizontalResolution) x_pos = (GraphicsOutput->Mode->Info->HorizontalResolution - dib->x) / 2; - if(dib->y < GraphicsOutput->Mode->Info->VerticalResolution) + if (dib->y < GraphicsOutput->Mode->Info->VerticalResolution) y_pos = (GraphicsOutput->Mode->Info->VerticalResolution - dib->y) / 2; uefi_call_wrapper(GraphicsOutput->Blt, 10, GraphicsOutput, diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 9633bc1792..1e250f34f4 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -20,6 +20,7 @@ #include "pefile.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 " ####"; @@ -97,6 +98,18 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { for (i = 0; i < cmdline_len; i++) line[i] = options[i]; cmdline = line; + +#ifdef SD_BOOT_LOG_TPM + /* Try to log any options to the TPM, escpecially manually edited options */ + err = tpm_log_event(SD_TPM_PCR, + (EFI_PHYSICAL_ADDRESS) loaded_image->LoadOptions, + loaded_image->LoadOptionsSize, loaded_image->LoadOptions); + if (EFI_ERROR(err)) { + Print(L"Unable to add image options measurement: %r", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + return err; + } +#endif } /* export the device path this image is started from */ diff --git a/src/bootchart/Makefile b/src/bootchart/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/bootchart/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile
\ No newline at end of file diff --git a/src/bootchart/bootchart.c b/src/bootchart/bootchart.c deleted file mode 100644 index 77d158f5f9..0000000000 --- a/src/bootchart/bootchart.c +++ /dev/null @@ -1,531 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2009-2013 Intel Corporation - - Authors: - Auke Kok <auke-jan.h.kok@intel.com> - - 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/>. - ***/ - -/*** - - Many thanks to those who contributed ideas and code: - - Ziga Mahkovec - Original bootchart author - - Anders Norgaard - PyBootchartgui - - Michael Meeks - bootchart2 - - Scott James Remnant - Ubuntu C-based logger - - Arjan van der Ven - for the idea to merge bootgraph.pl functionality - - ***/ - -#include <errno.h> -#include <fcntl.h> -#include <getopt.h> -#include <limits.h> -#include <signal.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/resource.h> -#include <time.h> -#include <unistd.h> - -#include "sd-journal.h" - -#include "alloc-util.h" -#include "bootchart.h" -#include "conf-parser.h" -#include "def.h" -#include "fd-util.h" -#include "fileio.h" -#include "io-util.h" -#include "list.h" -#include "macro.h" -#include "parse-util.h" -#include "path-util.h" -#include "store.h" -#include "string-util.h" -#include "strxcpyx.h" -#include "svg.h" -#include "util.h" - -static int exiting = 0; - -#define DEFAULT_SAMPLES_LEN 500 -#define DEFAULT_HZ 25.0 -#define DEFAULT_SCALE_X 100.0 /* 100px = 1sec */ -#define DEFAULT_SCALE_Y 20.0 /* 16px = 1 process bar */ -#define DEFAULT_INIT ROOTLIBEXECDIR "/systemd" -#define DEFAULT_OUTPUT "/run/log" - -/* graph defaults */ -bool arg_entropy = false; -bool arg_initcall = true; -bool arg_relative = false; -bool arg_filter = true; -bool arg_show_cmdline = false; -bool arg_show_cgroup = false; -bool arg_pss = false; -bool arg_percpu = false; -int arg_samples_len = DEFAULT_SAMPLES_LEN; /* we record len+1 (1 start sample) */ -double arg_hz = DEFAULT_HZ; -double arg_scale_x = DEFAULT_SCALE_X; -double arg_scale_y = DEFAULT_SCALE_Y; - -char arg_init_path[PATH_MAX] = DEFAULT_INIT; -char arg_output_path[PATH_MAX] = DEFAULT_OUTPUT; - -static void signal_handler(int sig) { - exiting = 1; -} - -#define BOOTCHART_MAX (16*1024*1024) - -static void parse_conf(void) { - char *init = NULL, *output = NULL; - const ConfigTableItem items[] = { - { "Bootchart", "Samples", config_parse_int, 0, &arg_samples_len }, - { "Bootchart", "Frequency", config_parse_double, 0, &arg_hz }, - { "Bootchart", "Relative", config_parse_bool, 0, &arg_relative }, - { "Bootchart", "Filter", config_parse_bool, 0, &arg_filter }, - { "Bootchart", "Output", config_parse_path, 0, &output }, - { "Bootchart", "Init", config_parse_path, 0, &init }, - { "Bootchart", "PlotMemoryUsage", config_parse_bool, 0, &arg_pss }, - { "Bootchart", "PlotEntropyGraph", config_parse_bool, 0, &arg_entropy }, - { "Bootchart", "ScaleX", config_parse_double, 0, &arg_scale_x }, - { "Bootchart", "ScaleY", config_parse_double, 0, &arg_scale_y }, - { "Bootchart", "ControlGroup", config_parse_bool, 0, &arg_show_cgroup }, - { "Bootchart", "PerCPU", config_parse_bool, 0, &arg_percpu }, - { NULL, NULL, NULL, 0, NULL } - }; - - config_parse_many(PKGSYSCONFDIR "/bootchart.conf", - CONF_PATHS_NULSTR("systemd/bootchart.conf.d"), - NULL, config_item_table_lookup, items, true, NULL); - - if (init != NULL) - strscpy(arg_init_path, sizeof(arg_init_path), init); - if (output != NULL) - strscpy(arg_output_path, sizeof(arg_output_path), output); -} - -static void help(void) { - printf("Usage: %s [OPTIONS]\n\n" - "Options:\n" - " -r --rel Record time relative to recording\n" - " -f --freq=FREQ Sample frequency [%g]\n" - " -n --samples=N Stop sampling at [%d] samples\n" - " -x --scale-x=N Scale the graph horizontally [%g] \n" - " -y --scale-y=N Scale the graph vertically [%g] \n" - " -p --pss Enable PSS graph (CPU intensive)\n" - " -e --entropy Enable the entropy_avail graph\n" - " -o --output=PATH Path to output files [%s]\n" - " -i --init=PATH Path to init executable [%s]\n" - " -F --no-filter Disable filtering of unimportant or ephemeral processes\n" - " -C --cmdline Display full command lines with arguments\n" - " -c --control-group Display process control group\n" - " --per-cpu Draw each CPU utilization and wait bar also\n" - " -h --help Display this message\n\n" - "See bootchart.conf for more information.\n", - program_invocation_short_name, - DEFAULT_HZ, - DEFAULT_SAMPLES_LEN, - DEFAULT_SCALE_X, - DEFAULT_SCALE_Y, - DEFAULT_OUTPUT, - DEFAULT_INIT); -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_PERCPU = 0x100, - }; - - static const struct option options[] = { - {"rel", no_argument, NULL, 'r' }, - {"freq", required_argument, NULL, 'f' }, - {"samples", required_argument, NULL, 'n' }, - {"pss", no_argument, NULL, 'p' }, - {"output", required_argument, NULL, 'o' }, - {"init", required_argument, NULL, 'i' }, - {"no-filter", no_argument, NULL, 'F' }, - {"cmdline", no_argument, NULL, 'C' }, - {"control-group", no_argument, NULL, 'c' }, - {"help", no_argument, NULL, 'h' }, - {"scale-x", required_argument, NULL, 'x' }, - {"scale-y", required_argument, NULL, 'y' }, - {"entropy", no_argument, NULL, 'e' }, - {"per-cpu", no_argument, NULL, ARG_PERCPU}, - {} - }; - int c, r; - - if (getpid() == 1) - opterr = 0; - - while ((c = getopt_long(argc, argv, "erpf:n:o:i:FCchx:y:", options, NULL)) >= 0) - switch (c) { - - case 'r': - arg_relative = true; - break; - case 'f': - r = safe_atod(optarg, &arg_hz); - if (r < 0) - log_warning_errno(r, "failed to parse --freq/-f argument '%s': %m", - optarg); - break; - case 'F': - arg_filter = false; - break; - case 'C': - arg_show_cmdline = true; - break; - case 'c': - arg_show_cgroup = true; - break; - case 'n': - r = safe_atoi(optarg, &arg_samples_len); - if (r < 0) - log_warning_errno(r, "failed to parse --samples/-n argument '%s': %m", - optarg); - break; - case 'o': - path_kill_slashes(optarg); - strscpy(arg_output_path, sizeof(arg_output_path), optarg); - break; - case 'i': - path_kill_slashes(optarg); - strscpy(arg_init_path, sizeof(arg_init_path), optarg); - break; - case 'p': - arg_pss = true; - break; - case 'x': - r = safe_atod(optarg, &arg_scale_x); - if (r < 0) - log_warning_errno(r, "failed to parse --scale-x/-x argument '%s': %m", - optarg); - break; - case 'y': - r = safe_atod(optarg, &arg_scale_y); - if (r < 0) - log_warning_errno(r, "failed to parse --scale-y/-y argument '%s': %m", - optarg); - break; - case 'e': - arg_entropy = true; - break; - case ARG_PERCPU: - arg_percpu = true; - break; - case 'h': - help(); - return 0; - case '?': - if (getpid() != 1) - return -EINVAL; - else - return 0; - default: - assert_not_reached("Unhandled option code."); - } - - if (arg_hz <= 0) { - log_error("Frequency needs to be > 0"); - return -EINVAL; - } - - return 1; -} - -static int do_journal_append(char *file) { - _cleanup_free_ char *bootchart_message = NULL; - _cleanup_free_ char *bootchart_file = NULL; - _cleanup_free_ char *p = NULL; - _cleanup_close_ int fd = -1; - struct iovec iovec[5]; - int r, j = 0; - ssize_t n; - - bootchart_file = strappend("BOOTCHART_FILE=", file); - if (!bootchart_file) - return log_oom(); - - IOVEC_SET_STRING(iovec[j++], bootchart_file); - IOVEC_SET_STRING(iovec[j++], "MESSAGE_ID=9f26aa562cf440c2b16c773d0479b518"); - IOVEC_SET_STRING(iovec[j++], "PRIORITY=7"); - bootchart_message = strjoin("MESSAGE=Bootchart created: ", file, NULL); - if (!bootchart_message) - return log_oom(); - - IOVEC_SET_STRING(iovec[j++], bootchart_message); - - p = malloc(10 + BOOTCHART_MAX); - if (!p) - return log_oom(); - - memcpy(p, "BOOTCHART=", 10); - - fd = open(file, O_RDONLY|O_CLOEXEC); - if (fd < 0) - return log_error_errno(errno, "Failed to open bootchart data \"%s\": %m", file); - - n = loop_read(fd, p + 10, BOOTCHART_MAX, false); - if (n < 0) - return log_error_errno(n, "Failed to read bootchart data: %m"); - - iovec[j].iov_base = p; - iovec[j].iov_len = 10 + n; - j++; - - r = sd_journal_sendv(iovec, j); - if (r < 0) - log_error_errno(r, "Failed to send bootchart: %m"); - - return 0; -} - -int main(int argc, char *argv[]) { - static struct list_sample_data *sampledata; - _cleanup_closedir_ DIR *proc = NULL; - _cleanup_free_ char *build = NULL; - _cleanup_fclose_ FILE *of = NULL; - _cleanup_close_ int sysfd = -1; - struct ps_struct *ps_first; - double graph_start; - double log_start; - double interval; - char output_file[PATH_MAX]; - char datestr[200]; - int pscount = 0; - int n_cpus = 0; - int overrun = 0; - time_t t = 0; - int r, samples; - struct ps_struct *ps; - struct rlimit rlim; - struct list_sample_data *head; - struct sigaction sig = { - .sa_handler = signal_handler, - }; - - parse_conf(); - - r = parse_argv(argc, argv); - if (r < 0) - return EXIT_FAILURE; - - if (r == 0) - return EXIT_SUCCESS; - - /* - * If the kernel executed us through init=/usr/lib/systemd/systemd-bootchart, then - * fork: - * - parent execs executable specified via init_path[] (/usr/lib/systemd/systemd by default) as pid=1 - * - child logs data - */ - if (getpid() == 1) { - if (fork()) - /* parent */ - execl(arg_init_path, arg_init_path, NULL); - } - argv[0][0] = '@'; - - rlim.rlim_cur = 4096; - rlim.rlim_max = 4096; - (void) setrlimit(RLIMIT_NOFILE, &rlim); - - /* start with empty ps LL */ - ps_first = new0(struct ps_struct, 1); - if (!ps_first) { - log_oom(); - return EXIT_FAILURE; - } - - /* handle TERM/INT nicely */ - sigaction(SIGHUP, &sig, NULL); - - interval = (1.0 / arg_hz) * 1000000000.0; - - if (arg_relative) - graph_start = log_start = gettime_ns(); - else { - struct timespec n; - double uptime; - - clock_gettime(clock_boottime_or_monotonic(), &n); - uptime = (n.tv_sec + (n.tv_nsec / (double) NSEC_PER_SEC)); - - log_start = gettime_ns(); - graph_start = log_start - uptime; - } - - if (graph_start < 0.0) { - log_error("Failed to setup graph start time.\n\n" - "The system uptime probably includes time that the system was suspended. " - "Use --rel to bypass this issue."); - return EXIT_FAILURE; - } - - LIST_HEAD_INIT(head); - - /* main program loop */ - for (samples = 0; !exiting && samples < arg_samples_len; samples++) { - int res; - double sample_stop; - double elapsed; - double timeleft; - - sampledata = new0(struct list_sample_data, 1); - if (sampledata == NULL) { - log_oom(); - return EXIT_FAILURE; - } - - sampledata->sampletime = gettime_ns(); - sampledata->counter = samples; - - if (sysfd < 0) - sysfd = open("/sys", O_RDONLY|O_CLOEXEC); - - if (!build) { - if (parse_env_file("/etc/os-release", NEWLINE, "PRETTY_NAME", &build, NULL) == -ENOENT) - parse_env_file("/usr/lib/os-release", NEWLINE, "PRETTY_NAME", &build, NULL); - } - - if (proc) - rewinddir(proc); - else - proc = opendir("/proc"); - - /* wait for /proc to become available, discarding samples */ - if (proc) { - r = log_sample(proc, samples, ps_first, &sampledata, &pscount, &n_cpus); - if (r < 0) - return EXIT_FAILURE; - } - - sample_stop = gettime_ns(); - - elapsed = (sample_stop - sampledata->sampletime) * 1000000000.0; - timeleft = interval - elapsed; - - /* - * check if we have not consumed our entire timeslice. If we - * do, don't sleep and take a new sample right away. - * we'll lose all the missed samples and overrun our total - * time - */ - if (timeleft > 0) { - struct timespec req; - - req.tv_sec = (time_t)(timeleft / 1000000000.0); - req.tv_nsec = (long)(timeleft - (req.tv_sec * 1000000000.0)); - - res = nanosleep(&req, NULL); - if (res) { - if (errno == EINTR) - /* caught signal, probably HUP! */ - break; - log_error_errno(errno, "nanosleep() failed: %m"); - return EXIT_FAILURE; - } - } else { - overrun++; - /* calculate how many samples we lost and scrap them */ - arg_samples_len -= (int)(-timeleft / interval); - } - LIST_PREPEND(link, head, sampledata); - } - - /* do some cleanup, close fd's */ - ps = ps_first; - while (ps->next_ps) { - ps = ps->next_ps; - ps->schedstat = safe_close(ps->schedstat); - ps->sched = safe_close(ps->sched); - ps->smaps = safe_fclose(ps->smaps); - } - - if (!of) { - t = time(NULL); - r = strftime(datestr, sizeof(datestr), "%Y%m%d-%H%M", localtime(&t)); - assert_se(r > 0); - - snprintf(output_file, PATH_MAX, "%s/bootchart-%s.svg", arg_output_path, datestr); - of = fopen(output_file, "we"); - } - - if (!of) { - log_error("Error opening output file '%s': %m\n", output_file); - return EXIT_FAILURE; - } - - r = svg_do(of, strna(build), head, ps_first, - samples, pscount, n_cpus, graph_start, - log_start, interval, overrun); - - if (r < 0) { - log_error_errno(r, "Error generating svg file: %m"); - return EXIT_FAILURE; - } - - log_info("systemd-bootchart wrote %s\n", output_file); - - r = do_journal_append(output_file); - if (r < 0) - return EXIT_FAILURE; - - /* nitpic cleanups */ - ps = ps_first->next_ps; - while (ps->next_ps) { - struct ps_struct *old; - - old = ps; - old->sample = ps->first; - ps = ps->next_ps; - while (old->sample->next) { - struct ps_sched_struct *oldsample = old->sample; - - old->sample = old->sample->next; - free(oldsample); - } - free(old->cgroup); - free(old->sample); - free(old); - } - - free(ps->cgroup); - free(ps->sample); - free(ps); - - sampledata = head; - while (sampledata->link_prev) { - struct list_sample_data *old_sampledata = sampledata; - sampledata = sampledata->link_prev; - free(old_sampledata); - } - free(sampledata); - - /* don't complain when overrun once, happens most commonly on 1st sample */ - if (overrun > 1) - log_warning("systemd-bootchart: sample time overrun %i times\n", overrun); - - return 0; -} diff --git a/src/bootchart/bootchart.conf b/src/bootchart/bootchart.conf deleted file mode 100644 index 4f5e50936e..0000000000 --- a/src/bootchart/bootchart.conf +++ /dev/null @@ -1,26 +0,0 @@ -# This file is part of systemd. -# -# 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. -# -# Entries in this file show the compile time defaults. -# You can change settings by editing this file. -# Defaults can be restored by simply deleting this file. -# -# See bootchart.conf(5) for details. - -[Bootchart] -#Samples=500 -#Frequency=25 -#Relative=no -#Filter=yes -#Output=<folder name, defaults to /run/log> -#Init=/path/to/init-binary -#PlotMemoryUsage=no -#PlotEntropyGraph=no -#ScaleX=100 -#ScaleY=20 -#ControlGroup=no -#PerCPU=no diff --git a/src/bootchart/bootchart.h b/src/bootchart/bootchart.h deleted file mode 100644 index 1b445b954b..0000000000 --- a/src/bootchart/bootchart.h +++ /dev/null @@ -1,119 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright (C) 2009-2013 Intel Corporation - - Authors: - Auke Kok <auke-jan.h.kok@intel.com> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <stdbool.h> - -#include "list.h" - -#define MAXCPUS 16 -#define MAXPIDS 65535 - -struct block_stat_struct { - /* /proc/vmstat pgpgin & pgpgout */ - int bi; - int bo; -}; - -struct cpu_stat_sample_struct { - /* /proc/schedstat fields 10 & 11 (after name) */ - double runtime; - double waittime; -}; - -/* per process, per sample data we will log */ -struct ps_sched_struct { - /* /proc/<n>/schedstat fields 1 & 2 */ - double runtime; - double waittime; - int pss; - struct list_sample_data *sampledata; - struct ps_sched_struct *next; - struct ps_sched_struct *prev; - struct ps_sched_struct *cross; /* cross pointer */ - struct ps_struct *ps_new; -}; - -struct list_sample_data { - double runtime[MAXCPUS]; - double waittime[MAXCPUS]; - double sampletime; - int entropy_avail; - struct block_stat_struct blockstat; - LIST_FIELDS(struct list_sample_data, link); /* DLL */ - int counter; -}; - -/* process info */ -struct ps_struct { - struct ps_struct *next_ps; /* SLL pointer */ - struct ps_struct *parent; /* ppid ref */ - struct ps_struct *children; /* children */ - struct ps_struct *next; /* siblings */ - - /* must match - otherwise it's a new process with same PID */ - char name[256]; - int pid; - int ppid; - char *cgroup; - - /* cache fd's */ - int sched; - int schedstat; - FILE *smaps; - - /* pointers to first/last seen timestamps */ - struct ps_sched_struct *first; - struct ps_sched_struct *last; - - /* records actual start time, may be way before bootchart runs */ - double starttime; - - /* record human readable total cpu time */ - double total; - - /* largest PSS size found */ - int pss_max; - - /* for drawing connection lines later */ - double pos_x; - double pos_y; - - struct ps_sched_struct *sample; -}; - -extern bool arg_relative; -extern bool arg_filter; -extern bool arg_show_cmdline; -extern bool arg_show_cgroup; -extern bool arg_pss; -extern bool arg_entropy; -extern bool arg_percpu; -extern bool arg_initcall; -extern int arg_samples_len; -extern double arg_hz; -extern double arg_scale_x; -extern double arg_scale_y; - -extern char arg_output_path[PATH_MAX]; -extern char arg_init_path[PATH_MAX]; diff --git a/src/bootchart/store.c b/src/bootchart/store.c deleted file mode 100644 index 42cb8043ce..0000000000 --- a/src/bootchart/store.c +++ /dev/null @@ -1,555 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2009-2013 Intel Corporation - - Authors: - Auke Kok <auke-jan.h.kok@intel.com> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. - ***/ - -#include <dirent.h> -#include <fcntl.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "bootchart.h" -#include "cgroup-util.h" -#include "dirent-util.h" -#include "fd-util.h" -#include "fileio.h" -#include "parse-util.h" -#include "store.h" -#include "string-util.h" -#include "strxcpyx.h" -#include "time-util.h" -#include "util.h" - -/* - * Alloc a static 4k buffer for stdio - primarily used to increase - * PSS buffering from the default 1k stdin buffer to reduce - * read() overhead. - */ -static char smaps_buf[4096]; -static int skip = 0; - -double gettime_ns(void) { - struct timespec n; - - clock_gettime(CLOCK_MONOTONIC, &n); - - return (n.tv_sec + (n.tv_nsec / (double) NSEC_PER_SEC)); -} - -static char *bufgetline(char *buf) { - char *c; - - if (!buf) - return NULL; - - c = strchr(buf, '\n'); - if (c) - c++; - - return c; -} - -static int pid_cmdline_strscpy(int procfd, char *buffer, size_t buf_len, int pid) { - char filename[PATH_MAX]; - _cleanup_close_ int fd = -1; - ssize_t n; - - sprintf(filename, "%d/cmdline", pid); - fd = openat(procfd, filename, O_RDONLY|O_CLOEXEC); - if (fd < 0) - return -errno; - - n = read(fd, buffer, buf_len-1); - if (n > 0) { - int i; - for (i = 0; i < n; i++) - if (buffer[i] == '\0') - buffer[i] = ' '; - buffer[n] = '\0'; - } - - return 0; -} - -int log_sample(DIR *proc, - int sample, - struct ps_struct *ps_first, - struct list_sample_data **ptr, - int *pscount, - int *cpus) { - - static int vmstat = -1; - _cleanup_free_ char *buf_schedstat = NULL; - char buf[4096]; - char key[256]; - char val[256]; - char rt[256]; - char wt[256]; - char *m; - int r; - int c; - int p; - int mod; - static int e_fd = -1; - ssize_t s; - ssize_t n; - struct dirent *ent; - int fd; - struct list_sample_data *sampledata; - struct ps_sched_struct *ps_prev = NULL; - int procfd; - int taskfd = -1; - - sampledata = *ptr; - - procfd = dirfd(proc); - if (procfd < 0) - return -errno; - - if (vmstat < 0) { - /* block stuff */ - vmstat = openat(procfd, "vmstat", O_RDONLY|O_CLOEXEC); - if (vmstat < 0) - return log_error_errno(errno, "Failed to open /proc/vmstat: %m"); - } - - n = pread(vmstat, buf, sizeof(buf) - 1, 0); - if (n <= 0) { - vmstat = safe_close(vmstat); - if (n < 0) - return -errno; - return -ENODATA; - } - - buf[n] = '\0'; - - m = buf; - while (m) { - if (sscanf(m, "%s %s", key, val) < 2) - goto vmstat_next; - if (streq(key, "pgpgin")) - sampledata->blockstat.bi = atoi(val); - if (streq(key, "pgpgout")) { - sampledata->blockstat.bo = atoi(val); - break; - } -vmstat_next: - m = bufgetline(m); - if (!m) - break; - } - - /* Parse "/proc/schedstat" for overall CPU utilization */ - r = read_full_file("/proc/schedstat", &buf_schedstat, NULL); - if (r < 0) - return log_error_errno(r, "Unable to read schedstat: %m"); - - m = buf_schedstat; - while (m) { - if (sscanf(m, "%s %*s %*s %*s %*s %*s %*s %s %s", key, rt, wt) < 3) - goto schedstat_next; - - if (strstr(key, "cpu")) { - r = safe_atoi((const char*)(key+3), &c); - if (r < 0 || c > MAXCPUS -1) - /* Oops, we only have room for MAXCPUS data */ - break; - sampledata->runtime[c] = atoll(rt); - sampledata->waittime[c] = atoll(wt); - - if (c == *cpus) - *cpus = c + 1; - } -schedstat_next: - m = bufgetline(m); - if (!m) - break; - } - - if (arg_entropy) { - if (e_fd < 0) { - e_fd = openat(procfd, "sys/kernel/random/entropy_avail", O_RDONLY|O_CLOEXEC); - if (e_fd < 0) - return log_error_errno(errno, "Failed to open /proc/sys/kernel/random/entropy_avail: %m"); - } - - n = pread(e_fd, buf, sizeof(buf) - 1, 0); - if (n <= 0) { - e_fd = safe_close(e_fd); - } else { - buf[n] = '\0'; - sampledata->entropy_avail = atoi(buf); - } - } - - while ((ent = readdir(proc)) != NULL) { - char filename[PATH_MAX]; - int pid; - struct ps_struct *ps; - - if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9')) - continue; - - pid = atoi(ent->d_name); - - if (pid >= MAXPIDS) - continue; - - ps = ps_first; - while (ps->next_ps) { - ps = ps->next_ps; - if (ps->pid == pid) - break; - } - - /* end of our LL? then append a new record */ - if (ps->pid != pid) { - _cleanup_fclose_ FILE *st = NULL; - char t[32]; - struct ps_struct *parent; - - ps->next_ps = new0(struct ps_struct, 1); - if (!ps->next_ps) - return log_oom(); - - ps = ps->next_ps; - ps->pid = pid; - ps->sched = -1; - ps->schedstat = -1; - - ps->sample = new0(struct ps_sched_struct, 1); - if (!ps->sample) - return log_oom(); - - ps->sample->sampledata = sampledata; - - (*pscount)++; - - /* mark our first sample */ - ps->first = ps->last = ps->sample; - ps->sample->runtime = atoll(rt); - ps->sample->waittime = atoll(wt); - - /* get name, start time */ - if (ps->sched < 0) { - sprintf(filename, "%d/sched", pid); - ps->sched = openat(procfd, filename, O_RDONLY|O_CLOEXEC); - if (ps->sched < 0) - continue; - } - - s = pread(ps->sched, buf, sizeof(buf) - 1, 0); - if (s <= 0) { - ps->sched = safe_close(ps->sched); - continue; - } - buf[s] = '\0'; - - if (!sscanf(buf, "%s %*s %*s", key)) - continue; - - strscpy(ps->name, sizeof(ps->name), key); - - /* cmdline */ - if (arg_show_cmdline) - pid_cmdline_strscpy(procfd, ps->name, sizeof(ps->name), pid); - - /* discard line 2 */ - m = bufgetline(buf); - if (!m) - continue; - - m = bufgetline(m); - if (!m) - continue; - - if (!sscanf(m, "%*s %*s %s", t)) - continue; - - r = safe_atod(t, &ps->starttime); - if (r < 0) - continue; - - ps->starttime /= 1000.0; - - if (arg_show_cgroup) - /* if this fails, that's OK */ - cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, - ps->pid, &ps->cgroup); - - /* ppid */ - sprintf(filename, "%d/stat", pid); - fd = openat(procfd, filename, O_RDONLY|O_CLOEXEC); - if (fd < 0) - continue; - - st = fdopen(fd, "re"); - if (!st) { - close(fd); - continue; - } - - if (!fscanf(st, "%*s %*s %*s %i", &p)) - continue; - - ps->ppid = p; - - /* - * setup child pointers - * - * these are used to paint the tree coherently later - * each parent has a LL of children, and a LL of siblings - */ - if (pid == 1) - continue; /* nothing to do for init atm */ - - /* kthreadd has ppid=0, which breaks our tree ordering */ - if (ps->ppid == 0) - ps->ppid = 1; - - parent = ps_first; - while ((parent->next_ps && parent->pid != ps->ppid)) - parent = parent->next_ps; - - if (parent->pid != ps->ppid) { - /* orphan */ - ps->ppid = 1; - parent = ps_first->next_ps; - } - - ps->parent = parent; - - if (!parent->children) { - /* it's the first child */ - parent->children = ps; - } else { - /* walk all children and append */ - struct ps_struct *children; - children = parent->children; - while (children->next) - children = children->next; - - children->next = ps; - } - } - - /* else -> found pid, append data in ps */ - - /* below here is all continuous logging parts - we get here on every - * iteration */ - - /* rt, wt */ - if (ps->schedstat < 0) { - sprintf(filename, "%d/schedstat", pid); - ps->schedstat = openat(procfd, filename, O_RDONLY|O_CLOEXEC); - if (ps->schedstat < 0) - continue; - } - - s = pread(ps->schedstat, buf, sizeof(buf) - 1, 0); - if (s <= 0) { - /* clean up our file descriptors - assume that the process exited */ - close(ps->schedstat); - ps->schedstat = -1; - ps->sched = safe_close(ps->sched); - continue; - } - - buf[s] = '\0'; - - if (!sscanf(buf, "%s %s %*s", rt, wt)) - continue; - - ps->sample->next = new0(struct ps_sched_struct, 1); - if (!ps->sample->next) - return log_oom(); - - ps->sample->next->prev = ps->sample; - ps->sample = ps->sample->next; - ps->last = ps->sample; - ps->sample->runtime = atoll(rt); - ps->sample->waittime = atoll(wt); - ps->sample->sampledata = sampledata; - ps->sample->ps_new = ps; - if (ps_prev) - ps_prev->cross = ps->sample; - - ps_prev = ps->sample; - ps->total = (ps->last->runtime - ps->first->runtime) - / 1000000000.0; - - /* Take into account CPU runtime/waittime spent in non-main threads of the process - * by parsing "/proc/[pid]/task/[tid]/schedstat" for all [tid] != [pid] - * See https://github.com/systemd/systemd/issues/139 - */ - - /* Browse directory "/proc/[pid]/task" to know the thread ids of process [pid] */ - snprintf(filename, sizeof(filename), PID_FMT "/task", pid); - taskfd = openat(procfd, filename, O_RDONLY|O_DIRECTORY|O_CLOEXEC); - if (taskfd >= 0) { - _cleanup_closedir_ DIR *taskdir = NULL; - - taskdir = fdopendir(taskfd); - if (!taskdir) { - safe_close(taskfd); - return -errno; - } - FOREACH_DIRENT(ent, taskdir, break) { - int tid = -1; - _cleanup_close_ int tid_schedstat = -1; - long long delta_rt; - long long delta_wt; - - if ((ent->d_name[0] < '0') || (ent->d_name[0] > '9')) - continue; - - /* Skip main thread as it was already accounted */ - r = safe_atoi(ent->d_name, &tid); - if (r < 0 || tid == pid) - continue; - - /* Parse "/proc/[pid]/task/[tid]/schedstat" */ - snprintf(filename, sizeof(filename), PID_FMT "/schedstat", tid); - tid_schedstat = openat(taskfd, filename, O_RDONLY|O_CLOEXEC); - - if (tid_schedstat == -1) - continue; - - s = pread(tid_schedstat, buf, sizeof(buf) - 1, 0); - if (s <= 0) - continue; - buf[s] = '\0'; - - if (!sscanf(buf, "%s %s %*s", rt, wt)) - continue; - - r = safe_atolli(rt, &delta_rt); - if (r < 0) - continue; - r = safe_atolli(rt, &delta_wt); - if (r < 0) - continue; - ps->sample->runtime += delta_rt; - ps->sample->waittime += delta_wt; - } - } - - if (!arg_pss) - goto catch_rename; - - /* Pss */ - if (!ps->smaps) { - sprintf(filename, "%d/smaps", pid); - fd = openat(procfd, filename, O_RDONLY|O_CLOEXEC); - if (fd < 0) - continue; - ps->smaps = fdopen(fd, "re"); - if (!ps->smaps) { - close(fd); - continue; - } - setvbuf(ps->smaps, smaps_buf, _IOFBF, sizeof(smaps_buf)); - } else { - rewind(ps->smaps); - } - - /* test to see if we need to skip another field */ - if (skip == 0) { - if (fgets(buf, sizeof(buf), ps->smaps) == NULL) { - continue; - } - if (fread(buf, 1, 28 * 15, ps->smaps) != (28 * 15)) { - continue; - } - if (buf[392] == 'V') { - skip = 2; - } - else { - skip = 1; - } - rewind(ps->smaps); - } - - while (1) { - int pss_kb; - - /* skip one line, this contains the object mapped. */ - if (fgets(buf, sizeof(buf), ps->smaps) == NULL) { - break; - } - /* then there's a 28 char 14 line block */ - if (fread(buf, 1, 28 * 14, ps->smaps) != 28 * 14) { - break; - } - pss_kb = atoi(&buf[61]); - ps->sample->pss += pss_kb; - - /* skip one more line if this is a newer kernel */ - if (skip == 2) { - if (fgets(buf, sizeof(buf), ps->smaps) == NULL) - break; - } - } - - if (ps->sample->pss > ps->pss_max) - ps->pss_max = ps->sample->pss; - -catch_rename: - /* catch process rename, try to randomize time */ - mod = (arg_hz < 4.0) ? 4.0 : (arg_hz / 4.0); - if (((sample - ps->pid) + pid) % (int)(mod) == 0) { - - /* re-fetch name */ - /* get name, start time */ - if (ps->sched < 0) { - sprintf(filename, "%d/sched", pid); - ps->sched = openat(procfd, filename, O_RDONLY|O_CLOEXEC); - if (ps->sched < 0) - continue; - } - - s = pread(ps->sched, buf, sizeof(buf) - 1, 0); - if (s <= 0) { - /* clean up file descriptors */ - ps->sched = safe_close(ps->sched); - ps->schedstat = safe_close(ps->schedstat); - continue; - } - - buf[s] = '\0'; - - if (!sscanf(buf, "%s %*s %*s", key)) - continue; - - strscpy(ps->name, sizeof(ps->name), key); - - /* cmdline */ - if (arg_show_cmdline) - pid_cmdline_strscpy(procfd, ps->name, sizeof(ps->name), pid); - } - } - - return 0; -} diff --git a/src/bootchart/svg.c b/src/bootchart/svg.c deleted file mode 100644 index f2af535061..0000000000 --- a/src/bootchart/svg.c +++ /dev/null @@ -1,1375 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2009-2013 Intel Corporation - - Authors: - Auke Kok <auke-jan.h.kok@intel.com> - - 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 <fcntl.h> -#include <limits.h> -#include <stdio.h> -#include <string.h> -#include <sys/utsname.h> -#include <time.h> -#include <unistd.h> - -#include "alloc-util.h" -#include "architecture.h" -#include "bootchart.h" -#include "fd-util.h" -#include "fileio.h" -#include "list.h" -#include "macro.h" -#include "stdio-util.h" -#include "store.h" -#include "svg.h" -#include "utf8.h" -#include "util.h" - -#define time_to_graph(t) ((t) * arg_scale_x) -#define ps_to_graph(n) ((n) * arg_scale_y) -#define kb_to_graph(m) ((m) * arg_scale_y * 0.0001) -#define to_color(n) (192.0 - ((n) * 192.0)) - -static const char * const colorwheel[12] = { - "rgb(255,32,32)", // red - "rgb(32,192,192)", // cyan - "rgb(255,128,32)", // orange - "rgb(128,32,192)", // blue-violet - "rgb(255,255,32)", // yellow - "rgb(192,32,128)", // red-violet - "rgb(32,255,32)", // green - "rgb(255,64,32)", // red-orange - "rgb(32,32,255)", // blue - "rgb(255,192,32)", // yellow-orange - "rgb(192,32,192)", // violet - "rgb(32,192,32)" // yellow-green -}; - -static double idletime = -1.0; -static int pfiltered = 0; -static int pcount = 0; -static int kcount = 0; -static double psize = 0; -static double ksize = 0; -static double esize = 0; -static struct list_sample_data *sampledata; -static struct list_sample_data *prev_sampledata; - -static void svg_header(FILE *of, struct list_sample_data *head, double graph_start, int n_cpus) { - double w; - double h; - struct list_sample_data *sampledata_last; - - assert(head); - - sampledata_last = head; - LIST_FOREACH_BEFORE(link, sampledata, head) { - sampledata_last = sampledata; - } - - /* min width is about 1600px due to the label */ - w = 150.0 + 10.0 + time_to_graph(sampledata_last->sampletime - graph_start); - w = ((w < 1600.0) ? 1600.0 : w); - - /* height is variable based on pss, psize, ksize */ - h = 400.0 + (arg_scale_y * 30.0) /* base graphs and title */ - + (arg_pss ? (100.0 * arg_scale_y) + (arg_scale_y * 7.0) : 0.0) /* pss estimate */ - + psize + ksize + esize + (n_cpus * 15 * arg_scale_y); - - fprintf(of, "<?xml version=\"1.0\" standalone=\"no\"?>\n"); - fprintf(of, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "); - fprintf(of, "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); - - //fprintf(of, "<g transform=\"translate(10,%d)\">\n", 1000 + 150 + (pcount * 20)); - fprintf(of, "<svg width=\"%.0fpx\" height=\"%.0fpx\" version=\"1.1\" ", w, h); - fprintf(of, "xmlns=\"http://www.w3.org/2000/svg\">\n\n"); - - /* write some basic info as a comment, including some help */ - fprintf(of, "<!-- This file is a bootchart SVG file. It is best rendered in a browser -->\n"); - fprintf(of, "<!-- such as Chrome, Chromium, or Firefox. Other applications that -->\n"); - fprintf(of, "<!-- render these files properly but more slowly are ImageMagick, gimp, -->\n"); - fprintf(of, "<!-- inkscape, etc. To display the files on your system, just point -->\n"); - fprintf(of, "<!-- your browser to file:///run/log/ and click. This bootchart was -->\n\n"); - - fprintf(of, "<!-- generated by bootchart version %s, running with options: -->\n", VERSION); - fprintf(of, "<!-- hz=\"%f\" n=\"%d\" -->\n", arg_hz, arg_samples_len); - fprintf(of, "<!-- x=\"%f\" y=\"%f\" -->\n", arg_scale_x, arg_scale_y); - fprintf(of, "<!-- rel=\"%d\" f=\"%d\" -->\n", arg_relative, arg_filter); - fprintf(of, "<!-- p=\"%d\" e=\"%d\" -->\n", arg_pss, arg_entropy); - fprintf(of, "<!-- o=\"%s\" i=\"%s\" -->\n\n", arg_output_path, arg_init_path); - - /* style sheet */ - fprintf(of, "<defs>\n <style type=\"text/css\">\n <![CDATA[\n"); - - fprintf(of, " rect { stroke-width: 1; }\n"); - fprintf(of, " rect.bg { fill: rgb(255,255,255); }\n"); - fprintf(of, " rect.cpu { fill: rgb(64,64,240); stroke-width: 0; fill-opacity: 0.7; }\n"); - fprintf(of, " rect.wait { fill: rgb(240,240,0); stroke-width: 0; fill-opacity: 0.7; }\n"); - fprintf(of, " rect.bi { fill: rgb(240,128,128); stroke-width: 0; fill-opacity: 0.7; }\n"); - fprintf(of, " rect.bo { fill: rgb(192,64,64); stroke-width: 0; fill-opacity: 0.7; }\n"); - fprintf(of, " rect.ps { fill: rgb(192,192,192); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n"); - fprintf(of, " rect.krnl { fill: rgb(240,240,0); stroke: rgb(128,128,128); fill-opacity: 0.7; }\n"); - fprintf(of, " rect.box { fill: rgb(240,240,240); stroke: rgb(192,192,192); }\n"); - fprintf(of, " rect.clrw { stroke-width: 0; fill-opacity: 0.7;}\n"); - fprintf(of, " line { stroke: rgb(64,64,64); stroke-width: 1; }\n"); - fprintf(of, "// line.sec1 { }\n"); - fprintf(of, " line.sec5 { stroke-width: 2; }\n"); - fprintf(of, " line.sec01 { stroke: rgb(224,224,224); stroke-width: 1; }\n"); - fprintf(of, " line.dot { stroke-dasharray: 2 4; }\n"); - fprintf(of, " line.idle { stroke: rgb(64,64,64); stroke-dasharray: 10 6; stroke-opacity: 0.7; }\n"); - - fprintf(of, " .run { font-size: 8; font-style: italic; }\n"); - fprintf(of, " text { font-family: Verdana, Helvetica; font-size: 10; }\n"); - fprintf(of, " text.sec { font-size: 8; }\n"); - fprintf(of, " text.t1 { font-size: 24; }\n"); - fprintf(of, " text.t2 { font-size: 12; }\n"); - fprintf(of, " text.idle { font-size: 18; }\n"); - - fprintf(of, " ]]>\n </style>\n</defs>\n\n"); -} - -static int svg_title(FILE *of, const char *build, int pscount, double log_start, int overrun) { - _cleanup_free_ char *cmdline = NULL; - _cleanup_free_ char *model = NULL; - _cleanup_free_ char *buf = NULL; - char date[256] = "Unknown"; - const char *cpu; - char *c; - time_t t; - int r; - struct utsname uts; - - r = read_one_line_file("/proc/cmdline", &cmdline); - if (r < 0) { - log_error_errno(r, "Unable to read cmdline: %m"); - return r; - } - - /* extract root fs so we can find disk model name in sysfs */ - /* FIXME: this works only in the simple case */ - c = strstr(cmdline, "root=/dev/"); - if (c) { - char rootbdev[4]; - char filename[32]; - - strncpy(rootbdev, &c[10], sizeof(rootbdev) - 1); - rootbdev[3] = '\0'; - xsprintf(filename, "/sys/block/%s/device/model", rootbdev); - - r = read_one_line_file(filename, &model); - if (r < 0) - log_info("Error reading disk model for %s: %m\n", rootbdev); - } - - /* various utsname parameters */ - r = uname(&uts); - if (r < 0) { - log_error("Error getting uname info\n"); - return -errno; - } - - /* date */ - t = time(NULL); - r = strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", localtime(&t)); - assert_se(r > 0); - - /* CPU type */ - r = get_proc_field("/proc/cpuinfo", PROC_CPUINFO_MODEL, "\n", &buf); - if (r < 0) - cpu = "Unknown"; - else - cpu = buf; - - fprintf(of, "<text class=\"t1\" x=\"0\" y=\"30\">Bootchart for %s - %s</text>\n", - uts.nodename, date); - fprintf(of, "<text class=\"t2\" x=\"20\" y=\"50\">System: %s %s %s %s</text>\n", - uts.sysname, uts.release, uts.version, uts.machine); - fprintf(of, "<text class=\"t2\" x=\"20\" y=\"65\">CPU: %s</text>\n", cpu); - if (model) - fprintf(of, "<text class=\"t2\" x=\"20\" y=\"80\">Disk: %s</text>\n", model); - fprintf(of, "<text class=\"t2\" x=\"20\" y=\"95\">Boot options: %s</text>\n", cmdline); - fprintf(of, "<text class=\"t2\" x=\"20\" y=\"110\">Build: %s</text>\n", build); - fprintf(of, "<text class=\"t2\" x=\"20\" y=\"125\">Log start time: %.03fs</text>\n", log_start); - fprintf(of, "<text class=\"t2\" x=\"20\" y=\"140\">Idle time: "); - - if (idletime >= 0.0) - fprintf(of, "%.03fs", idletime); - else - fprintf(of, "Not detected"); - - fprintf(of, "</text>\n"); - fprintf(of, "<text class=\"sec\" x=\"20\" y=\"155\">Graph data: %.03f samples/sec, recorded %i total, dropped %i samples, %i processes, %i filtered</text>\n", - arg_hz, arg_samples_len, overrun, pscount, pfiltered); - - return 0; -} - -static void svg_graph_box(FILE *of, struct list_sample_data *head, int height, double graph_start) { - double d = 0.0; - int i = 0; - double finalsample = 0.0; - struct list_sample_data *sampledata_last; - - sampledata_last = head; - LIST_FOREACH_BEFORE(link, sampledata, head) { - sampledata_last = sampledata; - } - - finalsample = sampledata_last->sampletime; - - /* outside box, fill */ - fprintf(of, "<rect class=\"box\" x=\"%.03f\" y=\"0\" width=\"%.03f\" height=\"%.03f\" />\n", - time_to_graph(0.0), - time_to_graph(finalsample - graph_start), - ps_to_graph(height)); - - for (d = graph_start; d <= finalsample; - d += (arg_scale_x < 2.0 ? 60.0 : arg_scale_x < 10.0 ? 1.0 : 0.1)) { - /* lines for each second */ - if (i % 50 == 0) - fprintf(of, " <line class=\"sec5\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n", - time_to_graph(d - graph_start), - time_to_graph(d - graph_start), - ps_to_graph(height)); - else if (i % 10 == 0) - fprintf(of, " <line class=\"sec1\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n", - time_to_graph(d - graph_start), - time_to_graph(d - graph_start), - ps_to_graph(height)); - else - fprintf(of, " <line class=\"sec01\" x1=\"%.03f\" y1=\"0\" x2=\"%.03f\" y2=\"%.03f\" />\n", - time_to_graph(d - graph_start), - time_to_graph(d - graph_start), - ps_to_graph(height)); - - /* time label */ - if (i % 10 == 0) - fprintf(of, " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\" >%.01fs</text>\n", - time_to_graph(d - graph_start), - -5.0, d - graph_start); - - i++; - } -} - -/* xml comments must not contain "--" */ -static char* xml_comment_encode(const char* name) { - char *enc_name, *p; - - enc_name = strdup(name); - if (!enc_name) - return NULL; - - for (p = enc_name; *p; p++) - if (p[0] == '-' && p[1] == '-') - p[1] = '_'; - - return enc_name; -} - -static void svg_pss_graph(FILE *of, - struct list_sample_data *head, - struct ps_struct *ps_first, - double graph_start) { - struct ps_struct *ps; - int i; - struct list_sample_data *sampledata_last; - - sampledata_last = head; - LIST_FOREACH_BEFORE(link, sampledata, head) { - sampledata_last = sampledata; - } - - - fprintf(of, "\n\n<!-- Pss memory size graph -->\n"); - - fprintf(of, "\n <text class=\"t2\" x=\"5\" y=\"-15\">Memory allocation - Pss</text>\n"); - - /* vsize 1000 == 1000mb */ - svg_graph_box(of, head, 100, graph_start); - /* draw some hlines for usable memory sizes */ - for (i = 100000; i < 1000000; i += 100000) { - fprintf(of, " <line class=\"sec01\" x1=\"%.03f\" y1=\"%.0f\" x2=\"%.03f\" y2=\"%.0f\"/>\n", - time_to_graph(.0), - kb_to_graph(i), - time_to_graph(sampledata_last->sampletime - graph_start), - kb_to_graph(i)); - fprintf(of, " <text class=\"sec\" x=\"%.03f\" y=\"%.0f\">%dM</text>\n", - time_to_graph(sampledata_last->sampletime - graph_start) + 5, - kb_to_graph(i), (1000000 - i) / 1000); - } - fprintf(of, "\n"); - - /* now plot the graph itself */ - i = 1; - prev_sampledata = head; - LIST_FOREACH_BEFORE(link, sampledata, head) { - int bottom; - int top; - struct ps_sched_struct *cross_place; - - bottom = 0; - top = 0; - - /* put all the small pss blocks into the bottom */ - ps = ps_first; - while (ps->next_ps) { - ps = ps->next_ps; - if (!ps) - continue; - ps->sample = ps->first; - while (ps->sample->next) { - ps->sample = ps->sample->next; - if (ps->sample->sampledata == sampledata) - break; - } - if (ps->sample->sampledata == sampledata) { - if (ps->sample->pss <= (100 * arg_scale_y)) - top += ps->sample->pss; - break; - } - } - while (ps->sample->cross) { - cross_place = ps->sample->cross; - ps = ps->sample->cross->ps_new; - ps->sample = cross_place; - if (ps->sample->pss <= (100 * arg_scale_y)) - top += ps->sample->pss; - } - - fprintf(of, " <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", - "rgb(64,64,64)", - time_to_graph(prev_sampledata->sampletime - graph_start), - kb_to_graph(1000000.0 - top), - time_to_graph(sampledata->sampletime - prev_sampledata->sampletime), - kb_to_graph(top - bottom)); - bottom = top; - - /* now plot the ones that are of significant size */ - ps = ps_first; - while (ps->next_ps) { - ps = ps->next_ps; - if (!ps) - continue; - ps->sample = ps->first; - while (ps->sample->next) { - ps->sample = ps->sample->next; - if (ps->sample->sampledata == sampledata) - break; - } - /* don't draw anything smaller than 2mb */ - if (ps->sample->sampledata != sampledata) - continue; - if (ps->sample->pss > (100 * arg_scale_y)) { - top = bottom + ps->sample->pss; - fprintf(of, " <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", - colorwheel[ps->pid % 12], - time_to_graph(prev_sampledata->sampletime - graph_start), - kb_to_graph(1000000.0 - top), - time_to_graph(sampledata->sampletime - prev_sampledata->sampletime), - kb_to_graph(top - bottom)); - bottom = top; - } - break; - } - - while ((cross_place = ps->sample->cross)) { - ps = ps->sample->cross->ps_new; - ps->sample = cross_place; - if (ps->sample->pss > (100 * arg_scale_y)) { - top = bottom + ps->sample->pss; - fprintf(of, " <rect class=\"clrw\" style=\"fill: %s\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", - colorwheel[ps->pid % 12], - time_to_graph(prev_sampledata->sampletime - graph_start), - kb_to_graph(1000000.0 - top), - time_to_graph(sampledata->sampletime - prev_sampledata->sampletime), - kb_to_graph(top - bottom)); - bottom = top; - } - } - - prev_sampledata = sampledata; - i++; - } - - /* overlay all the text labels */ - i = 1; - LIST_FOREACH_BEFORE(link, sampledata, head) { - int bottom; - int top = 0; - struct ps_sched_struct *prev_sample; - struct ps_sched_struct *cross_place; - - /* put all the small pss blocks into the bottom */ - ps = ps_first->next_ps; - while (ps->next_ps) { - ps = ps->next_ps; - if (!ps) - continue; - - ps->sample = ps->first; - while (ps->sample->next) { - ps->sample = ps->sample->next; - if (ps->sample->sampledata == sampledata) - break; - } - - if (ps->sample->sampledata == sampledata) { - if (ps->sample->pss <= (100 * arg_scale_y)) - top += ps->sample->pss; - - break; - } - } - - while ((cross_place = ps->sample->cross)) { - ps = ps->sample->cross->ps_new; - ps->sample = cross_place; - if (ps->sample->pss <= (100 * arg_scale_y)) - top += ps->sample->pss; - } - bottom = top; - - /* now plot the ones that are of significant size */ - ps = ps_first; - while (ps->next_ps) { - prev_sample = ps->sample; - ps = ps->next_ps; - if (!ps) - continue; - ps->sample = ps->first; - while (ps->sample->next) { - prev_sample = ps->sample; - ps->sample = ps->sample->next; - if (ps->sample->sampledata == sampledata) - break; - } - /* don't draw anything smaller than 2mb */ - if (ps->sample->sampledata == sampledata) { - if (ps->sample->pss > (100 * arg_scale_y)) { - top = bottom + ps->sample->pss; - /* draw a label with the process / PID */ - if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y))) - fprintf(of, " <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n", - time_to_graph(sampledata->sampletime - graph_start), - kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)), - ps->name, ps->pid); - bottom = top; - } - break; - } - } - while ((cross_place = ps->sample->cross)) { - ps = ps->sample->cross->ps_new; - ps->sample = cross_place; - prev_sample = ps->sample->prev; - if (ps->sample->pss > (100 * arg_scale_y)) { - top = bottom + ps->sample->pss; - /* draw a label with the process / PID */ - if ((i == 1) || (prev_sample->pss <= (100 * arg_scale_y))) - fprintf(of, " <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]</text>\n", - time_to_graph(sampledata->sampletime - graph_start), - kb_to_graph(1000000.0 - bottom - ((top - bottom) / 2)), - ps->name, ps->pid); - bottom = top; - } - } - - i++; - } - - /* debug output - full data dump */ - fprintf(of, "\n\n<!-- PSS map - csv format -->\n"); - ps = ps_first; - while (ps->next_ps) { - _cleanup_free_ char *enc_name = NULL; - ps = ps->next_ps; - if (!ps) - continue; - - enc_name = xml_comment_encode(ps->name); - if (!enc_name) - continue; - - fprintf(of, "<!-- %s [%d] pss=", enc_name, ps->pid); - - ps->sample = ps->first; - while (ps->sample->next) { - ps->sample = ps->sample->next; - fprintf(of, "%d," , ps->sample->pss); - } - - fprintf(of, " -->\n"); - } - -} - -static void svg_io_bi_bar(FILE *of, - struct list_sample_data *head, - int n_samples, - double graph_start, - double interval) { - - double max = 0.0; - double range; - int max_here = 0; - int i; - int k; - struct list_sample_data *start_sampledata; - struct list_sample_data *stop_sampledata; - - fprintf(of, "<!-- IO utilization graph - In -->\n"); - fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - read</text>\n"); - - /* - * calculate rounding range - * - * We need to round IO data since IO block data is not updated on - * each poll. Applying a smoothing function loses some burst data, - * so keep the smoothing range short. - */ - range = 0.25 / (1.0 / arg_hz); - if (range < 2.0) - range = 2.0; /* no smoothing */ - - /* surrounding box */ - svg_graph_box(of, head, 5, graph_start); - - /* find the max IO first */ - i = 1; - LIST_FOREACH_BEFORE(link, sampledata, head) { - int start; - int stop; - int diff; - double tot; - - start = MAX(i - ((range / 2) - 1), 0); - stop = MIN(i + (range / 2), n_samples - 1); - diff = (stop - start); - - start_sampledata = sampledata; - stop_sampledata = sampledata; - - for (k = 0; k < ((range/2) - 1) && start_sampledata->link_next; k++) - start_sampledata = start_sampledata->link_next; - - for (k = 0; k < (range/2) && stop_sampledata->link_prev; k++) - stop_sampledata = stop_sampledata->link_prev; - - tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi) / diff; - - if (tot > max) { - max = tot; - max_here = i; - } - - tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo) / diff; - - if (tot > max) - max = tot; - - i++; - } - - /* plot bi */ - i = 1; - prev_sampledata = head; - LIST_FOREACH_BEFORE(link, sampledata, head) { - int start; - int stop; - int diff; - double tot; - double pbi = 0; - - start = MAX(i - ((range / 2) - 1), 0); - stop = MIN(i + (range / 2), n_samples); - diff = (stop - start); - - start_sampledata = sampledata; - stop_sampledata = sampledata; - - for (k = 0; k < ((range/2)-1) && start_sampledata->link_next; k++) - start_sampledata = start_sampledata->link_next; - - for (k = 0; k < (range/2) && stop_sampledata->link_prev; k++) - stop_sampledata = stop_sampledata->link_prev; - - tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi) / diff; - - if (max > 0) - pbi = tot / max; - - if (pbi > 0.001) - fprintf(of, "<rect class=\"bi\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", - time_to_graph(prev_sampledata->sampletime - graph_start), - (arg_scale_y * 5) - (pbi * (arg_scale_y * 5)), - time_to_graph(sampledata->sampletime - prev_sampledata->sampletime), - pbi * (arg_scale_y * 5)); - - /* labels around highest value */ - if (i == max_here) - fprintf(of, " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n", - time_to_graph(sampledata->sampletime - graph_start) + 5, - ((arg_scale_y * 5) - (pbi * (arg_scale_y * 5))) + 15, - max / 1024.0 / (interval / 1000000000.0)); - - i++; - prev_sampledata = sampledata; - } -} - -static void svg_io_bo_bar(FILE *of, - struct list_sample_data *head, - int n_samples, - double graph_start, - double interval) { - double max = 0.0; - double range; - int max_here = 0; - int i; - int k; - struct list_sample_data *start_sampledata; - struct list_sample_data *stop_sampledata; - - fprintf(of, "<!-- IO utilization graph - out -->\n"); - fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">IO utilization - write</text>\n"); - - /* - * calculate rounding range - * - * We need to round IO data since IO block data is not updated on - * each poll. Applying a smoothing function loses some burst data, - * so keep the smoothing range short. - */ - range = 0.25 / (1.0 / arg_hz); - if (range < 2.0) - range = 2.0; /* no smoothing */ - - /* surrounding box */ - svg_graph_box(of, head, 5, graph_start); - - /* find the max IO first */ - i = 0; - LIST_FOREACH_BEFORE(link, sampledata, head) { - int start; - int stop; - int diff; - double tot; - - start = MAX(i - ((range / 2) - 1), 0); - stop = MIN(i + (range / 2), n_samples - 1); - diff = (stop - start); - - start_sampledata = sampledata; - stop_sampledata = sampledata; - - for (k = 0; k < (range/2) - 1 && start_sampledata->link_next; k++) - start_sampledata = start_sampledata->link_next; - - for (k = 0; k < (range/2) && stop_sampledata->link_prev; k++) - stop_sampledata = stop_sampledata->link_prev; - - tot = (double)(stop_sampledata->blockstat.bi - start_sampledata->blockstat.bi) / diff; - if (tot > max) - max = tot; - - tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo) / diff; - if (tot > max) { - max = tot; - max_here = i; - } - - i++; - } - - /* plot bo */ - prev_sampledata = head; - i = 1; - - LIST_FOREACH_BEFORE(link, sampledata, head) { - int start, stop, diff; - double tot, pbo; - - pbo = 0; - - start = MAX(i - ((range / 2) - 1), 0); - stop = MIN(i + (range / 2), n_samples); - diff = (stop - start); - - start_sampledata = sampledata; - stop_sampledata = sampledata; - - for (k = 0; k < ((range/2)-1) && start_sampledata->link_next; k++) - start_sampledata = start_sampledata->link_next; - - for (k = 0; k < (range/2) && stop_sampledata->link_prev; k++) - stop_sampledata = stop_sampledata->link_prev; - - tot = (double)(stop_sampledata->blockstat.bo - start_sampledata->blockstat.bo) - / diff; - - if (max > 0) - pbo = tot / max; - - if (pbo > 0.001) - fprintf(of, "<rect class=\"bo\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", - time_to_graph(prev_sampledata->sampletime - graph_start), - (arg_scale_y * 5) - (pbo * (arg_scale_y * 5)), - time_to_graph(sampledata->sampletime - prev_sampledata->sampletime), - pbo * (arg_scale_y * 5)); - - /* labels around highest bo value */ - if (i == max_here) - fprintf(of, " <text class=\"sec\" x=\"%.03f\" y=\"%.03f\">%0.2fmb/sec</text>\n", - time_to_graph(sampledata->sampletime - graph_start) + 5, - ((arg_scale_y * 5) - (pbo * (arg_scale_y * 5))), - max / 1024.0 / (interval / 1000000000.0)); - - i++; - prev_sampledata = sampledata; - } -} - -static void svg_cpu_bar(FILE *of, struct list_sample_data *head, int n_cpus, int cpu_num, double graph_start) { - - fprintf(of, "<!-- CPU utilization graph -->\n"); - - if (cpu_num < 0) - fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">CPU[overall] utilization</text>\n"); - else - fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">CPU[%d] utilization</text>\n", cpu_num); - - /* surrounding box */ - svg_graph_box(of, head, 5, graph_start); - - /* bars for each sample, proportional to the CPU util. */ - prev_sampledata = head; - LIST_FOREACH_BEFORE(link, sampledata, head) { - int c; - double trt; - double ptrt; - - ptrt = trt = 0.0; - - if (cpu_num < 0) - for (c = 0; c < n_cpus; c++) - trt += sampledata->runtime[c] - prev_sampledata->runtime[c]; - else - trt = sampledata->runtime[cpu_num] - prev_sampledata->runtime[cpu_num]; - - trt = trt / 1000000000.0; - - if (cpu_num < 0) - trt = trt / (double)n_cpus; - - if (trt > 0.0) - ptrt = trt / (sampledata->sampletime - prev_sampledata->sampletime); - - if (ptrt > 1.0) - ptrt = 1.0; - - if (ptrt > 0.001) - fprintf(of, "<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", - time_to_graph(prev_sampledata->sampletime - graph_start), - (arg_scale_y * 5) - (ptrt * (arg_scale_y * 5)), - time_to_graph(sampledata->sampletime - prev_sampledata->sampletime), - ptrt * (arg_scale_y * 5)); - - prev_sampledata = sampledata; - } -} - -static void svg_wait_bar(FILE *of, struct list_sample_data *head, int n_cpus, int cpu_num, double graph_start) { - - fprintf(of, "<!-- Wait time aggregation box -->\n"); - - if (cpu_num < 0) - fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">CPU[overall] wait</text>\n"); - else - fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">CPU[%d] wait</text>\n", cpu_num); - - /* surrounding box */ - svg_graph_box(of, head, 5, graph_start); - - /* bars for each sample, proportional to the CPU util. */ - prev_sampledata = head; - LIST_FOREACH_BEFORE(link, sampledata, head) { - int c; - double twt; - double ptwt; - - ptwt = twt = 0.0; - - if (cpu_num < 0) - for (c = 0; c < n_cpus; c++) - twt += sampledata->waittime[c] - prev_sampledata->waittime[c]; - else - twt = sampledata->waittime[cpu_num] - prev_sampledata->waittime[cpu_num]; - - twt = twt / 1000000000.0; - - if (cpu_num < 0) - twt = twt / (double)n_cpus; - - if (twt > 0.0) - ptwt = twt / (sampledata->sampletime - prev_sampledata->sampletime); - - if (ptwt > 1.0) - ptwt = 1.0; - - if (ptwt > 0.001) - fprintf(of, "<rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", - time_to_graph(prev_sampledata->sampletime - graph_start), - ((arg_scale_y * 5) - (ptwt * (arg_scale_y * 5))), - time_to_graph(sampledata->sampletime - prev_sampledata->sampletime), - ptwt * (arg_scale_y * 5)); - - prev_sampledata = sampledata; - } -} - -static void svg_entropy_bar(FILE *of, struct list_sample_data *head, double graph_start) { - - fprintf(of, "<!-- entropy pool graph -->\n"); - - fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">Entropy pool size</text>\n"); - /* surrounding box */ - svg_graph_box(of, head, 5, graph_start); - - /* bars for each sample, scale 0-4096 */ - prev_sampledata = head; - LIST_FOREACH_BEFORE(link, sampledata, head) { - fprintf(of, "<rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", - time_to_graph(prev_sampledata->sampletime - graph_start), - ((arg_scale_y * 5) - ((sampledata->entropy_avail / 4096.) * (arg_scale_y * 5))), - time_to_graph(sampledata->sampletime - prev_sampledata->sampletime), - (sampledata->entropy_avail / 4096.) * (arg_scale_y * 5)); - prev_sampledata = sampledata; - } -} - -static struct ps_struct *get_next_ps(struct ps_struct *ps, struct ps_struct *ps_first) { - /* - * walk the list of processes and return the next one to be - * painted - */ - if (ps == ps_first) - return ps->next_ps; - - /* go deep */ - if (ps->children) - return ps->children; - - /* find siblings */ - if (ps->next) - return ps->next; - - /* go back for parent siblings */ - for (;;) { - if (ps->parent && ps->parent->next) - return ps->parent->next; - - ps = ps->parent; - if (!ps) - return ps; - } - - return NULL; -} - -static bool ps_filter(struct ps_struct *ps) { - if (!arg_filter) - return false; - - /* can't draw data when there is only 1 sample (need start + stop) */ - if (ps->first == ps->last) - return true; - - /* don't filter kthreadd */ - if (ps->pid == 2) - return false; - - /* drop stuff that doesn't use any real CPU time */ - if (ps->total <= 0.001) - return true; - - return 0; -} - -static void svg_do_initcall(FILE *of, struct list_sample_data *head, int count_only, double graph_start) { - _cleanup_pclose_ FILE *f = NULL; - double t; - char func[256]; - int ret; - int usecs; - - /* can't plot initcall when disabled or in relative mode */ - if (!arg_initcall || arg_relative) { - kcount = 0; - return; - } - - if (!count_only) { - fprintf(of, "<!-- initcall -->\n"); - fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">Kernel init threads</text>\n"); - /* surrounding box */ - svg_graph_box(of, head, kcount, graph_start); - } - - kcount = 0; - - /* - * Initcall graphing - parses dmesg buffer and displays kernel threads - * This somewhat uses the same methods and scaling to show processes - * but looks a lot simpler. It's overlaid entirely onto the PS graph - * when appropriate. - */ - - f = popen("dmesg", "r"); - if (!f) - return; - - while (!feof(f)) { - int c; - int z = 0; - char l[256]; - - if (fgets(l, sizeof(l) - 1, f) == NULL) - continue; - - c = sscanf(l, "[%lf] initcall %s %*s %d %*s %d %*s", - &t, func, &ret, &usecs); - if (c != 4) { - /* also parse initcalls done by module loading */ - c = sscanf(l, "[%lf] initcall %s %*s %*s %d %*s %d %*s", - &t, func, &ret, &usecs); - if (c != 4) - continue; - } - - /* chop the +0xXX/0xXX stuff */ - while(func[z] != '+') - z++; - func[z] = 0; - - if (count_only) { - /* filter out irrelevant stuff */ - if (usecs >= 1000) - kcount++; - continue; - } - - fprintf(of, "<!-- thread=\"%s\" time=\"%.3f\" elapsed=\"%d\" result=\"%d\" -->\n", - func, t, usecs, ret); - - if (usecs < 1000) - continue; - - /* rect */ - fprintf(of, " <rect class=\"krnl\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", - time_to_graph(t - (usecs / 1000000.0)), - ps_to_graph(kcount), - time_to_graph(usecs / 1000000.0), - ps_to_graph(1)); - - /* label */ - fprintf(of, " <text x=\"%.03f\" y=\"%.03f\">%s <tspan class=\"run\">%.03fs</tspan></text>\n", - time_to_graph(t - (usecs / 1000000.0)) + 5, - ps_to_graph(kcount) + 15, - func, usecs / 1000000.0); - - kcount++; - } -} - -static void svg_ps_bars(FILE *of, - struct list_sample_data *head, - int n_samples, - int n_cpus, - struct ps_struct *ps_first, - double graph_start, - double interval) { - - struct ps_struct *ps; - int i = 0; - int j = 0; - int pid; - double w = 0.0; - - fprintf(of, "<!-- Process graph -->\n"); - fprintf(of, "<text class=\"t2\" x=\"5\" y=\"-15\">Processes</text>\n"); - - /* surrounding box */ - svg_graph_box(of, head, pcount, graph_start); - - /* pass 2 - ps boxes */ - ps = ps_first; - while ((ps = get_next_ps(ps, ps_first))) { - _cleanup_free_ char *enc_name = NULL, *escaped = NULL; - double endtime; - double starttime; - int t; - - if (!utf8_is_printable(ps->name, strlen(ps->name))) - escaped = utf8_escape_non_printable(ps->name); - - enc_name = xml_comment_encode(escaped ? escaped : ps->name); - if (!enc_name) - continue; - - /* leave some trace of what we actually filtered etc. */ - fprintf(of, "<!-- %s [%i] ppid=%i runtime=%.03fs -->\n", enc_name, ps->pid, - ps->ppid, ps->total); - - starttime = ps->first->sampledata->sampletime; - - if (!ps_filter(ps)) { - /* remember where _to_ our children need to draw a line */ - ps->pos_x = time_to_graph(starttime - graph_start); - ps->pos_y = ps_to_graph(j+1); /* bottom left corner */ - } else if (ps->parent){ - /* hook children to our parent coords instead */ - ps->pos_x = ps->parent->pos_x; - ps->pos_y = ps->parent->pos_y; - - /* if this is the last child, we might still need to draw a connecting line */ - if ((!ps->next) && (ps->parent)) - fprintf(of, " <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n", - ps->parent->pos_x, - ps_to_graph(j-1) + 10.0, /* whee, use the last value here */ - ps->parent->pos_x, - ps->parent->pos_y); - continue; - } - - endtime = ps->last->sampledata->sampletime; - fprintf(of, " <rect class=\"ps\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", - time_to_graph(starttime - graph_start), - ps_to_graph(j), - time_to_graph(ps->last->sampledata->sampletime - starttime), - ps_to_graph(1)); - - /* paint cpu load over these */ - ps->sample = ps->first; - t = 1; - while (ps->sample->next) { - double rt, prt; - double wt, wrt; - struct ps_sched_struct *prev; - - prev = ps->sample; - ps->sample = ps->sample->next; - - /* calculate over interval */ - rt = ps->sample->runtime - prev->runtime; - wt = ps->sample->waittime - prev->waittime; - - prt = (rt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime); - wrt = (wt / 1000000000) / (ps->sample->sampledata->sampletime - prev->sampledata->sampletime); - - /* this can happen if timekeeping isn't accurate enough */ - if (prt > 1.0) - prt = 1.0; - if (wrt > 1.0) - wrt = 1.0; - - if ((prt < 0.1) && (wrt < 0.1)) /* =~ 26 (color threshold) */ - continue; - - fprintf(of, " <rect class=\"wait\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", - time_to_graph(prev->sampledata->sampletime - graph_start), - ps_to_graph(j), - time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime), - ps_to_graph(wrt)); - - /* draw cpu over wait - TODO figure out how/why run + wait > interval */ - fprintf(of, " <rect class=\"cpu\" x=\"%.03f\" y=\"%.03f\" width=\"%.03f\" height=\"%.03f\" />\n", - time_to_graph(prev->sampledata->sampletime - graph_start), - ps_to_graph(j + (1.0 - prt)), - time_to_graph(ps->sample->sampledata->sampletime - prev->sampledata->sampletime), - ps_to_graph(prt)); - t++; - } - - /* determine where to display the process name */ - if ((endtime - starttime) < 1.5) - /* too small to fit label inside the box */ - w = endtime; - else - w = starttime; - - /* text label of process name */ - fprintf(of, " <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan> %s</text>\n", - time_to_graph(w - graph_start) + 5.0, - ps_to_graph(j) + 14.0, - escaped ? escaped : ps->name, - ps->pid, - (ps->last->runtime - ps->first->runtime) / 1000000000.0, - arg_show_cgroup ? ps->cgroup : ""); - /* paint lines to the parent process */ - if (ps->parent) { - /* horizontal part */ - fprintf(of, " <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n", - time_to_graph(starttime - graph_start), - ps_to_graph(j) + 10.0, - ps->parent->pos_x, - ps_to_graph(j) + 10.0); - - /* one vertical line connecting all the horizontal ones up */ - if (!ps->next) - fprintf(of, " <line class=\"dot\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n", - ps->parent->pos_x, - ps_to_graph(j) + 10.0, - ps->parent->pos_x, - ps->parent->pos_y); - } - - j++; /* count boxes */ - - fprintf(of, "\n"); - } - - /* last pass - determine when idle */ - pid = getpid(); - /* make sure we start counting from the point where we actually have - * data: assume that bootchart's first sample is when data started - */ - - ps = ps_first; - while (ps->next_ps) { - ps = ps->next_ps; - if (ps->pid == pid) - break; - } - - /* need to know last node first */ - ps->sample = ps->first; - i = ps->sample->next->sampledata->counter; - - while (ps->sample->next && i<(n_samples-(arg_hz/2))) { - double crt; - double brt; - int c; - int ii; - struct ps_sched_struct *sample_hz; - - ps->sample = ps->sample->next; - sample_hz = ps->sample; - for (ii = 0; (ii < (int)arg_hz/2) && sample_hz->next; ii++) - sample_hz = sample_hz->next; - - /* subtract bootchart cpu utilization from total */ - crt = 0.0; - for (c = 0; c < n_cpus; c++) - crt += sample_hz->sampledata->runtime[c] - ps->sample->sampledata->runtime[c]; - - brt = sample_hz->runtime - ps->sample->runtime; - /* - * our definition of "idle": - * - * if for (hz / 2) we've used less CPU than (interval / 2) ... - * defaults to 4.0%, which experimentally, is where atom idles - */ - if ((crt - brt) < (interval / 2.0)) { - idletime = ps->sample->sampledata->sampletime - graph_start; - fprintf(of, "\n<!-- idle detected at %.03f seconds -->\n", idletime); - fprintf(of, "<line class=\"idle\" x1=\"%.03f\" y1=\"%.03f\" x2=\"%.03f\" y2=\"%.03f\" />\n", - time_to_graph(idletime), - -arg_scale_y, - time_to_graph(idletime), - ps_to_graph(pcount) + arg_scale_y); - fprintf(of, "<text class=\"idle\" x=\"%.03f\" y=\"%.03f\">%.01fs</text>\n", - time_to_graph(idletime) + 5.0, - ps_to_graph(pcount) + arg_scale_y, - idletime); - break; - } - - i++; - } -} - -static void svg_top_ten_cpu(FILE *of, struct ps_struct *ps_first) { - struct ps_struct *top[10]; - struct ps_struct emptyps = {}; - struct ps_struct *ps; - int n, m; - - for (n = 0; n < (int) ELEMENTSOF(top); n++) - top[n] = &emptyps; - - /* walk all ps's and setup ptrs */ - ps = ps_first; - while ((ps = get_next_ps(ps, ps_first))) { - for (n = 0; n < 10; n++) { - if (ps->total <= top[n]->total) - continue; - /* cascade insert */ - for (m = 9; m > n; m--) - top[m] = top[m-1]; - top[n] = ps; - break; - } - } - - fprintf(of, "<text class=\"t2\" x=\"20\" y=\"0\">Top CPU consumers:</text>\n"); - for (n = 0; n < 10; n++) - fprintf(of, "<text class=\"t3\" x=\"20\" y=\"%d\">%3.03fs - <![CDATA[%s]]> [%d]</text>\n", - 20 + (n * 13), - top[n]->total, - top[n]->name, - top[n]->pid); -} - -static void svg_top_ten_pss(FILE *of, struct ps_struct *ps_first) { - struct ps_struct *top[10]; - struct ps_struct emptyps = {}; - struct ps_struct *ps; - int n, m; - - for (n = 0; n < (int) ELEMENTSOF(top); n++) - top[n] = &emptyps; - - /* walk all ps's and setup ptrs */ - ps = ps_first; - while ((ps = get_next_ps(ps, ps_first))) { - for (n = 0; n < 10; n++) { - if (ps->pss_max <= top[n]->pss_max) - continue; - - /* cascade insert */ - for (m = 9; m > n; m--) - top[m] = top[m-1]; - top[n] = ps; - break; - } - } - - fprintf(of, "<text class=\"t2\" x=\"20\" y=\"0\">Top PSS consumers:</text>\n"); - for (n = 0; n < 10; n++) - fprintf(of, "<text class=\"t3\" x=\"20\" y=\"%d\">%dK - <![CDATA[%s]]> [%d]</text>\n", - 20 + (n * 13), - top[n]->pss_max, - top[n]->name, - top[n]->pid); -} - -int svg_do(FILE *of, - const char *build, - struct list_sample_data *head, - struct ps_struct *ps_first, - int n_samples, - int pscount, - int n_cpus, - double graph_start, - double log_start, - double interval, - int overrun) { - - struct ps_struct *ps; - double offset = 7; - int r, c; - - sampledata = head; - LIST_FIND_TAIL(link, sampledata, head); - ps = ps_first; - - /* count initcall thread count first */ - svg_do_initcall(of, head, 1, graph_start); - ksize = kcount ? ps_to_graph(kcount) + (arg_scale_y * 2) : 0; - - /* then count processes */ - while ((ps = get_next_ps(ps, ps_first))) { - if (!ps_filter(ps)) - pcount++; - else - pfiltered++; - } - psize = ps_to_graph(pcount) + (arg_scale_y * 2); - - esize = (arg_entropy ? arg_scale_y * 7 : 0); - - /* after this, we can draw the header with proper sizing */ - svg_header(of, head, graph_start, arg_percpu ? n_cpus : 0); - fprintf(of, "<rect class=\"bg\" width=\"100%%\" height=\"100%%\" />\n\n"); - - fprintf(of, "<g transform=\"translate(10,400)\">\n"); - svg_io_bi_bar(of, head, n_samples, graph_start, interval); - fprintf(of, "</g>\n\n"); - - fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset)); - svg_io_bo_bar(of, head, n_samples, graph_start, interval); - fprintf(of, "</g>\n\n"); - - for (c = -1; c < (arg_percpu ? n_cpus : 0); c++) { - offset += 7; - fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset)); - svg_cpu_bar(of, head, n_cpus, c, graph_start); - fprintf(of, "</g>\n\n"); - - offset += 7; - fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset)); - svg_wait_bar(of, head, n_cpus, c, graph_start); - fprintf(of, "</g>\n\n"); - } - - if (kcount) { - offset += 7; - fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset)); - svg_do_initcall(of, head, 0, graph_start); - fprintf(of, "</g>\n\n"); - } - - offset += 7; - fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset) + ksize); - svg_ps_bars(of, head, n_samples, n_cpus, ps_first, graph_start, interval); - fprintf(of, "</g>\n\n"); - - fprintf(of, "<g transform=\"translate(10, 0)\">\n"); - r = svg_title(of, build, pscount, log_start, overrun); - fprintf(of, "</g>\n\n"); - - if (r < 0) - return r; - - fprintf(of, "<g transform=\"translate(10,200)\">\n"); - svg_top_ten_cpu(of, ps_first); - fprintf(of, "</g>\n\n"); - - if (arg_entropy) { - fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset) + ksize + psize); - svg_entropy_bar(of, head, graph_start); - fprintf(of, "</g>\n\n"); - } - - if (arg_pss) { - fprintf(of, "<g transform=\"translate(10,%.03f)\">\n", 400.0 + (arg_scale_y * offset) + ksize + psize + esize); - svg_pss_graph(of, head, ps_first, graph_start); - fprintf(of, "</g>\n\n"); - - fprintf(of, "<g transform=\"translate(410,200)\">\n"); - svg_top_ten_pss(of, ps_first); - fprintf(of, "</g>\n\n"); - } - - /* fprintf footer */ - fprintf(of, "\n</svg>\n"); - - return 0; -} diff --git a/src/bus-proxyd/Makefile b/src/bus-proxyd/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/bus-proxyd/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile
\ No newline at end of file diff --git a/src/bus-proxyd/bus-proxyd.c b/src/bus-proxyd/bus-proxyd.c deleted file mode 100644 index b6bd6eb390..0000000000 --- a/src/bus-proxyd/bus-proxyd.c +++ /dev/null @@ -1,328 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2013 Daniel Mack - Copyright 2014 Kay Sievers - Copyright 2015 David Herrmann - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <errno.h> -#include <getopt.h> -#include <pthread.h> -#include <stddef.h> -#include <string.h> -#include <sys/prctl.h> -#include <sys/socket.h> -#include <unistd.h> - -#include "sd-daemon.h" - -#include "alloc-util.h" -#include "bus-internal.h" -#include "bus-xml-policy.h" -#include "capability-util.h" -#include "def.h" -#include "fd-util.h" -#include "formats-util.h" -#include "log.h" -#include "proxy.h" -#include "string-util.h" -#include "strv.h" -#include "user-util.h" -#include "util.h" - -static char *arg_address = NULL; -static char **arg_configuration = NULL; - -typedef struct { - int fd; - SharedPolicy *policy; - uid_t bus_uid; -} ClientContext; - -static ClientContext *client_context_free(ClientContext *c) { - if (!c) - return NULL; - - safe_close(c->fd); - free(c); - - return NULL; -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(ClientContext*, client_context_free); - -static int client_context_new(ClientContext **out) { - _cleanup_(client_context_freep) ClientContext *c = NULL; - - c = new0(ClientContext, 1); - if (!c) - return -ENOMEM; - - c->fd = -1; - - *out = c; - c = NULL; - return 0; -} - -static void *run_client(void *userdata) { - _cleanup_(client_context_freep) ClientContext *c = userdata; - _cleanup_(proxy_freep) Proxy *p = NULL; - char comm[16]; - int r; - - r = proxy_new(&p, c->fd, c->fd, arg_address); - c->fd = -1; - - if (r < 0) - goto exit; - - /* set comm to "p$PIDu$UID" and suffix with '*' if truncated */ - r = snprintf(comm, sizeof(comm), "p" PID_FMT "u" UID_FMT, p->local_creds.pid, p->local_creds.uid); - if (r >= (ssize_t)sizeof(comm)) - comm[sizeof(comm) - 2] = '*'; - (void) prctl(PR_SET_NAME, comm); - - r = proxy_set_policy(p, c->policy, arg_configuration); - if (r < 0) - goto exit; - - r = proxy_hello_policy(p, c->bus_uid); - if (r < 0) - goto exit; - - r = proxy_run(p); - -exit: - return NULL; -} - -static int loop_clients(int accept_fd, uid_t bus_uid) { - _cleanup_(shared_policy_freep) SharedPolicy *sp = NULL; - pthread_attr_t attr; - int r; - - r = pthread_attr_init(&attr); - if (r != 0) - return log_error_errno(r, "Cannot initialize pthread attributes: %m"); - - r = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - if (r != 0) { - r = log_error_errno(r, "Cannot mark pthread attributes as detached: %m"); - goto finish; - } - - r = shared_policy_new(&sp); - if (r < 0) - goto finish; - - for (;;) { - ClientContext *c; - pthread_t tid; - int fd; - - fd = accept4(accept_fd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC); - if (fd < 0) { - if (errno == EAGAIN || errno == EINTR) - continue; - - r = log_error_errno(errno, "accept4() failed: %m"); - goto finish; - } - - r = client_context_new(&c); - if (r < 0) { - log_oom(); - close(fd); - continue; - } - - c->fd = fd; - c->policy = sp; - c->bus_uid = bus_uid; - - r = pthread_create(&tid, &attr, run_client, c); - if (r != 0) { - log_warning_errno(r, "Cannot spawn thread, ignoring: %m"); - client_context_free(c); - continue; - } - } - -finish: - pthread_attr_destroy(&attr); - return r; -} - -static int help(void) { - - printf("%s [OPTIONS...]\n\n" - "DBus proxy server.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --configuration=PATH Configuration file or directory\n" - " --machine=MACHINE Connect to specified machine\n" - " --address=ADDRESS Connect to the bus specified by ADDRESS\n" - " (default: " DEFAULT_SYSTEM_BUS_ADDRESS ")\n", - program_invocation_short_name); - - return 0; -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_ADDRESS, - ARG_CONFIGURATION, - ARG_MACHINE, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "address", required_argument, NULL, ARG_ADDRESS }, - { "configuration", required_argument, NULL, ARG_CONFIGURATION }, - { "machine", required_argument, NULL, ARG_MACHINE }, - {}, - }; - - int c, r; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_ADDRESS: - r = free_and_strdup(&arg_address, optarg); - if (r < 0) - return log_oom(); - break; - - case ARG_CONFIGURATION: - r = strv_extend(&arg_configuration, optarg); - if (r < 0) - return log_oom(); - break; - - case ARG_MACHINE: { - _cleanup_free_ char *e = NULL; - char *a; - - e = bus_address_escape(optarg); - if (!e) - return log_oom(); - - a = strjoin("x-machine-kernel:machine=", e, ";x-machine-unix:machine=", e, NULL); - if (!a) - return log_oom(); - - free(arg_address); - arg_address = a; - - break; - } - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (argc > optind) { - log_error("Too many arguments"); - return -EINVAL; - } - - if (!arg_address) { - arg_address = strdup(DEFAULT_SYSTEM_BUS_ADDRESS); - if (!arg_address) - return log_oom(); - } - - return 1; -} - -int main(int argc, char *argv[]) { - int r, accept_fd; - uid_t uid, bus_uid; - gid_t gid; - - log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); - log_parse_environment(); - log_open(); - - bus_uid = getuid(); - - if (geteuid() == 0) { - const char *user = "systemd-bus-proxy"; - - r = get_user_creds(&user, &uid, &gid, NULL, NULL); - if (r < 0) { - log_error_errno(r, "Cannot resolve user name %s: %m", user); - goto finish; - } - - r = drop_privileges(uid, gid, 1ULL << CAP_IPC_OWNER); - if (r < 0) { - log_error_errno(r, "Cannot drop privileges: %m"); - goto finish; - } - } - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - r = sd_listen_fds(0); - if (r != 1) { - log_error("Illegal number of file descriptors passed"); - goto finish; - } - - accept_fd = SD_LISTEN_FDS_START; - - r = fd_nonblock(accept_fd, false); - if (r < 0) { - log_error_errno(r, "Cannot mark accept-fd non-blocking: %m"); - goto finish; - } - - r = loop_clients(accept_fd, bus_uid); - -finish: - sd_notify(false, - "STOPPING=1\n" - "STATUS=Shutting down."); - - strv_free(arg_configuration); - free(arg_address); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/bus-proxyd/bus-xml-policy.c b/src/bus-proxyd/bus-xml-policy.c deleted file mode 100644 index 8943e0dc12..0000000000 --- a/src/bus-proxyd/bus-xml-policy.c +++ /dev/null @@ -1,1327 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "sd-login.h" - -#include "alloc-util.h" -#include "bus-internal.h" -#include "bus-xml-policy.h" -#include "conf-files.h" -#include "fileio.h" -#include "formats-util.h" -#include "locale-util.h" -#include "set.h" -#include "string-table.h" -#include "string-util.h" -#include "strv.h" -#include "user-util.h" -#include "xml.h" - -static void policy_item_free(PolicyItem *i) { - assert(i); - - free(i->interface); - free(i->member); - free(i->error); - free(i->name); - free(i->path); - free(i); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(PolicyItem*, policy_item_free); - -static void item_append(PolicyItem *i, PolicyItem **list) { - - PolicyItem *tail; - - LIST_FIND_TAIL(items, *list, tail); - LIST_INSERT_AFTER(items, *list, tail, i); -} - -static int file_load(Policy *p, const char *path) { - - _cleanup_free_ char *c = NULL, *policy_user = NULL, *policy_group = NULL; - _cleanup_(policy_item_freep) PolicyItem *i = NULL; - void *xml_state = NULL; - unsigned n_other = 0; - const char *q; - int r; - - enum { - STATE_OUTSIDE, - STATE_BUSCONFIG, - STATE_POLICY, - STATE_POLICY_CONTEXT, - STATE_POLICY_CONSOLE, - STATE_POLICY_USER, - STATE_POLICY_GROUP, - STATE_POLICY_OTHER_ATTRIBUTE, - STATE_ALLOW_DENY, - STATE_ALLOW_DENY_INTERFACE, - STATE_ALLOW_DENY_MEMBER, - STATE_ALLOW_DENY_ERROR, - STATE_ALLOW_DENY_PATH, - STATE_ALLOW_DENY_MESSAGE_TYPE, - STATE_ALLOW_DENY_NAME, - STATE_ALLOW_DENY_OTHER_ATTRIBUTE, - STATE_OTHER, - } state = STATE_OUTSIDE; - - enum { - POLICY_CATEGORY_NONE, - POLICY_CATEGORY_DEFAULT, - POLICY_CATEGORY_MANDATORY, - POLICY_CATEGORY_ON_CONSOLE, - POLICY_CATEGORY_NO_CONSOLE, - POLICY_CATEGORY_USER, - POLICY_CATEGORY_GROUP - } policy_category = POLICY_CATEGORY_NONE; - - unsigned line = 0; - - assert(p); - - r = read_full_file(path, &c, NULL); - if (r < 0) { - if (r == -ENOENT) - return 0; - if (r == -EISDIR) - return r; - - return log_error_errno(r, "Failed to load %s: %m", path); - } - - q = c; - for (;;) { - _cleanup_free_ char *name = NULL; - int t; - - t = xml_tokenize(&q, &name, &xml_state, &line); - if (t < 0) - return log_error_errno(t, "XML parse failure in %s: %m", path); - - switch (state) { - - case STATE_OUTSIDE: - - if (t == XML_TAG_OPEN) { - if (streq(name, "busconfig")) - state = STATE_BUSCONFIG; - else { - log_error("Unexpected tag %s at %s:%u.", name, path, line); - return -EINVAL; - } - - } else if (t == XML_END) - return 0; - else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token (1) at %s:%u.", path, line); - return -EINVAL; - } - - break; - - case STATE_BUSCONFIG: - - if (t == XML_TAG_OPEN) { - if (streq(name, "policy")) { - state = STATE_POLICY; - policy_category = POLICY_CATEGORY_NONE; - free(policy_user); - free(policy_group); - policy_user = policy_group = NULL; - } else { - state = STATE_OTHER; - n_other = 0; - } - } else if (t == XML_TAG_CLOSE_EMPTY || - (t == XML_TAG_CLOSE && streq(name, "busconfig"))) - state = STATE_OUTSIDE; - else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token (2) at %s:%u.", path, line); - return -EINVAL; - } - - break; - - case STATE_POLICY: - - if (t == XML_ATTRIBUTE_NAME) { - if (streq(name, "context")) - state = STATE_POLICY_CONTEXT; - else if (streq(name, "at_console")) - state = STATE_POLICY_CONSOLE; - else if (streq(name, "user")) - state = STATE_POLICY_USER; - else if (streq(name, "group")) - state = STATE_POLICY_GROUP; - else { - log_warning("Attribute %s of <policy> tag unknown at %s:%u, ignoring.", name, path, line); - state = STATE_POLICY_OTHER_ATTRIBUTE; - } - } else if (t == XML_TAG_CLOSE_EMPTY || - (t == XML_TAG_CLOSE && streq(name, "policy"))) - state = STATE_BUSCONFIG; - else if (t == XML_TAG_OPEN) { - PolicyItemType it; - - if (streq(name, "allow")) - it = POLICY_ITEM_ALLOW; - else if (streq(name, "deny")) - it = POLICY_ITEM_DENY; - else { - log_warning("Unknown tag %s in <policy> %s:%u.", name, path, line); - return -EINVAL; - } - - assert(!i); - i = new0(PolicyItem, 1); - if (!i) - return log_oom(); - - i->type = it; - state = STATE_ALLOW_DENY; - - } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token (3) at %s:%u.", path, line); - return -EINVAL; - } - - break; - - case STATE_POLICY_CONTEXT: - - if (t == XML_ATTRIBUTE_VALUE) { - if (streq(name, "default")) { - policy_category = POLICY_CATEGORY_DEFAULT; - state = STATE_POLICY; - } else if (streq(name, "mandatory")) { - policy_category = POLICY_CATEGORY_MANDATORY; - state = STATE_POLICY; - } else { - log_error("context= parameter %s unknown for <policy> at %s:%u.", name, path, line); - return -EINVAL; - } - } else { - log_error("Unexpected token (4) at %s:%u.", path, line); - return -EINVAL; - } - - break; - - case STATE_POLICY_CONSOLE: - - if (t == XML_ATTRIBUTE_VALUE) { - if (streq(name, "true")) { - policy_category = POLICY_CATEGORY_ON_CONSOLE; - state = STATE_POLICY; - } else if (streq(name, "false")) { - policy_category = POLICY_CATEGORY_NO_CONSOLE; - state = STATE_POLICY; - } else { - log_error("at_console= parameter %s unknown for <policy> at %s:%u.", name, path, line); - return -EINVAL; - } - } else { - log_error("Unexpected token (4.1) at %s:%u.", path, line); - return -EINVAL; - } - - break; - - case STATE_POLICY_USER: - - if (t == XML_ATTRIBUTE_VALUE) { - free(policy_user); - policy_user = name; - name = NULL; - policy_category = POLICY_CATEGORY_USER; - state = STATE_POLICY; - } else { - log_error("Unexpected token (5) in %s:%u.", path, line); - return -EINVAL; - } - - break; - - case STATE_POLICY_GROUP: - - if (t == XML_ATTRIBUTE_VALUE) { - free(policy_group); - policy_group = name; - name = NULL; - policy_category = POLICY_CATEGORY_GROUP; - state = STATE_POLICY; - } else { - log_error("Unexpected token (6) at %s:%u.", path, line); - return -EINVAL; - } - - break; - - case STATE_POLICY_OTHER_ATTRIBUTE: - - if (t == XML_ATTRIBUTE_VALUE) - state = STATE_POLICY; - else { - log_error("Unexpected token (7) in %s:%u.", path, line); - return -EINVAL; - } - - break; - - case STATE_ALLOW_DENY: - - assert(i); - - if (t == XML_ATTRIBUTE_NAME) { - PolicyItemClass ic; - - if (startswith(name, "send_")) - ic = POLICY_ITEM_SEND; - else if (startswith(name, "receive_")) - ic = POLICY_ITEM_RECV; - else if (streq(name, "own")) - ic = POLICY_ITEM_OWN; - else if (streq(name, "own_prefix")) - ic = POLICY_ITEM_OWN_PREFIX; - else if (streq(name, "user")) - ic = POLICY_ITEM_USER; - else if (streq(name, "group")) - ic = POLICY_ITEM_GROUP; - else if (STR_IN_SET(name, "eavesdrop", "log")) { - log_debug("Unsupported attribute %s= at %s:%u, ignoring.", name, path, line); - state = STATE_ALLOW_DENY_OTHER_ATTRIBUTE; - break; - } else { - log_error("Unknown attribute %s= at %s:%u, ignoring.", name, path, line); - state = STATE_ALLOW_DENY_OTHER_ATTRIBUTE; - break; - } - - if (i->class != _POLICY_ITEM_CLASS_UNSET && ic != i->class) { - log_error("send_, receive_/eavesdrop fields mixed on same tag at %s:%u.", path, line); - return -EINVAL; - } - - i->class = ic; - - if (ic == POLICY_ITEM_SEND || ic == POLICY_ITEM_RECV) { - const char *u; - - u = strchr(name, '_'); - assert(u); - - u++; - - if (streq(u, "interface")) - state = STATE_ALLOW_DENY_INTERFACE; - else if (streq(u, "member")) - state = STATE_ALLOW_DENY_MEMBER; - else if (streq(u, "error")) - state = STATE_ALLOW_DENY_ERROR; - else if (streq(u, "path")) - state = STATE_ALLOW_DENY_PATH; - else if (streq(u, "type")) - state = STATE_ALLOW_DENY_MESSAGE_TYPE; - else if ((streq(u, "destination") && ic == POLICY_ITEM_SEND) || - (streq(u, "sender") && ic == POLICY_ITEM_RECV)) - state = STATE_ALLOW_DENY_NAME; - else { - if (streq(u, "requested_reply")) - log_debug("Unsupported attribute %s= at %s:%u, ignoring.", name, path, line); - else - log_error("Unknown attribute %s= at %s:%u, ignoring.", name, path, line); - state = STATE_ALLOW_DENY_OTHER_ATTRIBUTE; - break; - } - } else - state = STATE_ALLOW_DENY_NAME; - - } else if (t == XML_TAG_CLOSE_EMPTY || - (t == XML_TAG_CLOSE && streq(name, i->type == POLICY_ITEM_ALLOW ? "allow" : "deny"))) { - - /* If the tag is fully empty so far, we consider it a recv */ - if (i->class == _POLICY_ITEM_CLASS_UNSET) - i->class = POLICY_ITEM_RECV; - - if (policy_category == POLICY_CATEGORY_DEFAULT) - item_append(i, &p->default_items); - else if (policy_category == POLICY_CATEGORY_MANDATORY) - item_append(i, &p->mandatory_items); - else if (policy_category == POLICY_CATEGORY_ON_CONSOLE) - item_append(i, &p->on_console_items); - else if (policy_category == POLICY_CATEGORY_NO_CONSOLE) - item_append(i, &p->no_console_items); - else if (policy_category == POLICY_CATEGORY_USER) { - const char *u = policy_user; - - assert_cc(sizeof(uid_t) == sizeof(uint32_t)); - - r = hashmap_ensure_allocated(&p->user_items, NULL); - if (r < 0) - return log_oom(); - - if (!u) { - log_error("User policy without name"); - return -EINVAL; - } - - r = get_user_creds(&u, &i->uid, NULL, NULL, NULL); - if (r < 0) { - log_error_errno(r, "Failed to resolve user %s, ignoring policy: %m", u); - free(i); - } else { - PolicyItem *first; - - first = hashmap_get(p->user_items, UID_TO_PTR(i->uid)); - item_append(i, &first); - i->uid_valid = true; - - r = hashmap_replace(p->user_items, UID_TO_PTR(i->uid), first); - if (r < 0) { - LIST_REMOVE(items, first, i); - return log_oom(); - } - } - - } else if (policy_category == POLICY_CATEGORY_GROUP) { - const char *g = policy_group; - - assert_cc(sizeof(gid_t) == sizeof(uint32_t)); - - r = hashmap_ensure_allocated(&p->group_items, NULL); - if (r < 0) - return log_oom(); - - if (!g) { - log_error("Group policy without name"); - return -EINVAL; - } - - r = get_group_creds(&g, &i->gid); - if (r < 0) { - log_error_errno(r, "Failed to resolve group %s, ignoring policy: %m", g); - free(i); - } else { - PolicyItem *first; - - first = hashmap_get(p->group_items, GID_TO_PTR(i->gid)); - item_append(i, &first); - i->gid_valid = true; - - r = hashmap_replace(p->group_items, GID_TO_PTR(i->gid), first); - if (r < 0) { - LIST_REMOVE(items, first, i); - return log_oom(); - } - } - } - - state = STATE_POLICY; - i = NULL; - - } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { - log_error("Unexpected token (8) at %s:%u.", path, line); - return -EINVAL; - } - - break; - - case STATE_ALLOW_DENY_INTERFACE: - - if (t == XML_ATTRIBUTE_VALUE) { - assert(i); - if (i->interface) { - log_error("Duplicate interface at %s:%u.", path, line); - return -EINVAL; - } - - if (!streq(name, "*")) { - i->interface = name; - name = NULL; - } - state = STATE_ALLOW_DENY; - } else { - log_error("Unexpected token (9) at %s:%u.", path, line); - return -EINVAL; - } - - break; - - case STATE_ALLOW_DENY_MEMBER: - - if (t == XML_ATTRIBUTE_VALUE) { - assert(i); - if (i->member) { - log_error("Duplicate member in %s:%u.", path, line); - return -EINVAL; - } - - if (!streq(name, "*")) { - i->member = name; - name = NULL; - } - state = STATE_ALLOW_DENY; - } else { - log_error("Unexpected token (10) in %s:%u.", path, line); - return -EINVAL; - } - - break; - - case STATE_ALLOW_DENY_ERROR: - - if (t == XML_ATTRIBUTE_VALUE) { - assert(i); - if (i->error) { - log_error("Duplicate error in %s:%u.", path, line); - return -EINVAL; - } - - if (!streq(name, "*")) { - i->error = name; - name = NULL; - } - state = STATE_ALLOW_DENY; - } else { - log_error("Unexpected token (11) in %s:%u.", path, line); - return -EINVAL; - } - - break; - - case STATE_ALLOW_DENY_PATH: - - if (t == XML_ATTRIBUTE_VALUE) { - assert(i); - if (i->path) { - log_error("Duplicate path in %s:%u.", path, line); - return -EINVAL; - } - - if (!streq(name, "*")) { - i->path = name; - name = NULL; - } - state = STATE_ALLOW_DENY; - } else { - log_error("Unexpected token (12) in %s:%u.", path, line); - return -EINVAL; - } - - break; - - case STATE_ALLOW_DENY_MESSAGE_TYPE: - - if (t == XML_ATTRIBUTE_VALUE) { - assert(i); - - if (i->message_type != 0) { - log_error("Duplicate message type in %s:%u.", path, line); - return -EINVAL; - } - - if (!streq(name, "*")) { - r = bus_message_type_from_string(name, &i->message_type); - if (r < 0) { - log_error("Invalid message type in %s:%u.", path, line); - return -EINVAL; - } - } - - state = STATE_ALLOW_DENY; - } else { - log_error("Unexpected token (13) in %s:%u.", path, line); - return -EINVAL; - } - - break; - - case STATE_ALLOW_DENY_NAME: - - if (t == XML_ATTRIBUTE_VALUE) { - assert(i); - if (i->name) { - log_error("Duplicate name in %s:%u.", path, line); - return -EINVAL; - } - - switch (i->class) { - case POLICY_ITEM_USER: - if (!streq(name, "*")) { - const char *u = name; - - r = get_user_creds(&u, &i->uid, NULL, NULL, NULL); - if (r < 0) - log_error_errno(r, "Failed to resolve user %s: %m", name); - else - i->uid_valid = true; - } - break; - case POLICY_ITEM_GROUP: - if (!streq(name, "*")) { - const char *g = name; - - r = get_group_creds(&g, &i->gid); - if (r < 0) - log_error_errno(r, "Failed to resolve group %s: %m", name); - else - i->gid_valid = true; - } - break; - - case POLICY_ITEM_SEND: - case POLICY_ITEM_RECV: - - if (streq(name, "*")) - name = mfree(name); - break; - - - default: - break; - } - - i->name = name; - name = NULL; - - state = STATE_ALLOW_DENY; - } else { - log_error("Unexpected token (14) in %s:%u.", path, line); - return -EINVAL; - } - - break; - - case STATE_ALLOW_DENY_OTHER_ATTRIBUTE: - - if (t == XML_ATTRIBUTE_VALUE) - state = STATE_ALLOW_DENY; - else { - log_error("Unexpected token (15) in %s:%u.", path, line); - return -EINVAL; - } - - break; - - case STATE_OTHER: - - if (t == XML_TAG_OPEN) - n_other++; - else if (t == XML_TAG_CLOSE || t == XML_TAG_CLOSE_EMPTY) { - - if (n_other == 0) - state = STATE_BUSCONFIG; - else - n_other--; - } - - break; - } - } -} - -enum { - DENY, - ALLOW, - DUNNO, -}; - -static const char *verdict_to_string(int v) { - switch (v) { - - case DENY: - return "DENY"; - case ALLOW: - return "ALLOW"; - case DUNNO: - return "DUNNO"; - } - - return NULL; -} - -struct policy_check_filter { - PolicyItemClass class; - uid_t uid; - gid_t gid; - int message_type; - const char *name; - const char *interface; - const char *path; - const char *member; -}; - -static int is_permissive(PolicyItem *i) { - - assert(i); - - return (i->type == POLICY_ITEM_ALLOW) ? ALLOW : DENY; -} - -static int check_policy_item(PolicyItem *i, const struct policy_check_filter *filter) { - - assert(i); - assert(filter); - - switch (i->class) { - case POLICY_ITEM_SEND: - case POLICY_ITEM_RECV: - - if (i->name && !streq_ptr(i->name, filter->name)) - break; - - if ((i->message_type != 0) && (i->message_type != filter->message_type)) - break; - - if (i->path && !streq_ptr(i->path, filter->path)) - break; - - if (i->member && !streq_ptr(i->member, filter->member)) - break; - - if (i->interface && !streq_ptr(i->interface, filter->interface)) - break; - - return is_permissive(i); - - case POLICY_ITEM_OWN: - assert(filter->name); - - if (streq(i->name, "*") || streq(i->name, filter->name)) - return is_permissive(i); - break; - - case POLICY_ITEM_OWN_PREFIX: - assert(filter->name); - - if (streq(i->name, "*") || service_name_startswith(filter->name, i->name)) - return is_permissive(i); - break; - - case POLICY_ITEM_USER: - if (filter->uid != UID_INVALID) - if ((streq_ptr(i->name, "*") || (i->uid_valid && i->uid == filter->uid))) - return is_permissive(i); - break; - - case POLICY_ITEM_GROUP: - if (filter->gid != GID_INVALID) - if ((streq_ptr(i->name, "*") || (i->gid_valid && i->gid == filter->gid))) - return is_permissive(i); - break; - - case POLICY_ITEM_IGNORE: - default: - break; - } - - return DUNNO; -} - -static int check_policy_items(PolicyItem *items, const struct policy_check_filter *filter) { - - PolicyItem *i; - int verdict = DUNNO; - - assert(filter); - - /* Check all policies in a set - a broader one might be followed by a more specific one, - * and the order of rules in policy definitions matters */ - LIST_FOREACH(items, i, items) { - int v; - - if (i->class != filter->class && - !(i->class == POLICY_ITEM_OWN_PREFIX && filter->class == POLICY_ITEM_OWN)) - continue; - - v = check_policy_item(i, filter); - if (v != DUNNO) - verdict = v; - } - - return verdict; -} - -static int policy_check(Policy *p, const struct policy_check_filter *filter) { - - PolicyItem *items; - int verdict, v; - - assert(p); - assert(filter); - - assert(IN_SET(filter->class, POLICY_ITEM_SEND, POLICY_ITEM_RECV, POLICY_ITEM_OWN, POLICY_ITEM_USER, POLICY_ITEM_GROUP)); - - /* - * The policy check is implemented by the following logic: - * - * 1. Check default items - * 2. Check group items - * 3. Check user items - * 4. Check on/no_console items - * 5. Check mandatory items - * - * Later rules override earlier rules. - */ - - verdict = check_policy_items(p->default_items, filter); - - if (filter->gid != GID_INVALID) { - items = hashmap_get(p->group_items, GID_TO_PTR(filter->gid)); - if (items) { - v = check_policy_items(items, filter); - if (v != DUNNO) - verdict = v; - } - } - - if (filter->uid != UID_INVALID) { - items = hashmap_get(p->user_items, UID_TO_PTR(filter->uid)); - if (items) { - v = check_policy_items(items, filter); - if (v != DUNNO) - verdict = v; - } - } - - if (filter->uid != UID_INVALID && sd_uid_get_seats(filter->uid, -1, NULL) > 0) - v = check_policy_items(p->on_console_items, filter); - else - v = check_policy_items(p->no_console_items, filter); - if (v != DUNNO) - verdict = v; - - v = check_policy_items(p->mandatory_items, filter); - if (v != DUNNO) - verdict = v; - - return verdict; -} - -bool policy_check_own(Policy *p, uid_t uid, gid_t gid, const char *name) { - - struct policy_check_filter filter = { - .class = POLICY_ITEM_OWN, - .uid = uid, - .gid = gid, - .name = name, - }; - - int verdict; - - assert(p); - assert(name); - - verdict = policy_check(p, &filter); - - log_full(LOG_AUTH | (verdict != ALLOW ? LOG_WARNING : LOG_DEBUG), - "Ownership permission check for uid=" UID_FMT " gid=" GID_FMT" name=%s: %s", - uid, gid, strna(name), strna(verdict_to_string(verdict))); - - return verdict == ALLOW; -} - -bool policy_check_hello(Policy *p, uid_t uid, gid_t gid) { - - struct policy_check_filter filter = { - .uid = uid, - .gid = gid, - }; - int verdict; - - assert(p); - - filter.class = POLICY_ITEM_USER; - verdict = policy_check(p, &filter); - - if (verdict != DENY) { - int v; - - filter.class = POLICY_ITEM_GROUP; - v = policy_check(p, &filter); - if (v != DUNNO) - verdict = v; - } - - log_full(LOG_AUTH | (verdict != ALLOW ? LOG_WARNING : LOG_DEBUG), - "Hello permission check for uid=" UID_FMT " gid=" GID_FMT": %s", - uid, gid, strna(verdict_to_string(verdict))); - - return verdict == ALLOW; -} - -bool policy_check_one_recv(Policy *p, - uid_t uid, - gid_t gid, - int message_type, - const char *name, - const char *path, - const char *interface, - const char *member) { - - struct policy_check_filter filter = { - .class = POLICY_ITEM_RECV, - .uid = uid, - .gid = gid, - .message_type = message_type, - .name = name, - .interface = interface, - .path = path, - .member = member, - }; - - assert(p); - - return policy_check(p, &filter) == ALLOW; -} - -bool policy_check_recv(Policy *p, - uid_t uid, - gid_t gid, - int message_type, - Set *names, - char **namesv, - const char *path, - const char *interface, - const char *member, - bool dbus_to_kernel) { - - char *n, **nv, *last = NULL; - bool allow = false; - Iterator i; - - assert(p); - - if (set_isempty(names) && strv_isempty(namesv)) { - allow = policy_check_one_recv(p, uid, gid, message_type, NULL, path, interface, member); - } else { - SET_FOREACH(n, names, i) { - last = n; - allow = policy_check_one_recv(p, uid, gid, message_type, n, path, interface, member); - if (allow) - break; - } - if (!allow) { - STRV_FOREACH(nv, namesv) { - last = *nv; - allow = policy_check_one_recv(p, uid, gid, message_type, *nv, path, interface, member); - if (allow) - break; - } - } - } - - log_full(LOG_AUTH | (!allow ? LOG_WARNING : LOG_DEBUG), - "Receive permission check %s for uid=" UID_FMT " gid=" GID_FMT" message=%s name=%s path=%s interface=%s member=%s: %s", - dbus_to_kernel ? "dbus-1 to kernel" : "kernel to dbus-1", uid, gid, bus_message_type_to_string(message_type), strna(last), - strna(path), strna(interface), strna(member), allow ? "ALLOW" : "DENY"); - - return allow; -} - -bool policy_check_one_send(Policy *p, - uid_t uid, - gid_t gid, - int message_type, - const char *name, - const char *path, - const char *interface, - const char *member) { - - struct policy_check_filter filter = { - .class = POLICY_ITEM_SEND, - .uid = uid, - .gid = gid, - .message_type = message_type, - .name = name, - .interface = interface, - .path = path, - .member = member, - }; - - assert(p); - - return policy_check(p, &filter) == ALLOW; -} - -bool policy_check_send(Policy *p, - uid_t uid, - gid_t gid, - int message_type, - Set *names, - char **namesv, - const char *path, - const char *interface, - const char *member, - bool dbus_to_kernel, - char **out_used_name) { - - char *n, **nv, *last = NULL; - bool allow = false; - Iterator i; - - assert(p); - - if (set_isempty(names) && strv_isempty(namesv)) { - allow = policy_check_one_send(p, uid, gid, message_type, NULL, path, interface, member); - } else { - SET_FOREACH(n, names, i) { - last = n; - allow = policy_check_one_send(p, uid, gid, message_type, n, path, interface, member); - if (allow) - break; - } - if (!allow) { - STRV_FOREACH(nv, namesv) { - last = *nv; - allow = policy_check_one_send(p, uid, gid, message_type, *nv, path, interface, member); - if (allow) - break; - } - } - } - - if (out_used_name) - *out_used_name = last; - - log_full(LOG_AUTH | (!allow ? LOG_WARNING : LOG_DEBUG), - "Send permission check %s for uid=" UID_FMT " gid=" GID_FMT" message=%s name=%s path=%s interface=%s member=%s: %s", - dbus_to_kernel ? "dbus-1 to kernel" : "kernel to dbus-1", uid, gid, bus_message_type_to_string(message_type), strna(last), - strna(path), strna(interface), strna(member), allow ? "ALLOW" : "DENY"); - - return allow; -} - -int policy_load(Policy *p, char **files) { - char **i; - int r; - - assert(p); - - STRV_FOREACH(i, files) { - - r = file_load(p, *i); - if (r == -EISDIR) { - _cleanup_strv_free_ char **l = NULL; - char **j; - - r = conf_files_list(&l, ".conf", NULL, *i, NULL); - if (r < 0) - return log_error_errno(r, "Failed to get configuration file list: %m"); - - STRV_FOREACH(j, l) - file_load(p, *j); - } - - /* We ignore all errors but EISDIR, and just proceed. */ - } - - return 0; -} - -void policy_free(Policy *p) { - PolicyItem *i, *first; - - if (!p) - return; - - while ((i = p->default_items)) { - LIST_REMOVE(items, p->default_items, i); - policy_item_free(i); - } - - while ((i = p->mandatory_items)) { - LIST_REMOVE(items, p->mandatory_items, i); - policy_item_free(i); - } - - while ((i = p->on_console_items)) { - LIST_REMOVE(items, p->on_console_items, i); - policy_item_free(i); - } - - while ((i = p->no_console_items)) { - LIST_REMOVE(items, p->no_console_items, i); - policy_item_free(i); - } - - while ((first = hashmap_steal_first(p->user_items))) { - - while ((i = first)) { - LIST_REMOVE(items, first, i); - policy_item_free(i); - } - } - - while ((first = hashmap_steal_first(p->group_items))) { - - while ((i = first)) { - LIST_REMOVE(items, first, i); - policy_item_free(i); - } - } - - hashmap_free(p->user_items); - hashmap_free(p->group_items); - - p->user_items = p->group_items = NULL; -} - -static void dump_items(PolicyItem *items, const char *prefix) { - - PolicyItem *i; - - if (!items) - return; - - if (!prefix) - prefix = ""; - - LIST_FOREACH(items, i, items) { - - printf("%sType: %s\n" - "%sClass: %s\n", - prefix, policy_item_type_to_string(i->type), - prefix, policy_item_class_to_string(i->class)); - - if (i->interface) - printf("%sInterface: %s\n", - prefix, i->interface); - - if (i->member) - printf("%sMember: %s\n", - prefix, i->member); - - if (i->error) - printf("%sError: %s\n", - prefix, i->error); - - if (i->path) - printf("%sPath: %s\n", - prefix, i->path); - - if (i->name) - printf("%sName: %s\n", - prefix, i->name); - - if (i->message_type != 0) - printf("%sMessage Type: %s\n", - prefix, bus_message_type_to_string(i->message_type)); - - if (i->uid_valid) { - _cleanup_free_ char *user; - - user = uid_to_name(i->uid); - - printf("%sUser: %s ("UID_FMT")\n", - prefix, strna(user), i->uid); - } - - if (i->gid_valid) { - _cleanup_free_ char *group; - - group = gid_to_name(i->gid); - - printf("%sGroup: %s ("GID_FMT")\n", - prefix, strna(group), i->gid); - } - printf("%s-\n", prefix); - } -} - -static void dump_hashmap_items(Hashmap *h) { - PolicyItem *i; - Iterator j; - void *k; - - HASHMAP_FOREACH_KEY(i, k, h, j) { - printf("\t%s Item for " UID_FMT ":\n", draw_special_char(DRAW_ARROW), PTR_TO_UID(k)); - dump_items(i, "\t\t"); - } -} - -void policy_dump(Policy *p) { - - printf("%s Default Items:\n", draw_special_char(DRAW_ARROW)); - dump_items(p->default_items, "\t"); - - printf("%s Group Items:\n", draw_special_char(DRAW_ARROW)); - dump_hashmap_items(p->group_items); - - printf("%s User Items:\n", draw_special_char(DRAW_ARROW)); - dump_hashmap_items(p->user_items); - - printf("%s On-Console Items:\n", draw_special_char(DRAW_ARROW)); - dump_items(p->on_console_items, "\t"); - - printf("%s No-Console Items:\n", draw_special_char(DRAW_ARROW)); - dump_items(p->no_console_items, "\t"); - - printf("%s Mandatory Items:\n", draw_special_char(DRAW_ARROW)); - dump_items(p->mandatory_items, "\t"); - - fflush(stdout); -} - -int shared_policy_new(SharedPolicy **out) { - SharedPolicy *sp; - int r; - - sp = new0(SharedPolicy, 1); - if (!sp) - return log_oom(); - - r = pthread_mutex_init(&sp->lock, NULL); - if (r != 0) { - r = log_error_errno(r, "Cannot initialize shared policy mutex: %m"); - goto exit_free; - } - - r = pthread_rwlock_init(&sp->rwlock, NULL); - if (r != 0) { - r = log_error_errno(r, "Cannot initialize shared policy rwlock: %m"); - goto exit_mutex; - } - - *out = sp; - sp = NULL; - return 0; - - /* pthread lock destruction is not fail-safe... meh! */ -exit_mutex: - pthread_mutex_destroy(&sp->lock); -exit_free: - free(sp); - return r; -} - -SharedPolicy *shared_policy_free(SharedPolicy *sp) { - if (!sp) - return NULL; - - policy_free(sp->policy); - pthread_rwlock_destroy(&sp->rwlock); - pthread_mutex_destroy(&sp->lock); - strv_free(sp->configuration); - free(sp); - - return NULL; -} - -static int shared_policy_reload_unlocked(SharedPolicy *sp, char **configuration) { - Policy old, buffer = {}; - bool free_old; - int r; - - assert(sp); - - r = policy_load(&buffer, configuration); - if (r < 0) - return log_error_errno(r, "Failed to load policy: %m"); - - log_debug("Reloading configuration"); - /* policy_dump(&buffer); */ - - pthread_rwlock_wrlock(&sp->rwlock); - memcpy(&old, &sp->buffer, sizeof(old)); - memcpy(&sp->buffer, &buffer, sizeof(buffer)); - free_old = !!sp->policy; - sp->policy = &sp->buffer; - pthread_rwlock_unlock(&sp->rwlock); - - if (free_old) - policy_free(&old); - - return 0; -} - -int shared_policy_reload(SharedPolicy *sp) { - int r; - - assert(sp); - - pthread_mutex_lock(&sp->lock); - r = shared_policy_reload_unlocked(sp, sp->configuration); - pthread_mutex_unlock(&sp->lock); - - return r; -} - -int shared_policy_preload(SharedPolicy *sp, char **configuration) { - _cleanup_strv_free_ char **conf = NULL; - int r = 0; - - assert(sp); - - conf = strv_copy(configuration); - if (!conf) - return log_oom(); - - pthread_mutex_lock(&sp->lock); - if (!sp->policy) { - r = shared_policy_reload_unlocked(sp, conf); - if (r >= 0) { - sp->configuration = conf; - conf = NULL; - } - } - pthread_mutex_unlock(&sp->lock); - - return r; -} - -Policy *shared_policy_acquire(SharedPolicy *sp) { - assert(sp); - - pthread_rwlock_rdlock(&sp->rwlock); - if (sp->policy) - return sp->policy; - pthread_rwlock_unlock(&sp->rwlock); - - return NULL; -} - -void shared_policy_release(SharedPolicy *sp, Policy *p) { - assert(sp); - assert(!p || sp->policy == p); - - if (p) - pthread_rwlock_unlock(&sp->rwlock); -} - -static const char* const policy_item_type_table[_POLICY_ITEM_TYPE_MAX] = { - [_POLICY_ITEM_TYPE_UNSET] = "unset", - [POLICY_ITEM_ALLOW] = "allow", - [POLICY_ITEM_DENY] = "deny", -}; -DEFINE_STRING_TABLE_LOOKUP(policy_item_type, PolicyItemType); - -static const char* const policy_item_class_table[_POLICY_ITEM_CLASS_MAX] = { - [_POLICY_ITEM_CLASS_UNSET] = "unset", - [POLICY_ITEM_SEND] = "send", - [POLICY_ITEM_RECV] = "recv", - [POLICY_ITEM_OWN] = "own", - [POLICY_ITEM_OWN_PREFIX] = "own-prefix", - [POLICY_ITEM_USER] = "user", - [POLICY_ITEM_GROUP] = "group", - [POLICY_ITEM_IGNORE] = "ignore", -}; -DEFINE_STRING_TABLE_LOOKUP(policy_item_class, PolicyItemClass); diff --git a/src/bus-proxyd/bus-xml-policy.h b/src/bus-proxyd/bus-xml-policy.h deleted file mode 100644 index 3dcddaa048..0000000000 --- a/src/bus-proxyd/bus-xml-policy.h +++ /dev/null @@ -1,147 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2013 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <pthread.h> - -#include "hashmap.h" -#include "list.h" - -typedef enum PolicyItemType { - _POLICY_ITEM_TYPE_UNSET = 0, - POLICY_ITEM_ALLOW, - POLICY_ITEM_DENY, - _POLICY_ITEM_TYPE_MAX, - _POLICY_ITEM_TYPE_INVALID = -1, -} PolicyItemType; - -typedef enum PolicyItemClass { - _POLICY_ITEM_CLASS_UNSET = 0, - POLICY_ITEM_SEND, - POLICY_ITEM_RECV, - POLICY_ITEM_OWN, - POLICY_ITEM_OWN_PREFIX, - POLICY_ITEM_USER, - POLICY_ITEM_GROUP, - POLICY_ITEM_IGNORE, - _POLICY_ITEM_CLASS_MAX, - _POLICY_ITEM_CLASS_INVALID = -1, -} PolicyItemClass; - -typedef struct PolicyItem PolicyItem; - -struct PolicyItem { - PolicyItemType type; - PolicyItemClass class; - char *interface; - char *member; - char *error; - char *path; - char *name; - uint8_t message_type; - uid_t uid; - gid_t gid; - - bool uid_valid, gid_valid; - - LIST_FIELDS(PolicyItem, items); -}; - -typedef struct Policy { - LIST_HEAD(PolicyItem, default_items); - LIST_HEAD(PolicyItem, mandatory_items); - LIST_HEAD(PolicyItem, on_console_items); - LIST_HEAD(PolicyItem, no_console_items); - Hashmap *user_items; - Hashmap *group_items; -} Policy; - -typedef struct SharedPolicy { - char **configuration; - pthread_mutex_t lock; - pthread_rwlock_t rwlock; - Policy buffer; - Policy *policy; -} SharedPolicy; - -/* policy */ - -int policy_load(Policy *p, char **files); -void policy_free(Policy *p); - -bool policy_check_own(Policy *p, uid_t uid, gid_t gid, const char *name); -bool policy_check_hello(Policy *p, uid_t uid, gid_t gid); -bool policy_check_one_recv(Policy *p, - uid_t uid, - gid_t gid, - int message_type, - const char *name, - const char *path, - const char *interface, - const char *member); -bool policy_check_recv(Policy *p, - uid_t uid, - gid_t gid, - int message_type, - Set *names, - char **namesv, - const char *path, - const char *interface, - const char *member, - bool dbus_to_kernel); -bool policy_check_one_send(Policy *p, - uid_t uid, - gid_t gid, - int message_type, - const char *name, - const char *path, - const char *interface, - const char *member); -bool policy_check_send(Policy *p, - uid_t uid, - gid_t gid, - int message_type, - Set *names, - char **namesv, - const char *path, - const char *interface, - const char *member, - bool dbus_to_kernel, - char **out_used_name); - -void policy_dump(Policy *p); - -const char* policy_item_type_to_string(PolicyItemType t) _const_; -PolicyItemType policy_item_type_from_string(const char *s) _pure_; - -const char* policy_item_class_to_string(PolicyItemClass t) _const_; -PolicyItemClass policy_item_class_from_string(const char *s) _pure_; - -/* shared policy */ - -int shared_policy_new(SharedPolicy **out); -SharedPolicy *shared_policy_free(SharedPolicy *sp); - -int shared_policy_reload(SharedPolicy *sp); -int shared_policy_preload(SharedPolicy *sp, char **configuration); -Policy *shared_policy_acquire(SharedPolicy *sp); -void shared_policy_release(SharedPolicy *sp, Policy *p); - -DEFINE_TRIVIAL_CLEANUP_FUNC(SharedPolicy*, shared_policy_free); diff --git a/src/bus-proxyd/driver.c b/src/bus-proxyd/driver.c deleted file mode 100644 index 1af5c310ea..0000000000 --- a/src/bus-proxyd/driver.c +++ /dev/null @@ -1,745 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2013 Daniel Mack - Copyright 2014 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <errno.h> -#include <stddef.h> -#include <string.h> - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-internal.h" -#include "bus-message.h" -#include "bus-util.h" -#include "driver.h" -#include "env-util.h" -#include "proxy.h" -#include "set.h" -#include "strv.h" -#include "synthesize.h" -#include "util.h" - -static int get_creds_by_name(sd_bus *bus, const char *name, uint64_t mask, sd_bus_creds **_creds, sd_bus_error *error) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; - int r; - - assert(bus); - assert(name); - assert(_creds); - - r = sd_bus_get_name_creds(bus, name, mask, &c); - if (r == -ESRCH || r == -ENXIO) - return sd_bus_error_setf(error, SD_BUS_ERROR_NAME_HAS_NO_OWNER, "Name %s is currently not owned by anyone.", name); - if (r < 0) - return r; - - *_creds = c; - c = NULL; - - return 0; -} - -static int get_creds_by_message(sd_bus *bus, sd_bus_message *m, uint64_t mask, sd_bus_creds **_creds, sd_bus_error *error) { - const char *name; - int r; - - assert(bus); - assert(m); - assert(_creds); - - r = sd_bus_message_read(m, "s", &name); - if (r < 0) - return r; - - return get_creds_by_name(bus, name, mask, _creds, error); -} - -static int driver_activation(sd_bus_message *reply, void *userdata, sd_bus_error *error) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - ProxyActivation *activation = userdata; - - /* - * The org.freedesktop.DBus.Peer.Ping() call returned. We don't care - * whether this succeeded, failed, was not implemented or timed out. We - * cannot assume that the target reacts to this properly. Hence, just - * send the reply to the activation request and be done. - */ - - m = activation->request; /* claim reference */ - - --activation->proxy->n_activations; - LIST_REMOVE(activations_by_proxy, activation->proxy->activations, activation); - sd_bus_slot_unref(activation->slot); - free(activation); - - return synthetic_reply_method_return(m, "u", BUS_START_REPLY_SUCCESS); -} - -int bus_proxy_process_driver(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m, SharedPolicy *sp, const struct ucred *ucred, Set *owned_names) { - int r; - - assert(a); - assert(b); - assert(m); - - if (!a->is_kernel) - return 0; - - if (!streq_ptr(sd_bus_message_get_destination(m), "org.freedesktop.DBus")) - return 0; - - /* The "Hello()" call is is handled in process_hello() */ - - if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) { - - if (!sd_bus_message_has_signature(m, "")) - return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); - - return synthetic_reply_method_return(m, "s", - "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" " - "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n" - "<node>\n" - " <interface name=\"org.freedesktop.DBus.Introspectable\">\n" - " <method name=\"Introspect\">\n" - " <arg name=\"data\" type=\"s\" direction=\"out\"/>\n" - " </method>\n" - " </interface>\n" - " <interface name=\"org.freedesktop.DBus\">\n" - " <method name=\"AddMatch\">\n" - " <arg type=\"s\" direction=\"in\"/>\n" - " </method>\n" - " <method name=\"RemoveMatch\">\n" - " <arg type=\"s\" direction=\"in\"/>\n" - " </method>\n" - " <method name=\"GetConnectionCredentials\">\n" - " <arg type=\"s\" direction=\"in\"/>\n" - " <arg type=\"a{sv}\" direction=\"out\"/>\n" - " </method>\n" - " <method name=\"GetConnectionSELinuxSecurityContext\">\n" - " <arg type=\"s\" direction=\"in\"/>\n" - " <arg type=\"ay\" direction=\"out\"/>\n" - " </method>\n" - " <method name=\"GetConnectionUnixProcessID\">\n" - " <arg type=\"s\" direction=\"in\"/>\n" - " <arg type=\"u\" direction=\"out\"/>\n" - " </method>\n" - " <method name=\"GetConnectionUnixUser\">\n" - " <arg type=\"s\" direction=\"in\"/>\n" - " <arg type=\"u\" direction=\"out\"/>\n" - " </method>\n" - " <method name=\"GetId\">\n" - " <arg type=\"s\" direction=\"out\"/>\n" - " </method>\n" - " <method name=\"GetNameOwner\">\n" - " <arg type=\"s\" direction=\"in\"/>\n" - " <arg type=\"s\" direction=\"out\"/>\n" - " </method>\n" - " <method name=\"Hello\">\n" - " <arg type=\"s\" direction=\"out\"/>\n" - " </method>\n" - " <method name=\"ListActivatableNames\">\n" - " <arg type=\"as\" direction=\"out\"/>\n" - " </method>\n" - " <method name=\"ListNames\">\n" - " <arg type=\"as\" direction=\"out\"/>\n" - " </method>\n" - " <method name=\"ListQueuedOwners\">\n" - " <arg type=\"s\" direction=\"in\"/>\n" - " <arg type=\"as\" direction=\"out\"/>\n" - " </method>\n" - " <method name=\"NameHasOwner\">\n" - " <arg type=\"s\" direction=\"in\"/>\n" - " <arg type=\"b\" direction=\"out\"/>\n" - " </method>\n" - " <method name=\"ReleaseName\">\n" - " <arg type=\"s\" direction=\"in\"/>\n" - " <arg type=\"u\" direction=\"out\"/>\n" - " </method>\n" - " <method name=\"ReloadConfig\">\n" - " </method>\n" - " <method name=\"RequestName\">\n" - " <arg type=\"s\" direction=\"in\"/>\n" - " <arg type=\"u\" direction=\"in\"/>\n" - " <arg type=\"u\" direction=\"out\"/>\n" - " </method>\n" - " <method name=\"StartServiceByName\">\n" - " <arg type=\"s\" direction=\"in\"/>\n" - " <arg type=\"u\" direction=\"in\"/>\n" - " <arg type=\"u\" direction=\"out\"/>\n" - " </method>\n" - " <method name=\"UpdateActivationEnvironment\">\n" - " <arg type=\"a{ss}\" direction=\"in\"/>\n" - " </method>\n" - " <signal name=\"NameAcquired\">\n" - " <arg type=\"s\"/>\n" - " </signal>\n" - " <signal name=\"NameLost\">\n" - " <arg type=\"s\"/>\n" - " </signal>\n" - " <signal name=\"NameOwnerChanged\">\n" - " <arg type=\"s\"/>\n" - " <arg type=\"s\"/>\n" - " <arg type=\"s\"/>\n" - " </signal>\n" - " </interface>\n" - "</node>\n"); - - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "AddMatch")) { - const char *match; - - if (!sd_bus_message_has_signature(m, "s")) - return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); - - r = sd_bus_message_read(m, "s", &match); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - r = sd_bus_add_match(a, NULL, match, proxy_match, p); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - return synthetic_reply_method_return(m, NULL); - - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "RemoveMatch")) { - const char *match; - - if (!sd_bus_message_has_signature(m, "s")) - return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); - - r = sd_bus_message_read(m, "s", &match); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - r = bus_remove_match_by_string(a, match, NULL, NULL); - if (r == 0) - return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_MATCH_RULE_NOT_FOUND, "Match rule not found")); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - return synthetic_reply_method_return(m, NULL); - - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionCredentials")) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - - if (!sd_bus_message_has_signature(m, "s")) - return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); - - r = get_creds_by_message(a, m, SD_BUS_CREDS_PID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SELINUX_CONTEXT, &creds, &error); - if (r < 0) - return synthetic_reply_method_errno(m, r, &error); - - r = sd_bus_message_new_method_return(m, &reply); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - r = sd_bus_message_open_container(reply, 'a', "{sv}"); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - /* Due to i.e. namespace translations some data might be missing */ - - if (creds->mask & SD_BUS_CREDS_PID) { - r = sd_bus_message_append(reply, "{sv}", "ProcessID", "u", (uint32_t) creds->pid); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - } - - if (creds->mask & SD_BUS_CREDS_EUID) { - r = sd_bus_message_append(reply, "{sv}", "UnixUserID", "u", (uint32_t) creds->euid); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - } - - if (creds->mask & SD_BUS_CREDS_SELINUX_CONTEXT) { - r = sd_bus_message_open_container(reply, 'e', "sv"); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - r = sd_bus_message_append(reply, "s", "LinuxSecurityLabel"); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - r = sd_bus_message_open_container(reply, 'v', "ay"); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - r = sd_bus_message_append_array(reply, 'y', creds->label, strlen(creds->label)); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - r = sd_bus_message_close_container(reply); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - r = sd_bus_message_close_container(reply); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - } - - r = sd_bus_message_close_container(reply); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - return synthetic_driver_send(m->bus, reply); - - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionSELinuxSecurityContext")) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - - if (!sd_bus_message_has_signature(m, "s")) - return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); - - r = get_creds_by_message(a, m, SD_BUS_CREDS_SELINUX_CONTEXT, &creds, &error); - if (r < 0) - return synthetic_reply_method_errno(m, r, &error); - - if (!(creds->mask & SD_BUS_CREDS_SELINUX_CONTEXT)) - return synthetic_reply_method_errno(m, -EOPNOTSUPP, NULL); - - r = sd_bus_message_new_method_return(m, &reply); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - r = sd_bus_message_append_array(reply, 'y', creds->label, strlen(creds->label)); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - return synthetic_driver_send(m->bus, reply); - - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionUnixProcessID")) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - - if (!sd_bus_message_has_signature(m, "s")) - return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); - - r = get_creds_by_message(a, m, SD_BUS_CREDS_PID, &creds, &error); - if (r < 0) - return synthetic_reply_method_errno(m, r, &error); - - if (!(creds->mask & SD_BUS_CREDS_PID)) - return synthetic_reply_method_errno(m, -EOPNOTSUPP, NULL); - - return synthetic_reply_method_return(m, "u", (uint32_t) creds->pid); - - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionUnixUser")) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - - if (!sd_bus_message_has_signature(m, "s")) - return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); - - r = get_creds_by_message(a, m, SD_BUS_CREDS_EUID, &creds, &error); - if (r < 0) - return synthetic_reply_method_errno(m, r, &error); - - if (!(creds->mask & SD_BUS_CREDS_EUID)) - return synthetic_reply_method_errno(m, -EOPNOTSUPP, NULL); - - return synthetic_reply_method_return(m, "u", (uint32_t) creds->euid); - - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetId")) { - sd_id128_t server_id; - char buf[SD_ID128_STRING_MAX]; - - if (!sd_bus_message_has_signature(m, "")) - return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); - - r = sd_bus_get_bus_id(a, &server_id); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - return synthetic_reply_method_return(m, "s", sd_id128_to_string(server_id, buf)); - - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetNameOwner")) { - const char *name; - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - - if (!sd_bus_message_has_signature(m, "s")) - return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); - - r = sd_bus_message_read(m, "s", &name); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - if (streq(name, "org.freedesktop.DBus")) - return synthetic_reply_method_return(m, "s", "org.freedesktop.DBus"); - - r = get_creds_by_name(a, name, SD_BUS_CREDS_UNIQUE_NAME, &creds, &error); - if (r < 0) - return synthetic_reply_method_errno(m, r, &error); - - if (!(creds->mask & SD_BUS_CREDS_UNIQUE_NAME)) - return synthetic_reply_method_errno(m, -EOPNOTSUPP, NULL); - - return synthetic_reply_method_return(m, "s", creds->unique_name); - - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ListActivatableNames")) { - _cleanup_strv_free_ char **names = NULL; - - if (!sd_bus_message_has_signature(m, "")) - return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); - - r = sd_bus_list_names(a, NULL, &names); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - /* Let's sort the names list to make it stable */ - strv_sort(names); - - return synthetic_reply_method_return_strv(m, names); - - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ListNames")) { - _cleanup_strv_free_ char **names = NULL; - - if (!sd_bus_message_has_signature(m, "")) - return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); - - r = sd_bus_list_names(a, &names, NULL); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - r = strv_extend(&names, "org.freedesktop.DBus"); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - /* Let's sort the names list to make it stable */ - strv_sort(names); - - return synthetic_reply_method_return_strv(m, names); - - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ListQueuedOwners")) { - struct kdbus_cmd_list cmd = { - .flags = KDBUS_LIST_QUEUED, - .size = sizeof(cmd), - }; - struct kdbus_info *name_list, *name; - _cleanup_strv_free_ char **owners = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char *arg0; - int err = 0; - - if (!sd_bus_message_has_signature(m, "s")) - return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); - - r = sd_bus_message_read(m, "s", &arg0); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - r = sd_bus_get_name_creds(a, arg0, 0, NULL); - if (r == -ESRCH || r == -ENXIO) { - sd_bus_error_setf(&error, SD_BUS_ERROR_NAME_HAS_NO_OWNER, "Could not get owners of name '%s': no such name.", arg0); - return synthetic_reply_method_errno(m, r, &error); - } - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - r = ioctl(a->input_fd, KDBUS_CMD_LIST, &cmd); - if (r < 0) - return synthetic_reply_method_errno(m, -errno, NULL); - - name_list = (struct kdbus_info *) ((uint8_t *) a->kdbus_buffer + cmd.offset); - - KDBUS_FOREACH(name, name_list, cmd.list_size) { - struct kdbus_item *item; - char *n; - - KDBUS_ITEM_FOREACH(item, name, items) { - if (item->type == KDBUS_ITEM_OWNED_NAME) { - if (!streq_ptr(item->name.name, arg0)) - continue; - - if (asprintf(&n, ":1.%llu", (unsigned long long) name->id) < 0) { - err = -ENOMEM; - break; - } - - r = strv_consume(&owners, n); - if (r < 0) { - err = r; - break; - } - } - } - - if (err < 0) - break; - } - - r = bus_kernel_cmd_free(a, cmd.offset); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - if (err < 0) - return synthetic_reply_method_errno(m, err, NULL); - - return synthetic_reply_method_return_strv(m, owners); - - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "NameHasOwner")) { - const char *name; - - if (!sd_bus_message_has_signature(m, "s")) - return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); - - r = sd_bus_message_read(m, "s", &name); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - if (streq(name, "org.freedesktop.DBus")) - return synthetic_reply_method_return(m, "b", true); - - r = sd_bus_get_name_creds(a, name, 0, NULL); - if (r < 0 && r != -ESRCH && r != -ENXIO) - return synthetic_reply_method_errno(m, r, NULL); - - return synthetic_reply_method_return(m, "b", r >= 0); - - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ReleaseName")) { - const char *name; - - if (!sd_bus_message_has_signature(m, "s")) - return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); - - r = sd_bus_message_read(m, "s", &name); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - r = sd_bus_release_name(a, name); - if (r < 0) { - if (r == -ESRCH) - return synthetic_reply_method_return(m, "u", BUS_NAME_NON_EXISTENT); - if (r == -EADDRINUSE) - return synthetic_reply_method_return(m, "u", BUS_NAME_NOT_OWNER); - - return synthetic_reply_method_errno(m, r, NULL); - } - - set_remove(owned_names, (char*) name); - - return synthetic_reply_method_return(m, "u", BUS_NAME_RELEASED); - - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ReloadConfig")) { - if (!sd_bus_message_has_signature(m, "")) - return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); - - r = shared_policy_reload(sp); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - return synthetic_reply_method_return(m, NULL); - - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "RequestName")) { - const char *name; - uint32_t flags, param; - bool in_queue; - - if (!sd_bus_message_has_signature(m, "su")) - return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); - - r = sd_bus_message_read(m, "su", &name, &flags); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - if (sp) { - Policy *policy; - bool denied; - - policy = shared_policy_acquire(sp); - denied = !policy_check_own(policy, ucred->uid, ucred->gid, name); - shared_policy_release(sp, policy); - if (denied) - return synthetic_reply_method_errno(m, -EPERM, NULL); - } - - if ((flags & ~(BUS_NAME_ALLOW_REPLACEMENT|BUS_NAME_REPLACE_EXISTING|BUS_NAME_DO_NOT_QUEUE)) != 0) - return synthetic_reply_method_errno(m, -EINVAL, NULL); - - param = 0; - if (flags & BUS_NAME_ALLOW_REPLACEMENT) - param |= SD_BUS_NAME_ALLOW_REPLACEMENT; - if (flags & BUS_NAME_REPLACE_EXISTING) - param |= SD_BUS_NAME_REPLACE_EXISTING; - if (!(flags & BUS_NAME_DO_NOT_QUEUE)) - param |= SD_BUS_NAME_QUEUE; - - r = set_put_strdup(owned_names, name); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - r = sd_bus_request_name(a, name, param); - if (r < 0) { - if (r == -EALREADY) - return synthetic_reply_method_return(m, "u", BUS_NAME_ALREADY_OWNER); - - set_remove(owned_names, (char*) name); - - if (r == -EEXIST) - return synthetic_reply_method_return(m, "u", BUS_NAME_EXISTS); - return synthetic_reply_method_errno(m, r, NULL); - } - - in_queue = (r == 0); - - if (in_queue) - return synthetic_reply_method_return(m, "u", BUS_NAME_IN_QUEUE); - - return synthetic_reply_method_return(m, "u", BUS_NAME_PRIMARY_OWNER); - - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "StartServiceByName")) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *msg = NULL; - ProxyActivation *activation; - const char *name; - uint64_t cookie; - uint32_t flags; - - if (!sd_bus_message_has_signature(m, "su")) - return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); - - r = sd_bus_message_read(m, "su", &name, &flags); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - if (flags != 0) - return synthetic_reply_method_errno(m, -EINVAL, NULL); - - r = sd_bus_get_name_creds(a, name, 0, NULL); - if (r >= 0 || streq(name, "org.freedesktop.DBus")) - return synthetic_reply_method_return(m, "u", BUS_START_REPLY_ALREADY_RUNNING); - if (r != -ESRCH) - return synthetic_reply_method_errno(m, r, NULL); - - if (p->n_activations >= PROXY_ACTIVATIONS_MAX) - return synthetic_reply_method_errno(m, -EMFILE, NULL); - - r = sd_bus_message_get_cookie(m, &cookie); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - r = sd_bus_message_new_method_call(a, - &msg, - name, - "/", - "org.freedesktop.DBus.Peer", - "Ping"); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - r = bus_message_seal(msg, cookie, BUS_DEFAULT_TIMEOUT); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - activation = new0(ProxyActivation, 1); - if (!activation) - return synthetic_reply_method_errno(m, -ENOMEM, NULL); - - r = sd_bus_call_async(a, - &activation->slot, - msg, - driver_activation, - activation, - 0); - if (r < 0) { - free(activation); - return synthetic_reply_method_errno(m, r, NULL); - } - - activation->proxy = p; - activation->request = sd_bus_message_ref(m); - LIST_PREPEND(activations_by_proxy, p->activations, activation); - ++p->n_activations; - return 1; - - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "UpdateActivationEnvironment")) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *msg = NULL; - _cleanup_strv_free_ char **args = NULL; - - if (!sd_bus_message_has_signature(m, "a{ss}")) - return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{ss}"); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "ss")) > 0) { - _cleanup_free_ char *s = NULL; - const char *key; - const char *value; - - r = sd_bus_message_read(m, "ss", &key, &value); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - s = strjoin(key, "=", value, NULL); - if (!s) - return synthetic_reply_method_errno(m, -ENOMEM, NULL); - - if (!env_assignment_is_valid(s)) { - log_warning("UpdateActivationEnvironment() called with invalid assignment, discarding: %s", s); - } else { - r = strv_extend(&args, s); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - } - - r = sd_bus_message_exit_container(m); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - } - - r = sd_bus_message_exit_container(m); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - if (strv_isempty(args)) /* nothing to do? */ - return synthetic_reply_method_return(m, NULL); - - r = sd_bus_message_new_method_call( - a, - &msg, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "SetEnvironment"); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - r = sd_bus_message_append_strv(msg, args); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - r = sd_bus_call(a, msg, 0, NULL, NULL); - if (r < 0) - return synthetic_reply_method_errno(m, r, NULL); - - return synthetic_reply_method_return(m, NULL); - - } else { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - - r = sd_bus_error_setf(&error, SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method '%s'.", m->member); - - return synthetic_reply_method_errno(m, r, &error); - } -} diff --git a/src/bus-proxyd/proxy.c b/src/bus-proxyd/proxy.c deleted file mode 100644 index 907d93d4c7..0000000000 --- a/src/bus-proxyd/proxy.c +++ /dev/null @@ -1,953 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2013 Daniel Mack - Copyright 2014 Kay Sievers - Copyright 2014 David Herrmann - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <errno.h> -#include <poll.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/types.h> - -#include "sd-bus.h" -#include "sd-daemon.h" - -#include "alloc-util.h" -#include "bus-control.h" -#include "bus-internal.h" -#include "bus-message.h" -#include "bus-util.h" -#include "bus-xml-policy.h" -#include "driver.h" -#include "fd-util.h" -#include "formats-util.h" -#include "log.h" -#include "proxy.h" -#include "set.h" -#include "strv.h" -#include "synthesize.h" -#include "user-util.h" -#include "util.h" - -static int proxy_create_destination(Proxy *p, const char *destination, const char *local_sec, bool negotiate_fds) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *b = NULL; - int r; - - r = sd_bus_new(&b); - if (r < 0) - return log_error_errno(r, "Failed to allocate bus: %m"); - - r = sd_bus_set_description(b, "sd-proxy"); - if (r < 0) - return log_error_errno(r, "Failed to set bus name: %m"); - - r = sd_bus_set_address(b, destination); - if (r < 0) - return log_error_errno(r, "Failed to set address to connect to: %m"); - - r = sd_bus_negotiate_fds(b, negotiate_fds); - if (r < 0) - return log_error_errno(r, "Failed to set FD negotiation: %m"); - - r = sd_bus_negotiate_creds(b, true, SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SELINUX_CONTEXT); - if (r < 0) - return log_error_errno(r, "Failed to set credential negotiation: %m"); - - if (p->local_creds.pid > 0) { - b->fake_pids.pid = p->local_creds.pid; - b->fake_pids_valid = true; - - b->fake_creds.uid = UID_INVALID; - b->fake_creds.euid = p->local_creds.uid; - b->fake_creds.suid = UID_INVALID; - b->fake_creds.fsuid = UID_INVALID; - b->fake_creds.gid = GID_INVALID; - b->fake_creds.egid = p->local_creds.gid; - b->fake_creds.sgid = GID_INVALID; - b->fake_creds.fsgid = GID_INVALID; - b->fake_creds_valid = true; - } - - if (local_sec) { - b->fake_label = strdup(local_sec); - if (!b->fake_label) - return log_oom(); - } - - b->manual_peer_interface = true; - - r = sd_bus_start(b); - if (r < 0) - return log_error_errno(r, "Failed to start bus client: %m"); - - p->destination_bus = b; - b = NULL; - return 0; -} - -static int proxy_create_local(Proxy *p, bool negotiate_fds) { - sd_id128_t server_id; - sd_bus *b; - int r; - - r = sd_bus_new(&b); - if (r < 0) - return log_error_errno(r, "Failed to allocate bus: %m"); - - r = sd_bus_set_fd(b, p->local_in, p->local_out); - if (r < 0) { - sd_bus_unref(b); - return log_error_errno(r, "Failed to set fds: %m"); - } - - /* The fds are now owned by the bus, and we indicate that by - * storing the bus object in the proxy object. */ - p->local_bus = b; - - r = sd_bus_get_bus_id(p->destination_bus, &server_id); - if (r < 0) - return log_error_errno(r, "Failed to get server ID: %m"); - - r = sd_bus_set_server(b, 1, server_id); - if (r < 0) - return log_error_errno(r, "Failed to set server mode: %m"); - - r = sd_bus_negotiate_fds(b, negotiate_fds); - if (r < 0) - return log_error_errno(r, "Failed to set FD negotiation: %m"); - - r = sd_bus_negotiate_creds(b, true, SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SELINUX_CONTEXT); - if (r < 0) - return log_error_errno(r, "Failed to set credential negotiation: %m"); - - r = sd_bus_set_anonymous(b, true); - if (r < 0) - return log_error_errno(r, "Failed to set anonymous authentication: %m"); - - b->manual_peer_interface = true; - - r = sd_bus_start(b); - if (r < 0) - return log_error_errno(r, "Failed to start bus client: %m"); - - return 0; -} - -static int proxy_match_synthetic(sd_bus_message *m, void *userdata, sd_bus_error *error) { - Proxy *p = userdata; - - p->synthetic_matched = true; - return 0; /* make sure to continue processing it in further handlers */ -} - -/* - * We always need NameOwnerChanged so we can synthesize NameLost and - * NameAcquired. Furthermore, dbus-1 always passes unicast-signals through, so - * subscribe unconditionally. - */ -static int proxy_prepare_matches(Proxy *p) { - _cleanup_free_ char *match = NULL; - const char *unique; - int r; - - if (!p->destination_bus->is_kernel) - return 0; - - r = sd_bus_get_unique_name(p->destination_bus, &unique); - if (r < 0) - return log_error_errno(r, "Failed to get unique name: %m"); - - match = strjoin("type='signal'," - "sender='org.freedesktop.DBus'," - "path='/org/freedesktop/DBus'," - "interface='org.freedesktop.DBus'," - "member='NameOwnerChanged'," - "arg1='", - unique, - "'", - NULL); - if (!match) - return log_oom(); - - r = sd_bus_add_match(p->destination_bus, NULL, match, proxy_match_synthetic, p); - if (r < 0) - return log_error_errno(r, "Failed to add match for NameLost: %m"); - - free(match); - match = strjoin("type='signal'," - "sender='org.freedesktop.DBus'," - "path='/org/freedesktop/DBus'," - "interface='org.freedesktop.DBus'," - "member='NameOwnerChanged'," - "arg2='", - unique, - "'", - NULL); - if (!match) - return log_oom(); - - r = sd_bus_add_match(p->destination_bus, NULL, match, proxy_match_synthetic, p); - if (r < 0) - return log_error_errno(r, "Failed to add match for NameAcquired: %m"); - - free(match); - match = strjoin("type='signal'," - "destination='", - unique, - "'", - NULL); - if (!match) - return log_oom(); - - r = sd_bus_add_match(p->destination_bus, NULL, match, proxy_match_synthetic, p); - if (r < 0) - log_error_errno(r, "Failed to add match for directed signals: %m"); - /* FIXME: temporarily ignore error to support older kdbus versions */ - - return 0; -} - -int proxy_new(Proxy **out, int in_fd, int out_fd, const char *destination) { - _cleanup_(proxy_freep) Proxy *p = NULL; - _cleanup_free_ char *local_sec = NULL; - bool is_unix; - int r; - - /* This takes possession/destroys the file descriptors passed - * in even on failure. The caller should hence forget about - * the fds in all cases after calling this function and not - * close them. */ - - p = new0(Proxy, 1); - if (!p) { - safe_close(in_fd); - safe_close(out_fd); - return log_oom(); - } - - p->local_in = in_fd; - p->local_out = out_fd; - - p->owned_names = set_new(&string_hash_ops); - if (!p->owned_names) - return log_oom(); - - is_unix = sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 && - sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0; - - if (is_unix) { - (void) getpeercred(in_fd, &p->local_creds); - (void) getpeersec(in_fd, &local_sec); - } - - r = proxy_create_destination(p, destination, local_sec, is_unix); - if (r < 0) - return r; - - r = proxy_create_local(p, is_unix); - if (r < 0) - return r; - - r = proxy_prepare_matches(p); - if (r < 0) - return r; - - *out = p; - p = NULL; - - return 0; -} - -Proxy *proxy_free(Proxy *p) { - ProxyActivation *activation; - - if (!p) - return NULL; - - while ((activation = p->activations)) { - LIST_REMOVE(activations_by_proxy, p->activations, activation); - sd_bus_message_unref(activation->request); - sd_bus_slot_unref(activation->slot); - free(activation); - } - - if (p->local_bus) - sd_bus_flush_close_unref(p->local_bus); - else { - safe_close(p->local_in); - if (p->local_out != p->local_in) - safe_close(p->local_out); - } - - sd_bus_flush_close_unref(p->destination_bus); - set_free_free(p->owned_names); - free(p); - - return NULL; -} - -int proxy_set_policy(Proxy *p, SharedPolicy *sp, char **configuration) { - _cleanup_strv_free_ char **strv = NULL; - Policy *policy; - int r; - - assert(p); - assert(sp); - - /* no need to load legacy policy if destination is not kdbus */ - if (!p->destination_bus->is_kernel) - return 0; - - p->policy = sp; - - policy = shared_policy_acquire(sp); - if (policy) { - /* policy already pre-loaded */ - shared_policy_release(sp, policy); - return 0; - } - - if (!configuration) { - const char *scope; - - r = sd_bus_get_scope(p->destination_bus, &scope); - if (r < 0) - return log_error_errno(r, "Couldn't determine bus scope: %m"); - - if (streq(scope, "system")) - strv = strv_new("/usr/share/dbus-1/system.conf", - "/etc/dbus-1/system.conf", - "/usr/share/dbus-1/system.d/", - "/etc/dbus-1/system.d/", - "/etc/dbus-1/system-local.conf", - NULL); - else if (streq(scope, "user")) - strv = strv_new("/usr/share/dbus-1/session.conf", - "/etc/dbus-1/session.conf", - "/usr/share/dbus-1/session.d/", - "/etc/dbus-1/session.d/", - "/etc/dbus-1/session-local.conf", - NULL); - else - return log_error("Unknown scope %s, don't know which policy to load. Refusing.", scope); - - if (!strv) - return log_oom(); - - configuration = strv; - } - - return shared_policy_preload(sp, configuration); -} - -int proxy_hello_policy(Proxy *p, uid_t original_uid) { - Policy *policy; - int r = 0; - - assert(p); - - if (!p->policy) - return 0; - - policy = shared_policy_acquire(p->policy); - - if (p->local_creds.uid == original_uid) - log_debug("Permitting access, since bus owner matches bus client."); - else if (policy_check_hello(policy, p->local_creds.uid, p->local_creds.gid)) - log_debug("Permitting access due to XML policy."); - else - r = log_error_errno(EPERM, "Policy denied connection."); - - shared_policy_release(p->policy, policy); - - return r; -} - -static int proxy_wait(Proxy *p) { - uint64_t timeout_destination, timeout_local, t; - int events_destination, events_local, fd; - struct timespec _ts, *ts; - struct pollfd *pollfd; - int r; - - assert(p); - - fd = sd_bus_get_fd(p->destination_bus); - if (fd < 0) - return log_error_errno(fd, "Failed to get fd: %m"); - - events_destination = sd_bus_get_events(p->destination_bus); - if (events_destination < 0) - return log_error_errno(events_destination, "Failed to get events mask: %m"); - - r = sd_bus_get_timeout(p->destination_bus, &timeout_destination); - if (r < 0) - return log_error_errno(r, "Failed to get timeout: %m"); - - events_local = sd_bus_get_events(p->local_bus); - if (events_local < 0) - return log_error_errno(events_local, "Failed to get events mask: %m"); - - r = sd_bus_get_timeout(p->local_bus, &timeout_local); - if (r < 0) - return log_error_errno(r, "Failed to get timeout: %m"); - - t = timeout_destination; - if (t == (uint64_t) -1 || (timeout_local != (uint64_t) -1 && timeout_local < timeout_destination)) - t = timeout_local; - - if (t == (uint64_t) -1) - ts = NULL; - else { - usec_t nw; - - nw = now(CLOCK_MONOTONIC); - if (t > nw) - t -= nw; - else - t = 0; - - ts = timespec_store(&_ts, t); - } - - pollfd = (struct pollfd[3]) { - { .fd = fd, .events = events_destination, }, - { .fd = p->local_in, .events = events_local & POLLIN, }, - { .fd = p->local_out, .events = events_local & POLLOUT, }, - }; - - r = ppoll(pollfd, 3, ts, NULL); - if (r < 0) - return log_error_errno(errno, "ppoll() failed: %m"); - - return 0; -} - -static int handle_policy_error(sd_bus_message *m, int r) { - if (r == -ESRCH || r == -ENXIO) - return synthetic_reply_method_errorf(m, SD_BUS_ERROR_NAME_HAS_NO_OWNER, "Name %s is currently not owned by anyone.", m->destination); - - return r; -} - -static int process_policy_unlocked(sd_bus *from, sd_bus *to, sd_bus_message *m, Policy *policy, const struct ucred *our_ucred, Set *owned_names) { - int r; - - assert(from); - assert(to); - assert(m); - - if (!policy) - return 0; - - /* - * dbus-1 distinguishes expected and non-expected replies by tracking - * method-calls and timeouts. By default, DENY rules are *NEVER* applied - * on expected replies, unless explicitly specified. But we dont track - * method-calls, thus, we cannot know whether a reply is expected. - * Fortunately, the kdbus forbids non-expected replies, so we can safely - * ignore any policy on those and let the kernel deal with it. - * - * TODO: To be correct, we should only ignore policy-tags that are - * applied on non-expected replies. However, so far we don't parse those - * tags so we let everything pass. I haven't seen a DENY policy tag on - * expected-replies, ever, so don't bother.. - */ - if (m->reply_cookie > 0) - return 0; - - if (from->is_kernel) { - uid_t sender_uid = UID_INVALID; - gid_t sender_gid = GID_INVALID; - char **sender_names = NULL; - - /* Driver messages are always OK */ - if (streq_ptr(m->sender, "org.freedesktop.DBus")) - return 0; - - /* The message came from the kernel, and is sent to our legacy client. */ - (void) sd_bus_creds_get_well_known_names(&m->creds, &sender_names); - - (void) sd_bus_creds_get_euid(&m->creds, &sender_uid); - (void) sd_bus_creds_get_egid(&m->creds, &sender_gid); - - if (sender_uid == UID_INVALID || sender_gid == GID_INVALID) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *sender_creds = NULL; - - /* If the message came from another legacy - * client, then the message creds will be - * missing, simply because on legacy clients - * per-message creds were unknown. In this - * case, query the creds of the peer - * instead. */ - - r = bus_get_name_creds_kdbus(from, m->sender, SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID, true, &sender_creds); - if (r < 0) - return handle_policy_error(m, r); - - (void) sd_bus_creds_get_euid(sender_creds, &sender_uid); - (void) sd_bus_creds_get_egid(sender_creds, &sender_gid); - } - - /* First check whether the sender can send the message to our name */ - if (policy_check_send(policy, sender_uid, sender_gid, m->header->type, owned_names, NULL, m->path, m->interface, m->member, false, NULL) && - policy_check_recv(policy, our_ucred->uid, our_ucred->gid, m->header->type, NULL, sender_names, m->path, m->interface, m->member, false)) - return 0; - - /* Return an error back to the caller */ - if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) - return synthetic_reply_method_errorf(m, SD_BUS_ERROR_ACCESS_DENIED, "Access prohibited by XML receiver policy."); - - /* Return 1, indicating that the message shall not be processed any further */ - return 1; - } - - if (to->is_kernel) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *destination_creds = NULL; - uid_t destination_uid = UID_INVALID; - gid_t destination_gid = GID_INVALID; - const char *destination_unique = NULL; - char **destination_names = NULL; - char *n; - - /* Driver messages are always OK */ - if (streq_ptr(m->destination, "org.freedesktop.DBus")) - return 0; - - /* The message came from the legacy client, and is sent to kdbus. */ - if (m->destination) { - r = bus_get_name_creds_kdbus(to, m->destination, - SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME| - SD_BUS_CREDS_EUID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_PID, - true, &destination_creds); - if (r < 0) - return handle_policy_error(m, r); - - r = sd_bus_creds_get_unique_name(destination_creds, &destination_unique); - if (r < 0) - return handle_policy_error(m, r); - - (void) sd_bus_creds_get_well_known_names(destination_creds, &destination_names); - - (void) sd_bus_creds_get_euid(destination_creds, &destination_uid); - (void) sd_bus_creds_get_egid(destination_creds, &destination_gid); - } - - /* First check if we (the sender) can send to this name */ - if (sd_bus_message_is_signal(m, NULL, NULL)) { - /* If we forward a signal from dbus-1 to kdbus, we have - * no idea who the recipient is. Therefore, we cannot - * apply any dbus-1 policies that match on receiver - * credentials. We know sd-bus always sets - * KDBUS_MSG_SIGNAL, so the kernel applies policies to - * the message. Therefore, skip policy checks in this - * case. */ - return 0; - } else if (policy_check_send(policy, our_ucred->uid, our_ucred->gid, m->header->type, NULL, destination_names, m->path, m->interface, m->member, true, &n)) { - if (n) { - /* If we made a receiver decision, then remember which - * name's policy we used, and to which unique ID it - * mapped when we made the decision. Then, let's pass - * this to the kernel when sending the message, so that - * it refuses the operation should the name and unique - * ID not map to each other anymore. */ - - r = free_and_strdup(&m->destination_ptr, n); - if (r < 0) - return r; - - r = bus_kernel_parse_unique_name(destination_unique, &m->verify_destination_id); - if (r < 0) - return r; - } - - if (policy_check_recv(policy, destination_uid, destination_gid, m->header->type, owned_names, NULL, m->path, m->interface, m->member, true)) - return 0; - } - - /* Return an error back to the caller */ - if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) - return synthetic_reply_method_errorf(m, SD_BUS_ERROR_ACCESS_DENIED, "Access prohibited by XML sender policy."); - - /* Return 1, indicating that the message shall not be processed any further */ - return 1; - } - - return 0; -} - -static int process_policy(sd_bus *from, sd_bus *to, sd_bus_message *m, SharedPolicy *sp, const struct ucred *our_ucred, Set *owned_names) { - Policy *policy; - int r; - - assert(sp); - - policy = shared_policy_acquire(sp); - r = process_policy_unlocked(from, to, m, policy, our_ucred, owned_names); - shared_policy_release(sp, policy); - - return r; -} - -static int process_hello(Proxy *p, sd_bus_message *m) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *n = NULL; - bool is_hello; - int r; - - assert(p); - assert(m); - - /* As reaction to hello we need to respond with two messages: - * the callback reply and the NameAcquired for the unique - * name, since hello is otherwise obsolete on kdbus. */ - - is_hello = - sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "Hello") && - streq_ptr(m->destination, "org.freedesktop.DBus"); - - if (!is_hello) { - if (p->got_hello) - return 0; - - return log_error_errno(EIO, "First packet isn't hello (it's %s.%s), aborting.", m->interface, m->member); - } - - if (p->got_hello) - return log_error_errno(EIO, "Got duplicate hello, aborting."); - - p->got_hello = true; - - if (!p->destination_bus->is_kernel) - return 0; - - r = sd_bus_message_new_method_return(m, &n); - if (r < 0) - return log_error_errno(r, "Failed to generate HELLO reply: %m"); - - r = sd_bus_message_append(n, "s", p->destination_bus->unique_name); - if (r < 0) - return log_error_errno(r, "Failed to append unique name to HELLO reply: %m"); - - r = bus_message_append_sender(n, "org.freedesktop.DBus"); - if (r < 0) - return log_error_errno(r, "Failed to append sender to HELLO reply: %m"); - - r = bus_seal_synthetic_message(p->local_bus, n); - if (r < 0) - return log_error_errno(r, "Failed to seal HELLO reply: %m"); - - r = sd_bus_send(p->local_bus, n, NULL); - if (r < 0) - return log_error_errno(r, "Failed to send HELLO reply: %m"); - - n = sd_bus_message_unref(n); - r = sd_bus_message_new_signal( - p->local_bus, - &n, - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "NameAcquired"); - if (r < 0) - return log_error_errno(r, "Failed to allocate initial NameAcquired message: %m"); - - r = sd_bus_message_append(n, "s", p->destination_bus->unique_name); - if (r < 0) - return log_error_errno(r, "Failed to append unique name to NameAcquired message: %m"); - - r = bus_message_append_sender(n, "org.freedesktop.DBus"); - if (r < 0) - return log_error_errno(r, "Failed to append sender to NameAcquired message: %m"); - - r = sd_bus_message_set_destination(n, p->destination_bus->unique_name); - if (r < 0) - return log_error_errno(r, "Failed to set destination for NameAcquired message: %m"); - - r = bus_seal_synthetic_message(p->local_bus, n); - if (r < 0) - return log_error_errno(r, "Failed to seal NameAcquired message: %m"); - - r = sd_bus_send(p->local_bus, n, NULL); - if (r < 0) - return log_error_errno(r, "Failed to send NameAcquired message: %m"); - - return 1; -} - -static int patch_sender(sd_bus *a, sd_bus_message *m) { - char **well_known = NULL; - sd_bus_creds *c; - int r; - - assert(a); - assert(m); - - if (!a->is_kernel) - return 0; - - /* We will change the sender of messages from the bus driver - * so that they originate from the bus driver. This is a - * speciality originating from dbus1, where the bus driver did - * not have a unique id, but only the well-known name. */ - - c = sd_bus_message_get_creds(m); - if (!c) - return 0; - - r = sd_bus_creds_get_well_known_names(c, &well_known); - if (r < 0) - return r; - - if (strv_contains(well_known, "org.freedesktop.DBus")) - m->sender = "org.freedesktop.DBus"; - - return 0; -} - -static int proxy_process_destination_to_local(Proxy *p) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - bool matched, matched_synthetic; - int r; - - assert(p); - - /* - * Usually, we would just take any message that the bus passes to us - * and forward it to the local connection. However, there are actually - * applications that fail if they receive broadcasts that they didn't - * subscribe to. Therefore, we actually emulate a real broadcast - * matching here, and discard any broadcasts that weren't matched. Our - * match-handlers remembers whether a message was matched by any rule, - * by marking it in @p->message_matched. - */ - - r = sd_bus_process(p->destination_bus, &m); - - matched = p->message_matched; - matched_synthetic = p->synthetic_matched; - p->message_matched = false; - p->synthetic_matched = false; - - if (r == -ECONNRESET || r == -ENOTCONN) /* Treat 'connection reset by peer' as clean exit condition */ - return r; - if (r < 0) { - log_error_errno(r, "Failed to process destination bus: %m"); - return r; - } - if (r == 0) - return 0; - if (!m) - return 1; - - /* We officially got EOF, let's quit */ - if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) - return -ECONNRESET; - - r = synthesize_name_acquired(p, p->destination_bus, p->local_bus, m); - if (r == -ECONNRESET || r == -ENOTCONN) - return r; - if (r < 0) - return log_error_errno(r, "Failed to synthesize message: %m"); - - /* discard broadcasts that were not matched by any MATCH rule */ - if (!matched && !sd_bus_message_get_destination(m)) { - if (!matched_synthetic) - log_debug("Dropped unmatched broadcast: uid=" UID_FMT " gid=" GID_FMT " pid=" PID_FMT " message=%s path=%s interface=%s member=%s sender=%s destination=%s", - p->local_creds.uid, p->local_creds.gid, p->local_creds.pid, bus_message_type_to_string(m->header->type), - strna(m->path), strna(m->interface), strna(m->member), strna(m->sender), strna(m->destination)); - return 1; - } - - patch_sender(p->destination_bus, m); - - if (p->policy) { - r = process_policy(p->destination_bus, p->local_bus, m, p->policy, &p->local_creds, p->owned_names); - if (r == -ECONNRESET || r == -ENOTCONN) - return r; - if (r < 0) - return log_error_errno(r, "Failed to process policy: %m"); - if (r > 0) - return 1; - } - - r = sd_bus_send(p->local_bus, m, NULL); - if (r < 0) { - if (r == -ECONNRESET || r == -ENOTCONN) - return r; - - /* If the peer tries to send a reply and it is - * rejected with EBADSLT by the kernel, we ignore the - * error. This catches cases where the original - * method-call didn't had EXPECT_REPLY set, but the - * proxy-peer still sends a reply. This is allowed in - * dbus1, but not in kdbus. We don't want to track - * reply-windows in the proxy, so we simply ignore - * EBADSLT for all replies. The only downside is, that - * callers are no longer notified if their replies are - * dropped. However, this is equivalent to the - * caller's timeout to expire, so this should be - * acceptable. Nobody sane sends replies without a - * matching method-call, so nobody should care. */ - - /* FIXME: remove -EPERM when kdbus is updated */ - if ((r == -EPERM || r == -EBADSLT) && m->reply_cookie > 0) - return 1; - - /* Return the error to the client, if we can */ - synthetic_reply_method_errnof(m, r, "Failed to forward message we got from destination: %m"); - if (r == -ENOBUFS) { - /* if local dbus1 peer does not dispatch its queue, warn only once */ - if (!p->queue_overflow) - log_error("Dropped messages due to queue overflow of local peer (pid: "PID_FMT" uid: "UID_FMT")", p->local_creds.pid, p->local_creds.uid); - p->queue_overflow = true; - } else - log_error_errno(r, - "Failed to forward message we got from destination: uid=" UID_FMT " gid=" GID_FMT" message=%s destination=%s path=%s interface=%s member=%s: %m", - p->local_creds.uid, p->local_creds.gid, bus_message_type_to_string(m->header->type), - strna(m->destination), strna(m->path), strna(m->interface), strna(m->member)); - - return 1; - } - - p->queue_overflow = false; - return 1; -} - -static int proxy_process_local_to_destination(Proxy *p) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - int r; - - assert(p); - - r = sd_bus_process(p->local_bus, &m); - if (r == -ECONNRESET || r == -ENOTCONN) /* Treat 'connection reset by peer' as clean exit condition */ - return r; - if (r < 0) { - log_error_errno(r, "Failed to process local bus: %m"); - return r; - } - if (r == 0) - return 0; - if (!m) - return 1; - - /* We officially got EOF, let's quit */ - if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) - return -ECONNRESET; - - r = process_hello(p, m); - if (r == -ECONNRESET || r == -ENOTCONN) - return r; - if (r < 0) - return log_error_errno(r, "Failed to process HELLO: %m"); - if (r > 0) - return 1; - - r = bus_proxy_process_driver(p, p->destination_bus, p->local_bus, m, p->policy, &p->local_creds, p->owned_names); - if (r == -ECONNRESET || r == -ENOTCONN) - return r; - if (r < 0) - return log_error_errno(r, "Failed to process driver calls: %m"); - if (r > 0) - return 1; - - for (;;) { - if (p->policy) { - r = process_policy(p->local_bus, p->destination_bus, m, p->policy, &p->local_creds, p->owned_names); - if (r == -ECONNRESET || r == -ENOTCONN) - return r; - if (r < 0) - return log_error_errno(r, "Failed to process policy: %m"); - if (r > 0) - return 1; - } - - r = sd_bus_send(p->destination_bus, m, NULL); - if (r < 0) { - if (r == -ECONNRESET || r == -ENOTCONN) - return r; - - /* The name database changed since the policy check, hence let's check again */ - if (r == -EREMCHG) - continue; - - /* see above why EBADSLT is ignored for replies */ - if ((r == -EPERM || r == -EBADSLT) && m->reply_cookie > 0) - return 1; - - synthetic_reply_method_errnof(m, r, "Failed to forward message we got from local: %m"); - log_error_errno(r, - "Failed to forward message we got from local: uid=" UID_FMT " gid=" GID_FMT" message=%s destination=%s path=%s interface=%s member=%s: %m", - p->local_creds.uid, p->local_creds.gid, bus_message_type_to_string(m->header->type), - strna(m->destination), strna(m->path), strna(m->interface), strna(m->member)); - return 1; - } - - break; - } - - return 1; -} - -int proxy_match(sd_bus_message *m, void *userdata, sd_bus_error *error) { - Proxy *p = userdata; - - p->message_matched = true; - return 0; /* make sure to continue processing it in further handlers */ -} - -int proxy_run(Proxy *p) { - int r; - - assert(p); - - for (;;) { - bool busy = false; - - if (p->got_hello) { - /* Read messages from bus, to pass them on to our client */ - r = proxy_process_destination_to_local(p); - if (r == -ECONNRESET || r == -ENOTCONN) - return 0; - if (r < 0) - return r; - if (r > 0) - busy = true; - } - - /* Read messages from our client, to pass them on to the bus */ - r = proxy_process_local_to_destination(p); - if (r == -ECONNRESET || r == -ENOTCONN) - return 0; - if (r < 0) - return r; - if (r > 0) - busy = true; - - if (!busy) { - r = proxy_wait(p); - if (r == -ECONNRESET || r == -ENOTCONN) - return 0; - if (r < 0) - return r; - } - } - - return 0; -} diff --git a/src/bus-proxyd/proxy.h b/src/bus-proxyd/proxy.h deleted file mode 100644 index d9e75cf73b..0000000000 --- a/src/bus-proxyd/proxy.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 David Herrmann - - 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-bus.h" - -#include "bus-xml-policy.h" - -typedef struct Proxy Proxy; -typedef struct ProxyActivation ProxyActivation; - -#define PROXY_ACTIVATIONS_MAX (16) /* max parallel activation requests */ - -struct Proxy { - sd_bus *local_bus; - struct ucred local_creds; - int local_in; - int local_out; - - sd_bus *destination_bus; - - Set *owned_names; - SharedPolicy *policy; - - LIST_HEAD(ProxyActivation, activations); - size_t n_activations; - - bool got_hello : 1; - bool queue_overflow : 1; - bool message_matched : 1; - bool synthetic_matched : 1; -}; - -struct ProxyActivation { - LIST_FIELDS(ProxyActivation, activations_by_proxy); - Proxy *proxy; - sd_bus_message *request; - sd_bus_slot *slot; -}; - -int proxy_new(Proxy **out, int in_fd, int out_fd, const char *dest); -Proxy *proxy_free(Proxy *p); - -int proxy_set_policy(Proxy *p, SharedPolicy *policy, char **configuration); -int proxy_hello_policy(Proxy *p, uid_t original_uid); -int proxy_match(sd_bus_message *m, void *userdata, sd_bus_error *error); -int proxy_run(Proxy *p); - -DEFINE_TRIVIAL_CLEANUP_FUNC(Proxy*, proxy_free); diff --git a/src/bus-proxyd/stdio-bridge.c b/src/bus-proxyd/stdio-bridge.c deleted file mode 100644 index 291c1b09e3..0000000000 --- a/src/bus-proxyd/stdio-bridge.c +++ /dev/null @@ -1,244 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2013 Daniel Mack - Copyright 2014 Kay Sievers - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <errno.h> -#include <getopt.h> -#include <stddef.h> -#include <string.h> -#include <unistd.h> - -#include "sd-bus.h" -#include "sd-daemon.h" - -#include "alloc-util.h" -#include "bus-internal.h" -#include "bus-util.h" -#include "def.h" -#include "formats-util.h" -#include "log.h" -#include "proxy.h" -#include "strv.h" -#include "user-util.h" -#include "util.h" - -static char *arg_address = NULL; -static char *arg_command_line_buffer = NULL; - -static int help(void) { - - printf("%s [OPTIONS...]\n\n" - "Connect STDIO to a given bus address.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --machine=MACHINE Connect to specified machine\n" - " --address=ADDRESS Connect to the bus specified by ADDRESS\n" - " (default: " DEFAULT_SYSTEM_BUS_ADDRESS ")\n", - program_invocation_short_name); - - return 0; -} - -static int parse_argv(int argc, char *argv[]) { - - enum { - ARG_VERSION = 0x100, - ARG_ADDRESS, - ARG_MACHINE, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "address", required_argument, NULL, ARG_ADDRESS }, - { "machine", required_argument, NULL, ARG_MACHINE }, - {}, - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - - switch (c) { - - case 'h': - help(); - return 0; - - case ARG_VERSION: - return version(); - - case ARG_ADDRESS: { - char *a; - - a = strdup(optarg); - if (!a) - return log_oom(); - - free(arg_address); - arg_address = a; - break; - } - - case ARG_MACHINE: { - _cleanup_free_ char *e = NULL; - char *a; - - e = bus_address_escape(optarg); - if (!e) - return log_oom(); - - a = strjoin("x-machine-kernel:machine=", e, ";x-machine-unix:machine=", e, NULL); - if (!a) - return log_oom(); - - free(arg_address); - arg_address = a; - - break; - } - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - /* If the first command line argument is only "x" characters - * we'll write who we are talking to into it, so that "ps" is - * explanatory */ - arg_command_line_buffer = argv[optind]; - if (argc > optind + 1 || (arg_command_line_buffer && !in_charset(arg_command_line_buffer, "x"))) { - log_error("Too many arguments"); - return -EINVAL; - } - - if (!arg_address) { - arg_address = strdup(DEFAULT_SYSTEM_BUS_ADDRESS); - if (!arg_address) - return log_oom(); - } - - return 1; -} - -static int rename_service(sd_bus *a, sd_bus *b) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - _cleanup_free_ char *p = NULL, *name = NULL; - const char *comm; - char **cmdline; - uid_t uid; - pid_t pid; - int r; - - assert(a); - assert(b); - - r = sd_bus_get_owner_creds(b, SD_BUS_CREDS_EUID|SD_BUS_CREDS_PID|SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_COMM|SD_BUS_CREDS_AUGMENT, &creds); - if (r < 0) - return r; - - r = sd_bus_creds_get_euid(creds, &uid); - if (r < 0) - return r; - - r = sd_bus_creds_get_pid(creds, &pid); - if (r < 0) - return r; - - r = sd_bus_creds_get_cmdline(creds, &cmdline); - if (r < 0) - return r; - - r = sd_bus_creds_get_comm(creds, &comm); - if (r < 0) - return r; - - name = uid_to_name(uid); - if (!name) - return -ENOMEM; - - p = strv_join(cmdline, " "); - if (!p) - return -ENOMEM; - - /* The status string gets the full command line ... */ - sd_notifyf(false, - "STATUS=Processing requests from client PID "PID_FMT" (%s); UID "UID_FMT" (%s)", - pid, p, - uid, name); - - /* ... and the argv line only the short comm */ - if (arg_command_line_buffer) { - size_t m, w; - - m = strlen(arg_command_line_buffer); - w = snprintf(arg_command_line_buffer, m, - "[PID "PID_FMT"/%s; UID "UID_FMT"/%s]", - pid, comm, - uid, name); - - if (m > w) - memzero(arg_command_line_buffer + w, m - w); - } - - log_debug("Running on behalf of PID "PID_FMT" (%s), UID "UID_FMT" (%s), %s", - pid, p, - uid, name, - a->unique_name); - - return 0; -} - -int main(int argc, char *argv[]) { - _cleanup_(proxy_freep) Proxy *p = NULL; - int r; - - log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - r = proxy_new(&p, STDIN_FILENO, STDOUT_FILENO, arg_address); - if (r < 0) - goto finish; - - r = rename_service(p->destination_bus, p->local_bus); - if (r < 0) - log_debug_errno(r, "Failed to rename process: %m"); - - r = proxy_run(p); - -finish: - sd_notify(false, - "STOPPING=1\n" - "STATUS=Shutting down."); - - free(arg_address); - - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} diff --git a/src/bus-proxyd/synthesize.c b/src/bus-proxyd/synthesize.c deleted file mode 100644 index 8eea7dc5b9..0000000000 --- a/src/bus-proxyd/synthesize.c +++ /dev/null @@ -1,225 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - Copyright 2013 Daniel Mack - Copyright 2014 Kay Sievers - - 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 <stddef.h> - -#include "sd-bus.h" - -#include "bus-internal.h" -#include "bus-match.h" -#include "bus-message.h" -#include "bus-util.h" -#include "synthesize.h" -#include "util.h" - -int synthetic_driver_send(sd_bus *b, sd_bus_message *m) { - int r; - - assert(b); - assert(m); - - r = bus_message_append_sender(m, "org.freedesktop.DBus"); - if (r < 0) - return r; - - r = bus_seal_synthetic_message(b, m); - if (r < 0) - return r; - - return sd_bus_send(b, m, NULL); -} - -int synthetic_reply_method_error(sd_bus_message *call, const sd_bus_error *e) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - int r; - - assert(call); - - if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) - return 0; - - r = sd_bus_message_new_method_error(call, &m, e); - if (r < 0) - return r; - - return synthetic_driver_send(call->bus, m); -} - -int synthetic_reply_method_errorf(sd_bus_message *call, const char *name, const char *format, ...) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - va_list ap; - - va_start(ap, format); - bus_error_setfv(&error, name, format, ap); - va_end(ap); - - return synthetic_reply_method_error(call, &error); -} - -int synthetic_reply_method_errno(sd_bus_message *call, int error, const sd_bus_error *p) { - _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL; - - assert(call); - - if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) - return 0; - - if (sd_bus_error_is_set(p)) - return synthetic_reply_method_error(call, p); - - sd_bus_error_set_errno(&berror, error); - - return synthetic_reply_method_error(call, &berror); -} - -int synthetic_reply_method_errnof(sd_bus_message *call, int error, const char *format, ...) { - _cleanup_(sd_bus_error_free) sd_bus_error berror = SD_BUS_ERROR_NULL; - va_list ap; - - assert(call); - - if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) - return 0; - - va_start(ap, format); - sd_bus_error_set_errnofv(&berror, error, format, ap); - va_end(ap); - - return synthetic_reply_method_error(call, &berror); -} - -int synthetic_reply_method_return(sd_bus_message *call, const char *types, ...) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - int r; - - assert(call); - - if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) - return 0; - - r = sd_bus_message_new_method_return(call, &m); - if (r < 0) - return r; - - if (!isempty(types)) { - va_list ap; - - va_start(ap, types); - r = bus_message_append_ap(m, types, ap); - va_end(ap); - if (r < 0) - return r; - } - - return synthetic_driver_send(call->bus, m); -} - -int synthetic_reply_method_return_strv(sd_bus_message *call, char **l) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; - int r; - - assert(call); - - if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) - return 0; - - r = sd_bus_message_new_method_return(call, &m); - if (r < 0) - return synthetic_reply_method_errno(call, r, NULL); - - r = sd_bus_message_append_strv(m, l); - if (r < 0) - return synthetic_reply_method_errno(call, r, NULL); - - return synthetic_driver_send(call->bus, m); -} - -int synthesize_name_acquired(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *n = NULL; - const char *name, *old_owner, *new_owner; - int r; - - assert(p); - assert(a); - assert(b); - assert(m); - - /* If we get NameOwnerChanged for our own name, we need to - * synthesize NameLost/NameAcquired, since socket clients need - * that, even though it is obsoleted on kdbus */ - - if (!a->is_kernel) - return 0; - - if (!sd_bus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged") || - !streq_ptr(m->path, "/org/freedesktop/DBus") || - !streq_ptr(m->sender, "org.freedesktop.DBus")) - return 0; - - r = sd_bus_message_read(m, "sss", &name, &old_owner, &new_owner); - if (r < 0) - return r; - - r = sd_bus_message_rewind(m, true); - if (r < 0) - return r; - - if (streq(old_owner, a->unique_name)) { - - r = sd_bus_message_new_signal( - b, - &n, - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "NameLost"); - - } else if (streq(new_owner, a->unique_name)) { - - r = sd_bus_message_new_signal( - b, - &n, - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "NameAcquired"); - } else - return 0; - - if (r < 0) - return r; - - r = sd_bus_message_append(n, "s", name); - if (r < 0) - return r; - - r = bus_message_append_sender(n, "org.freedesktop.DBus"); - if (r < 0) - return r; - - r = sd_bus_message_set_destination(n, a->unique_name); - if (r < 0) - return r; - - r = bus_seal_synthetic_message(b, n); - if (r < 0) - return r; - - return sd_bus_send(b, n, NULL); -} diff --git a/src/bus-proxyd/synthesize.h b/src/bus-proxyd/synthesize.h deleted file mode 100644 index 1b7197f8ec..0000000000 --- a/src/bus-proxyd/synthesize.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include "sd-bus.h" - -#include "proxy.h" - -int synthetic_driver_send(sd_bus *b, sd_bus_message *m); - -int synthetic_reply_method_return(sd_bus_message *call, const char *types, ...); -int synthetic_reply_method_return_strv(sd_bus_message *call, char **l); - -int synthetic_reply_method_error(sd_bus_message *call, const sd_bus_error *e); -int synthetic_reply_method_errorf(sd_bus_message *call, const char *name, const char *format, ...) _sd_printf_(3, 4); -int synthetic_reply_method_errno(sd_bus_message *call, int error, const sd_bus_error *p); -int synthetic_reply_method_errnof(sd_bus_message *call, int error, const char *format, ...) _sd_printf_(3, 4); - -int synthesize_name_acquired(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m); diff --git a/src/bus-proxyd/test-bus-xml-policy.c b/src/bus-proxyd/test-bus-xml-policy.c deleted file mode 100644 index af7c9128a2..0000000000 --- a/src/bus-proxyd/test-bus-xml-policy.c +++ /dev/null @@ -1,170 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Daniel Mack - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <errno.h> -#include <stddef.h> -#include <unistd.h> - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-xml-policy.h" -#include "log.h" -#include "string-util.h" -#include "strv.h" -#include "util.h" - -static int test_policy_load(Policy *p, const char *name) { - _cleanup_free_ char *path = NULL; - int r = 0; - - path = strjoin(TEST_DIR, "/bus-policy/", name, NULL); - assert_se(path); - - if (access(path, R_OK) == 0) - r = policy_load(p, STRV_MAKE(path)); - else - r = -ENOENT; - - return r; -} - -static int show_policy(const char *fn) { - Policy p = {}; - int r; - - r = policy_load(&p, STRV_MAKE(fn)); - if (r < 0) { - log_error_errno(r, "Failed to load policy %s: %m", fn); - return r; - } - - policy_dump(&p); - policy_free(&p); - - return 0; -} - -int main(int argc, char *argv[]) { - - Policy p = {}; - - printf("Showing session policy BEGIN\n"); - show_policy("/etc/dbus-1/session.conf"); - printf("Showing session policy END\n"); - - printf("Showing system policy BEGIN\n"); - show_policy("/etc/dbus-1/system.conf"); - printf("Showing system policy END\n"); - - /* Ownership tests */ - assert_se(test_policy_load(&p, "ownerships.conf") == 0); - - assert_se(policy_check_own(&p, 0, 0, "org.test.test1") == true); - assert_se(policy_check_own(&p, 1, 0, "org.test.test1") == true); - - assert_se(policy_check_own(&p, 0, 0, "org.test.test2") == true); - assert_se(policy_check_own(&p, 1, 0, "org.test.test2") == false); - - assert_se(policy_check_own(&p, 0, 0, "org.test.test3") == false); - assert_se(policy_check_own(&p, 1, 0, "org.test.test3") == false); - - assert_se(policy_check_own(&p, 0, 0, "org.test.test4") == false); - assert_se(policy_check_own(&p, 1, 0, "org.test.test4") == true); - - policy_free(&p); - - /* Signaltest */ - assert_se(test_policy_load(&p, "signals.conf") == 0); - - assert_se(policy_check_one_send(&p, 0, 0, SD_BUS_MESSAGE_SIGNAL, "bli.bla.blubb", NULL, "/an/object/path", NULL) == true); - assert_se(policy_check_one_send(&p, 1, 0, SD_BUS_MESSAGE_SIGNAL, "bli.bla.blubb", NULL, "/an/object/path", NULL) == false); - - policy_free(&p); - - /* Method calls */ - assert_se(test_policy_load(&p, "methods.conf") == 0); - policy_dump(&p); - - assert_se(policy_check_one_send(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "bli.bla.blubb", "Member") == false); - assert_se(policy_check_one_send(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "bli.bla.blubb", "Member") == false); - assert_se(policy_check_one_send(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.test.int1", "Member") == true); - assert_se(policy_check_one_send(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.test.int2", "Member") == true); - - assert_se(policy_check_one_recv(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test3", "/an/object/path", "org.test.int3", "Member111") == true); - - policy_free(&p); - - /* User and groups */ - assert_se(test_policy_load(&p, "hello.conf") == 0); - policy_dump(&p); - - assert_se(policy_check_hello(&p, 0, 0) == true); - assert_se(policy_check_hello(&p, 1, 0) == false); - assert_se(policy_check_hello(&p, 0, 1) == false); - - policy_free(&p); - - /* dbus1 test file: ownership */ - - assert_se(test_policy_load(&p, "check-own-rules.conf") >= 0); - policy_dump(&p); - - assert_se(policy_check_own(&p, 0, 0, "org.freedesktop") == false); - assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystem") == false); - assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystems") == true); - assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystems.foo") == true); - assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystems.foo.bar") == true); - assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystems2") == false); - assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystems2.foo") == false); - assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystems2.foo.bar") == false); - - policy_free(&p); - - /* dbus1 test file: many rules */ - - assert_se(test_policy_load(&p, "many-rules.conf") >= 0); - policy_dump(&p); - policy_free(&p); - - /* dbus1 test file: generic test */ - - assert_se(test_policy_load(&p, "test.conf") >= 0); - policy_dump(&p); - - assert_se(policy_check_own(&p, 0, 0, "org.foo.FooService") == true); - assert_se(policy_check_own(&p, 0, 0, "org.foo.FooService2") == false); - assert_se(policy_check_one_send(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.test.int2", "Member") == false); - assert_se(policy_check_one_send(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == true); - assert_se(policy_check_one_recv(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == true); - assert_se(policy_check_one_recv(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService", "/an/object/path", "org.foo.FooBroadcastInterface2", "Member") == false); - assert_se(policy_check_one_recv(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService2", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == false); - - assert_se(policy_check_own(&p, 100, 0, "org.foo.FooService") == false); - assert_se(policy_check_own(&p, 100, 0, "org.foo.FooService2") == false); - assert_se(policy_check_one_send(&p, 100, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.test.int2", "Member") == false); - assert_se(policy_check_one_send(&p, 100, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == false); - assert_se(policy_check_one_recv(&p, 100, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == true); - assert_se(policy_check_one_recv(&p, 100, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService", "/an/object/path", "org.foo.FooBroadcastInterface2", "Member") == false); - assert_se(policy_check_one_recv(&p, 100, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService2", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == false); - - policy_free(&p); - - return EXIT_SUCCESS; -} diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c index b839fadd04..dcb5912b83 100644 --- a/src/cgls/cgls.c +++ b/src/cgls/cgls.c @@ -184,14 +184,15 @@ int main(int argc, char *argv[]) { goto finish; if (!arg_no_pager) { - r = pager_open(false); + r = pager_open(arg_no_pager, false); if (r > 0 && arg_full < 0) arg_full = true; } output_flags = arg_all * OUTPUT_SHOW_ALL | - (arg_full > 0) * OUTPUT_FULL_WIDTH; + (arg_full > 0) * OUTPUT_FULL_WIDTH | + arg_kernel_threads * OUTPUT_KERNEL_THREADS; if (optind < argc) { _cleanup_free_ char *root = NULL; @@ -209,7 +210,7 @@ int main(int argc, char *argv[]) { printf("Directory %s:\n", argv[i]); fflush(stdout); - q = show_cgroup_by_path(argv[i], NULL, 0, arg_kernel_threads, output_flags); + q = show_cgroup_by_path(argv[i], NULL, 0, output_flags); } else { _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL; const char *controller, *path; @@ -235,7 +236,7 @@ int main(int argc, char *argv[]) { show_cg_info(controller, path); - q = show_cgroup(controller, path, NULL, 0, arg_kernel_threads, output_flags); + q = show_cgroup(controller, path, NULL, 0, output_flags); } if (q < 0) @@ -258,7 +259,7 @@ int main(int argc, char *argv[]) { printf("Working directory %s:\n", cwd); fflush(stdout); - r = show_cgroup_by_path(cwd, NULL, 0, arg_kernel_threads, output_flags); + r = show_cgroup_by_path(cwd, NULL, 0, output_flags); done = true; } } @@ -273,7 +274,7 @@ int main(int argc, char *argv[]) { show_cg_info(SYSTEMD_CGROUP_CONTROLLER, root); printf("-.slice\n"); - r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, arg_kernel_threads, output_flags); + r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, output_flags); } } diff --git a/src/cgroups-agent/cgroups-agent.c b/src/cgroups-agent/cgroups-agent.c index aadfba0707..d7c722ac3d 100644 --- a/src/cgroups-agent/cgroups-agent.c +++ b/src/cgroups-agent/cgroups-agent.c @@ -18,15 +18,22 @@ ***/ #include <stdlib.h> +#include <sys/socket.h> -#include "sd-bus.h" - -#include "bus-util.h" +#include "fd-util.h" #include "log.h" +#include "socket-util.h" int main(int argc, char *argv[]) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - int r; + + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/cgroups-agent", + }; + + _cleanup_close_ int fd = -1; + ssize_t n; + size_t l; if (argc != 2) { log_error("Incorrect number of arguments."); @@ -37,27 +44,22 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); - /* We send this event to the private D-Bus socket and then the - * system instance will forward this to the system bus. We do - * this to avoid an activation loop when we start dbus when we - * are called when the dbus service is shut down. */ - - r = bus_connect_system_systemd(&bus); - if (r < 0) { - /* If we couldn't connect we assume this was triggered - * while systemd got restarted/transitioned from - * initrd to the system, so let's ignore this */ - log_debug_errno(r, "Failed to get D-Bus connection: %m"); + fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); + if (fd < 0) { + log_debug_errno(errno, "Failed to allocate socket: %m"); + return EXIT_FAILURE; + } + + l = strlen(argv[1]); + + n = sendto(fd, argv[1], l, 0, &sa.sa, SOCKADDR_UN_LEN(sa.un)); + if (n < 0) { + log_debug_errno(errno, "Failed to send cgroups agent message: %m"); return EXIT_FAILURE; } - r = sd_bus_emit_signal(bus, - "/org/freedesktop/systemd1/agent", - "org.freedesktop.systemd1.Agent", - "Released", - "s", argv[1]); - if (r < 0) { - log_debug_errno(r, "Failed to send signal message on private connection: %m"); + if ((size_t) n != l) { + log_debug("Datagram size mismatch"); return EXIT_FAILURE; } diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index 60d6da3246..e088e4b197 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -72,13 +72,13 @@ static bool arg_batch = false; static bool arg_raw = false; static usec_t arg_delay = 1*USEC_PER_SEC; static char* arg_machine = NULL; +static bool arg_recursive = true; -enum { +static enum { COUNT_PIDS, COUNT_USERSPACE_PROCESSES, COUNT_ALL_PROCESSES, } arg_count = COUNT_PIDS; -static bool arg_recursive = true; static enum { ORDER_PATH, @@ -269,13 +269,15 @@ static int process( if (g->memory > 0) g->memory_valid = true; - } else if (streq(controller, "blkio") && cg_unified() <= 0) { + } else if ((streq(controller, "io") && cg_unified() > 0) || + (streq(controller, "blkio") && cg_unified() <= 0)) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *p = NULL; + bool unified = cg_unified() > 0; uint64_t wr = 0, rd = 0; nsec_t timestamp; - r = cg_get_path(controller, path, "blkio.io_service_bytes", &p); + r = cg_get_path(controller, path, unified ? "io.stat" : "blkio.io_service_bytes", &p); if (r < 0) return r; @@ -293,25 +295,38 @@ static int process( if (!fgets(line, sizeof(line), f)) break; + /* Trim and skip the device */ l = strstrip(line); l += strcspn(l, WHITESPACE); l += strspn(l, WHITESPACE); - if (first_word(l, "Read")) { - l += 4; - q = &rd; - } else if (first_word(l, "Write")) { - l += 5; - q = ≀ - } else - continue; + if (unified) { + while (!isempty(l)) { + if (sscanf(l, "rbytes=%" SCNu64, &k)) + rd += k; + else if (sscanf(l, "wbytes=%" SCNu64, &k)) + wr += k; - l += strspn(l, WHITESPACE); - r = safe_atou64(l, &k); - if (r < 0) - continue; - - *q += k; + l += strcspn(l, WHITESPACE); + l += strspn(l, WHITESPACE); + } + } else { + if (first_word(l, "Read")) { + l += 4; + q = &rd; + } else if (first_word(l, "Write")) { + l += 5; + q = ≀ + } else + continue; + + l += strspn(l, WHITESPACE); + r = safe_atou64(l, &k); + if (r < 0) + continue; + + *q += k; + } } timestamp = now_nsec(CLOCK_MONOTONIC); @@ -362,7 +377,7 @@ static int refresh_one( Group **ret) { _cleanup_closedir_ DIR *d = NULL; - Group *ours; + Group *ours = NULL; int r; assert(controller); @@ -439,6 +454,9 @@ static int refresh(const char *root, Hashmap *a, Hashmap *b, unsigned iteration) r = refresh_one("memory", root, a, b, iteration, 0, NULL); if (r < 0) return r; + r = refresh_one("io", root, a, b, iteration, 0, NULL); + if (r < 0) + return r; r = refresh_one("blkio", root, a, b, iteration, 0, NULL); if (r < 0) return r; diff --git a/src/compat-libs/.gitignore b/src/compat-libs/.gitignore deleted file mode 100644 index 662c154cdd..0000000000 --- a/src/compat-libs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/libsystemd-*.pc diff --git a/src/compat-libs/libsystemd-daemon.pc.in b/src/compat-libs/libsystemd-daemon.pc.in deleted file mode 100644 index 847afc9d60..0000000000 --- a/src/compat-libs/libsystemd-daemon.pc.in +++ /dev/null @@ -1,19 +0,0 @@ -# Permission is hereby granted, free of charge, to any person -# obtaining a copy of this software and associated documentation files -# (the "Software"), to deal in the Software without restriction, -# including without limitation the rights to use, copy, modify, merge, -# publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: - -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: systemd -Description: systemd Daemon Utility Library - deprecated -URL: @PACKAGE_URL@ -Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lsystemd -Cflags: -I${includedir} diff --git a/src/compat-libs/libsystemd-daemon.sym b/src/compat-libs/libsystemd-daemon.sym deleted file mode 100644 index f440238931..0000000000 --- a/src/compat-libs/libsystemd-daemon.sym +++ /dev/null @@ -1,27 +0,0 @@ -/*** - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, - including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: -***/ - -/* Original symbols from systemd v31 */ - -LIBSYSTEMD_DAEMON_31 { -global: - sd_booted; - sd_is_fifo; - sd_is_mq; - sd_is_socket; - sd_is_socket_inet; - sd_is_socket_unix; - sd_is_special; - sd_listen_fds; - sd_notify; - sd_notifyf; -local: - *; -}; diff --git a/src/compat-libs/libsystemd-id128.pc.in b/src/compat-libs/libsystemd-id128.pc.in deleted file mode 100644 index 80f8fee6c3..0000000000 --- a/src/compat-libs/libsystemd-id128.pc.in +++ /dev/null @@ -1,18 +0,0 @@ -# This file is part of systemd. -# -# 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. - -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: systemd -Description: systemd 128 Bit ID Utility Library - deprecated -URL: @PACKAGE_URL@ -Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lsystemd -Cflags: -I${includedir} diff --git a/src/compat-libs/libsystemd-id128.sym b/src/compat-libs/libsystemd-id128.sym deleted file mode 100644 index 604c0026c6..0000000000 --- a/src/compat-libs/libsystemd-id128.sym +++ /dev/null @@ -1,21 +0,0 @@ -/*** - This file is part of systemd. - - 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. -***/ - -/* Original symbols from systemd v38 */ - -LIBSYSTEMD_ID128_38 { -global: - sd_id128_to_string; - sd_id128_from_string; - sd_id128_randomize; - sd_id128_get_machine; - sd_id128_get_boot; -local: - *; -}; diff --git a/src/compat-libs/libsystemd-journal.pc.in b/src/compat-libs/libsystemd-journal.pc.in deleted file mode 100644 index 395f71005b..0000000000 --- a/src/compat-libs/libsystemd-journal.pc.in +++ /dev/null @@ -1,19 +0,0 @@ -# This file is part of systemd. -# -# 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. - -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: systemd -Description: systemd Journal Utility Library - deprecated -URL: @PACKAGE_URL@ -Version: @PACKAGE_VERSION@ -Requires: libsystemd = @PACKAGE_VERSION@ -Libs: -L${libdir} -lsystemd -Cflags: -I${includedir} diff --git a/src/compat-libs/libsystemd-journal.sym b/src/compat-libs/libsystemd-journal.sym deleted file mode 100644 index 4eb15910d2..0000000000 --- a/src/compat-libs/libsystemd-journal.sym +++ /dev/null @@ -1,111 +0,0 @@ -/*** - This file is part of systemd. - - 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. -***/ - -/* Original symbols from systemd v38 */ - -LIBSYSTEMD_JOURNAL_38 { -global: - sd_journal_print; - sd_journal_printv; - sd_journal_send; - sd_journal_sendv; - sd_journal_stream_fd; - sd_journal_open; - sd_journal_close; - sd_journal_previous; - sd_journal_next; - sd_journal_previous_skip; - sd_journal_next_skip; - sd_journal_get_realtime_usec; - sd_journal_get_monotonic_usec; - sd_journal_get_data; - sd_journal_enumerate_data; - sd_journal_restart_data; - sd_journal_add_match; - sd_journal_flush_matches; - sd_journal_seek_head; - sd_journal_seek_tail; - sd_journal_seek_monotonic_usec; - sd_journal_seek_realtime_usec; - sd_journal_seek_cursor; - sd_journal_get_cursor; - sd_journal_get_fd; - sd_journal_process; -local: - *; -}; - -LIBSYSTEMD_JOURNAL_183 { -global: - sd_journal_print_with_location; - sd_journal_printv_with_location; - sd_journal_send_with_location; - sd_journal_sendv_with_location; -} LIBSYSTEMD_JOURNAL_38; - -LIBSYSTEMD_JOURNAL_184 { -global: - sd_journal_get_cutoff_realtime_usec; - sd_journal_get_cutoff_monotonic_usec; -} LIBSYSTEMD_JOURNAL_183; - -LIBSYSTEMD_JOURNAL_187 { -global: - sd_journal_wait; - sd_journal_open_directory; - sd_journal_add_disjunction; -} LIBSYSTEMD_JOURNAL_184; - -LIBSYSTEMD_JOURNAL_188 { -global: - sd_journal_perror; - sd_journal_perror_with_location; -} LIBSYSTEMD_JOURNAL_187; - -LIBSYSTEMD_JOURNAL_190 { -global: - sd_journal_get_usage; -} LIBSYSTEMD_JOURNAL_188; - -LIBSYSTEMD_JOURNAL_195 { -global: - sd_journal_test_cursor; - sd_journal_query_unique; - sd_journal_enumerate_unique; - sd_journal_restart_unique; -} LIBSYSTEMD_JOURNAL_190; - -LIBSYSTEMD_JOURNAL_196 { -global: - sd_journal_get_catalog; - sd_journal_get_catalog_for_message_id; - sd_journal_set_data_threshold; - sd_journal_get_data_threshold; -} LIBSYSTEMD_JOURNAL_195; - -LIBSYSTEMD_JOURNAL_198 { -global: - sd_journal_reliable_fd; -} LIBSYSTEMD_JOURNAL_196; - -LIBSYSTEMD_JOURNAL_201 { -global: - sd_journal_get_events; - sd_journal_get_timeout; -} LIBSYSTEMD_JOURNAL_198; - -LIBSYSTEMD_JOURNAL_202 { -global: - sd_journal_add_conjunction; -} LIBSYSTEMD_JOURNAL_201; - -LIBSYSTEMD_JOURNAL_205 { -global: - sd_journal_open_files; -} LIBSYSTEMD_JOURNAL_202; diff --git a/src/compat-libs/libsystemd-login.pc.in b/src/compat-libs/libsystemd-login.pc.in deleted file mode 100644 index db3f79c99a..0000000000 --- a/src/compat-libs/libsystemd-login.pc.in +++ /dev/null @@ -1,18 +0,0 @@ -# This file is part of systemd. -# -# 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. - -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: systemd -Description: systemd Login Utility Library - deprecated -URL: @PACKAGE_URL@ -Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lsystemd -Cflags: -I${includedir} diff --git a/src/compat-libs/libsystemd-login.sym b/src/compat-libs/libsystemd-login.sym deleted file mode 100644 index 54aa91c609..0000000000 --- a/src/compat-libs/libsystemd-login.sym +++ /dev/null @@ -1,87 +0,0 @@ -/*** - This file is part of systemd. - - 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. -***/ - -/* Original symbols from systemd v31 */ - -LIBSYSTEMD_LOGIN_31 { -global: - sd_get_seats; - sd_get_sessions; - sd_get_uids; - sd_login_monitor_flush; - sd_login_monitor_get_fd; - sd_login_monitor_new; - sd_login_monitor_unref; - sd_pid_get_owner_uid; - sd_pid_get_session; - sd_seat_can_multi_session; - sd_seat_get_active; - sd_seat_get_sessions; - sd_session_get_seat; - sd_session_get_uid; - sd_session_is_active; - sd_uid_get_seats; - sd_uid_get_sessions; - sd_uid_get_state; - sd_uid_is_on_seat; -local: - *; -}; - -LIBSYSTEMD_LOGIN_38 { -global: - sd_pid_get_unit; - sd_session_get_service; -} LIBSYSTEMD_LOGIN_31; - -LIBSYSTEMD_LOGIN_43 { -global: - sd_session_get_type; - sd_session_get_class; - sd_session_get_display; -} LIBSYSTEMD_LOGIN_38; - -LIBSYSTEMD_LOGIN_186 { -global: - sd_session_get_state; - sd_seat_can_tty; - sd_seat_can_graphical; -} LIBSYSTEMD_LOGIN_43; - -LIBSYSTEMD_LOGIN_198 { -global: - sd_session_get_tty; -} LIBSYSTEMD_LOGIN_186; - -LIBSYSTEMD_LOGIN_201 { -global: - sd_login_monitor_get_events; - sd_login_monitor_get_timeout; -} LIBSYSTEMD_LOGIN_198; - -LIBSYSTEMD_LOGIN_202 { -global: - sd_pid_get_user_unit; - sd_pid_get_machine_name; -} LIBSYSTEMD_LOGIN_201; - -LIBSYSTEMD_LOGIN_203 { -global: - sd_get_machine_names; -} LIBSYSTEMD_LOGIN_202; - -LIBSYSTEMD_LOGIN_205 { -global: - sd_pid_get_slice; -} LIBSYSTEMD_LOGIN_203; - -LIBSYSTEMD_LOGIN_207 { -global: - sd_session_get_vt; -} LIBSYSTEMD_LOGIN_205; diff --git a/src/compat-libs/linkwarning.h b/src/compat-libs/linkwarning.h deleted file mode 100644 index 79ece9e7d1..0000000000 --- a/src/compat-libs/linkwarning.h +++ /dev/null @@ -1,35 +0,0 @@ -/*** - This file is part of systemd, but is heavily based on - glibc's libc-symbols.h. - - Copyright (C) 1995-1998,2000-2006,2008,2009 Free Software Foundation, Inc - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#pragma once - -#define __make_section_unallocated(section_string) \ - asm (".section " section_string "\n\t.previous"); - -#define __sec_comment "\n#APP\n\t#" - -#define link_warning(symbol, msg) \ - __make_section_unallocated (".gnu.warning." #symbol) \ - static const char __evoke_link_warning_##symbol[] \ - __attribute__ ((used, section (".gnu.warning." #symbol __sec_comment))) \ - = msg - -#define obsolete_lib(name, lib) \ - link_warning(name, #name " was moved to libsystemd. Do not use " #lib ".") diff --git a/src/core/automount.c b/src/core/automount.c index 5dc6fd98e7..f06d837e30 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -75,6 +75,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct expire_data*, expire_data_free); static int open_dev_autofs(Manager *m); static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, void *userdata); +static int automount_start_expire(Automount *a); +static void automount_stop_expire(Automount *a); +static int automount_send_ready(Automount *a, Set *tokens, int status); static void automount_init(Unit *u) { Automount *a = AUTOMOUNT(u); @@ -87,8 +90,6 @@ static void automount_init(Unit *u) { UNIT(a)->ignore_on_isolate = true; } -static int automount_send_ready(Automount *a, Set *tokens, int status); - static void unmount_autofs(Automount *a) { int r; @@ -149,7 +150,7 @@ static int automount_add_default_dependencies(Automount *a) { if (!UNIT(a)->default_dependencies) return 0; - if (UNIT(a)->manager->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(UNIT(a)->manager)) return 0; r = unit_add_two_dependencies_by_name(UNIT(a), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true); @@ -235,6 +236,9 @@ static void automount_set_state(Automount *a, AutomountState state) { old_state = a->state; a->state = state; + if (state != AUTOMOUNT_RUNNING) + automount_stop_expire(a); + if (state != AUTOMOUNT_WAITING && state != AUTOMOUNT_RUNNING) unmount_autofs(a); @@ -408,7 +412,7 @@ static int autofs_send_ready(int dev_autofs_fd, int ioctl_fd, uint32_t token, in init_autofs_dev_ioctl(¶m); param.ioctlfd = ioctl_fd; - if (status) { + if (status != 0) { param.fail.token = token; param.fail.status = status; } else @@ -435,7 +439,7 @@ static int automount_send_ready(Automount *a, Set *tokens, int status) { if (ioctl_fd < 0) return ioctl_fd; - if (status) + if (status != 0) log_unit_debug_errno(UNIT(a), status, "Sending failure: %m"); else log_unit_debug(UNIT(a), "Sending success."); @@ -462,59 +466,54 @@ static int automount_send_ready(Automount *a, Set *tokens, int status) { return r; } -static int automount_start_expire(Automount *a); - -int automount_update_mount(Automount *a, MountState old_state, MountState state) { +static void automount_trigger_notify(Unit *u, Unit *other) { + Automount *a = AUTOMOUNT(u); int r; assert(a); + assert(other); + + /* Filter out invocations with bogus state */ + if (other->load_state != UNIT_LOADED || other->type != UNIT_MOUNT) + return; + + /* Don't propagate state changes from the mount if we are already down */ + if (!IN_SET(a->state, AUTOMOUNT_WAITING, AUTOMOUNT_RUNNING)) + return; + + /* Propagate start limit hit state */ + if (other->start_limit_hit) { + automount_enter_dead(a, AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT); + return; + } + + /* Don't propagate anything if there's still a job queued */ + if (other->job) + return; + + /* The mount is successfully established */ + if (IN_SET(MOUNT(other)->state, MOUNT_MOUNTED, MOUNT_REMOUNTING)) { + (void) automount_send_ready(a, a->tokens, 0); - switch (state) { - case MOUNT_MOUNTED: - case MOUNT_REMOUNTING: - automount_send_ready(a, a->tokens, 0); r = automount_start_expire(a); if (r < 0) log_unit_warning_errno(UNIT(a), r, "Failed to start expiration timer, ignoring: %m"); - break; - case MOUNT_DEAD: - case MOUNT_UNMOUNTING: - case MOUNT_MOUNTING_SIGTERM: - case MOUNT_MOUNTING_SIGKILL: - case MOUNT_REMOUNTING_SIGTERM: - case MOUNT_REMOUNTING_SIGKILL: - case MOUNT_UNMOUNTING_SIGTERM: - case MOUNT_UNMOUNTING_SIGKILL: - case MOUNT_FAILED: - if (old_state != state) - automount_send_ready(a, a->tokens, -ENODEV); - (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF); - break; - default: - break; - } - switch (state) { - case MOUNT_DEAD: - automount_send_ready(a, a->expire_tokens, 0); - break; - case MOUNT_MOUNTING: - case MOUNT_MOUNTING_DONE: - case MOUNT_MOUNTING_SIGTERM: - case MOUNT_MOUNTING_SIGKILL: - case MOUNT_REMOUNTING_SIGTERM: - case MOUNT_REMOUNTING_SIGKILL: - case MOUNT_UNMOUNTING_SIGTERM: - case MOUNT_UNMOUNTING_SIGKILL: - case MOUNT_FAILED: - if (old_state != state) - automount_send_ready(a, a->expire_tokens, -ENODEV); - break; - default: - break; + automount_set_state(a, AUTOMOUNT_RUNNING); } - return 0; + /* The mount is in some unhappy state now, let's unfreeze any waiting clients */ + if (IN_SET(MOUNT(other)->state, + MOUNT_DEAD, MOUNT_UNMOUNTING, + MOUNT_MOUNTING_SIGTERM, MOUNT_MOUNTING_SIGKILL, + MOUNT_REMOUNTING_SIGTERM, MOUNT_REMOUNTING_SIGKILL, + MOUNT_UNMOUNTING_SIGTERM, MOUNT_UNMOUNTING_SIGKILL, + MOUNT_FAILED)) { + + (void) automount_send_ready(a, a->tokens, -ENODEV); + + automount_set_state(a, AUTOMOUNT_WAITING); + } } static void automount_enter_waiting(Automount *a) { @@ -699,6 +698,15 @@ static int automount_start_expire(Automount *a) { return 0; } +static void automount_stop_expire(Automount *a) { + assert(a); + + if (!a->expire_event_source) + return; + + (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF); +} + static void automount_enter_runnning(Automount *a) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; struct stat st; @@ -751,6 +759,7 @@ fail: static int automount_start(Unit *u) { Automount *a = AUTOMOUNT(u); Unit *trigger; + int r; assert(a); assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED); @@ -766,6 +775,12 @@ static int automount_start(Unit *u) { return -ENOENT; } + r = unit_start_limit_test(u); + if (r < 0) { + automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT); + return r; + } + a->result = AUTOMOUNT_SUCCESS; automount_enter_waiting(a); return 1; @@ -958,7 +973,7 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo case autofs_ptype_expire_direct: log_unit_debug(UNIT(a), "Got direct umount request on %s", a->where); - (void) sd_event_source_set_enabled(a->expire_event_source, SD_EVENT_OFF); + automount_stop_expire(a); r = set_ensure_allocated(&a->expire_tokens, NULL); if (r < 0) { @@ -1037,7 +1052,9 @@ static bool automount_supported(void) { static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = { [AUTOMOUNT_SUCCESS] = "success", - [AUTOMOUNT_FAILURE_RESOURCES] = "resources" + [AUTOMOUNT_FAILURE_RESOURCES] = "resources", + [AUTOMOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit", + [AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT] = "mount-start-limit-hit", }; DEFINE_STRING_TABLE_LOOKUP(automount_result, AutomountResult); @@ -1050,9 +1067,6 @@ const UnitVTable automount_vtable = { "Automount\0" "Install\0", - .no_alias = true, - .no_instances = true, - .init = automount_init, .load = automount_load, .done = automount_done, @@ -1072,6 +1086,8 @@ const UnitVTable automount_vtable = { .check_gc = automount_check_gc, + .trigger_notify = automount_trigger_notify, + .reset_failed = automount_reset_failed, .bus_vtable = bus_automount_vtable, diff --git a/src/core/automount.h b/src/core/automount.h index cf5b1cf994..76a201178e 100644 --- a/src/core/automount.h +++ b/src/core/automount.h @@ -26,6 +26,8 @@ typedef struct Automount Automount; typedef enum AutomountResult { AUTOMOUNT_SUCCESS, AUTOMOUNT_FAILURE_RESOURCES, + AUTOMOUNT_FAILURE_START_LIMIT_HIT, + AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT, _AUTOMOUNT_RESULT_MAX, _AUTOMOUNT_RESULT_INVALID = -1 } AutomountResult; @@ -53,7 +55,5 @@ struct Automount { extern const UnitVTable automount_vtable; -int automount_update_mount(Automount *a, MountState old_state, MountState state); - const char* automount_result_to_string(AutomountResult i) _const_; AutomountResult automount_result_from_string(const char *s) _pure_; diff --git a/src/core/bus-endpoint.c b/src/core/bus-endpoint.c deleted file mode 100644 index d22a80c91f..0000000000 --- a/src/core/bus-endpoint.c +++ /dev/null @@ -1,135 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Daniel Mack - - 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 <stdlib.h> - -#include "alloc-util.h" -#include "bus-endpoint.h" -#include "bus-kernel.h" -#include "bus-policy.h" -#include "kdbus.h" - -int bus_kernel_set_endpoint_policy(int fd, uid_t uid, BusEndpoint *ep) { - - struct kdbus_cmd *update; - struct kdbus_item *n; - BusEndpointPolicy *po; - Iterator i; - size_t size; - int r; - - size = ALIGN8(offsetof(struct kdbus_cmd, items)); - - HASHMAP_FOREACH(po, ep->policy_hash, i) { - size += ALIGN8(offsetof(struct kdbus_item, str) + strlen(po->name) + 1); - size += ALIGN8(offsetof(struct kdbus_item, policy_access) + sizeof(struct kdbus_policy_access)); - } - - update = alloca0_align(size, 8); - update->size = size; - - n = update->items; - - HASHMAP_FOREACH(po, ep->policy_hash, i) { - n->type = KDBUS_ITEM_NAME; - n->size = offsetof(struct kdbus_item, str) + strlen(po->name) + 1; - strcpy(n->str, po->name); - n = KDBUS_ITEM_NEXT(n); - - n->type = KDBUS_ITEM_POLICY_ACCESS; - n->size = offsetof(struct kdbus_item, policy_access) + sizeof(struct kdbus_policy_access); - - n->policy_access.type = KDBUS_POLICY_ACCESS_USER; - n->policy_access.access = bus_kernel_translate_access(po->access); - n->policy_access.id = uid; - - n = KDBUS_ITEM_NEXT(n); - } - - r = ioctl(fd, KDBUS_CMD_ENDPOINT_UPDATE, update); - if (r < 0) - return -errno; - - return 0; -} - -int bus_endpoint_new(BusEndpoint **ep) { - assert(ep); - - *ep = new0(BusEndpoint, 1); - if (!*ep) - return -ENOMEM; - - return 0; -} - -int bus_endpoint_add_policy(BusEndpoint *ep, const char *name, BusPolicyAccess access) { - _cleanup_free_ BusEndpointPolicy *po = NULL; - _cleanup_free_ char *key = NULL; - int r; - - assert(ep); - assert(name); - assert(access > _BUS_POLICY_ACCESS_INVALID && access < _BUS_POLICY_ACCESS_MAX); - - /* check if we already have this name in the policy list. If we do, see if the new access level - * is higher than the exising one, and upgrade the entry in that case. Otherwise, do nothing. - */ - - if (ep->policy_hash) { - po = hashmap_get(ep->policy_hash, name); - if (po) { - if (po->access < access) - po->access = access; - - return 0; - } - } else { - ep->policy_hash = hashmap_new(&string_hash_ops); - if (!ep->policy_hash) - return -ENOMEM; - } - - po = new0(BusEndpointPolicy, 1); - if (!po) - return -ENOMEM; - - key = strdup(name); - if (!key) - return -ENOMEM; - - po->name = key; - po->access = access; - - r = hashmap_put(ep->policy_hash, key, po); - if (r < 0) - return r; - - po = NULL; - key = NULL; - return 0; -} - -void bus_endpoint_free(BusEndpoint *endpoint) { - if (!endpoint) - return; - - hashmap_free_free_free(endpoint->policy_hash); - free(endpoint); -} diff --git a/src/core/busname.c b/src/core/busname.c index de2a21ccde..f03a95c24e 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -149,7 +149,7 @@ static int busname_add_default_default_dependencies(BusName *n) { if (r < 0) return r; - if (UNIT(n)->manager->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(UNIT(n)->manager)) { r = unit_add_two_dependencies_by_name(UNIT(n), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); if (r < 0) return r; @@ -318,7 +318,7 @@ static int busname_open_fd(BusName *n) { if (n->starter_fd >= 0) return 0; - mode = UNIT(n)->manager->running_as == MANAGER_SYSTEM ? "system" : "user"; + mode = MANAGER_IS_SYSTEM(UNIT(n)->manager) ? "system" : "user"; n->starter_fd = bus_kernel_open_bus_fd(mode, &path); if (n->starter_fd < 0) return log_unit_warning_errno(UNIT(n), n->starter_fd, "Failed to open %s: %m", path ?: "kdbus"); @@ -607,6 +607,7 @@ fail: static int busname_start(Unit *u) { BusName *n = BUSNAME(u); + int r; assert(n); @@ -632,6 +633,12 @@ static int busname_start(Unit *u) { assert(IN_SET(n->state, BUSNAME_DEAD, BUSNAME_FAILED)); + r = unit_start_limit_test(u); + if (r < 0) { + busname_enter_dead(n, BUSNAME_FAILURE_START_LIMIT_HIT); + return r; + } + n->result = BUSNAME_SUCCESS; busname_enter_making(n); @@ -999,6 +1006,14 @@ static bool busname_supported(void) { return supported; } +static int busname_control_pid(Unit *u) { + BusName *n = BUSNAME(u); + + assert(n); + + return n->control_pid; +} + static const char* const busname_result_table[_BUSNAME_RESULT_MAX] = { [BUSNAME_SUCCESS] = "success", [BUSNAME_FAILURE_RESOURCES] = "resources", @@ -1006,6 +1021,7 @@ static const char* const busname_result_table[_BUSNAME_RESULT_MAX] = { [BUSNAME_FAILURE_EXIT_CODE] = "exit-code", [BUSNAME_FAILURE_SIGNAL] = "signal", [BUSNAME_FAILURE_CORE_DUMP] = "core-dump", + [BUSNAME_FAILURE_START_LIMIT_HIT] = "start-limit-hit", [BUSNAME_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit", }; @@ -1020,9 +1036,6 @@ const UnitVTable busname_vtable = { "Install\0", .private_section = "BusName", - .no_alias = true, - .no_instances = true, - .init = busname_init, .done = busname_done, .load = busname_load, @@ -1052,6 +1065,8 @@ const UnitVTable busname_vtable = { .supported = busname_supported, + .control_pid = busname_control_pid, + .bus_vtable = bus_busname_vtable, .status_message_formats = { diff --git a/src/core/busname.h b/src/core/busname.h index 6b6f6c62d4..a8562db458 100644 --- a/src/core/busname.h +++ b/src/core/busname.h @@ -23,6 +23,7 @@ typedef struct BusName BusName; typedef struct BusNamePolicy BusNamePolicy; #include "unit.h" +#include "bus-policy.h" typedef enum BusNameResult { BUSNAME_SUCCESS, @@ -31,6 +32,7 @@ typedef enum BusNameResult { BUSNAME_FAILURE_EXIT_CODE, BUSNAME_FAILURE_SIGNAL, BUSNAME_FAILURE_CORE_DUMP, + BUSNAME_FAILURE_START_LIMIT_HIT, BUSNAME_FAILURE_SERVICE_START_LIMIT_HIT, _BUSNAME_RESULT_MAX, _BUSNAME_RESULT_INVALID = -1 diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 39235a95f6..0fb63b1bd1 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -32,6 +32,7 @@ #include "special.h" #include "string-table.h" #include "string-util.h" +#include "stdio-util.h" #define CGROUP_CPU_QUOTA_PERIOD_USEC ((usec_t) 100 * USEC_PER_MSEC) @@ -47,6 +48,9 @@ void cgroup_context_init(CGroupContext *c) { c->memory_limit = (uint64_t) -1; + c->io_weight = CGROUP_WEIGHT_INVALID; + c->startup_io_weight = CGROUP_WEIGHT_INVALID; + c->blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID; c->startup_blockio_weight = CGROUP_BLKIO_WEIGHT_INVALID; @@ -62,6 +66,24 @@ void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a) { free(a); } +void cgroup_context_free_io_device_weight(CGroupContext *c, CGroupIODeviceWeight *w) { + assert(c); + assert(w); + + LIST_REMOVE(device_weights, c->io_device_weights, w); + free(w->path); + free(w); +} + +void cgroup_context_free_io_device_limit(CGroupContext *c, CGroupIODeviceLimit *l) { + assert(c); + assert(l); + + LIST_REMOVE(device_limits, c->io_device_limits, l); + free(l->path); + free(l); +} + void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w) { assert(c); assert(w); @@ -83,6 +105,12 @@ void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockI void cgroup_context_done(CGroupContext *c) { assert(c); + while (c->io_device_weights) + cgroup_context_free_io_device_weight(c, c->io_device_weights); + + while (c->io_device_limits) + cgroup_context_free_io_device_limit(c, c->io_device_limits); + while (c->blockio_device_weights) cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights); @@ -94,6 +122,8 @@ void cgroup_context_done(CGroupContext *c) { } void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { + CGroupIODeviceLimit *il; + CGroupIODeviceWeight *iw; CGroupBlockIODeviceBandwidth *b; CGroupBlockIODeviceWeight *w; CGroupDeviceAllow *a; @@ -106,12 +136,15 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { fprintf(f, "%sCPUAccounting=%s\n" + "%sIOAccounting=%s\n" "%sBlockIOAccounting=%s\n" "%sMemoryAccounting=%s\n" "%sTasksAccounting=%s\n" "%sCPUShares=%" PRIu64 "\n" "%sStartupCPUShares=%" PRIu64 "\n" "%sCPUQuotaPerSecSec=%s\n" + "%sIOWeight=%" PRIu64 "\n" + "%sStartupIOWeight=%" PRIu64 "\n" "%sBlockIOWeight=%" PRIu64 "\n" "%sStartupBlockIOWeight=%" PRIu64 "\n" "%sMemoryLimit=%" PRIu64 "\n" @@ -119,12 +152,15 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { "%sDevicePolicy=%s\n" "%sDelegate=%s\n", prefix, yes_no(c->cpu_accounting), + prefix, yes_no(c->io_accounting), prefix, yes_no(c->blockio_accounting), prefix, yes_no(c->memory_accounting), prefix, yes_no(c->tasks_accounting), prefix, c->cpu_shares, prefix, c->startup_cpu_shares, prefix, format_timespan(u, sizeof(u), c->cpu_quota_per_sec_usec, 1), + prefix, c->io_weight, + prefix, c->startup_io_weight, prefix, c->blockio_weight, prefix, c->startup_blockio_weight, prefix, c->memory_limit, @@ -139,6 +175,27 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { a->path, a->r ? "r" : "", a->w ? "w" : "", a->m ? "m" : ""); + LIST_FOREACH(device_weights, iw, c->io_device_weights) + fprintf(f, + "%sIODeviceWeight=%s %" PRIu64, + prefix, + iw->path, + iw->weight); + + LIST_FOREACH(device_limits, il, c->io_device_limits) { + char buf[FORMAT_BYTES_MAX]; + CGroupIOLimitType type; + + for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++) + if (il->limits[type] != cgroup_io_limit_defaults[type]) + fprintf(f, + "%s%s=%s %s\n", + prefix, + cgroup_io_limit_type_to_string(type), + il->path, + format_bytes(buf, sizeof(buf), il->limits[type])); + } + LIST_FOREACH(device_weights, w, c->blockio_device_weights) fprintf(f, "%sBlockIODeviceWeight=%s %" PRIu64, @@ -149,16 +206,22 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { char buf[FORMAT_BYTES_MAX]; - fprintf(f, - "%s%s=%s %s\n", - prefix, - b->read ? "BlockIOReadBandwidth" : "BlockIOWriteBandwidth", - b->path, - format_bytes(buf, sizeof(buf), b->bandwidth)); + if (b->rbps != CGROUP_LIMIT_MAX) + fprintf(f, + "%sBlockIOReadBandwidth=%s %s\n", + prefix, + b->path, + format_bytes(buf, sizeof(buf), b->rbps)); + if (b->wbps != CGROUP_LIMIT_MAX) + fprintf(f, + "%sBlockIOWriteBandwidth=%s %s\n", + prefix, + b->path, + format_bytes(buf, sizeof(buf), b->wbps)); } } -static int lookup_blkio_device(const char *p, dev_t *dev) { +static int lookup_block_device(const char *p, dev_t *dev) { struct stat st; int r; @@ -295,6 +358,144 @@ fail: return -errno; } +static bool cgroup_context_has_io_config(CGroupContext *c) { + return c->io_accounting || + c->io_weight != CGROUP_WEIGHT_INVALID || + c->startup_io_weight != CGROUP_WEIGHT_INVALID || + c->io_device_weights || + c->io_device_limits; +} + +static bool cgroup_context_has_blockio_config(CGroupContext *c) { + return c->blockio_accounting || + c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID || + c->startup_blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID || + c->blockio_device_weights || + c->blockio_device_bandwidths; +} + +static uint64_t cgroup_context_io_weight(CGroupContext *c, ManagerState state) { + if (IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && + c->startup_io_weight != CGROUP_WEIGHT_INVALID) + return c->startup_io_weight; + else if (c->io_weight != CGROUP_WEIGHT_INVALID) + return c->io_weight; + else + return CGROUP_WEIGHT_DEFAULT; +} + +static uint64_t cgroup_context_blkio_weight(CGroupContext *c, ManagerState state) { + if (IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && + c->startup_blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID) + return c->startup_blockio_weight; + else if (c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID) + return c->blockio_weight; + else + return CGROUP_BLKIO_WEIGHT_DEFAULT; +} + +static uint64_t cgroup_weight_blkio_to_io(uint64_t blkio_weight) { + return CLAMP(blkio_weight * CGROUP_WEIGHT_DEFAULT / CGROUP_BLKIO_WEIGHT_DEFAULT, + CGROUP_WEIGHT_MIN, CGROUP_WEIGHT_MAX); +} + +static uint64_t cgroup_weight_io_to_blkio(uint64_t io_weight) { + return CLAMP(io_weight * CGROUP_BLKIO_WEIGHT_DEFAULT / CGROUP_WEIGHT_DEFAULT, + CGROUP_BLKIO_WEIGHT_MIN, CGROUP_BLKIO_WEIGHT_MAX); +} + +static void cgroup_apply_io_device_weight(const char *path, const char *dev_path, uint64_t io_weight) { + char buf[DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1]; + dev_t dev; + int r; + + r = lookup_block_device(dev_path, &dev); + if (r < 0) + return; + + xsprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), io_weight); + r = cg_set_attribute("io", path, "io.weight", buf); + if (r < 0) + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set io.weight on %s: %m", path); +} + +static void cgroup_apply_blkio_device_weight(const char *path, const char *dev_path, uint64_t blkio_weight) { + char buf[DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1]; + dev_t dev; + int r; + + r = lookup_block_device(dev_path, &dev); + if (r < 0) + return; + + xsprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), blkio_weight); + r = cg_set_attribute("blkio", path, "blkio.weight_device", buf); + if (r < 0) + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set blkio.weight_device on %s: %m", path); +} + +static unsigned cgroup_apply_io_device_limit(const char *path, const char *dev_path, uint64_t *limits) { + char limit_bufs[_CGROUP_IO_LIMIT_TYPE_MAX][DECIMAL_STR_MAX(uint64_t)]; + char buf[DECIMAL_STR_MAX(dev_t)*2+2+(6+DECIMAL_STR_MAX(uint64_t)+1)*4]; + CGroupIOLimitType type; + dev_t dev; + unsigned n = 0; + int r; + + r = lookup_block_device(dev_path, &dev); + if (r < 0) + return 0; + + for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++) { + if (limits[type] != cgroup_io_limit_defaults[type]) { + xsprintf(limit_bufs[type], "%" PRIu64, limits[type]); + n++; + } else { + xsprintf(limit_bufs[type], "%s", limits[type] == CGROUP_LIMIT_MAX ? "max" : "0"); + } + } + + xsprintf(buf, "%u:%u rbps=%s wbps=%s riops=%s wiops=%s\n", major(dev), minor(dev), + limit_bufs[CGROUP_IO_RBPS_MAX], limit_bufs[CGROUP_IO_WBPS_MAX], + limit_bufs[CGROUP_IO_RIOPS_MAX], limit_bufs[CGROUP_IO_WIOPS_MAX]); + r = cg_set_attribute("io", path, "io.max", buf); + if (r < 0) + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set io.max on %s: %m", path); + return n; +} + +static unsigned cgroup_apply_blkio_device_limit(const char *path, const char *dev_path, uint64_t rbps, uint64_t wbps) { + char buf[DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1]; + dev_t dev; + unsigned n = 0; + int r; + + r = lookup_block_device(dev_path, &dev); + if (r < 0) + return 0; + + if (rbps != CGROUP_LIMIT_MAX) + n++; + sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), rbps); + r = cg_set_attribute("blkio", path, "blkio.throttle.read_bps_device", buf); + if (r < 0) + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set blkio.throttle.read_bps_device on %s: %m", path); + + if (wbps != CGROUP_LIMIT_MAX) + n++; + sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), wbps); + r = cg_set_attribute("blkio", path, "blkio.throttle.write_bps_device", buf); + if (r < 0) + log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set blkio.throttle.write_bps_device on %s: %m", path); + + return n; +} + void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, ManagerState state) { bool is_root; int r; @@ -343,53 +544,120 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M "Failed to set cpu.cfs_quota_us on %s: %m", path); } - if (mask & CGROUP_MASK_BLKIO) { - char buf[MAX(DECIMAL_STR_MAX(uint64_t)+1, - DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1)]; - CGroupBlockIODeviceWeight *w; - CGroupBlockIODeviceBandwidth *b; + if (mask & CGROUP_MASK_IO) { + bool has_io = cgroup_context_has_io_config(c); + bool has_blockio = cgroup_context_has_blockio_config(c); if (!is_root) { - sprintf(buf, "%" PRIu64 "\n", - IN_SET(state, MANAGER_STARTING, MANAGER_INITIALIZING) && c->startup_blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID ? c->startup_blockio_weight : - c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID ? c->blockio_weight : CGROUP_BLKIO_WEIGHT_DEFAULT); - r = cg_set_attribute("blkio", path, "blkio.weight", buf); + char buf[8+DECIMAL_STR_MAX(uint64_t)+1]; + uint64_t weight; + + if (has_io) + weight = cgroup_context_io_weight(c, state); + else if (has_blockio) + weight = cgroup_weight_blkio_to_io(cgroup_context_blkio_weight(c, state)); + else + weight = CGROUP_WEIGHT_DEFAULT; + + xsprintf(buf, "default %" PRIu64 "\n", weight); + r = cg_set_attribute("io", path, "io.weight", buf); if (r < 0) log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set blkio.weight on %s: %m", path); + "Failed to set io.weight on %s: %m", path); - /* FIXME: no way to reset this list */ - LIST_FOREACH(device_weights, w, c->blockio_device_weights) { - dev_t dev; + if (has_io) { + CGroupIODeviceWeight *w; - r = lookup_blkio_device(w->path, &dev); - if (r < 0) - continue; + /* FIXME: no way to reset this list */ + LIST_FOREACH(device_weights, w, c->io_device_weights) + cgroup_apply_io_device_weight(path, w->path, w->weight); + } else if (has_blockio) { + CGroupBlockIODeviceWeight *w; - sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), w->weight); - r = cg_set_attribute("blkio", path, "blkio.weight_device", buf); - if (r < 0) - log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set blkio.weight_device on %s: %m", path); + /* FIXME: no way to reset this list */ + LIST_FOREACH(device_weights, w, c->blockio_device_weights) + cgroup_apply_io_device_weight(path, w->path, cgroup_weight_blkio_to_io(w->weight)); } } - /* FIXME: no way to reset this list */ - LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { - const char *a; - dev_t dev; + /* Apply limits and free ones without config. */ + if (has_io) { + CGroupIODeviceLimit *l, *next; - r = lookup_blkio_device(b->path, &dev); - if (r < 0) - continue; + LIST_FOREACH_SAFE(device_limits, l, next, c->io_device_limits) { + if (!cgroup_apply_io_device_limit(path, l->path, l->limits)) + cgroup_context_free_io_device_limit(c, l); + } + } else if (has_blockio) { + CGroupBlockIODeviceBandwidth *b, *next; - a = b->read ? "blkio.throttle.read_bps_device" : "blkio.throttle.write_bps_device"; + LIST_FOREACH_SAFE(device_bandwidths, b, next, c->blockio_device_bandwidths) { + uint64_t limits[_CGROUP_IO_LIMIT_TYPE_MAX]; + CGroupIOLimitType type; - sprintf(buf, "%u:%u %" PRIu64 "\n", major(dev), minor(dev), b->bandwidth); - r = cg_set_attribute("blkio", path, a, buf); + for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++) + limits[type] = cgroup_io_limit_defaults[type]; + + limits[CGROUP_IO_RBPS_MAX] = b->rbps; + limits[CGROUP_IO_WBPS_MAX] = b->wbps; + + if (!cgroup_apply_io_device_limit(path, b->path, limits)) + cgroup_context_free_blockio_device_bandwidth(c, b); + } + } + } + + if (mask & CGROUP_MASK_BLKIO) { + bool has_io = cgroup_context_has_io_config(c); + bool has_blockio = cgroup_context_has_blockio_config(c); + + if (!is_root) { + char buf[DECIMAL_STR_MAX(uint64_t)+1]; + uint64_t weight; + + if (has_blockio) + weight = cgroup_context_blkio_weight(c, state); + else if (has_io) + weight = cgroup_weight_io_to_blkio(cgroup_context_io_weight(c, state)); + else + weight = CGROUP_BLKIO_WEIGHT_DEFAULT; + + xsprintf(buf, "%" PRIu64 "\n", weight); + r = cg_set_attribute("blkio", path, "blkio.weight", buf); if (r < 0) log_full_errno(IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set %s on %s: %m", a, path); + "Failed to set blkio.weight on %s: %m", path); + + if (has_blockio) { + CGroupBlockIODeviceWeight *w; + + /* FIXME: no way to reset this list */ + LIST_FOREACH(device_weights, w, c->blockio_device_weights) + cgroup_apply_blkio_device_weight(path, w->path, w->weight); + } else if (has_io) { + CGroupIODeviceWeight *w; + + /* FIXME: no way to reset this list */ + LIST_FOREACH(device_weights, w, c->io_device_weights) + cgroup_apply_blkio_device_weight(path, w->path, cgroup_weight_io_to_blkio(w->weight)); + } + } + + /* Apply limits and free ones without config. */ + if (has_blockio) { + CGroupBlockIODeviceBandwidth *b, *next; + + LIST_FOREACH_SAFE(device_bandwidths, b, next, c->blockio_device_bandwidths) { + if (!cgroup_apply_blkio_device_limit(path, b->path, b->rbps, b->wbps)) + cgroup_context_free_blockio_device_bandwidth(c, b); + } + } else if (has_io) { + CGroupIODeviceLimit *l, *next; + + LIST_FOREACH_SAFE(device_limits, l, next, c->io_device_limits) { + if (!cgroup_apply_blkio_device_limit(path, l->path, l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX])) + cgroup_context_free_io_device_limit(c, l); + } } } @@ -506,12 +774,8 @@ CGroupMask cgroup_context_get_mask(CGroupContext *c) { c->cpu_quota_per_sec_usec != USEC_INFINITY) mask |= CGROUP_MASK_CPUACCT | CGROUP_MASK_CPU; - if (c->blockio_accounting || - c->blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID || - c->startup_blockio_weight != CGROUP_BLKIO_WEIGHT_INVALID || - c->blockio_device_weights || - c->blockio_device_bandwidths) - mask |= CGROUP_MASK_BLKIO; + if (cgroup_context_has_io_config(c) || cgroup_context_has_blockio_config(c)) + mask |= CGROUP_MASK_IO | CGROUP_MASK_BLKIO; if (c->memory_accounting || c->memory_limit != (uint64_t) -1) @@ -765,7 +1029,7 @@ int unit_set_cgroup_path(Unit *u, const char *path) { } int unit_watch_cgroup(Unit *u) { - _cleanup_free_ char *populated = NULL; + _cleanup_free_ char *events = NULL; int r; assert(u); @@ -791,11 +1055,11 @@ int unit_watch_cgroup(Unit *u) { if (r < 0) return log_oom(); - r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.populated", &populated); + r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.events", &events); if (r < 0) return log_oom(); - u->cgroup_inotify_wd = inotify_add_watch(u->manager->cgroup_inotify_fd, populated, IN_MODIFY); + u->cgroup_inotify_wd = inotify_add_watch(u->manager->cgroup_inotify_fd, events, IN_MODIFY); if (u->cgroup_inotify_wd < 0) { if (errno == ENOENT) /* If the directory is already @@ -857,6 +1121,7 @@ static int unit_create_cgroup( /* Keep track that this is now realized */ u->cgroup_realized = true; u->cgroup_realized_mask = target_mask; + u->cgroup_enabled_mask = enable_mask; if (u->type != UNIT_SLICE && !c->delegate) { @@ -886,10 +1151,10 @@ int unit_attach_pids_to_cgroup(Unit *u) { return 0; } -static bool unit_has_mask_realized(Unit *u, CGroupMask target_mask) { +static bool unit_has_mask_realized(Unit *u, CGroupMask target_mask, CGroupMask enable_mask) { assert(u); - return u->cgroup_realized && u->cgroup_realized_mask == target_mask; + return u->cgroup_realized && u->cgroup_realized_mask == target_mask && u->cgroup_enabled_mask == enable_mask; } /* Check if necessary controllers and attributes for a unit are in place. @@ -910,7 +1175,9 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) { } target_mask = unit_get_target_mask(u); - if (unit_has_mask_realized(u, target_mask)) + enable_mask = unit_get_enable_mask(u); + + if (unit_has_mask_realized(u, target_mask, enable_mask)) return 0; /* First, realize parents */ @@ -921,7 +1188,6 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) { } /* And then do the real work */ - enable_mask = unit_get_enable_mask(u); r = unit_create_cgroup(u, target_mask, enable_mask); if (r < 0) return r; @@ -990,7 +1256,7 @@ static void unit_queue_siblings(Unit *u) { /* If the unit doesn't need any new controllers * and has current ones realized, it doesn't need * any changes. */ - if (unit_has_mask_realized(m, unit_get_target_mask(m))) + if (unit_has_mask_realized(m, unit_get_target_mask(m), unit_get_enable_mask(m))) continue; unit_add_to_cgroup_queue(m); @@ -1069,6 +1335,7 @@ void unit_prune_cgroup(Unit *u) { u->cgroup_realized = false; u->cgroup_realized_mask = 0; + u->cgroup_enabled_mask = 0; } int unit_search_main_pid(Unit *u, pid_t *ret) { @@ -1265,7 +1532,7 @@ int manager_setup_cgroup(Manager *m) { * it. This is to support live upgrades from older systemd * versions where PID 1 was moved there. Also see * cg_get_root_path(). */ - if (!e && m->running_as == MANAGER_SYSTEM) { + if (!e && MANAGER_IS_SYSTEM(m)) { e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE); if (!e) e = endswith(m->cgroup_root, "/system"); /* even more legacy */ @@ -1312,13 +1579,15 @@ int manager_setup_cgroup(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to watch control group inotify object: %m"); - r = sd_event_source_set_priority(m->cgroup_inotify_event_source, SD_EVENT_PRIORITY_IDLE - 5); + /* Process cgroup empty notifications early, but after service notifications and SIGCHLD. Also + * see handling of cgroup agent notifications, for the classic cgroup hierarchy support. */ + r = sd_event_source_set_priority(m->cgroup_inotify_event_source, SD_EVENT_PRIORITY_NORMAL-5); if (r < 0) return log_error_errno(r, "Failed to set priority of inotify event source: %m"); (void) sd_event_source_set_description(m->cgroup_inotify_event_source, "cgroup-inotify"); - } else if (m->running_as == MANAGER_SYSTEM) { + } else if (MANAGER_IS_SYSTEM(m)) { /* On the legacy hierarchy we only get * notifications via cgroup agents. (Which @@ -1458,6 +1727,8 @@ int manager_notify_cgroup_empty(Manager *m, const char *cgroup) { assert(m); assert(cgroup); + log_debug("Got cgroup empty notification for: %s", cgroup); + u = manager_get_unit_by_cgroup(m, cgroup); if (!u) return 0; @@ -1594,6 +1865,10 @@ void unit_invalidate_cgroup(Unit *u, CGroupMask m) { if (m == 0) return; + /* always invalidate compat pairs together */ + if (m & (CGROUP_MASK_IO | CGROUP_MASK_BLKIO)) + m |= CGROUP_MASK_IO | CGROUP_MASK_BLKIO; + if ((u->cgroup_realized_mask & m) == 0) return; @@ -1608,7 +1883,7 @@ void manager_invalidate_startup_units(Manager *m) { assert(m); SET_FOREACH(u, m->startup_units, i) - unit_invalidate_cgroup(u, CGROUP_MASK_CPU|CGROUP_MASK_BLKIO); + unit_invalidate_cgroup(u, CGROUP_MASK_CPU|CGROUP_MASK_IO|CGROUP_MASK_BLKIO); } static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = { diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 360bbca30f..2b1edbafc4 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -23,9 +23,12 @@ #include "list.h" #include "time-util.h" +#include "cgroup-util.h" typedef struct CGroupContext CGroupContext; typedef struct CGroupDeviceAllow CGroupDeviceAllow; +typedef struct CGroupIODeviceWeight CGroupIODeviceWeight; +typedef struct CGroupIODeviceLimit CGroupIODeviceLimit; typedef struct CGroupBlockIODeviceWeight CGroupBlockIODeviceWeight; typedef struct CGroupBlockIODeviceBandwidth CGroupBlockIODeviceBandwidth; @@ -53,6 +56,18 @@ struct CGroupDeviceAllow { bool m:1; }; +struct CGroupIODeviceWeight { + LIST_FIELDS(CGroupIODeviceWeight, device_weights); + char *path; + uint64_t weight; +}; + +struct CGroupIODeviceLimit { + LIST_FIELDS(CGroupIODeviceLimit, device_limits); + char *path; + uint64_t limits[_CGROUP_IO_LIMIT_TYPE_MAX]; +}; + struct CGroupBlockIODeviceWeight { LIST_FIELDS(CGroupBlockIODeviceWeight, device_weights); char *path; @@ -62,16 +77,24 @@ struct CGroupBlockIODeviceWeight { struct CGroupBlockIODeviceBandwidth { LIST_FIELDS(CGroupBlockIODeviceBandwidth, device_bandwidths); char *path; - uint64_t bandwidth; - bool read; + uint64_t rbps; + uint64_t wbps; }; struct CGroupContext { bool cpu_accounting; + bool io_accounting; bool blockio_accounting; bool memory_accounting; bool tasks_accounting; + /* For unified hierarchy */ + uint64_t io_weight; + uint64_t startup_io_weight; + LIST_HEAD(CGroupIODeviceWeight, io_device_weights); + LIST_HEAD(CGroupIODeviceLimit, io_device_limits); + + /* For legacy hierarchies */ uint64_t cpu_shares; uint64_t startup_cpu_shares; usec_t cpu_quota_per_sec_usec; @@ -86,6 +109,7 @@ struct CGroupContext { CGroupDevicePolicy device_policy; LIST_HEAD(CGroupDeviceAllow, device_allow); + /* Common */ uint64_t tasks_max; bool delegate; @@ -102,6 +126,8 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M CGroupMask cgroup_context_get_mask(CGroupContext *c); void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a); +void cgroup_context_free_io_device_weight(CGroupContext *c, CGroupIODeviceWeight *w); +void cgroup_context_free_io_device_limit(CGroupContext *c, CGroupIODeviceLimit *l); void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w); void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b); diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c index 859d155ec1..eef1c47c14 100644 --- a/src/core/dbus-cgroup.c +++ b/src/core/dbus-cgroup.c @@ -28,6 +28,72 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_cgroup_device_policy, cgroup_device_policy, CGroupDevicePolicy); +static int property_get_io_device_weight( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + CGroupContext *c = userdata; + CGroupIODeviceWeight *w; + int r; + + assert(bus); + assert(reply); + assert(c); + + r = sd_bus_message_open_container(reply, 'a', "(st)"); + if (r < 0) + return r; + + LIST_FOREACH(device_weights, w, c->io_device_weights) { + r = sd_bus_message_append(reply, "(st)", w->path, w->weight); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +static int property_get_io_device_limits( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + CGroupContext *c = userdata; + CGroupIODeviceLimit *l; + int r; + + assert(bus); + assert(reply); + assert(c); + + r = sd_bus_message_open_container(reply, 'a', "(st)"); + if (r < 0) + return r; + + LIST_FOREACH(device_limits, l, c->io_device_limits) { + CGroupIOLimitType type; + + type = cgroup_io_limit_type_from_string(property); + if (type < 0 || l->limits[type] == cgroup_io_limit_defaults[type]) + continue; + + r = sd_bus_message_append(reply, "(st)", l->path, l->limits[type]); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + static int property_get_blockio_device_weight( sd_bus *bus, const char *path, @@ -80,11 +146,17 @@ static int property_get_blockio_device_bandwidths( return r; LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { + uint64_t v; - if (streq(property, "BlockIOReadBandwidth") != b->read) + if (streq(property, "BlockIOReadBandwidth")) + v = b->rbps; + else + v = b->wbps; + + if (v == CGROUP_LIMIT_MAX) continue; - r = sd_bus_message_append(reply, "(st)", b->path, b->bandwidth); + r = sd_bus_message_append(reply, "(st)", b->path, v); if (r < 0) return r; } @@ -141,6 +213,14 @@ const sd_bus_vtable bus_cgroup_vtable[] = { SD_BUS_PROPERTY("CPUShares", "t", NULL, offsetof(CGroupContext, cpu_shares), 0), SD_BUS_PROPERTY("StartupCPUShares", "t", NULL, offsetof(CGroupContext, startup_cpu_shares), 0), SD_BUS_PROPERTY("CPUQuotaPerSecUSec", "t", bus_property_get_usec, offsetof(CGroupContext, cpu_quota_per_sec_usec), 0), + SD_BUS_PROPERTY("IOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, io_accounting), 0), + SD_BUS_PROPERTY("IOWeight", "t", NULL, offsetof(CGroupContext, io_weight), 0), + SD_BUS_PROPERTY("StartupIOWeight", "t", NULL, offsetof(CGroupContext, startup_io_weight), 0), + SD_BUS_PROPERTY("IODeviceWeight", "a(st)", property_get_io_device_weight, 0, 0), + SD_BUS_PROPERTY("IOReadBandwidthMax", "a(st)", property_get_io_device_limits, 0, 0), + SD_BUS_PROPERTY("IOWriteBandwidthMax", "a(st)", property_get_io_device_limits, 0, 0), + SD_BUS_PROPERTY("IOReadIOPSMax", "a(st)", property_get_io_device_limits, 0, 0), + SD_BUS_PROPERTY("IOWriteIOPSMax", "a(st)", property_get_io_device_limits, 0, 0), SD_BUS_PROPERTY("BlockIOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, blockio_accounting), 0), SD_BUS_PROPERTY("BlockIOWeight", "t", NULL, offsetof(CGroupContext, blockio_weight), 0), SD_BUS_PROPERTY("StartupBlockIOWeight", "t", NULL, offsetof(CGroupContext, startup_blockio_weight), 0), @@ -197,6 +277,7 @@ int bus_cgroup_set_property( UnitSetPropertiesMode mode, sd_bus_error *error) { + CGroupIOLimitType iol_type; int r; assert(u); @@ -281,6 +362,223 @@ int bus_cgroup_set_property( return 1; + } else if (streq(name, "IOAccounting")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->io_accounting = b; + unit_invalidate_cgroup(u, CGROUP_MASK_IO); + unit_write_drop_in_private(u, mode, name, b ? "IOAccounting=yes" : "IOAccounting=no"); + } + + return 1; + + } else if (streq(name, "IOWeight")) { + uint64_t weight; + + r = sd_bus_message_read(message, "t", &weight); + if (r < 0) + return r; + + if (!CGROUP_WEIGHT_IS_OK(weight)) + return sd_bus_error_set_errnof(error, EINVAL, "IOWeight value out of range"); + + if (mode != UNIT_CHECK) { + c->io_weight = weight; + unit_invalidate_cgroup(u, CGROUP_MASK_IO); + + if (weight == CGROUP_WEIGHT_INVALID) + unit_write_drop_in_private(u, mode, name, "IOWeight="); + else + unit_write_drop_in_private_format(u, mode, name, "IOWeight=%" PRIu64, weight); + } + + return 1; + + } else if (streq(name, "StartupIOWeight")) { + uint64_t weight; + + r = sd_bus_message_read(message, "t", &weight); + if (r < 0) + return r; + + if (CGROUP_WEIGHT_IS_OK(weight)) + return sd_bus_error_set_errnof(error, EINVAL, "StartupIOWeight value out of range"); + + if (mode != UNIT_CHECK) { + c->startup_io_weight = weight; + unit_invalidate_cgroup(u, CGROUP_MASK_IO); + + if (weight == CGROUP_WEIGHT_INVALID) + unit_write_drop_in_private(u, mode, name, "StartupIOWeight="); + else + unit_write_drop_in_private_format(u, mode, name, "StartupIOWeight=%" PRIu64, weight); + } + + return 1; + + } else if ((iol_type = cgroup_io_limit_type_from_string(name)) >= 0) { + const char *path; + unsigned n = 0; + uint64_t u64; + + r = sd_bus_message_enter_container(message, 'a', "(st)"); + if (r < 0) + return r; + + while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) { + + if (mode != UNIT_CHECK) { + CGroupIODeviceLimit *a = NULL, *b; + + LIST_FOREACH(device_limits, b, c->io_device_limits) { + if (path_equal(path, b->path)) { + a = b; + break; + } + } + + if (!a) { + CGroupIOLimitType type; + + a = new0(CGroupIODeviceLimit, 1); + if (!a) + return -ENOMEM; + + a->path = strdup(path); + if (!a->path) { + free(a); + return -ENOMEM; + } + + for (type = 0; type < _CGROUP_IO_LIMIT_TYPE_MAX; type++) + a->limits[type] = cgroup_io_limit_defaults[type]; + + LIST_PREPEND(device_limits, c->io_device_limits, a); + } + + a->limits[iol_type] = u64; + } + + n++; + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + CGroupIODeviceLimit *a; + _cleanup_free_ char *buf = NULL; + _cleanup_fclose_ FILE *f = NULL; + size_t size = 0; + + if (n == 0) { + LIST_FOREACH(device_limits, a, c->io_device_limits) + a->limits[iol_type] = cgroup_io_limit_defaults[iol_type]; + } + + unit_invalidate_cgroup(u, CGROUP_MASK_IO); + + f = open_memstream(&buf, &size); + if (!f) + return -ENOMEM; + + fprintf(f, "%s=\n", name); + LIST_FOREACH(device_limits, a, c->io_device_limits) + if (a->limits[iol_type] != cgroup_io_limit_defaults[iol_type]) + fprintf(f, "%s=%s %" PRIu64 "\n", name, a->path, a->limits[iol_type]); + + r = fflush_and_check(f); + if (r < 0) + return r; + unit_write_drop_in_private(u, mode, name, buf); + } + + return 1; + + } else if (streq(name, "IODeviceWeight")) { + const char *path; + uint64_t weight; + unsigned n = 0; + + r = sd_bus_message_enter_container(message, 'a', "(st)"); + if (r < 0) + return r; + + while ((r = sd_bus_message_read(message, "(st)", &path, &weight)) > 0) { + + if (!CGROUP_WEIGHT_IS_OK(weight) || weight == CGROUP_WEIGHT_INVALID) + return sd_bus_error_set_errnof(error, EINVAL, "IODeviceWeight out of range"); + + if (mode != UNIT_CHECK) { + CGroupIODeviceWeight *a = NULL, *b; + + LIST_FOREACH(device_weights, b, c->io_device_weights) { + if (path_equal(b->path, path)) { + a = b; + break; + } + } + + if (!a) { + a = new0(CGroupIODeviceWeight, 1); + if (!a) + return -ENOMEM; + + a->path = strdup(path); + if (!a->path) { + free(a); + return -ENOMEM; + } + LIST_PREPEND(device_weights,c->io_device_weights, a); + } + + a->weight = weight; + } + + n++; + } + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *buf = NULL; + _cleanup_fclose_ FILE *f = NULL; + CGroupIODeviceWeight *a; + size_t size = 0; + + if (n == 0) { + while (c->io_device_weights) + cgroup_context_free_io_device_weight(c, c->io_device_weights); + } + + unit_invalidate_cgroup(u, CGROUP_MASK_IO); + + f = open_memstream(&buf, &size); + if (!f) + return -ENOMEM; + + fputs("IODeviceWeight=\n", f); + LIST_FOREACH(device_weights, a, c->io_device_weights) + fprintf(f, "IODeviceWeight=%s %" PRIu64 "\n", a->path, a->weight); + + r = fflush_and_check(f); + if (r < 0) + return r; + unit_write_drop_in_private(u, mode, name, buf); + } + + return 1; + } else if (streq(name, "BlockIOAccounting")) { int b; @@ -359,7 +657,7 @@ int bus_cgroup_set_property( CGroupBlockIODeviceBandwidth *a = NULL, *b; LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { - if (path_equal(path, b->path) && read == b->read) { + if (path_equal(path, b->path)) { a = b; break; } @@ -370,7 +668,8 @@ int bus_cgroup_set_property( if (!a) return -ENOMEM; - a->read = read; + a->rbps = CGROUP_LIMIT_MAX; + a->wbps = CGROUP_LIMIT_MAX; a->path = strdup(path); if (!a->path) { free(a); @@ -380,7 +679,10 @@ int bus_cgroup_set_property( LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, a); } - a->bandwidth = u64; + if (read) + a->rbps = u64; + else + a->wbps = u64; } n++; @@ -393,15 +695,18 @@ int bus_cgroup_set_property( return r; if (mode != UNIT_CHECK) { - CGroupBlockIODeviceBandwidth *a, *next; + CGroupBlockIODeviceBandwidth *a; _cleanup_free_ char *buf = NULL; _cleanup_fclose_ FILE *f = NULL; size_t size = 0; if (n == 0) { - LIST_FOREACH_SAFE(device_bandwidths, a, next, c->blockio_device_bandwidths) - if (a->read == read) - cgroup_context_free_blockio_device_bandwidth(c, a); + LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths) { + if (read) + a->rbps = CGROUP_LIMIT_MAX; + else + a->wbps = CGROUP_LIMIT_MAX; + } } unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); @@ -413,13 +718,13 @@ int bus_cgroup_set_property( if (read) { fputs("BlockIOReadBandwidth=\n", f); LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths) - if (a->read) - fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth); + if (a->rbps != CGROUP_LIMIT_MAX) + fprintf(f, "BlockIOReadBandwidth=%s %" PRIu64 "\n", a->path, a->rbps); } else { fputs("BlockIOWriteBandwidth=\n", f); LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths) - if (!a->read) - fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->bandwidth); + if (a->wbps != CGROUP_LIMIT_MAX) + fprintf(f, "BlockIOWriteBandwidth=%s %" PRIu64 "\n", a->path, a->wbps); } r = fflush_and_check(f); diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index f2fc301f8e..06943c6365 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -312,7 +312,7 @@ static int property_get_ambient_capabilities( return sd_bus_message_append(reply, "t", c->capability_ambient_set); } -static int property_get_capabilities( +static int property_get_empty_string( sd_bus *bus, const char *path, const char *interface, @@ -321,23 +321,10 @@ static int property_get_capabilities( void *userdata, sd_bus_error *error) { - ExecContext *c = userdata; - _cleanup_cap_free_charp_ char *t = NULL; - const char *s; - assert(bus); assert(reply); - assert(c); - - if (c->capabilities) - s = t = cap_to_text(c->capabilities, NULL); - else - s = ""; - if (!s) - return -ENOMEM; - - return sd_bus_message_append(reply, "s", s); + return sd_bus_message_append(reply, "s", ""); } static int property_get_syscall_filter( @@ -700,7 +687,7 @@ 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_capabilities, 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), @@ -850,18 +837,10 @@ int bus_exec_context_set_transient_property( if (mode != UNIT_CHECK) { - if (isempty(uu)) { + if (isempty(uu)) c->user = mfree(c->user); - } else { - char *t; - - t = strdup(uu); - if (!t) - return -ENOMEM; - - free(c->user); - c->user = t; - } + else if (free_and_strdup(&c->user, uu) < 0) + return -ENOMEM; unit_write_drop_in_private_format(u, mode, name, "User=%s\n", uu); } @@ -877,18 +856,10 @@ int bus_exec_context_set_transient_property( if (mode != UNIT_CHECK) { - if (isempty(gg)) { + if (isempty(gg)) c->group = mfree(c->group); - } else { - char *t; - - t = strdup(gg); - if (!t) - return -ENOMEM; - - free(c->group); - c->group = t; - } + else if (free_and_strdup(&c->group, gg) < 0) + return -ENOMEM; unit_write_drop_in_private_format(u, mode, name, "Group=%s\n", gg); } @@ -903,18 +874,10 @@ int bus_exec_context_set_transient_property( if (mode != UNIT_CHECK) { - if (isempty(id)) { + if (isempty(id)) c->syslog_identifier = mfree(c->syslog_identifier); - } else { - char *t; - - t = strdup(id); - if (!t) - return -ENOMEM; - - free(c->syslog_identifier); - c->syslog_identifier = t; - } + else if (free_and_strdup(&c->syslog_identifier, id) < 0) + return -ENOMEM; unit_write_drop_in_private_format(u, mode, name, "SyslogIdentifier=%s\n", id); } @@ -1491,6 +1454,24 @@ int bus_exec_context_set_transient_property( return 1; + } else if (streq(name, "SELinuxContext")) { + const char *s; + + r = sd_bus_message_read(message, "s", &s); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + if (isempty(s)) + c->selinux_context = mfree(c->selinux_context); + else if (free_and_strdup(&c->selinux_context, s) < 0) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, strempty(s)); + } + + return 1; + } ri = rlimit_from_string(name); diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c index 97a93fb2f1..ccf7453d47 100644 --- a/src/core/dbus-job.c +++ b/src/core/dbus-job.c @@ -75,7 +75,7 @@ int bus_job_method_cancel(sd_bus_message *message, void *userdata, sd_bus_error return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ } - job_finish_and_invalidate(j, JOB_CANCELED, true); + job_finish_and_invalidate(j, JOB_CANCELED, true, false); return sd_bus_reply_method_return(message, NULL); } diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c index fc50fafaad..0f54c6b84b 100644 --- a/src/core/dbus-kill.c +++ b/src/core/dbus-kill.c @@ -58,7 +58,7 @@ int bus_kill_context_set_transient_property( k = kill_mode_from_string(m); if (k < 0) - return -EINVAL; + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Kill mode '%s' not known.", m); if (mode != UNIT_CHECK) { c->kill_mode = k; @@ -75,7 +75,7 @@ int bus_kill_context_set_transient_property( if (r < 0) return r; - if (sig <= 0 || sig >= _NSIG) + if (!SIGNAL_VALID(sig)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal %i out of range", sig); if (mode != UNIT_CHECK) { diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index f939196397..86722e1162 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -139,7 +139,7 @@ static int property_get_tainted( if (access("/proc/cgroups", F_OK) < 0) e = stpcpy(e, "cgroups-missing:"); - if (clock_is_localtime() > 0) + if (clock_is_localtime(NULL) > 0) e = stpcpy(e, "local-hwclock:"); /* remove the last ':' */ @@ -642,6 +642,104 @@ static int method_set_unit_properties(sd_bus_message *message, void *userdata, s return bus_unit_method_set_properties(message, u, error); } +static int reply_unit_info(sd_bus_message *reply, Unit *u) { + _cleanup_free_ char *unit_path = NULL, *job_path = NULL; + Unit *following; + + following = unit_following(u); + + unit_path = unit_dbus_path(u); + if (!unit_path) + return -ENOMEM; + + if (u->job) { + job_path = job_dbus_path(u->job); + if (!job_path) + return -ENOMEM; + } + + return sd_bus_message_append( + reply, "(ssssssouso)", + u->id, + unit_description(u), + unit_load_state_to_string(u->load_state), + unit_active_state_to_string(unit_active_state(u)), + unit_sub_state_to_string(u), + following ? following->id : "", + unit_path, + u->job ? u->job->id : 0, + u->job ? job_type_to_string(u->job->type) : "", + job_path ? job_path : "/"); +} + +static int method_list_units_by_names(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Manager *m = userdata; + int r; + char **unit; + _cleanup_strv_free_ char **units = NULL; + + assert(message); + assert(m); + + r = sd_bus_message_read_strv(message, &units); + if (r < 0) + return r; + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(ssssssouso)"); + if (r < 0) + return r; + + STRV_FOREACH(unit, units) { + Unit *u; + + if (!unit_name_is_valid(*unit, UNIT_NAME_ANY)) + continue; + + r = manager_load_unit(m, *unit, NULL, error, &u); + if (r < 0) + return r; + + r = reply_unit_info(reply, u); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +static int method_get_unit_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + const char *name; + Unit *u; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = manager_load_unit(m, name, NULL, error, &u); + if (r < 0) + return r; + + r = bus_unit_check_load_state(u, error); + if (r < 0) + return r; + + return bus_unit_method_get_processes(message, u, error); +} + static int transient_unit_from_message( Manager *m, sd_bus_message *message, @@ -865,7 +963,7 @@ static int method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_e return sd_bus_reply_method_return(message, NULL); } -static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states) { +static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states, char **patterns) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; Manager *m = userdata; const char *k; @@ -891,42 +989,20 @@ static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_e return r; HASHMAP_FOREACH_KEY(u, k, m->units, i) { - _cleanup_free_ char *unit_path = NULL, *job_path = NULL; - Unit *following; - if (k != u->id) continue; - following = unit_following(u); - if (!strv_isempty(states) && !strv_contains(states, unit_load_state_to_string(u->load_state)) && !strv_contains(states, unit_active_state_to_string(unit_active_state(u))) && !strv_contains(states, unit_sub_state_to_string(u))) continue; - unit_path = unit_dbus_path(u); - if (!unit_path) - return -ENOMEM; - - if (u->job) { - job_path = job_dbus_path(u->job); - if (!job_path) - return -ENOMEM; - } + if (!strv_isempty(patterns) && + !strv_fnmatch_or_empty(patterns, u->id, FNM_NOESCAPE)) + continue; - r = sd_bus_message_append( - reply, "(ssssssouso)", - u->id, - unit_description(u), - unit_load_state_to_string(u->load_state), - unit_active_state_to_string(unit_active_state(u)), - unit_sub_state_to_string(u), - following ? following->id : "", - unit_path, - u->job ? u->job->id : 0, - u->job ? job_type_to_string(u->job->type) : "", - job_path ? job_path : "/"); + r = reply_unit_info(reply, u); if (r < 0) return r; } @@ -939,7 +1015,7 @@ static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_e } static int method_list_units(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return list_units_filtered(message, userdata, error, NULL); + return list_units_filtered(message, userdata, error, NULL, NULL); } static int method_list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -950,7 +1026,23 @@ static int method_list_units_filtered(sd_bus_message *message, void *userdata, s if (r < 0) return r; - return list_units_filtered(message, userdata, error, states); + return list_units_filtered(message, userdata, error, states, NULL); +} + +static int method_list_units_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_strv_free_ char **states = NULL; + _cleanup_strv_free_ char **patterns = NULL; + int r; + + r = sd_bus_message_read_strv(message, &states); + if (r < 0) + return r; + + r = sd_bus_message_read_strv(message, &patterns); + if (r < 0) + return r; + + return list_units_filtered(message, userdata, error, states, patterns); } static int method_list_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -1187,7 +1279,7 @@ static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error * if (r < 0) return r; - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers."); m->exit_code = MANAGER_REBOOT; @@ -1206,7 +1298,7 @@ static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error if (r < 0) return r; - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers."); m->exit_code = MANAGER_POWEROFF; @@ -1225,7 +1317,7 @@ static int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *er if (r < 0) return r; - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Halt is only supported for system managers."); m->exit_code = MANAGER_HALT; @@ -1244,7 +1336,7 @@ static int method_kexec(sd_bus_message *message, void *userdata, sd_bus_error *e if (r < 0) return r; - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "KExec is only supported for system managers."); m->exit_code = MANAGER_KEXEC; @@ -1265,7 +1357,7 @@ static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_er if (r < 0) return r; - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Root switching is only supported by system manager."); r = sd_bus_message_read(message, "ss", &root, &init); @@ -1433,7 +1525,7 @@ static int method_set_exit_code(sd_bus_message *message, void *userdata, sd_bus_ if (r < 0) return r; - if (m->running_as == MANAGER_SYSTEM && detect_container() <= 0) + if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "ExitCode can only be set for user service managers or in containers."); m->return_value = code; @@ -1441,7 +1533,7 @@ static int method_set_exit_code(sd_bus_message *message, void *userdata, sd_bus_ return sd_bus_reply_method_return(message, NULL); } -static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { +static int list_unit_files_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states, char **patterns) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; Manager *m = userdata; UnitFileList *item; @@ -1466,7 +1558,7 @@ static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bu if (!h) return -ENOMEM; - r = unit_file_get_list(m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, NULL, h); + r = unit_file_get_list(m->unit_file_scope, NULL, h, states, patterns); if (r < 0) goto fail; @@ -1494,11 +1586,30 @@ fail: return r; } +static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return list_unit_files_by_patterns(message, userdata, error, NULL, NULL); +} + +static int method_list_unit_files_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_strv_free_ char **states = NULL; + _cleanup_strv_free_ char **patterns = NULL; + int r; + + r = sd_bus_message_read_strv(message, &states); + if (r < 0) + return r; + + r = sd_bus_message_read_strv(message, &patterns); + if (r < 0) + return r; + + return list_unit_files_by_patterns(message, userdata, error, states, patterns); +} + static int method_get_unit_file_state(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; const char *name; UnitFileState state; - UnitFileScope scope; int r; assert(message); @@ -1514,9 +1625,7 @@ static int method_get_unit_file_state(sd_bus_message *message, void *userdata, s if (r < 0) return r; - scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; - - r = unit_file_get_state(scope, NULL, name, &state); + r = unit_file_get_state(m->unit_file_scope, NULL, name, &state); if (r < 0) return r; @@ -1526,7 +1635,6 @@ static int method_get_unit_file_state(sd_bus_message *message, void *userdata, s static int method_get_default_target(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_free_ char *default_target = NULL; Manager *m = userdata; - UnitFileScope scope; int r; assert(message); @@ -1538,9 +1646,7 @@ static int method_get_default_target(sd_bus_message *message, void *userdata, sd if (r < 0) return r; - scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; - - r = unit_file_get_default(scope, NULL, &default_target); + r = unit_file_get_default(m->unit_file_scope, NULL, &default_target); if (r < 0) return r; @@ -1571,7 +1677,7 @@ static int reply_unit_file_changes_and_free( unsigned i; int r; - if (n_changes > 0) { + if (unit_file_changes_have_modification(changes, n_changes)) { r = bus_foreach_bus(m, NULL, send_unit_files_changed, NULL); if (r < 0) log_debug_errno(r, "Failed to send UnitFilesChanged signal: %m"); @@ -1591,15 +1697,19 @@ static int reply_unit_file_changes_and_free( if (r < 0) goto fail; - for (i = 0; i < n_changes; i++) { - r = sd_bus_message_append( - reply, "(sss)", - unit_file_change_type_to_string(changes[i].type), - changes[i].path, - changes[i].source); - if (r < 0) - goto fail; - } + for (i = 0; i < n_changes; i++) + if (changes[i].type >= 0) { + const char *change = unit_file_change_type_to_string(changes[i].type); + assert(change != NULL); + + r = sd_bus_message_append( + reply, "(sss)", + change, + changes[i].path, + changes[i].source); + if (r < 0) + goto fail; + } r = sd_bus_message_close_container(reply); if (r < 0) @@ -1613,10 +1723,61 @@ fail: return r; } +/* Create an error reply, using the error information from changes[] + * if possible, and fall back to generating an error from error code c. + * The error message only describes the first error. + * + * Coordinate with unit_file_dump_changes() in install.c. + */ +static int install_error( + sd_bus_error *error, + int c, + UnitFileChange *changes, + unsigned n_changes) { + int r; + unsigned i; + assert(c < 0); + + for (i = 0; i < n_changes; i++) + switch(changes[i].type) { + case 0 ... INT_MAX: + continue; + case -EEXIST: + if (changes[i].source) + r = sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, + "File %s already exists and is a symlink to %s.", + changes[i].path, changes[i].source); + else + r = sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, + "File %s already exists.", + changes[i].path); + goto found; + case -ERFKILL: + r = sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, + "Unit file %s is masked.", changes[i].path); + goto found; + case -EADDRNOTAVAIL: + r = sd_bus_error_setf(error, BUS_ERROR_UNIT_GENERATED, + "Unit %s is transient or generated.", changes[i].path); + goto found; + case -ELOOP: + r = sd_bus_error_setf(error, BUS_ERROR_UNIT_LINKED, + "Refusing to operate on linked unit file %s", changes[i].path); + goto found; + default: + r = sd_bus_error_set_errnof(error, changes[i].type, "File %s: %m", changes[i].path); + goto found; + } + + r = c; + found: + unit_file_changes_free(changes, n_changes); + return r; +} + static int method_enable_unit_files_generic( sd_bus_message *message, Manager *m, - const char *verb, int (*call)(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes), bool carries_install_info, sd_bus_error *error) { @@ -1624,7 +1785,6 @@ static int method_enable_unit_files_generic( _cleanup_strv_free_ char **l = NULL; UnitFileChange *changes = NULL; unsigned n_changes = 0; - UnitFileScope scope; int runtime, force, r; assert(message); @@ -1644,27 +1804,23 @@ static int method_enable_unit_files_generic( if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; - - r = call(scope, runtime, NULL, l, force, &changes, &n_changes); - if (r == -ESHUTDOWN) - return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file is masked"); + r = call(m->unit_file_scope, runtime, NULL, l, force, &changes, &n_changes); if (r < 0) - return r; + return install_error(error, r, changes, n_changes); return reply_unit_file_changes_and_free(m, message, carries_install_info ? r : -1, changes, n_changes); } static int method_enable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_enable_unit_files_generic(message, userdata, "enable", unit_file_enable, true, error); + return method_enable_unit_files_generic(message, userdata, unit_file_enable, true, error); } static int method_reenable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_enable_unit_files_generic(message, userdata, "enable", unit_file_reenable, true, error); + return method_enable_unit_files_generic(message, userdata, unit_file_reenable, true, error); } static int method_link_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_enable_unit_files_generic(message, userdata, "enable", unit_file_link, false, error); + return method_enable_unit_files_generic(message, userdata, unit_file_link, false, error); } static int unit_file_preset_without_mode(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes) { @@ -1672,11 +1828,11 @@ static int unit_file_preset_without_mode(UnitFileScope scope, bool runtime, cons } static int method_preset_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_enable_unit_files_generic(message, userdata, "enable", unit_file_preset_without_mode, true, error); + return method_enable_unit_files_generic(message, userdata, unit_file_preset_without_mode, true, error); } static int method_mask_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_enable_unit_files_generic(message, userdata, "disable", unit_file_mask, false, error); + return method_enable_unit_files_generic(message, userdata, unit_file_mask, false, error); } static int method_preset_unit_files_with_mode(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -1686,7 +1842,6 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use unsigned n_changes = 0; Manager *m = userdata; UnitFilePresetMode mm; - UnitFileScope scope; int runtime, force, r; const char *mode; @@ -1715,26 +1870,22 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; - - r = unit_file_preset(scope, runtime, NULL, l, mm, force, &changes, &n_changes); + r = unit_file_preset(m->unit_file_scope, runtime, NULL, l, mm, force, &changes, &n_changes); if (r < 0) - return r; + return install_error(error, r, changes, n_changes); return reply_unit_file_changes_and_free(m, message, r, changes, n_changes); } static int method_disable_unit_files_generic( sd_bus_message *message, - Manager *m, const - char *verb, + Manager *m, int (*call)(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes), sd_bus_error *error) { _cleanup_strv_free_ char **l = NULL; UnitFileChange *changes = NULL; unsigned n_changes = 0; - UnitFileScope scope; int r, runtime; assert(message); @@ -1748,34 +1899,58 @@ static int method_disable_unit_files_generic( if (r < 0) return r; - scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; - r = bus_verify_manage_unit_files_async(m, message, error); if (r < 0) return r; if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - r = call(scope, runtime, NULL, l, &changes, &n_changes); + r = call(m->unit_file_scope, runtime, NULL, l, &changes, &n_changes); if (r < 0) - return r; + return install_error(error, r, changes, n_changes); return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); } static int method_disable_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_disable_unit_files_generic(message, userdata, "disable", unit_file_disable, error); + return method_disable_unit_files_generic(message, userdata, unit_file_disable, error); } static int method_unmask_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return method_disable_unit_files_generic(message, userdata, "enable", unit_file_unmask, error); + return method_disable_unit_files_generic(message, userdata, unit_file_unmask, error); +} + +static int method_revert_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_strv_free_ char **l = NULL; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + r = bus_verify_manage_unit_files_async(m, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = unit_file_revert(m->unit_file_scope, NULL, l, &changes, &n_changes); + if (r < 0) + return install_error(error, r, changes, n_changes); + + return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); } static int method_set_default_target(sd_bus_message *message, void *userdata, sd_bus_error *error) { UnitFileChange *changes = NULL; unsigned n_changes = 0; Manager *m = userdata; - UnitFileScope scope; const char *name; int force, r; @@ -1796,11 +1971,9 @@ static int method_set_default_target(sd_bus_message *message, void *userdata, sd if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; - - r = unit_file_set_default(scope, NULL, name, force, &changes, &n_changes); + r = unit_file_set_default(m->unit_file_scope, NULL, name, force, &changes, &n_changes); if (r < 0) - return r; + return install_error(error, r, changes, n_changes); return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); } @@ -1810,7 +1983,6 @@ static int method_preset_all_unit_files(sd_bus_message *message, void *userdata, unsigned n_changes = 0; Manager *m = userdata; UnitFilePresetMode mm; - UnitFileScope scope; const char *mode; int force, runtime, r; @@ -1839,13 +2011,9 @@ static int method_preset_all_unit_files(sd_bus_message *message, void *userdata, if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; - - r = unit_file_preset_all(scope, runtime, NULL, mm, force, &changes, &n_changes); - if (r < 0) { - unit_file_changes_free(changes, n_changes); - return r; - } + r = unit_file_preset_all(m->unit_file_scope, runtime, NULL, mm, force, &changes, &n_changes); + if (r < 0) + return install_error(error, r, changes, n_changes); return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); } @@ -1855,10 +2023,8 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd Manager *m = userdata; UnitFileChange *changes = NULL; unsigned n_changes = 0; - UnitFileScope scope; int runtime, force, r; - char *target; - char *type; + char *target, *type; UnitDependency dep; assert(message); @@ -1882,13 +2048,9 @@ static int method_add_dependency_unit_files(sd_bus_message *message, void *userd if (dep < 0) return -EINVAL; - scope = m->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER; - - r = unit_file_add_dependency(scope, runtime, NULL, l, target, dep, force, &changes, &n_changes); - if (r == -ESHUTDOWN) - return sd_bus_error_setf(error, BUS_ERROR_UNIT_MASKED, "Unit file is masked"); + r = unit_file_add_dependency(m->unit_file_scope, runtime, NULL, l, target, dep, force, &changes, &n_changes); if (r < 0) - return r; + return install_error(error, r, changes, n_changes); return reply_unit_file_changes_and_free(m, message, -1, changes, n_changes); } @@ -1924,7 +2086,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_PROPERTY("Environment", "as", NULL, offsetof(Manager, environment), 0), SD_BUS_PROPERTY("ConfirmSpawn", "b", bus_property_get_bool, offsetof(Manager, confirm_spawn), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ShowStatus", "b", bus_property_get_bool, offsetof(Manager, show_status), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("UnitPath", "as", NULL, offsetof(Manager, lookup_paths.unit_path), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("UnitPath", "as", NULL, offsetof(Manager, lookup_paths.search_path), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultStandardOutput", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultStandardError", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogUSec", "t", bus_property_get_usec, property_set_runtime_watchdog, offsetof(Manager, runtime_watchdog), 0), @@ -1936,7 +2098,8 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_PROPERTY("DefaultTimeoutStartUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultTimeoutStopUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultRestartUSec", "t", bus_property_get_usec, offsetof(Manager, default_restart_usec), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("DefaultStartLimitInterval", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultStartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("DefaultStartLimitInterval", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* obsolete alias name */ SD_BUS_PROPERTY("DefaultStartLimitBurst", "u", bus_property_get_unsigned, offsetof(Manager, default_start_limit_burst), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultCPUAccounting", "b", bus_property_get_bool, offsetof(Manager, default_cpu_accounting), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultBlockIOAccounting", "b", bus_property_get_bool, offsetof(Manager, default_blockio_accounting), SD_BUS_VTABLE_PROPERTY_CONST), @@ -1992,12 +2155,15 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_METHOD("ResetFailedUnit", "s", NULL, method_reset_failed_unit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetUnitProperties", "sba(sv)", NULL, method_set_unit_properties, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("StartTransientUnit", "ssa(sv)a(sa(sv))", "o", method_start_transient_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetUnitProcesses", "s", "a(sus)", method_get_unit_processes, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetJob", "u", "o", method_get_job, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CancelJob", "u", NULL, method_cancel_job, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ClearJobs", NULL, NULL, method_clear_jobs, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ResetFailed", NULL, NULL, method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ListUnits", NULL, "a(ssssssouso)", method_list_units, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ListUnitsFiltered", "as", "a(ssssssouso)", method_list_units_filtered, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListUnitsByPatterns", "asas", "a(ssssssouso)", method_list_units_by_patterns, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListUnitsByNames", "as", "a(ssssssouso)", method_list_units_by_names, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ListJobs", NULL, "a(usssoo)", method_list_jobs, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Subscribe", NULL, NULL, method_subscribe, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Unsubscribe", NULL, NULL, method_unsubscribe, SD_BUS_VTABLE_UNPRIVILEGED), @@ -2016,6 +2182,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_METHOD("UnsetEnvironment", "as", NULL, method_unset_environment, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("UnsetAndSetEnvironment", "asas", NULL, method_unset_and_set_environment, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ListUnitFiles", NULL, "a(ss)", method_list_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListUnitFilesByPatterns", "asas", "a(ss)", method_list_unit_files_by_patterns, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetUnitFileState", "s", "s", method_get_unit_file_state, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("EnableUnitFiles", "asbb", "ba(sss)", method_enable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("DisableUnitFiles", "asb", "a(sss)", method_disable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), @@ -2025,6 +2192,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_METHOD("PresetUnitFilesWithMode", "assbb", "ba(sss)", method_preset_unit_files_with_mode, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("MaskUnitFiles", "asbb", "a(sss)", method_mask_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("UnmaskUnitFiles", "asb", "a(sss)", method_unmask_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("RevertUnitFiles", "as", "a(sss)", method_revert_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetDefaultTarget", "sb", "a(sss)", method_set_default_target, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetDefaultTarget", NULL, "s", method_get_default_target, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("PresetAllUnitFiles", "sbb", "a(sss)", method_preset_all_unit_files, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index d33e494f6b..961340608d 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -149,6 +149,8 @@ const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_PROPERTY("NAccepted", "u", bus_property_get_unsigned, offsetof(Socket, n_accepted), 0), SD_BUS_PROPERTY("FileDescriptorName", "s", property_get_fdname, 0, 0), SD_BUS_PROPERTY("SocketProtocol", "i", bus_property_get_int, offsetof(Socket, socket_protocol), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TriggerLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Socket, trigger_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TriggerLimitBurst", "u", bus_property_get_unsigned, offsetof(Socket, trigger_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c index bc121b83a2..a0e61b023e 100644 --- a/src/core/dbus-timer.c +++ b/src/core/dbus-timer.c @@ -156,7 +156,7 @@ static int property_get_next_elapse_monotonic( usec_t a, b; a = now(CLOCK_MONOTONIC); - b = now(CLOCK_BOOTTIME); + b = now(clock_boottime_or_monotonic()); if (t->next_elapse_monotonic_or_boottime + a > b) x = t->next_elapse_monotonic_or_boottime + a - b; diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index b351f6a2c2..e912fe2192 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -24,9 +24,12 @@ #include "cgroup-util.h" #include "dbus-unit.h" #include "dbus.h" +#include "fd-util.h" #include "locale-util.h" #include "log.h" +#include "process-util.h" #include "selinux-access.h" +#include "signal-util.h" #include "special.h" #include "string-util.h" #include "strv.h" @@ -547,7 +550,7 @@ int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error * return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid who argument %s", swho); } - if (signo <= 0 || signo >= _NSIG) + if (!SIGNAL_VALID(signo)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range."); r = bus_verify_manage_units_async_full( @@ -701,7 +704,8 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("Asserts", "a(sbbsi)", property_get_conditions, offsetof(Unit, asserts), 0), SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* obsolete alias name */ SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST), @@ -840,6 +844,145 @@ static int property_get_cgroup( return sd_bus_message_append(reply, "s", t); } +static int append_process(sd_bus_message *reply, const char *p, pid_t pid, Set *pids) { + _cleanup_free_ char *buf = NULL, *cmdline = NULL; + int r; + + assert(reply); + assert(pid > 0); + + r = set_put(pids, PID_TO_PTR(pid)); + if (r == -EEXIST || r == 0) + return 0; + if (r < 0) + return r; + + if (!p) { + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &buf); + if (r == -ESRCH) + return 0; + if (r < 0) + return r; + + p = buf; + } + + (void) get_process_cmdline(pid, 0, true, &cmdline); + + return sd_bus_message_append(reply, + "(sus)", + p, + (uint32_t) pid, + cmdline); +} + +static int append_cgroup(sd_bus_message *reply, const char *p, Set *pids) { + _cleanup_closedir_ DIR *d = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(reply); + assert(p); + + r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, p, &f); + if (r == ENOENT) + return 0; + if (r < 0) + return r; + + for (;;) { + pid_t pid; + + r = cg_read_pid(f, &pid); + if (r < 0) + return r; + if (r == 0) + break; + + if (is_kernel_thread(pid) > 0) + continue; + + r = append_process(reply, p, pid, pids); + if (r < 0) + return r; + } + + r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, p, &d); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + + for (;;) { + _cleanup_free_ char *g = NULL, *j = NULL; + + r = cg_read_subgroup(d, &g); + if (r < 0) + return r; + if (r == 0) + break; + + j = strjoin(p, "/", g, NULL); + if (!j) + return -ENOMEM; + + r = append_cgroup(reply, j, pids); + if (r < 0) + return r; + } + + return 0; +} + +int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(set_freep) Set *pids = NULL; + Unit *u = userdata; + pid_t pid; + int r; + + assert(message); + + pids = set_new(NULL); + if (!pids) + return -ENOMEM; + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(sus)"); + if (r < 0) + return r; + + if (u->cgroup_path) { + r = append_cgroup(reply, u->cgroup_path, pids); + if (r < 0) + return r; + } + + /* The main and control pids might live outside of the cgroup, hence fetch them separately */ + pid = unit_main_pid(u); + if (pid > 0) { + r = append_process(reply, NULL, pid, pids); + if (r < 0) + return r; + } + + pid = unit_control_pid(u); + if (pid > 0) { + r = append_process(reply, NULL, pid, pids); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + const sd_bus_vtable bus_unit_cgroup_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0), @@ -847,6 +990,7 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = { SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0), SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0), SD_BUS_PROPERTY("TasksCurrent", "t", property_get_current_tasks, 0, 0), + SD_BUS_METHOD("GetProcesses", NULL, "a(sus)", bus_unit_method_get_processes, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END }; @@ -1002,7 +1146,6 @@ int bus_unit_queue_job( type = JOB_TRY_RELOAD; } - if (type == JOB_STOP && (u->load_state == UNIT_NOT_FOUND || u->load_state == UNIT_ERROR) && unit_active_state(u) == UNIT_INACTIVE) @@ -1099,7 +1242,10 @@ static int bus_unit_set_transient_property( if (!unit_name_is_valid(s, UNIT_NAME_PLAIN)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name '%s'", s); - r = manager_load_unit(u->manager, s, NULL, error, &slice); + /* Note that we do not dispatch the load queue here yet, as we don't want our own transient unit to be + * loaded while we are still setting it up. Or in other words, we use manager_load_unit_prepare() + * instead of manager_load_unit() on purpose, here. */ + r = manager_load_unit_prepare(u->manager, s, NULL, error, &slice); if (r < 0) return r; @@ -1259,6 +1405,7 @@ int bus_unit_set_properties( } int bus_unit_check_load_state(Unit *u, sd_bus_error *error) { + assert(u); if (u->load_state == UNIT_LOADED) return 0; diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h index 07948b9cd0..4db88dbebc 100644 --- a/src/core/dbus-unit.h +++ b/src/core/dbus-unit.h @@ -36,5 +36,6 @@ int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error); int bus_unit_set_properties(Unit *u, sd_bus_message *message, UnitSetPropertiesMode mode, bool commit, sd_bus_error *error); int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_unit_check_load_state(Unit *u, sd_bus_error *error); diff --git a/src/core/dbus.c b/src/core/dbus.c index 413489373f..3422a02d68 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -71,28 +71,42 @@ int bus_send_queued_message(Manager *m) { return 0; } +int bus_forward_agent_released(Manager *m, const char *path) { + int r; + + assert(m); + assert(path); + + if (!MANAGER_IS_SYSTEM(m)) + return 0; + + if (!m->system_bus) + return 0; + + /* If we are running a system instance we forward the agent message on the system bus, so that the user + * instances get notified about this, too */ + + r = sd_bus_emit_signal(m->system_bus, + "/org/freedesktop/systemd1/agent", + "org.freedesktop.systemd1.Agent", + "Released", + "s", path); + if (r < 0) + return log_warning_errno(r, "Failed to propagate agent release message: %m"); + + return 1; +} + static int signal_agent_released(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - const char *cgroup, *me; Manager *m = userdata; + const char *cgroup; uid_t sender_uid; - sd_bus *bus; int r; assert(message); assert(m); - /* ignore recursive events sent by us on the system/user bus */ - bus = sd_bus_message_get_bus(message); - if (!sd_bus_is_server(bus)) { - r = sd_bus_get_unique_name(bus, &me); - if (r < 0) - return r; - - if (streq_ptr(sd_bus_message_get_sender(message), me)) - return 0; - } - /* only accept org.freedesktop.systemd1.Agent from UID=0 */ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); if (r < 0) @@ -110,16 +124,6 @@ static int signal_agent_released(sd_bus_message *message, void *userdata, sd_bus } manager_notify_cgroup_empty(m, cgroup); - - /* if running as system-instance, forward under our name */ - if (m->running_as == MANAGER_SYSTEM && m->system_bus) { - r = sd_bus_message_rewind(message, 1); - if (r >= 0) - r = sd_bus_send(m->system_bus, message, NULL); - if (r < 0) - log_warning_errno(r, "Failed to forward Released message: %m"); - } - return 0; } @@ -690,25 +694,6 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void return 0; } - if (m->running_as == MANAGER_SYSTEM) { - /* When we run as system instance we get the Released - * signal via a direct connection */ - - r = sd_bus_add_match( - bus, - NULL, - "type='signal'," - "interface='org.freedesktop.systemd1.Agent'," - "member='Released'," - "path='/org/freedesktop/systemd1/agent'", - signal_agent_released, m); - - if (r < 0) { - log_warning_errno(r, "Failed to register Released match on new connection bus: %m"); - return 0; - } - } - r = bus_setup_disconnected_match(m, bus); if (r < 0) return 0; @@ -864,10 +849,10 @@ static int bus_init_api(Manager *m) { return 0; /* The API and system bus is the same if we are running in system mode */ - if (m->running_as == MANAGER_SYSTEM && m->system_bus) + if (MANAGER_IS_SYSTEM(m) && m->system_bus) bus = sd_bus_ref(m->system_bus); else { - if (m->running_as == MANAGER_SYSTEM) + if (MANAGER_IS_SYSTEM(m)) r = sd_bus_open_system(&bus); else r = sd_bus_open_user(&bus); @@ -906,8 +891,8 @@ static int bus_setup_system(Manager *m, sd_bus *bus) { assert(m); assert(bus); - /* On kdbus or if we are a user instance we get the Released message via the system bus */ - if (m->running_as == MANAGER_USER || m->kdbus_fd >= 0) { + /* if we are a user instance we get the Released message via the system bus */ + if (MANAGER_IS_USER(m)) { r = sd_bus_add_match( bus, NULL, @@ -932,7 +917,7 @@ static int bus_init_system(Manager *m) { return 0; /* The API and system bus is the same if we are running in system mode */ - if (m->running_as == MANAGER_SYSTEM && m->api_bus) { + if (MANAGER_IS_SYSTEM(m) && m->api_bus) { m->system_bus = sd_bus_ref(m->api_bus); return 0; } @@ -983,14 +968,14 @@ static int bus_init_private(Manager *m) { if (m->kdbus_fd >= 0) return 0; - if (m->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(m)) { /* We want the private bus only when running as init */ if (getpid() != 1) return 0; strcpy(sa.un.sun_path, "/run/systemd/private"); - salen = offsetof(union sockaddr_union, un.sun_path) + strlen("/run/systemd/private"); + salen = SOCKADDR_UN_LEN(sa.un); } else { size_t left = sizeof(sa.un.sun_path); char *p = sa.un.sun_path; @@ -1082,7 +1067,7 @@ static void destroy_bus(Manager *m, sd_bus **bus) { /* Possibly flush unwritten data, but only if we are * unprivileged, since we don't want to sync here */ - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) sd_bus_flush(*bus); /* And destroy the object */ diff --git a/src/core/dbus.h b/src/core/dbus.h index e16a84fbb8..6baaffbd75 100644 --- a/src/core/dbus.h +++ b/src/core/dbus.h @@ -40,3 +40,5 @@ int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error int bus_verify_manage_unit_files_async(Manager *m, sd_bus_message *call, sd_bus_error *error); int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_error *error); int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_error *error); + +int bus_forward_agent_released(Manager *m, const char *path); diff --git a/src/core/device.c b/src/core/device.c index d201dc5e4b..16e56efcc3 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -265,7 +265,7 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) { assert(u); assert(dev); - property = u->manager->running_as == MANAGER_USER ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS"; + property = MANAGER_IS_USER(u->manager) ? "SYSTEMD_USER_WANTS" : "SYSTEMD_WANTS"; wants = udev_device_get_property_value(dev, property); if (!wants) return 0; @@ -318,11 +318,11 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa * the GC to have garbaged it. That's desired since the device * unit may have a dependency on the mount unit which was * added during the loading of the later. */ - if (u && DEVICE(u)->state == DEVICE_PLUGGED) { + if (dev && u && DEVICE(u)->state == DEVICE_PLUGGED) { /* This unit is in plugged state: we're sure it's * attached to a device. */ if (!path_equal(DEVICE(u)->sysfs, sysfs)) { - log_unit_error(u, "Dev %s appeared twice with different sysfs paths %s and %s", + log_unit_debug(u, "Dev %s appeared twice with different sysfs paths %s and %s", e, DEVICE(u)->sysfs, sysfs); return -EEXIST; } @@ -841,8 +841,6 @@ const UnitVTable device_vtable = { "Device\0" "Install\0", - .no_instances = true, - .init = device_init, .done = device_done, .load = unit_load_fragment_and_dropin_optional, diff --git a/src/core/execute.c b/src/core/execute.c index 1e4630182d..5eb3f13695 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -24,6 +24,7 @@ #include <poll.h> #include <signal.h> #include <string.h> +#include <sys/capability.h> #include <sys/personality.h> #include <sys/prctl.h> #include <sys/socket.h> @@ -57,7 +58,6 @@ #endif #include "async.h" #include "barrier.h" -#include "bus-endpoint.h" #include "cap-list.h" #include "capability-util.h" #include "def.h" @@ -271,7 +271,7 @@ static int connect_journal_socket(int fd, uid_t uid, gid_t gid) { } } - r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)); + r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) r = -errno; @@ -747,10 +747,10 @@ static int enforce_groups(const ExecContext *context, const char *username, gid_ static int enforce_user(const ExecContext *context, uid_t uid) { assert(context); - /* Sets (but doesn't lookup) the uid and make sure we keep the + /* Sets (but doesn't look up) the uid and make sure we keep the * capabilities while doing so. */ - if (context->capabilities || context->capability_ambient_set != 0) { + if (context->capability_ambient_set != 0) { /* First step: If we need to keep capabilities but * drop privileges we need to make sure we keep our @@ -762,31 +762,9 @@ static int enforce_user(const ExecContext *context, uid_t uid) { if (prctl(PR_SET_SECUREBITS, sb) < 0) return -errno; } - - /* Second step: set the capabilities. This will reduce - * the capabilities to the minimum we need. */ - - if (context->capabilities) { - _cleanup_cap_free_ cap_t d = NULL; - static const cap_value_t bits[] = { - CAP_SETUID, /* Necessary so that we can run setresuid() below */ - CAP_SETPCAP /* Necessary so that we can set PR_SET_SECUREBITS later on */ - }; - - d = cap_dup(context->capabilities); - if (!d) - return -errno; - - if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 || - cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0) - return -errno; - - if (cap_set_proc(d) < 0) - return -errno; - } } - /* Third step: actually set the uids */ + /* Second step: actually set the uids */ if (setresuid(uid, uid, uid) < 0) return -errno; @@ -1387,9 +1365,6 @@ static bool exec_needs_mount_namespace( if (context->private_tmp && runtime && (runtime->tmp_dir || runtime->var_tmp_dir)) return true; - if (params->bus_endpoint_path) - return true; - if (context->private_devices || context->protect_system != PROTECT_SYSTEM_NO || context->protect_home != PROTECT_HOME_NO) @@ -1423,9 +1398,6 @@ static int close_remaining_fds( n_dont_close += n_fds; } - if (params->bus_endpoint_fd >= 0) - dont_close[n_dont_close++] = params->bus_endpoint_fd; - if (runtime) { if (runtime->netns_storage_socket[0] >= 0) dont_close[n_dont_close++] = runtime->netns_storage_socket[0]; @@ -1655,16 +1627,6 @@ static int exec_child( } } - if (params->bus_endpoint_fd >= 0 && context->bus_endpoint) { - uid_t ep_uid = (uid == UID_INVALID) ? 0 : uid; - - r = bus_kernel_set_endpoint_policy(params->bus_endpoint_fd, ep_uid, context->bus_endpoint); - if (r < 0) { - *exit_status = EXIT_BUS_ENDPOINT; - return r; - } - } - /* If delegation is enabled we'll pass ownership of the cgroup * (but only in systemd's own controller hierarchy!) to the * user of the new process. */ @@ -1787,7 +1749,6 @@ static int exec_child( context->inaccessible_dirs, tmp, var, - params->bus_endpoint_path, context->private_devices, context->protect_home, context->protect_system, @@ -1864,6 +1825,11 @@ static int exec_child( if (params->apply_permissions) { + bool use_address_families = context->address_families_whitelist || + !set_isempty(context->address_families); + bool use_syscall_filter = context->syscall_whitelist || + !set_isempty(context->syscall_filter) || + !set_isempty(context->syscall_archs); int secure_bits = context->secure_bits; for (i = 0; i < _RLIMIT_MAX; i++) { @@ -1892,21 +1858,6 @@ static int exec_child( *exit_status = EXIT_CAPABILITIES; return r; } - - if (context->capabilities) { - - /* The capabilities in ambient set need to be also in the inherited - * set. If they aren't, trying to get them will fail. Add the ambient - * set inherited capabilities to the capability set in the context. - * This is needed because if capabilities are set (using "Capabilities=" - * keyword), they will override whatever we set now. */ - - r = capability_update_inherited_set(context->capabilities, context->capability_ambient_set); - if (r < 0) { - *exit_status = EXIT_CAPABILITIES; - return r; - } - } } if (context->user) { @@ -1931,7 +1882,7 @@ static int exec_child( * also to the context secure_bits so that we don't try to * drop the bit away next. */ - secure_bits |= 1<<SECURE_KEEP_CAPS; + secure_bits |= 1<<SECURE_KEEP_CAPS; } } @@ -1945,21 +1896,15 @@ static int exec_child( return -errno; } - if (context->capabilities) - if (cap_set_proc(context->capabilities) < 0) { - *exit_status = EXIT_CAPABILITIES; - return -errno; - } - - if (context->no_new_privileges) + if (context->no_new_privileges || + (!have_effective_cap(CAP_SYS_ADMIN) && (use_address_families || use_syscall_filter))) if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) { *exit_status = EXIT_NO_NEW_PRIVILEGES; return -errno; } #ifdef HAVE_SECCOMP - if (context->address_families_whitelist || - !set_isempty(context->address_families)) { + if (use_address_families) { r = apply_address_families(context); if (r < 0) { *exit_status = EXIT_ADDRESS_FAMILIES; @@ -1967,9 +1912,7 @@ static int exec_child( } } - if (context->syscall_whitelist || - !set_isempty(context->syscall_filter) || - !set_isempty(context->syscall_archs)) { + if (use_syscall_filter) { r = apply_seccomp(context); if (r < 0) { *exit_status = EXIT_SECCOMP; @@ -2193,11 +2136,6 @@ void exec_context_done(ExecContext *c) { c->pam_name = mfree(c->pam_name); - if (c->capabilities) { - cap_free(c->capabilities); - c->capabilities = NULL; - } - c->read_only_dirs = strv_free(c->read_only_dirs); c->read_write_dirs = strv_free(c->read_write_dirs); c->inaccessible_dirs = strv_free(c->inaccessible_dirs); @@ -2214,9 +2152,6 @@ void exec_context_done(ExecContext *c) { c->address_families = set_free(c->address_families); c->runtime_directory = strv_free(c->runtime_directory); - - bus_endpoint_free(c->bus_endpoint); - c->bus_endpoint = NULL; } int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_prefix) { @@ -2306,7 +2241,7 @@ int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l) { if (fn[0] == '-') { ignore = true; - fn ++; + fn++; } if (!path_is_absolute(fn)) { @@ -2559,14 +2494,6 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { prefix, strna(lvl_str)); } - if (c->capabilities) { - _cleanup_cap_free_charp_ char *t; - - t = cap_to_text(c->capabilities, NULL); - if (t) - fprintf(f, "%sCapabilities: %s\n", prefix, t); - } - if (c->secure_bits) fprintf(f, "%sSecure Bits:%s%s%s%s%s%s\n", prefix, diff --git a/src/core/execute.h b/src/core/execute.h index 578f85b6bc..41148bcea2 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -30,7 +30,6 @@ typedef struct ExecParameters ExecParameters; #include <stdio.h> #include <sys/capability.h> -#include "bus-endpoint.h" #include "fdset.h" #include "list.h" #include "missing.h" @@ -156,10 +155,7 @@ struct ExecContext { unsigned long mount_flags; uint64_t capability_bounding_set; - uint64_t capability_ambient_set; - - cap_t capabilities; int secure_bits; int syslog_priority; @@ -201,9 +197,6 @@ struct ExecContext { bool ioprio_set:1; bool cpu_sched_set:1; bool no_new_privileges_set:1; - - /* custom dbus enpoint */ - BusEndpoint *bus_endpoint; }; #include "cgroup-util.h" @@ -234,9 +227,6 @@ struct ExecParameters { int *idle_pipe; - char *bus_endpoint_path; - int bus_endpoint_fd; - int stdin_fd; int stdout_fd; int stderr_fd; diff --git a/src/core/failure-action.c b/src/core/failure-action.c index 39f5519ca1..ddae46190f 100644 --- a/src/core/failure-action.c +++ b/src/core/failure-action.c @@ -47,7 +47,7 @@ int failure_action( if (action == FAILURE_ACTION_NONE) return -ECANCELED; - if (m->running_as == MANAGER_USER) { + if (!MANAGER_IS_SYSTEM(m)) { /* Downgrade all options to simply exiting if we run * in user mode */ @@ -61,16 +61,17 @@ int failure_action( case FAILURE_ACTION_REBOOT: log_and_status(m, "Rebooting as result of failure."); - update_reboot_param_file(reboot_arg); - (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE, NULL); + (void) update_reboot_parameter_and_warn(reboot_arg); + (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL); break; case FAILURE_ACTION_REBOOT_FORCE: log_and_status(m, "Forcibly rebooting as result of failure."); - update_reboot_param_file(reboot_arg); + (void) update_reboot_parameter_and_warn(reboot_arg); m->exit_code = MANAGER_REBOOT; + break; case FAILURE_ACTION_REBOOT_IMMEDIATE: @@ -78,9 +79,10 @@ int failure_action( sync(); - if (reboot_arg) { + if (!isempty(reboot_arg)) { log_info("Rebooting with argument '%s'.", reboot_arg); syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, reboot_arg); + log_warning_errno(errno, "Failed to reboot with parameter, retrying without: %m"); } log_info("Rebooting."); @@ -89,7 +91,7 @@ int failure_action( case FAILURE_ACTION_POWEROFF: log_and_status(m, "Powering off as result of failure."); - (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE, NULL); + (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL); break; case FAILURE_ACTION_POWEROFF_FORCE: diff --git a/src/core/ima-setup.c b/src/core/ima-setup.c index ff7558d500..d1b0ce76ef 100644 --- a/src/core/ima-setup.c +++ b/src/core/ima-setup.c @@ -3,7 +3,7 @@ Copyright 2010 Lennart Poettering Copyright (C) 2012 Roberto Sassu - Politecnico di Torino, Italy - TORSEC group -- http://security.polito.it + TORSEC group — http://security.polito.it 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 diff --git a/src/core/ima-setup.h b/src/core/ima-setup.h index 3bad74b246..472b58cb00 100644 --- a/src/core/ima-setup.h +++ b/src/core/ima-setup.h @@ -5,7 +5,7 @@ Copyright 2010 Lennart Poettering Copyright (C) 2012 Roberto Sassu - Politecnico di Torino, Italy - TORSEC group -- http://security.polito.it + TORSEC group — http://security.polito.it 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 diff --git a/src/core/job.c b/src/core/job.c index 97304c4d05..7557874d4d 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -137,7 +137,7 @@ void job_uninstall(Job *j) { /* Detach from next 'bigger' objects */ /* daemon-reload should be transparent to job observers */ - if (j->manager->n_reloading <= 0) + if (!MANAGER_IS_RELOADING(j->manager)) bus_job_send_removed_signal(j); *pj = NULL; @@ -191,7 +191,7 @@ Job* job_install(Job *j) { if (uj) { if (job_type_is_conflicting(uj->type, j->type)) - job_finish_and_invalidate(uj, JOB_CANCELED, false); + job_finish_and_invalidate(uj, JOB_CANCELED, false, false); else { /* not conflicting, i.e. mergeable */ @@ -222,7 +222,7 @@ Job* job_install(Job *j) { *pj = j; j->installed = true; - j->manager->n_installed_jobs ++; + j->manager->n_installed_jobs++; log_unit_debug(j->unit, "Installed new job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id); @@ -614,19 +614,19 @@ int job_run_and_invalidate(Job *j) { if (j) { if (r == -EALREADY) - r = job_finish_and_invalidate(j, JOB_DONE, true); + r = job_finish_and_invalidate(j, JOB_DONE, true, true); else if (r == -EBADR) - r = job_finish_and_invalidate(j, JOB_SKIPPED, true); + r = job_finish_and_invalidate(j, JOB_SKIPPED, true, false); else if (r == -ENOEXEC) - r = job_finish_and_invalidate(j, JOB_INVALID, true); + r = job_finish_and_invalidate(j, JOB_INVALID, true, false); else if (r == -EPROTO) - r = job_finish_and_invalidate(j, JOB_ASSERT, true); + r = job_finish_and_invalidate(j, JOB_ASSERT, true, false); else if (r == -EOPNOTSUPP) - r = job_finish_and_invalidate(j, JOB_UNSUPPORTED, true); + r = job_finish_and_invalidate(j, JOB_UNSUPPORTED, true, false); else if (r == -EAGAIN) job_set_state(j, JOB_WAITING); else if (r < 0) - r = job_finish_and_invalidate(j, JOB_FAILED, true); + r = job_finish_and_invalidate(j, JOB_FAILED, true, false); } return r; @@ -645,7 +645,7 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR static const char *const generic_finished_stop_job[_JOB_RESULT_MAX] = { [JOB_DONE] = "Stopped %s.", [JOB_FAILED] = "Stopped (with error) %s.", - [JOB_TIMEOUT] = "Timed out stoppping %s.", + [JOB_TIMEOUT] = "Timed out stopping %s.", }; static const char *const generic_finished_reload_job[_JOB_RESULT_MAX] = { [JOB_DONE] = "Reloaded %s.", @@ -690,17 +690,20 @@ _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobR } static void job_print_status_message(Unit *u, JobType t, JobResult result) { - static const char* const job_result_status_table[_JOB_RESULT_MAX] = { - [JOB_DONE] = ANSI_GREEN " OK " ANSI_NORMAL, - [JOB_TIMEOUT] = ANSI_HIGHLIGHT_RED " TIME " ANSI_NORMAL, - [JOB_FAILED] = ANSI_HIGHLIGHT_RED "FAILED" ANSI_NORMAL, - [JOB_DEPENDENCY] = ANSI_HIGHLIGHT_YELLOW "DEPEND" ANSI_NORMAL, - [JOB_SKIPPED] = ANSI_HIGHLIGHT " INFO " ANSI_NORMAL, - [JOB_ASSERT] = ANSI_HIGHLIGHT_YELLOW "ASSERT" ANSI_NORMAL, - [JOB_UNSUPPORTED] = ANSI_HIGHLIGHT_YELLOW "UNSUPP" ANSI_NORMAL, + static struct { + const char *color, *word; + } const 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"}, }; const char *format; + const char *status; assert(u); assert(t >= 0); @@ -714,11 +717,16 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { if (!format) return; + if (log_get_show_color()) + status = strjoina(statuses[result].color, statuses[result].word, ANSI_NORMAL); + else + status = statuses[result].word; + if (result != JOB_DONE) manager_flip_auto_status(u->manager, true); DISABLE_WARNING_FORMAT_NONLITERAL; - unit_status_printf(u, job_result_status_table[result], format); + unit_status_printf(u, status, format); REENABLE_WARNING; if (t == JOB_START && result == JOB_FAILED) { @@ -819,11 +827,11 @@ static void job_fail_dependencies(Unit *u, UnitDependency d) { if (!IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE)) continue; - job_finish_and_invalidate(j, JOB_DEPENDENCY, true); + job_finish_and_invalidate(j, JOB_DEPENDENCY, true, false); } } -int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { +int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool already) { Unit *u; Unit *other; JobType t; @@ -840,7 +848,9 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { log_unit_debug(u, "Job %s/%s finished, result=%s", u->id, job_type_to_string(t), job_result_to_string(result)); - job_emit_status_message(u, t, result); + /* If this job did nothing to respective unit we don't log the status message */ + if (!already) + job_emit_status_message(u, t, result); job_add_to_dbus_queue(j); @@ -856,7 +866,7 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { } if (result == JOB_FAILED || result == JOB_INVALID) - j->manager->n_failed_jobs ++; + j->manager->n_failed_jobs++; job_uninstall(j); job_free(j); @@ -915,7 +925,7 @@ static int job_dispatch_timer(sd_event_source *s, uint64_t monotonic, void *user log_unit_warning(j->unit, "Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type)); u = j->unit; - job_finish_and_invalidate(j, JOB_TIMEOUT, true); + job_finish_and_invalidate(j, JOB_TIMEOUT, true, false); failure_action(u->manager, u->job_timeout_action, u->job_timeout_reboot_arg); @@ -1148,7 +1158,7 @@ void job_shutdown_magic(Job *j) { if (j->type != JOB_START) return; - if (j->unit->manager->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(j->unit->manager)) return; if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET)) diff --git a/src/core/job.h b/src/core/job.h index 856b0ce829..d359e8bb3e 100644 --- a/src/core/job.h +++ b/src/core/job.h @@ -219,7 +219,7 @@ void job_add_to_dbus_queue(Job *j); int job_start_timer(Job *j); int job_run_and_invalidate(Job *j); -int job_finish_and_invalidate(Job *j, JobResult result, bool recursive); +int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool already); char *job_dbus_path(Job *j); diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c index 22b71b6f5e..f83fa09301 100644 --- a/src/core/load-dropin.c +++ b/src/core/load-dropin.c @@ -44,6 +44,7 @@ static int add_dependency_consumer( } int unit_load_dropin(Unit *u) { + _cleanup_strv_free_ char **l = NULL; Iterator i; char *t, **f; int r; @@ -55,7 +56,7 @@ int unit_load_dropin(Unit *u) { SET_FOREACH(t, u->names, i) { char **p; - STRV_FOREACH(p, u->manager->lookup_paths.unit_path) { + STRV_FOREACH(p, u->manager->lookup_paths.search_path) { unit_file_process_dir(u->manager->unit_path_cache, *p, t, ".wants", UNIT_WANTS, add_dependency_consumer, u, NULL); unit_file_process_dir(u->manager->unit_path_cache, *p, t, ".requires", UNIT_REQUIRES, @@ -63,11 +64,19 @@ int unit_load_dropin(Unit *u) { } } - u->dropin_paths = strv_free(u->dropin_paths); - r = unit_find_dropin_paths(u, &u->dropin_paths); + r = unit_find_dropin_paths(u, &l); if (r <= 0) return 0; + if (!u->dropin_paths) { + u->dropin_paths = l; + l = NULL; + } else { + r = strv_extend_strv(&u->dropin_paths, l, true); + if (r < 0) + return log_oom(); + } + STRV_FOREACH(f, u->dropin_paths) { config_parse(u->id, *f, NULL, UNIT_VTABLE(u)->sections, diff --git a/src/core/load-dropin.h b/src/core/load-dropin.h index d8a4aefbb3..942d26724e 100644 --- a/src/core/load-dropin.h +++ b/src/core/load-dropin.h @@ -25,7 +25,7 @@ /* Read service data supplementary drop-in directories */ static inline int unit_find_dropin_paths(Unit *u, char ***paths) { - return unit_file_find_dropin_paths(u->manager->lookup_paths.unit_path, + return unit_file_find_dropin_paths(u->manager->lookup_paths.search_path, u->manager->unit_path_cache, u->names, paths); diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 5024fd19a5..8193418980 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -45,7 +45,7 @@ $1.SyslogIdentifier, config_parse_unit_string_printf, 0, $1.SyslogFacility, config_parse_log_facility, 0, offsetof($1, exec_context.syslog_priority) $1.SyslogLevel, config_parse_log_level, 0, offsetof($1, exec_context.syslog_priority) $1.SyslogLevelPrefix, config_parse_bool, 0, offsetof($1, exec_context.syslog_level_prefix) -$1.Capabilities, config_parse_exec_capabilities, 0, offsetof($1, exec_context) +$1.Capabilities, config_parse_warn_compat, DISABLED_LEGACY, offsetof($1, exec_context) $1.SecureBits, config_parse_exec_secure_bits, 0, offsetof($1, exec_context) $1.CapabilityBoundingSet, config_parse_capability_set, 0, offsetof($1, exec_context.capability_bounding_set) $1.AmbientCapabilities, config_parse_capability_set, 0, offsetof($1, exec_context.capability_ambient_set) @@ -120,6 +120,14 @@ $1.MemoryAccounting, config_parse_bool, 0, $1.MemoryLimit, config_parse_memory_limit, 0, offsetof($1, cgroup_context) $1.DeviceAllow, config_parse_device_allow, 0, offsetof($1, cgroup_context) $1.DevicePolicy, config_parse_device_policy, 0, offsetof($1, cgroup_context.device_policy) +$1.IOAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.io_accounting) +$1.IOWeight, config_parse_io_weight, 0, offsetof($1, cgroup_context.io_weight) +$1.StartupIOWeight, config_parse_io_weight, 0, offsetof($1, cgroup_context.startup_io_weight) +$1.IODeviceWeight, config_parse_io_device_weight, 0, offsetof($1, cgroup_context) +$1.IOReadBandwidthMax, config_parse_io_limit, 0, offsetof($1, cgroup_context) +$1.IOWriteBandwidthMax, config_parse_io_limit, 0, offsetof($1, cgroup_context) +$1.IOReadIOPSMax, config_parse_io_limit, 0, offsetof($1, cgroup_context) +$1.IOWriteIOPSMax, config_parse_io_limit, 0, offsetof($1, cgroup_context) $1.BlockIOAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.blockio_accounting) $1.BlockIOWeight, config_parse_blockio_weight, 0, offsetof($1, cgroup_context.blockio_weight) $1.StartupBlockIOWeight, config_parse_blockio_weight, 0, offsetof($1, cgroup_context.startup_blockio_weight) @@ -164,6 +172,8 @@ Unit.IgnoreOnSnapshot, config_parse_warn_compat, DISABLED_LE Unit.JobTimeoutSec, config_parse_sec_fix_0, 0, offsetof(Unit, job_timeout) Unit.JobTimeoutAction, config_parse_failure_action, 0, offsetof(Unit, job_timeout_action) Unit.JobTimeoutRebootArgument, config_parse_string, 0, offsetof(Unit, job_timeout_reboot_arg) +Unit.StartLimitIntervalSec, config_parse_sec, 0, offsetof(Unit, start_limit.interval) +m4_dnl The following is a legacy alias name for compatibility Unit.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval) Unit.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst) Unit.StartLimitAction, config_parse_failure_action, 0, offsetof(Unit, start_limit_action) @@ -220,6 +230,7 @@ Service.TimeoutStartSec, config_parse_service_timeout, 0, Service.TimeoutStopSec, config_parse_service_timeout, 0, 0 Service.RuntimeMaxSec, config_parse_sec, 0, offsetof(Service, runtime_max_usec) Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec) +m4_dnl The following three only exist for compatibility, they moved into Unit, see above Service.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval) Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst) Service.StartLimitAction, config_parse_failure_action, 0, offsetof(Unit, start_limit_action) @@ -240,7 +251,7 @@ Service.BusName, config_parse_bus_name, 0, Service.FileDescriptorStoreMax, config_parse_unsigned, 0, offsetof(Service, n_fd_store_max) Service.NotifyAccess, config_parse_notify_access, 0, offsetof(Service, notify_access) Service.Sockets, config_parse_service_sockets, 0, 0 -Service.BusPolicy, config_parse_bus_endpoint_policy, 0, offsetof(Service, exec_context) +Service.BusPolicy, config_parse_warn_compat, DISABLED_LEGACY, 0 Service.USBFunctionDescriptors, config_parse_path, 0, offsetof(Service, usb_function_descriptors) Service.USBFunctionStrings, config_parse_path, 0, offsetof(Service, usb_function_strings) EXEC_CONTEXT_CONFIG_ITEMS(Service)m4_dnl @@ -297,6 +308,8 @@ Socket.RemoveOnStop, config_parse_bool, 0, Socket.Symlinks, config_parse_unit_path_strv_printf, 0, offsetof(Socket, symlinks) Socket.FileDescriptorName, config_parse_fdname, 0, 0 Socket.Service, config_parse_socket_service, 0, 0 +Socket.TriggerLimitIntervalSec, config_parse_sec, 0, offsetof(Socket, trigger_limit.interval) +Socket.TriggerLimitBurst, config_parse_unsigned, 0, offsetof(Socket, trigger_limit.burst) m4_ifdef(`HAVE_SMACK', `Socket.SmackLabel, config_parse_string, 0, offsetof(Socket, smack) Socket.SmackLabelIPIn, config_parse_string, 0, offsetof(Socket, smack_ip_in) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index b3dec7b8cc..86b4fb071b 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -119,7 +119,7 @@ int config_parse_unit_deps( assert(rvalue); p = rvalue; - for(;;) { + for (;;) { _cleanup_free_ char *word = NULL, *k = NULL; int r; @@ -574,9 +574,7 @@ int config_parse_exec( void *data, void *userdata) { - _cleanup_free_ char *cmd = NULL; ExecCommand **e = data; - Unit *u = userdata; const char *p; bool semicolon; int r; @@ -585,7 +583,6 @@ int config_parse_exec( assert(lvalue); assert(rvalue); assert(e); - assert(u); e += ltype; rvalue += strspn(rvalue, WHITESPACE); @@ -596,13 +593,7 @@ int config_parse_exec( return 0; } - r = unit_full_printf(u, rvalue, &cmd); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s, ignoring: %m", rvalue); - return 0; - } - - p = cmd; + p = rvalue; do { _cleanup_free_ char *path = NULL, *firstword = NULL; bool separate_argv0 = false, ignore = false; @@ -629,7 +620,7 @@ int config_parse_exec( separate_argv0 = true; else break; - f ++; + f++; } if (isempty(f)) { @@ -677,7 +668,7 @@ int config_parse_exec( /* Check explicitly for an unquoted semicolon as * command separator token. */ if (p[0] == ';' && (!p[1] || strchr(WHITESPACE, p[1]))) { - p ++; + p++; p += strspn(p, WHITESPACE); semicolon = true; break; @@ -741,16 +732,17 @@ int config_parse_exec( DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type"); DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier"); -int config_parse_socket_bindtodevice(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_socket_bindtodevice( + 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) { Socket *s = data; char *n; @@ -761,6 +753,11 @@ int config_parse_socket_bindtodevice(const char* unit, assert(data); if (rvalue[0] && !streq(rvalue, "*")) { + if (!ifname_valid(rvalue)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is invalid, ignoring: %s", rvalue); + return 0; + } + n = strdup(rvalue); if (!n) return log_oom(); @@ -951,38 +948,6 @@ int config_parse_exec_cpu_affinity(const char *unit, return 0; } -int config_parse_exec_capabilities(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) { - - ExecContext *c = data; - cap_t cap; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - cap = cap_from_text(rvalue); - if (!cap) { - log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse capabilities, ignoring: %s", rvalue); - return 0; - } - - if (c->capabilities) - cap_free(c->capabilities); - c->capabilities = cap; - - return 0; -} - int config_parse_exec_secure_bits(const char *unit, const char *filename, unsigned line, @@ -1640,7 +1605,7 @@ int config_parse_service_sockets( assert(data); p = rvalue; - for(;;) { + for (;;) { _cleanup_free_ char *word = NULL, *k = NULL; r = extract_first_word(&p, &word, NULL, 0); @@ -1902,59 +1867,6 @@ int config_parse_bus_policy( return 0; } -int config_parse_bus_endpoint_policy( - 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_free_ char *name = NULL; - BusPolicyAccess access; - ExecContext *c = data; - char *access_str; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - name = strdup(rvalue); - if (!name) - return log_oom(); - - access_str = strpbrk(name, WHITESPACE); - if (!access_str) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid endpoint policy value '%s'", rvalue); - return 0; - } - - *access_str = '\0'; - access_str++; - access_str += strspn(access_str, WHITESPACE); - - access = bus_policy_access_from_string(access_str); - if (access <= _BUS_POLICY_ACCESS_INVALID || - access >= _BUS_POLICY_ACCESS_MAX) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid endpoint policy access type '%s'", access_str); - return 0; - } - - if (!c->bus_endpoint) { - r = bus_endpoint_new(&c->bus_endpoint); - if (r < 0) - return log_error_errno(r, "Failed to create bus endpoint object: %m"); - } - - return bus_endpoint_add_policy(c->bus_endpoint, name, access); -} - int config_parse_working_directory( const char *unit, const char *filename, @@ -2589,7 +2501,7 @@ int config_parse_syscall_filter( /* Turn on NNP, but only if it wasn't configured explicitly * before, and only if we are in user mode. */ - if (!c->no_new_privileges_set && u->manager->running_as == MANAGER_USER) + if (!c->no_new_privileges_set && MANAGER_IS_USER(u->manager)) c->no_new_privileges = true; return 0; @@ -2941,11 +2853,12 @@ int config_parse_device_allow( void *data, void *userdata) { - _cleanup_free_ char *path = NULL; + _cleanup_free_ char *path = NULL, *t = NULL; CGroupContext *c = data; CGroupDeviceAllow *a; - const char *m; + const char *m = NULL; size_t n; + int r; if (isempty(rvalue)) { while (c->device_allow) @@ -2954,8 +2867,16 @@ int config_parse_device_allow( return 0; } - n = strcspn(rvalue, WHITESPACE); - path = strndup(rvalue, n); + r = unit_full_printf(userdata, rvalue, &t); + if(r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to resolve specifiers in %s, ignoring: %m", + rvalue); + } + + n = strcspn(t, WHITESPACE); + + path = strndup(t, n); if (!path) return log_oom(); @@ -2966,7 +2887,7 @@ int config_parse_device_allow( return 0; } - m = rvalue + n + strspn(rvalue + n, WHITESPACE); + m = t + n + strspn(t + n, WHITESPACE); if (isempty(m)) m = "rwm"; @@ -2989,6 +2910,193 @@ int config_parse_device_allow( return 0; } +int config_parse_io_weight( + 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) { + + uint64_t *weight = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = cg_weight_parse(rvalue, weight); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", rvalue); + return 0; + } + + return 0; +} + +int config_parse_io_device_weight( + 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_free_ char *path = NULL; + CGroupIODeviceWeight *w; + CGroupContext *c = data; + const char *weight; + uint64_t u; + size_t n; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + while (c->io_device_weights) + cgroup_context_free_io_device_weight(c, c->io_device_weights); + + return 0; + } + + n = strcspn(rvalue, WHITESPACE); + weight = rvalue + n; + weight += strspn(weight, WHITESPACE); + + if (isempty(weight)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Expected block device and device weight. Ignoring."); + return 0; + } + + path = strndup(rvalue, n); + if (!path) + return log_oom(); + + if (!path_startswith(path, "/dev")) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); + return 0; + } + + r = cg_weight_parse(weight, &u); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "IO weight '%s' invalid. Ignoring.", weight); + return 0; + } + + assert(u != CGROUP_WEIGHT_INVALID); + + w = new0(CGroupIODeviceWeight, 1); + if (!w) + return log_oom(); + + w->path = path; + path = NULL; + + w->weight = u; + + LIST_PREPEND(device_weights, c->io_device_weights, w); + return 0; +} + +int config_parse_io_limit( + 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_free_ char *path = NULL; + CGroupIODeviceLimit *l = NULL, *t; + CGroupContext *c = data; + CGroupIOLimitType type; + const char *limit; + uint64_t num; + size_t n; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + type = cgroup_io_limit_type_from_string(lvalue); + assert(type >= 0); + + if (isempty(rvalue)) { + LIST_FOREACH(device_limits, l, c->io_device_limits) + l->limits[type] = cgroup_io_limit_defaults[type]; + return 0; + } + + n = strcspn(rvalue, WHITESPACE); + limit = rvalue + n; + limit += strspn(limit, WHITESPACE); + + if (!*limit) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Expected space separated pair of device node and bandwidth. Ignoring."); + return 0; + } + + path = strndup(rvalue, n); + if (!path) + return log_oom(); + + if (!path_startswith(path, "/dev")) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); + return 0; + } + + if (streq("max", limit)) { + num = CGROUP_LIMIT_MAX; + } else { + r = parse_size(limit, 1000, &num); + if (r < 0 || num <= 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "IO Limit '%s' invalid. Ignoring.", rvalue); + return 0; + } + } + + LIST_FOREACH(device_limits, t, c->io_device_limits) { + if (path_equal(path, t->path)) { + l = t; + break; + } + } + + if (!l) { + CGroupIOLimitType ttype; + + l = new0(CGroupIODeviceLimit, 1); + if (!l) + return log_oom(); + + l->path = path; + path = NULL; + for (ttype = 0; ttype < _CGROUP_IO_LIMIT_TYPE_MAX; ttype++) + l->limits[ttype] = cgroup_io_limit_defaults[ttype]; + + LIST_PREPEND(device_limits, c->io_device_limits, l); + } + + l->limits[type] = num; + + return 0; +} + int config_parse_blockio_weight( const char *unit, const char *filename, @@ -3100,7 +3208,7 @@ int config_parse_blockio_bandwidth( void *userdata) { _cleanup_free_ char *path = NULL; - CGroupBlockIODeviceBandwidth *b; + CGroupBlockIODeviceBandwidth *b = NULL, *t; CGroupContext *c = data; const char *bandwidth; uint64_t bytes; @@ -3115,12 +3223,10 @@ int config_parse_blockio_bandwidth( read = streq("BlockIOReadBandwidth", lvalue); if (isempty(rvalue)) { - CGroupBlockIODeviceBandwidth *next; - - LIST_FOREACH_SAFE (device_bandwidths, b, next, c->blockio_device_bandwidths) - if (b->read == read) - cgroup_context_free_blockio_device_bandwidth(c, b); - + LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { + b->rbps = CGROUP_LIMIT_MAX; + b->wbps = CGROUP_LIMIT_MAX; + } return 0; } @@ -3148,16 +3254,30 @@ int config_parse_blockio_bandwidth( return 0; } - b = new0(CGroupBlockIODeviceBandwidth, 1); - if (!b) - return log_oom(); + LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) { + if (path_equal(path, t->path)) { + b = t; + break; + } + } - b->path = path; - path = NULL; - b->bandwidth = bytes; - b->read = read; + if (!t) { + b = new0(CGroupBlockIODeviceBandwidth, 1); + if (!b) + return log_oom(); + + b->path = path; + path = NULL; + b->rbps = CGROUP_LIMIT_MAX; + b->wbps = CGROUP_LIMIT_MAX; + + LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b); + } - LIST_PREPEND(device_bandwidths, c->blockio_device_bandwidths, b); + if (read) + b->rbps = bytes; + else + b->wbps = bytes; return 0; } @@ -3455,7 +3575,7 @@ int config_parse_protect_home( ProtectHome h; h = protect_home_from_string(rvalue); - if (h < 0){ + if (h < 0) { log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue); return 0; } @@ -3498,7 +3618,7 @@ int config_parse_protect_system( ProtectSystem s; s = protect_system_from_string(rvalue); - if (s < 0){ + if (s < 0) { log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue); return 0; } @@ -3512,10 +3632,10 @@ int config_parse_protect_system( #define FOLLOW_MAX 8 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { + char *id = NULL; unsigned c = 0; int fd, r; FILE *f; - char *id = NULL; assert(filename); assert(*filename); @@ -3537,7 +3657,6 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { * the names of this unit, but only if it is a valid * unit name. */ name = basename(*filename); - if (unit_name_is_valid(name, UNIT_NAME_ANY)) { id = set_get(names, name); @@ -3577,6 +3696,7 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { *_f = f; *_final = id; + return 0; } @@ -3601,7 +3721,19 @@ static int merge_by_names(Unit **u, Set *names, const char *id) { * ours? Then let's try it the other way * round */ - other = manager_get_unit((*u)->manager, k); + /* If the symlink name we are looking at is unit template, then + we must search for instance of this template */ + if (unit_name_is_valid(k, UNIT_NAME_TEMPLATE)) { + _cleanup_free_ char *instance = NULL; + + r = unit_name_replace_instance(k, (*u)->instance, &instance); + if (r < 0) + return r; + + other = manager_get_unit((*u)->manager, instance); + } else + other = manager_get_unit((*u)->manager, k); + free(k); if (other) { @@ -3625,13 +3757,13 @@ static int merge_by_names(Unit **u, Set *names, const char *id) { } static int load_from_path(Unit *u, const char *path) { - int r; _cleanup_set_free_free_ Set *symlink_names = NULL; _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *filename = NULL; char *id = NULL; Unit *merged; struct stat st; + int r; assert(u); assert(path); @@ -3656,7 +3788,7 @@ static int load_from_path(Unit *u, const char *path) { } else { char **p; - STRV_FOREACH(p, u->manager->lookup_paths.unit_path) { + STRV_FOREACH(p, u->manager->lookup_paths.search_path) { /* Instead of opening the path right away, we manually * follow all symlinks and add their name to our unit @@ -3670,18 +3802,14 @@ static int load_from_path(Unit *u, const char *path) { r = -ENOENT; else r = open_follow(&filename, &f, symlink_names, &id); + if (r >= 0) + break; + filename = mfree(filename); + if (r != -ENOENT) + return r; - if (r < 0) { - filename = mfree(filename); - if (r != -ENOENT) - return r; - - /* Empty the symlink names for the next run */ - set_clear_free(symlink_names); - continue; - } - - break; + /* Empty the symlink names for the next run */ + set_clear_free(symlink_names); } } @@ -3689,6 +3817,11 @@ static int load_from_path(Unit *u, const char *path) { /* Hmm, no suitable file found? */ return 0; + if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) { + log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id); + return -ELOOP; + } + merged = u; r = merge_by_names(&merged, symlink_names, id); if (r < 0) @@ -3702,10 +3835,12 @@ static int load_from_path(Unit *u, const char *path) { if (fstat(fileno(f), &st) < 0) return -errno; - if (null_or_empty(&st)) + if (null_or_empty(&st)) { u->load_state = UNIT_MASKED; - else { + u->fragment_mtime = 0; + } else { u->load_state = UNIT_LOADED; + u->fragment_mtime = timespec_load(&st.st_mtim); /* Now, parse the file contents */ r = config_parse(u->id, filename, f, @@ -3720,8 +3855,6 @@ static int load_from_path(Unit *u, const char *path) { u->fragment_path = filename; filename = NULL; - u->fragment_mtime = timespec_load(&st.st_mtim); - if (u->source_path) { if (stat(u->source_path, &st) >= 0) u->source_mtime = timespec_load(&st.st_mtim); @@ -3850,7 +3983,6 @@ void unit_dump_config_items(FILE *f) { { config_parse_input, "INPUT" }, { config_parse_log_facility, "FACILITY" }, { config_parse_log_level, "LEVEL" }, - { config_parse_exec_capabilities, "CAPABILITIES" }, { config_parse_exec_secure_bits, "SECUREBITS" }, { config_parse_capability_set, "BOUNDINGSET" }, { config_parse_limit, "LIMIT" }, @@ -3897,6 +4029,9 @@ void unit_dump_config_items(FILE *f) { { config_parse_memory_limit, "LIMIT" }, { config_parse_device_allow, "DEVICE" }, { config_parse_device_policy, "POLICY" }, + { config_parse_io_limit, "LIMIT" }, + { config_parse_io_weight, "WEIGHT" }, + { config_parse_io_device_weight, "DEVICEWEIGHT" }, { config_parse_blockio_bandwidth, "BANDWIDTH" }, { config_parse_blockio_weight, "WEIGHT" }, { config_parse_blockio_device_weight, "DEVICEWEIGHT" }, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 5fb5910919..b36a2e3a02 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -52,7 +52,6 @@ int config_parse_exec_io_priority(const char *unit, const char *filename, unsign int config_parse_exec_cpu_sched_policy(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_exec_cpu_sched_prio(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_exec_cpu_affinity(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_exec_capabilities(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_exec_secure_bits(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_capability_set(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_limit(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); @@ -67,7 +66,6 @@ int config_parse_service_sockets(const char *unit, const char *filename, unsigne int config_parse_busname_service(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_bus_policy(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_bus_policy_world(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_bus_endpoint_policy(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_unit_env_file(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_tos(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_unit_condition_path(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); @@ -88,6 +86,9 @@ int config_parse_memory_limit(const char *unit, const char *filename, unsigned l int config_parse_tasks_max(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_device_policy(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_device_allow(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_io_weight(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_io_device_weight(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_io_limit(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_blockio_weight(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_blockio_device_weight(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_blockio_bandwidth(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/core/machine-id-setup.c b/src/core/machine-id-setup.c index 9e6b3d3292..0145fe2894 100644 --- a/src/core/machine-id-setup.c +++ b/src/core/machine-id-setup.c @@ -101,14 +101,23 @@ static int read_machine_id(int fd, char id[34]) { return 0; } -static int write_machine_id(int fd, char id[34]) { +static int write_machine_id(int fd, const char id[34]) { + int r; + assert(fd >= 0); assert(id); if (lseek(fd, 0, SEEK_SET) < 0) return -errno; - return loop_write(fd, id, 33, false); + r = loop_write(fd, id, 33, false); + if (r < 0) + return r; + + if (fsync(fd) < 0) + return -errno; + + return 0; } static int generate_machine_id(char id[34], const char *root) { @@ -120,10 +129,7 @@ static int generate_machine_id(char id[34], const char *root) { assert(id); - if (isempty(root)) - dbus_machine_id = "/var/lib/dbus/machine-id"; - else - dbus_machine_id = strjoina(root, "/var/lib/dbus/machine-id"); + dbus_machine_id = prefix_roota(root, "/var/lib/dbus/machine-id"); /* First, try reading the D-Bus machine id, unless it is a symlink */ fd = open(dbus_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); @@ -203,18 +209,8 @@ int machine_id_setup(const char *root, sd_id128_t machine_id) { char id[34]; /* 32 + \n + \0 */ int r; - if (isempty(root)) { - etc_machine_id = "/etc/machine-id"; - run_machine_id = "/run/machine-id"; - } else { - char *x; - - x = strjoina(root, "/etc/machine-id"); - etc_machine_id = path_kill_slashes(x); - - x = strjoina(root, "/run/machine-id"); - run_machine_id = path_kill_slashes(x); - } + etc_machine_id = prefix_roota(root, "/etc/machine-id"); + run_machine_id = prefix_roota(root, "/run/machine-id"); RUN_WITH_UMASK(0000) { /* We create this 0444, to indicate that this isn't really @@ -274,10 +270,10 @@ int machine_id_setup(const char *root, sd_id128_t machine_id) { RUN_WITH_UMASK(0022) { r = write_string_file(run_machine_id, id, WRITE_STRING_FILE_CREATE); - } - if (r < 0) { - (void) unlink(run_machine_id); - return log_error_errno(r, "Cannot write %s: %m", run_machine_id); + if (r < 0) { + (void) unlink(run_machine_id); + return log_error_errno(r, "Cannot write %s: %m", run_machine_id); + } } /* And now, let's mount it over */ @@ -301,14 +297,7 @@ int machine_id_commit(const char *root) { char id[34]; /* 32 + \n + \0 */ int r; - if (isempty(root)) - etc_machine_id = "/etc/machine-id"; - else { - char *x; - - x = strjoina(root, "/etc/machine-id"); - etc_machine_id = path_kill_slashes(x); - } + etc_machine_id = prefix_roota(root, "/etc/machine-id"); r = path_is_mount_point(etc_machine_id, 0); if (r < 0) diff --git a/src/core/main.c b/src/core/main.c index e2088574c0..6397aadc73 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -81,6 +81,7 @@ #include "strv.h" #include "switch-root.h" #include "terminal-util.h" +#include "umask-util.h" #include "user-util.h" #include "virt.h" #include "watchdog.h" @@ -94,7 +95,7 @@ static enum { ACTION_DONE } arg_action = ACTION_RUN; static char *arg_default_unit = NULL; -static ManagerRunningAs arg_running_as = _MANAGER_RUNNING_AS_INVALID; +static bool arg_system = false; static bool arg_dump_core = true; static int arg_crash_chvt = -1; static bool arg_crash_shell = false; @@ -102,7 +103,7 @@ static bool arg_crash_reboot = false; static bool arg_confirm_spawn = false; static ShowStatus arg_show_status = _SHOW_STATUS_UNSET; static bool arg_switched_root = false; -static int arg_no_pager = -1; +static bool arg_no_pager = false; static char ***arg_join_controllers = NULL; static ExecOutput arg_default_std_output = EXEC_OUTPUT_JOURNAL; static ExecOutput arg_default_std_error = EXEC_OUTPUT_INHERIT; @@ -121,20 +122,13 @@ static usec_t arg_default_timer_accuracy_usec = 1 * USEC_PER_MINUTE; static Set* arg_syscall_archs = NULL; static FILE* arg_serialization = NULL; static bool arg_default_cpu_accounting = false; +static bool arg_default_io_accounting = false; static bool arg_default_blockio_accounting = false; static bool arg_default_memory_accounting = false; static bool arg_default_tasks_accounting = true; static uint64_t arg_default_tasks_max = UINT64_C(512); static sd_id128_t arg_machine_id = {}; -static void pager_open_if_enabled(void) { - - if (arg_no_pager <= 0) - return; - - pager_open(false); -} - noreturn static void freeze_or_reboot(void) { if (arg_crash_reboot) { @@ -296,6 +290,7 @@ static int parse_crash_chvt(const char *value) { } static int set_machine_id(const char *m) { + assert(m); if (sd_id128_from_string(m, &arg_machine_id) < 0) return -EINVAL; @@ -420,6 +415,15 @@ static int parse_proc_cmdline_item(const char *key, const char *value) { target = runlevel_to_target(key); if (target) return free_and_strdup(&arg_default_unit, target); + + } else if (streq(key, "systemd.default_timeout_start_sec") && value) { + + r = parse_sec(value, &arg_default_timeout_start_usec); + if (r < 0) + log_warning_errno(r, "Failed to parse default start timeout: %s, ignoring.", value); + + if (arg_default_timeout_start_usec <= 0) + arg_default_timeout_start_usec = USEC_INFINITY; } return 0; @@ -667,7 +671,8 @@ static int parse_config_file(void) { { "Manager", "DefaultTimeoutStartSec", config_parse_sec, 0, &arg_default_timeout_start_usec }, { "Manager", "DefaultTimeoutStopSec", config_parse_sec, 0, &arg_default_timeout_stop_usec }, { "Manager", "DefaultRestartSec", config_parse_sec, 0, &arg_default_restart_usec }, - { "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval }, + { "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval }, /* obsolete alias */ + { "Manager", "DefaultStartLimitIntervalSec",config_parse_sec, 0, &arg_default_start_limit_interval }, { "Manager", "DefaultStartLimitBurst", config_parse_unsigned, 0, &arg_default_start_limit_burst }, { "Manager", "DefaultEnvironment", config_parse_environ, 0, &arg_default_environment }, { "Manager", "DefaultLimitCPU", config_parse_limit, RLIMIT_CPU, arg_default_rlimit }, @@ -687,6 +692,7 @@ static int parse_config_file(void) { { "Manager", "DefaultLimitRTPRIO", config_parse_limit, RLIMIT_RTPRIO, arg_default_rlimit }, { "Manager", "DefaultLimitRTTIME", config_parse_limit, RLIMIT_RTTIME, arg_default_rlimit }, { "Manager", "DefaultCPUAccounting", config_parse_bool, 0, &arg_default_cpu_accounting }, + { "Manager", "DefaultIOAccounting", config_parse_bool, 0, &arg_default_io_accounting }, { "Manager", "DefaultBlockIOAccounting", config_parse_bool, 0, &arg_default_blockio_accounting }, { "Manager", "DefaultMemoryAccounting", config_parse_bool, 0, &arg_default_memory_accounting }, { "Manager", "DefaultTasksAccounting", config_parse_bool, 0, &arg_default_tasks_accounting }, @@ -696,11 +702,11 @@ static int parse_config_file(void) { const char *fn, *conf_dirs_nulstr; - fn = arg_running_as == MANAGER_SYSTEM ? + fn = arg_system ? PKGSYSCONFDIR "/system.conf" : PKGSYSCONFDIR "/user.conf"; - conf_dirs_nulstr = arg_running_as == MANAGER_SYSTEM ? + conf_dirs_nulstr = arg_system ? CONF_PATHS_NULSTR("systemd/system.conf.d") : CONF_PATHS_NULSTR("systemd/user.conf.d"); @@ -729,6 +735,7 @@ static void manager_set_defaults(Manager *m) { m->default_start_limit_interval = arg_default_start_limit_interval; m->default_start_limit_burst = arg_default_start_limit_burst; m->default_cpu_accounting = arg_default_cpu_accounting; + m->default_io_accounting = arg_default_io_accounting; m->default_blockio_accounting = arg_default_blockio_accounting; m->default_memory_accounting = arg_default_memory_accounting; m->default_tasks_accounting = arg_default_tasks_accounting; @@ -874,17 +881,15 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_SYSTEM: - arg_running_as = MANAGER_SYSTEM; + arg_system = true; break; case ARG_USER: - arg_running_as = MANAGER_USER; + arg_system = false; break; case ARG_TEST: arg_action = ACTION_TEST; - if (arg_no_pager < 0) - arg_no_pager = true; break; case ARG_NO_PAGER: @@ -994,8 +999,6 @@ static int parse_argv(int argc, char *argv[]) { case 'h': arg_action = ACTION_HELP; - if (arg_no_pager < 0) - arg_no_pager = true; break; case 'D': @@ -1073,7 +1076,7 @@ static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching return log_error_errno(r, "Failed to create serialization file: %m"); /* Make sure nothing is really destructed when we shut down */ - m->n_reloading ++; + m->n_reloading++; bus_manager_send_reloading(m, true); fds = fdset_new(); @@ -1230,10 +1233,15 @@ static int status_welcome(void) { if (r < 0 && r != -ENOENT) log_warning_errno(r, "Failed to read os-release file: %m"); - return status_printf(NULL, false, false, - "\nWelcome to \x1B[%sm%s\x1B[0m!\n", - isempty(ansi_color) ? "1" : ansi_color, - isempty(pretty_name) ? "Linux" : pretty_name); + if (log_get_show_color()) + return status_printf(NULL, false, false, + "\nWelcome to \x1B[%sm%s\x1B[0m!\n", + isempty(ansi_color) ? "1" : ansi_color, + isempty(pretty_name) ? "Linux" : pretty_name); + else + return status_printf(NULL, false, false, + "\nWelcome to %s!\n", + isempty(pretty_name) ? "Linux" : pretty_name); } static int write_container_id(void) { @@ -1244,7 +1252,8 @@ static int write_container_id(void) { if (isempty(c)) return 0; - r = write_string_file("/run/systemd/container", c, WRITE_STRING_FILE_CREATE); + RUN_WITH_UMASK(0022) + r = write_string_file("/run/systemd/container", c, WRITE_STRING_FILE_CREATE); if (r < 0) return log_warning_errno(r, "Failed to write /run/systemd/container, ignoring: %m"); @@ -1313,7 +1322,6 @@ int main(int argc, char *argv[]) { /* This is compatibility support for SysV, where * calling init as a user is identical to telinit. */ - errno = -ENOENT; execv(SYSTEMCTL_BINARY_PATH, argv); log_error_errno(errno, "Failed to exec " SYSTEMCTL_BINARY_PATH ": %m"); return 1; @@ -1344,7 +1352,7 @@ int main(int argc, char *argv[]) { saved_argv = argv; saved_argc = argc; - log_show_color(isatty(STDERR_FILENO) > 0); + log_show_color(colors_enabled()); log_set_upgrade_syslog_to_journal(true); /* Disable the umask logic */ @@ -1354,7 +1362,7 @@ int main(int argc, char *argv[]) { if (getpid() == 1 && detect_container() <= 0) { /* Running outside of a container as PID 1 */ - arg_running_as = MANAGER_SYSTEM; + arg_system = true; make_null_stdio(); log_set_target(LOG_TARGET_KMSG); log_open(); @@ -1382,13 +1390,13 @@ int main(int argc, char *argv[]) { dual_timestamp_get(&security_finish_timestamp); } - if (mac_selinux_init(NULL) < 0) { + if (mac_selinux_init() < 0) { error_message = "Failed to initialize SELinux policy"; goto finish; } if (!skip_setup) { - if (clock_is_localtime() > 0) { + if (clock_is_localtime(NULL) > 0) { int min; /* @@ -1438,7 +1446,7 @@ int main(int argc, char *argv[]) { } else if (getpid() == 1) { /* Running inside a container, as PID 1 */ - arg_running_as = MANAGER_SYSTEM; + arg_system = true; log_set_target(LOG_TARGET_CONSOLE); log_close_console(); /* force reopen of /dev/console */ log_open(); @@ -1448,12 +1456,10 @@ int main(int argc, char *argv[]) { /* clear the kernel timestamp, * because we are in a container */ - kernel_timestamp.monotonic = 0ULL; - kernel_timestamp.realtime = 0ULL; - + kernel_timestamp = DUAL_TIMESTAMP_NULL; } else { /* Running as user instance */ - arg_running_as = MANAGER_USER; + arg_system = false; log_set_target(LOG_TARGET_AUTO); log_open(); @@ -1511,7 +1517,7 @@ int main(int argc, char *argv[]) { goto finish; } - if (arg_running_as == MANAGER_SYSTEM) { + if (arg_system) { r = parse_proc_cmdline(parse_proc_cmdline_item); if (r < 0) log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); @@ -1532,14 +1538,14 @@ int main(int argc, char *argv[]) { goto finish; } - if (arg_running_as == MANAGER_USER && + if (!arg_system && arg_action == ACTION_RUN && sd_booted() <= 0) { log_error("Trying to run as user instance, but the system has not been booted with systemd."); goto finish; } - if (arg_running_as == MANAGER_SYSTEM && + if (arg_system && arg_action == ACTION_RUN && running_in_chroot() > 0) { log_error("Cannot be run in a chroot() environment."); @@ -1549,7 +1555,8 @@ int main(int argc, char *argv[]) { if (arg_action == ACTION_TEST) skip_setup = true; - pager_open_if_enabled(); + if (arg_action == ACTION_TEST || arg_action == ACTION_HELP) + pager_open(arg_no_pager, false); if (arg_action == ACTION_HELP) { retval = help(); @@ -1566,7 +1573,7 @@ int main(int argc, char *argv[]) { goto finish; } - if (arg_running_as == MANAGER_USER && + if (!arg_system && !getenv("XDG_RUNTIME_DIR")) { log_error("Trying to run as user instance, but $XDG_RUNTIME_DIR is not set."); goto finish; @@ -1589,7 +1596,7 @@ int main(int argc, char *argv[]) { if (arg_serialization) assert_se(fdset_remove(fds, fileno(arg_serialization)) >= 0); - if (arg_running_as == MANAGER_SYSTEM) + if (arg_system) /* Become a session leader if we aren't one yet. */ setsid(); @@ -1598,7 +1605,7 @@ int main(int argc, char *argv[]) { /* Reset the console, but only if this is really init and we * are freshly booted */ - if (arg_running_as == MANAGER_SYSTEM && arg_action == ACTION_RUN) { + if (arg_system && arg_action == ACTION_RUN) { /* If we are init, we connect stdin/stdout/stderr to * /dev/null and make sure we don't have a controlling @@ -1625,7 +1632,7 @@ int main(int argc, char *argv[]) { goto finish; } - if (arg_running_as == MANAGER_SYSTEM) { + if (arg_system) { int v; log_info(PACKAGE_STRING " running in %ssystem mode. (" SYSTEMD_FEATURES ")", @@ -1661,7 +1668,7 @@ int main(int argc, char *argv[]) { arg_action == ACTION_TEST ? " test" : "", getuid(), t); } - if (arg_running_as == MANAGER_SYSTEM && !skip_setup) { + if (arg_system && !skip_setup) { if (arg_show_status > 0) status_welcome(); @@ -1673,7 +1680,7 @@ int main(int argc, char *argv[]) { test_usr(); } - if (arg_running_as == MANAGER_SYSTEM && arg_runtime_watchdog > 0) + if (arg_system && arg_runtime_watchdog > 0 && arg_runtime_watchdog != USEC_INFINITY) watchdog_set_timeout(&arg_runtime_watchdog); if (arg_timer_slack_nsec != NSEC_INFINITY) @@ -1703,12 +1710,12 @@ int main(int argc, char *argv[]) { } } - if (arg_running_as == MANAGER_USER) + if (!arg_system) /* Become reaper of our children */ if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) log_warning_errno(errno, "Failed to make us a subreaper: %m"); - if (arg_running_as == MANAGER_SYSTEM) { + if (arg_system) { bump_rlimit_nofile(&saved_rlimit_nofile); if (empty_etc) { @@ -1720,7 +1727,7 @@ int main(int argc, char *argv[]) { } } - r = manager_new(arg_running_as, arg_action == ACTION_TEST, &m); + r = manager_new(arg_system ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, arg_action == ACTION_TEST, &m); if (r < 0) { log_emergency_errno(r, "Failed to allocate manager object: %m"); error_message = "Failed to allocate manager object"; @@ -1883,7 +1890,7 @@ int main(int argc, char *argv[]) { case MANAGER_EXIT: retval = m->return_value; - if (m->running_as == MANAGER_USER) { + if (MANAGER_IS_USER(m)) { log_debug("Exit."); goto finish; } @@ -1979,7 +1986,7 @@ finish: args[i++] = SYSTEMD_BINARY_PATH; if (switch_root_dir) args[i++] = "--switched-root"; - args[i++] = arg_running_as == MANAGER_SYSTEM ? "--system" : "--user"; + args[i++] = arg_system ? "--system" : "--user"; args[i++] = "--deserialize"; args[i++] = sfd; args[i++] = NULL; @@ -2104,7 +2111,7 @@ finish: assert(pos < ELEMENTSOF(command_line)); - if (arm_reboot_watchdog && arg_shutdown_watchdog > 0) { + if (arm_reboot_watchdog && arg_shutdown_watchdog > 0 && arg_shutdown_watchdog != USEC_INFINITY) { char *e; /* If we reboot let's set the shutdown diff --git a/src/core/manager.c b/src/core/manager.c index f36cf5e320..7838f56fd2 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -49,6 +49,7 @@ #include "dbus-manager.h" #include "dbus-unit.h" #include "dbus.h" +#include "dirent-util.h" #include "env-util.h" #include "escape.h" #include "exit-status.h" @@ -63,6 +64,7 @@ #include "manager.h" #include "missing.h" #include "mkdir.h" +#include "mkdir.h" #include "parse-util.h" #include "path-lookup.h" #include "path-util.h" @@ -85,6 +87,7 @@ #include "watchdog.h" #define NOTIFY_RCVBUF_SIZE (8*1024*1024) +#define CGROUPS_AGENT_RCVBUF_SIZE (8*1024*1024) /* Initial delay and the interval for printing status messages about running jobs */ #define JOBS_IN_PROGRESS_WAIT_USEC (5*USEC_PER_SEC) @@ -92,13 +95,13 @@ #define JOBS_IN_PROGRESS_PERIOD_DIVISOR 3 static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); +static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata); static int manager_dispatch_run_queue(sd_event_source *source, void *userdata); static int manager_run_generators(Manager *m); -static void manager_undo_generators(Manager *m); static void manager_watch_jobs_in_progress(Manager *m) { usec_t next; @@ -483,15 +486,15 @@ static int manager_setup_signals(Manager *m) { (void) sd_event_source_set_description(m->signal_event_source, "manager-signal"); - /* Process signals a bit earlier than the rest of things, but - * later than notify_fd processing, so that the notify - * processing can still figure out to which process/service a - * message belongs, before we reap the process. */ - r = sd_event_source_set_priority(m->signal_event_source, SD_EVENT_PRIORITY_NORMAL-5); + /* Process signals a bit earlier than the rest of things, but later than notify_fd processing, so that the + * notify processing can still figure out to which process/service a message belongs, before we reap the + * process. Also, process this before handling cgroup notifications, so that we always collect child exit + * status information before detecting that there's no process in a cgroup. */ + r = sd_event_source_set_priority(m->signal_event_source, SD_EVENT_PRIORITY_NORMAL-6); if (r < 0) return r; - if (m->running_as == MANAGER_SYSTEM) + if (MANAGER_IS_SYSTEM(m)) return enable_special_signals(m); return 0; @@ -518,7 +521,7 @@ static void manager_clean_environment(Manager *m) { static int manager_default_environment(Manager *m) { assert(m); - if (m->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(m)) { /* The system manager always starts with a clean * environment for its children. It does not import * the kernel or the parents exported variables. @@ -547,52 +550,45 @@ static int manager_default_environment(Manager *m) { } -int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) { - - static const char * const unit_log_fields[_MANAGER_RUNNING_AS_MAX] = { - [MANAGER_SYSTEM] = "UNIT=", - [MANAGER_USER] = "USER_UNIT=", - }; - - static const char * const unit_log_format_strings[_MANAGER_RUNNING_AS_MAX] = { - [MANAGER_SYSTEM] = "UNIT=%s", - [MANAGER_USER] = "USER_UNIT=%s", - }; - +int manager_new(UnitFileScope scope, bool test_run, Manager **_m) { Manager *m; int r; assert(_m); - assert(running_as >= 0); - assert(running_as < _MANAGER_RUNNING_AS_MAX); + assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER)); m = new0(Manager, 1); if (!m) return -ENOMEM; -#ifdef ENABLE_EFI - if (running_as == MANAGER_SYSTEM && detect_container() <= 0) - boot_timestamps(&m->userspace_timestamp, &m->firmware_timestamp, &m->loader_timestamp); -#endif - - m->running_as = running_as; + m->unit_file_scope = scope; m->exit_code = _MANAGER_EXIT_CODE_INVALID; m->default_timer_accuracy_usec = USEC_PER_MINUTE; m->default_tasks_accounting = true; m->default_tasks_max = UINT64_C(512); +#ifdef ENABLE_EFI + if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) + boot_timestamps(&m->userspace_timestamp, &m->firmware_timestamp, &m->loader_timestamp); +#endif + /* Prepare log fields we can use for structured logging */ - m->unit_log_field = unit_log_fields[running_as]; - m->unit_log_format_string = unit_log_format_strings[running_as]; + if (MANAGER_IS_SYSTEM(m)) { + m->unit_log_field = "UNIT="; + m->unit_log_format_string = "UNIT=%s"; + } else { + m->unit_log_field = "USER_UNIT="; + m->unit_log_format_string = "USER_UNIT=%s"; + } m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1; - m->pin_cgroupfs_fd = m->notify_fd = m->signal_fd = m->time_change_fd = - m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->cgroup_inotify_fd = -1; + m->pin_cgroupfs_fd = m->notify_fd = m->cgroups_agent_fd = m->signal_fd = m->time_change_fd = + m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->cgroup_inotify_fd = + m->ask_password_inotify_fd = -1; m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ - m->ask_password_inotify_fd = -1; m->have_ask_password = -EINVAL; /* we don't know */ m->first_boot = -1; @@ -683,6 +679,7 @@ static int manager_setup_notify(Manager *m) { .sa.sa_family = AF_UNIX, }; static const int one = 1; + const char *e; /* First free all secondary fields */ m->notify_socket = mfree(m->notify_socket); @@ -694,19 +691,13 @@ static int manager_setup_notify(Manager *m) { fd_inc_rcvbuf(fd, NOTIFY_RCVBUF_SIZE); - if (m->running_as == MANAGER_SYSTEM) - m->notify_socket = strdup("/run/systemd/notify"); - else { - const char *e; - - e = getenv("XDG_RUNTIME_DIR"); - if (!e) { - log_error_errno(errno, "XDG_RUNTIME_DIR is not set: %m"); - return -EINVAL; - } - - m->notify_socket = strappend(e, "/systemd/notify"); + e = manager_get_runtime_prefix(m); + if (!e) { + log_error("Failed to determine runtime prefix."); + return -EINVAL; } + + m->notify_socket = strappend(e, "/systemd/notify"); if (!m->notify_socket) return log_oom(); @@ -714,7 +705,7 @@ static int manager_setup_notify(Manager *m) { (void) unlink(m->notify_socket); strncpy(sa.un.sun_path, m->notify_socket, sizeof(sa.un.sun_path)-1); - r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)); + r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); @@ -733,8 +724,8 @@ static int manager_setup_notify(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to allocate notify event source: %m"); - /* Process signals a bit earlier than SIGCHLD, so that we can - * still identify to which service an exit message belongs */ + /* Process notification messages a bit earlier than SIGCHLD, so that we can still identify to which + * service an exit message belongs. */ r = sd_event_source_set_priority(m->notify_event_source, SD_EVENT_PRIORITY_NORMAL-7); if (r < 0) return log_error_errno(r, "Failed to set priority of notify event source: %m"); @@ -745,6 +736,79 @@ static int manager_setup_notify(Manager *m) { return 0; } +static int manager_setup_cgroups_agent(Manager *m) { + + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/cgroups-agent", + }; + int r; + + /* This creates a listening socket we receive cgroups agent messages on. We do not use D-Bus for delivering + * these messages from the cgroups agent binary to PID 1, as the cgroups agent binary is very short-living, and + * each instance of it needs a new D-Bus connection. Since D-Bus connections are SOCK_STREAM/AF_UNIX, on + * overloaded systems the backlog of the D-Bus socket becomes relevant, as not more than the configured number + * of D-Bus connections may be queued until the kernel will start dropping further incoming connections, + * possibly resulting in lost cgroups agent messages. To avoid this, we'll use a private SOCK_DGRAM/AF_UNIX + * socket, where no backlog is relevant as communication may take place without an actual connect() cycle, and + * we thus won't lose messages. + * + * Note that PID 1 will forward the agent message to system bus, so that the user systemd instance may listen + * to it. The system instance hence listens on this special socket, but the user instances listen on the system + * bus for these messages. */ + + if (m->test_run) + return 0; + + if (!MANAGER_IS_SYSTEM(m)) + return 0; + + if (cg_unified() > 0) /* We don't need this anymore on the unified hierarchy */ + return 0; + + if (m->cgroups_agent_fd < 0) { + _cleanup_close_ int fd = -1; + + /* First free all secondary fields */ + m->cgroups_agent_event_source = sd_event_source_unref(m->cgroups_agent_event_source); + + fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (fd < 0) + return log_error_errno(errno, "Failed to allocate cgroups agent socket: %m"); + + fd_inc_rcvbuf(fd, CGROUPS_AGENT_RCVBUF_SIZE); + + (void) unlink(sa.un.sun_path); + + /* Only allow root to connect to this socket */ + RUN_WITH_UMASK(0077) + r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); + if (r < 0) + return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); + + m->cgroups_agent_fd = fd; + fd = -1; + } + + if (!m->cgroups_agent_event_source) { + r = sd_event_add_io(m->event, &m->cgroups_agent_event_source, m->cgroups_agent_fd, EPOLLIN, manager_dispatch_cgroups_agent_fd, m); + if (r < 0) + return log_error_errno(r, "Failed to allocate cgroups agent event source: %m"); + + /* Process cgroups notifications early, but after having processed service notification messages or + * SIGCHLD signals, so that a cgroup running empty is always just the last safety net of notification, + * and we collected the metadata the notification and SIGCHLD stuff offers first. Also see handling of + * cgroup inotify for the unified cgroup stuff. */ + r = sd_event_source_set_priority(m->cgroups_agent_event_source, SD_EVENT_PRIORITY_NORMAL-5); + if (r < 0) + return log_error_errno(r, "Failed to set priority of cgroups agent event source: %m"); + + (void) sd_event_source_set_description(m->cgroups_agent_event_source, "manager-cgroups-agent"); + } + + return 0; +} + static int manager_setup_kdbus(Manager *m) { _cleanup_free_ char *p = NULL; @@ -756,8 +820,8 @@ static int manager_setup_kdbus(Manager *m) { return -ESOCKTNOSUPPORT; m->kdbus_fd = bus_kernel_create_bus( - m->running_as == MANAGER_SYSTEM ? "system" : "user", - m->running_as == MANAGER_SYSTEM, &p); + MANAGER_IS_SYSTEM(m) ? "system" : "user", + MANAGER_IS_SYSTEM(m), &p); if (m->kdbus_fd < 0) return log_debug_errno(m->kdbus_fd, "Failed to set up kdbus: %m"); @@ -778,7 +842,7 @@ static int manager_connect_bus(Manager *m, bool reexecuting) { try_bus_connect = m->kdbus_fd >= 0 || reexecuting || - (m->running_as == MANAGER_USER && getenv("DBUS_SESSION_BUS_ADDRESS")); + (MANAGER_IS_USER(m) && getenv("DBUS_SESSION_BUS_ADDRESS")); /* Try to connect to the buses, if possible. */ return bus_init(m, try_bus_connect); @@ -940,7 +1004,7 @@ Manager* manager_free(Manager *m) { * around */ manager_shutdown_cgroup(m, m->exit_code != MANAGER_REEXECUTE); - manager_undo_generators(m); + lookup_paths_flush_generator(&m->lookup_paths); bus_done(m); @@ -955,12 +1019,14 @@ Manager* manager_free(Manager *m) { sd_event_source_unref(m->signal_event_source); sd_event_source_unref(m->notify_event_source); + sd_event_source_unref(m->cgroups_agent_event_source); sd_event_source_unref(m->time_change_event_source); sd_event_source_unref(m->jobs_in_progress_event_source); sd_event_source_unref(m->run_queue_event_source); safe_close(m->signal_fd); safe_close(m->notify_fd); + safe_close(m->cgroups_agent_fd); safe_close(m->time_change_fd); safe_close(m->kdbus_fd); @@ -1037,7 +1103,6 @@ static void manager_coldplug(Manager *m) { static void manager_build_unit_path_cache(Manager *m) { char **i; - _cleanup_closedir_ DIR *d = NULL; int r; assert(m); @@ -1046,29 +1111,27 @@ static void manager_build_unit_path_cache(Manager *m) { m->unit_path_cache = set_new(&string_hash_ops); if (!m->unit_path_cache) { - log_error("Failed to allocate unit path cache."); - return; + r = -ENOMEM; + goto fail; } /* This simply builds a list of files we know exist, so that * we don't always have to go to disk */ - STRV_FOREACH(i, m->lookup_paths.unit_path) { + STRV_FOREACH(i, m->lookup_paths.search_path) { + _cleanup_closedir_ DIR *d = NULL; struct dirent *de; d = opendir(*i); if (!d) { if (errno != ENOENT) - log_error_errno(errno, "Failed to open directory %s: %m", *i); + log_warning_errno(errno, "Failed to open directory %s, ignoring: %m", *i); continue; } - while ((de = readdir(d))) { + FOREACH_DIRENT(de, d, r = -errno; goto fail) { char *p; - if (hidden_file(de->d_name)) - continue; - p = strjoin(streq(*i, "/") ? "" : *i, "/", de->d_name, NULL); if (!p) { r = -ENOMEM; @@ -1079,20 +1142,15 @@ static void manager_build_unit_path_cache(Manager *m) { if (r < 0) goto fail; } - - d = safe_closedir(d); } return; fail: - log_error_errno(r, "Failed to build unit path cache: %m"); - - set_free_free(m->unit_path_cache); - m->unit_path_cache = NULL; + log_warning_errno(r, "Failed to build unit path cache, proceeding without: %m"); + m->unit_path_cache = set_free_free(m->unit_path_cache); } - static void manager_distribute_fds(Manager *m, FDSet *fds) { Iterator i; Unit *u; @@ -1116,28 +1174,29 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { assert(m); - dual_timestamp_get(&m->generators_start_timestamp); - r = manager_run_generators(m); - dual_timestamp_get(&m->generators_finish_timestamp); + r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL); + if (r < 0) + return r; + + /* Make sure the transient directory always exists, so that it remains in the search path */ + r = mkdir_p_label(m->lookup_paths.transient, 0755); if (r < 0) return r; - r = lookup_paths_init( - &m->lookup_paths, m->running_as, true, - NULL, - m->generator_unit_path, - m->generator_unit_path_early, - m->generator_unit_path_late); + dual_timestamp_get(&m->generators_start_timestamp); + r = manager_run_generators(m); + dual_timestamp_get(&m->generators_finish_timestamp); if (r < 0) return r; + lookup_paths_reduce(&m->lookup_paths); manager_build_unit_path_cache(m); /* If we will deserialize make sure that during enumeration * this is already known, so we increase the counter here * already */ if (serialization) - m->n_reloading ++; + m->n_reloading++; /* First, enumerate what we can from all config files */ dual_timestamp_get(&m->units_load_start_timestamp); @@ -1160,6 +1219,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { if (q < 0 && r == 0) r = q; + q = manager_setup_cgroups_agent(m); + if (q < 0 && r == 0) + r = q; + /* We might have deserialized the kdbus control fd, but if we * didn't, then let's create the bus now. */ manager_setup_kdbus(m); @@ -1171,7 +1234,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { if (serialization) { assert(m->n_reloading > 0); - m->n_reloading --; + m->n_reloading--; /* Let's wait for the UnitNew/JobNew messages being * sent, before we notify that the reload is @@ -1333,8 +1396,12 @@ int manager_load_unit_prepare( t = unit_name_to_type(name); - if (t == _UNIT_TYPE_INVALID || !unit_name_is_valid(name, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) + if (t == _UNIT_TYPE_INVALID || !unit_name_is_valid(name, UNIT_NAME_PLAIN|UNIT_NAME_INSTANCE)) { + if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) + return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is missing the instance name.", name); + return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is not valid.", name); + } ret = manager_get_unit(m, name); if (ret) { @@ -1427,7 +1494,7 @@ void manager_clear_jobs(Manager *m) { while ((j = hashmap_first(m->jobs))) /* No need to recurse. We're cancelling all jobs. */ - job_finish_and_invalidate(j, JOB_CANCELED, false); + job_finish_and_invalidate(j, JOB_CANCELED, false, false); } static int manager_dispatch_run_queue(sd_event_source *source, void *userdata) { @@ -1493,6 +1560,35 @@ static unsigned manager_dispatch_dbus_queue(Manager *m) { return n; } +static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + Manager *m = userdata; + char buf[PATH_MAX+1]; + ssize_t n; + + n = recv(fd, buf, sizeof(buf), 0); + if (n < 0) + return log_error_errno(errno, "Failed to read cgroups agent message: %m"); + if (n == 0) { + log_error("Got zero-length cgroups agent message, ignoring."); + return 0; + } + if ((size_t) n >= sizeof(buf)) { + log_error("Got overly long cgroups agent message, ignoring."); + return 0; + } + + if (memchr(buf, 0, n)) { + log_error("Got cgroups agent message with embedded NUL byte, ignoring."); + return 0; + } + buf[n] = 0; + + manager_notify_cgroup_empty(m, buf); + bus_forward_agent_released(m, buf); + + return 0; +} + static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, const char *buf, size_t n, FDSet *fds) { _cleanup_strv_free_ char **tags = NULL; @@ -1631,7 +1727,9 @@ static void invoke_sigchld_event(Manager *m, Unit *u, const siginfo_t *si) { log_unit_debug(u, "Child "PID_FMT" belongs to %s", si->si_pid, u->id); unit_unwatch_pid(u, si->si_pid); - UNIT_VTABLE(u)->sigchld_event(u, si->si_pid, si->si_code, si->si_status); + + if (UNIT_VTABLE(u)->sigchld_event) + UNIT_VTABLE(u)->sigchld_event(u, si->si_pid, si->si_code, si->si_status); } static int manager_dispatch_sigchld(Manager *m) { @@ -1738,7 +1836,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t } log_received_signal(sfsi.ssi_signo == SIGCHLD || - (sfsi.ssi_signo == SIGTERM && m->running_as == MANAGER_USER) + (sfsi.ssi_signo == SIGTERM && MANAGER_IS_USER(m)) ? LOG_DEBUG : LOG_INFO, &sfsi); @@ -1749,7 +1847,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t break; case SIGTERM: - if (m->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(m)) { /* This is for compatibility with the * original sysvinit */ m->exit_code = MANAGER_REEXECUTE; @@ -1759,7 +1857,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t /* Fall through */ case SIGINT: - if (m->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(m)) { /* If the user presses C-A-D more than * 7 times within 2s, we reboot @@ -1785,14 +1883,14 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t break; case SIGWINCH: - if (m->running_as == MANAGER_SYSTEM) + if (MANAGER_IS_SYSTEM(m)) manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE); /* This is a nop on non-init */ break; case SIGPWR: - if (m->running_as == MANAGER_SYSTEM) + if (MANAGER_IS_SYSTEM(m)) manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE); /* This is a nop on non-init */ @@ -1900,7 +1998,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t break; case 24: - if (m->running_as == MANAGER_USER) { + if (MANAGER_IS_USER(m)) { m->exit_code = MANAGER_EXIT; return 0; } @@ -2016,7 +2114,7 @@ int manager_loop(Manager *m) { while (m->exit_code == MANAGER_OK) { usec_t wait_usec; - if (m->runtime_watchdog > 0 && m->running_as == MANAGER_SYSTEM) + if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m)) watchdog_ping(); if (!ratelimit_test(&rl)) { @@ -2041,7 +2139,7 @@ int manager_loop(Manager *m) { continue; /* Sleep for half the watchdog time */ - if (m->runtime_watchdog > 0 && m->running_as == MANAGER_SYSTEM) { + if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m)) { wait_usec = m->runtime_watchdog / 2; if (wait_usec <= 0) wait_usec = 1; @@ -2112,7 +2210,7 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { const char *msg; int audit_fd, r; - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return; audit_fd = get_audit_fd(); @@ -2121,7 +2219,7 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { /* Don't generate audit events if the service was already * started and we're just deserializing */ - if (m->n_reloading > 0) + if (MANAGER_IS_RELOADING(m)) return; if (u->type != UNIT_SERVICE) @@ -2147,18 +2245,17 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { } void manager_send_unit_plymouth(Manager *m, Unit *u) { - union sockaddr_union sa = PLYMOUTH_SOCKET; - - int n = 0; + static const union sockaddr_union sa = PLYMOUTH_SOCKET; _cleanup_free_ char *message = NULL; _cleanup_close_ int fd = -1; + int n = 0; /* Don't generate plymouth events if the service was already * started and we're just deserializing */ - if (m->n_reloading > 0) + if (MANAGER_IS_RELOADING(m)) return; - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return; if (detect_container() > 0) @@ -2177,7 +2274,7 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) { return; } - if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) { + if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { if (!IN_SET(errno, EPIPE, EAGAIN, ENOENT, ECONNREFUSED, ECONNRESET, ECONNABORTED)) log_error_errno(errno, "connect() failed: %m"); @@ -2202,8 +2299,8 @@ int manager_open_serialization(Manager *m, FILE **_f) { assert(_f); - path = m->running_as == MANAGER_SYSTEM ? "/run/systemd" : "/tmp"; - fd = open_tmpfile(path, O_RDWR|O_CLOEXEC); + path = MANAGER_IS_SYSTEM(m) ? "/run/systemd" : "/tmp"; + fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC); if (fd < 0) return -errno; @@ -2231,7 +2328,7 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { assert(f); assert(fds); - m->n_reloading ++; + m->n_reloading++; fprintf(f, "current-job-id=%"PRIu32"\n", m->current_job_id); fprintf(f, "taint-usr=%s\n", yes_no(m->taint_usr)); @@ -2277,6 +2374,16 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { fprintf(f, "notify-socket=%s\n", m->notify_socket); } + if (m->cgroups_agent_fd >= 0) { + int copy; + + copy = fdset_put_dup(fds, m->cgroups_agent_fd); + if (copy < 0) + return copy; + + fprintf(f, "cgroups-agent-fd=%i\n", copy); + } + if (m->kdbus_fd >= 0) { int copy; @@ -2301,13 +2408,13 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { r = unit_serialize(u, f, fds, !switching_root); if (r < 0) { - m->n_reloading --; + m->n_reloading--; return r; } } assert(m->n_reloading > 0); - m->n_reloading --; + m->n_reloading--; if (ferror(f)) return -EIO; @@ -2327,7 +2434,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { log_debug("Deserializing state..."); - m->n_reloading ++; + m->n_reloading++; for (;;) { char line[LINE_MAX], *l; @@ -2444,6 +2551,17 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { free(m->notify_socket); m->notify_socket = n; + } else if (startswith(l, "cgroups-agent-fd=")) { + int fd; + + if (safe_atoi(l + 17, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) + log_debug("Failed to parse cgroups agent fd: %s", l + 10); + else { + m->cgroups_agent_event_source = sd_event_source_unref(m->cgroups_agent_event_source); + safe_close(m->cgroups_agent_fd); + m->cgroups_agent_fd = fdset_remove(fds, fd); + } + } else if (startswith(l, "kdbus-fd=")) { int fd; @@ -2495,7 +2613,7 @@ finish: r = -EIO; assert(m->n_reloading > 0); - m->n_reloading --; + m->n_reloading--; return r; } @@ -2511,45 +2629,41 @@ int manager_reload(Manager *m) { if (r < 0) return r; - m->n_reloading ++; + m->n_reloading++; bus_manager_send_reloading(m, true); fds = fdset_new(); if (!fds) { - m->n_reloading --; + m->n_reloading--; return -ENOMEM; } r = manager_serialize(m, f, fds, false); if (r < 0) { - m->n_reloading --; + m->n_reloading--; return r; } if (fseeko(f, 0, SEEK_SET) < 0) { - m->n_reloading --; + m->n_reloading--; return -errno; } /* From here on there is no way back. */ manager_clear_jobs_and_units(m); - manager_undo_generators(m); + lookup_paths_flush_generator(&m->lookup_paths); lookup_paths_free(&m->lookup_paths); - /* Find new unit paths */ - q = manager_run_generators(m); + q = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL); if (q < 0 && r >= 0) r = q; - q = lookup_paths_init( - &m->lookup_paths, m->running_as, true, - NULL, - m->generator_unit_path, - m->generator_unit_path_early, - m->generator_unit_path_late); + /* Find new unit paths */ + q = manager_run_generators(m); if (q < 0 && r >= 0) r = q; + lookup_paths_reduce(&m->lookup_paths); manager_build_unit_path_cache(m); /* First, enumerate what we can from all config files */ @@ -2568,6 +2682,10 @@ int manager_reload(Manager *m) { if (q < 0 && r >= 0) r = q; + q = manager_setup_cgroups_agent(m); + if (q < 0 && r >= 0) + r = q; + /* Third, fire things up! */ manager_coldplug(m); @@ -2583,12 +2701,6 @@ int manager_reload(Manager *m) { return r; } -bool manager_is_reloading_or_reexecuting(Manager *m) { - assert(m); - - return m->n_reloading != 0; -} - void manager_reset_failed(Manager *m) { Unit *u; Iterator i; @@ -2620,7 +2732,7 @@ static void manager_notify_finished(Manager *m) { if (m->test_run) return; - if (m->running_as == MANAGER_SYSTEM && detect_container() <= 0) { + if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) { /* Note that m->kernel_usec.monotonic is always at 0, * and m->firmware_usec.monotonic and @@ -2685,7 +2797,7 @@ static void manager_notify_finished(Manager *m) { void manager_check_finished(Manager *m) { assert(m); - if (m->n_reloading > 0) + if (MANAGER_IS_RELOADING(m)) return; /* Verify that we are actually running currently. Initially @@ -2726,77 +2838,6 @@ void manager_check_finished(Manager *m) { manager_invalidate_startup_units(m); } -static int create_generator_dir(Manager *m, char **generator, const char *name) { - char *p; - int r; - - assert(m); - assert(generator); - assert(name); - - if (*generator) - return 0; - - if (m->running_as == MANAGER_SYSTEM && getpid() == 1) { - /* systemd --system, not running --test */ - - p = strappend("/run/systemd/", name); - if (!p) - return log_oom(); - - r = mkdir_p_label(p, 0755); - if (r < 0) { - log_error_errno(r, "Failed to create generator directory %s: %m", p); - free(p); - return r; - } - } else if (m->running_as == MANAGER_USER) { - const char *s = NULL; - - s = getenv("XDG_RUNTIME_DIR"); - if (!s) - return -EINVAL; - p = strjoin(s, "/systemd/", name, NULL); - if (!p) - return log_oom(); - - r = mkdir_p_label(p, 0755); - if (r < 0) { - log_error_errno(r, "Failed to create generator directory %s: %m", p); - free(p); - return r; - } - } else { - /* systemd --system --test */ - - p = strjoin("/tmp/systemd-", name, ".XXXXXX", NULL); - if (!p) - return log_oom(); - - if (!mkdtemp(p)) { - log_error_errno(errno, "Failed to create generator directory %s: %m", p); - free(p); - return -errno; - } - } - - *generator = p; - return 0; -} - -static void trim_generator_dir(Manager *m, char **generator) { - assert(m); - assert(generator); - - if (!*generator) - return; - - if (rmdir(*generator) >= 0) - *generator = mfree(*generator); - - return; -} - static int manager_run_generators(Manager *m) { _cleanup_strv_free_ char **paths = NULL; const char *argv[5]; @@ -2808,71 +2849,40 @@ static int manager_run_generators(Manager *m) { if (m->test_run) return 0; - paths = generator_paths(m->running_as); + paths = generator_binary_paths(m->unit_file_scope); if (!paths) return log_oom(); /* Optimize by skipping the whole process by not creating output directories * if no generators are found. */ STRV_FOREACH(path, paths) { - r = access(*path, F_OK); - if (r == 0) + if (access(*path, F_OK) >= 0) goto found; if (errno != ENOENT) log_warning_errno(errno, "Failed to open generator directory %s: %m", *path); } + return 0; found: - r = create_generator_dir(m, &m->generator_unit_path, "generator"); - if (r < 0) - goto finish; - - r = create_generator_dir(m, &m->generator_unit_path_early, "generator.early"); - if (r < 0) - goto finish; - - r = create_generator_dir(m, &m->generator_unit_path_late, "generator.late"); + r = lookup_paths_mkdir_generator(&m->lookup_paths); if (r < 0) goto finish; argv[0] = NULL; /* Leave this empty, execute_directory() will fill something in */ - argv[1] = m->generator_unit_path; - argv[2] = m->generator_unit_path_early; - argv[3] = m->generator_unit_path_late; + argv[1] = m->lookup_paths.generator; + argv[2] = m->lookup_paths.generator_early; + argv[3] = m->lookup_paths.generator_late; argv[4] = NULL; RUN_WITH_UMASK(0022) execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, (char**) argv); finish: - trim_generator_dir(m, &m->generator_unit_path); - trim_generator_dir(m, &m->generator_unit_path_early); - trim_generator_dir(m, &m->generator_unit_path_late); + lookup_paths_trim_generator(&m->lookup_paths); return r; } -static void remove_generator_dir(Manager *m, char **generator) { - assert(m); - assert(generator); - - if (!*generator) - return; - - strv_remove(m->lookup_paths.unit_path, *generator); - (void) rm_rf(*generator, REMOVE_ROOT); - - *generator = mfree(*generator); -} - -static void manager_undo_generators(Manager *m) { - assert(m); - - remove_generator_dir(m, &m->generator_unit_path); - remove_generator_dir(m, &m->generator_unit_path_early); - remove_generator_dir(m, &m->generator_unit_path_late); -} - int manager_environment_add(Manager *m, char **minus, char **plus) { char **a = NULL, **b = NULL, **l; assert(m); @@ -2935,7 +2945,7 @@ void manager_recheck_journal(Manager *m) { assert(m); - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return; u = manager_get_unit(m, SPECIAL_JOURNALD_SOCKET); @@ -2959,7 +2969,7 @@ void manager_set_show_status(Manager *m, ShowStatus mode) { assert(m); assert(IN_SET(mode, SHOW_STATUS_AUTO, SHOW_STATUS_NO, SHOW_STATUS_YES, SHOW_STATUS_TEMPORARY)); - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return; if (m->show_status != mode) @@ -2976,7 +2986,7 @@ void manager_set_show_status(Manager *m, ShowStatus mode) { static bool manager_get_show_status(Manager *m, StatusType type) { assert(m); - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return false; if (m->no_console_output) @@ -2998,7 +3008,7 @@ static bool manager_get_show_status(Manager *m, StatusType type) { void manager_set_first_boot(Manager *m, bool b) { assert(m); - if (m->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(m)) return; if (m->first_boot != (int) b) { @@ -3044,7 +3054,7 @@ Set *manager_get_units_requiring_mounts_for(Manager *m, const char *path) { const char *manager_get_runtime_prefix(Manager *m) { assert(m); - return m->running_as == MANAGER_SYSTEM ? + return MANAGER_IS_SYSTEM(m) ? "/run" : getenv("XDG_RUNTIME_DIR"); } diff --git a/src/core/manager.h b/src/core/manager.h index 9803f73129..6ed15c1a41 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -132,6 +132,9 @@ struct Manager { int notify_fd; sd_event_source *notify_event_source; + int cgroups_agent_fd; + sd_event_source *cgroups_agent_event_source; + int signal_fd; sd_event_source *signal_event_source; @@ -140,6 +143,7 @@ struct Manager { sd_event_source *jobs_in_progress_event_source; + UnitFileScope unit_file_scope; LookupPaths lookup_paths; Set *unit_path_cache; @@ -162,10 +166,6 @@ struct Manager { dual_timestamp units_load_start_timestamp; dual_timestamp units_load_finish_timestamp; - char *generator_unit_path; - char *generator_unit_path_early; - char *generator_unit_path_late; - struct udev* udev; /* Data specific to the device subsystem */ @@ -228,7 +228,6 @@ struct Manager { unsigned n_in_gc_queue; /* Flags */ - ManagerRunningAs running_as; ManagerExitCode exit_code:5; bool dispatching_load_queue:1; @@ -256,6 +255,7 @@ struct Manager { bool default_cpu_accounting; bool default_memory_accounting; + bool default_io_accounting; bool default_blockio_accounting; bool default_tasks_accounting; @@ -304,10 +304,15 @@ struct Manager { const char *unit_log_field; const char *unit_log_format_string; - int first_boot; + int first_boot; /* tri-state */ }; -int manager_new(ManagerRunningAs running_as, bool test_run, Manager **m); +#define MANAGER_IS_SYSTEM(m) ((m)->unit_file_scope == UNIT_FILE_SYSTEM) +#define MANAGER_IS_USER(m) ((m)->unit_file_scope != UNIT_FILE_SYSTEM) + +#define MANAGER_IS_RELOADING(m) ((m)->n_reloading > 0) + +int manager_new(UnitFileScope scope, bool test_run, Manager **m); Manager* manager_free(Manager *m); void manager_enumerate(Manager *m); @@ -345,8 +350,6 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds); int manager_reload(Manager *m); -bool manager_is_reloading_or_reexecuting(Manager *m) _pure_; - void manager_reset_failed(Manager *m); void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success); diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c index de1a361cc4..40fc548b42 100644 --- a/src/core/mount-setup.c +++ b/src/core/mount-setup.c @@ -94,7 +94,7 @@ static const MountPoint mount_table[] = { #endif { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, NULL, MNT_FATAL|MNT_IN_CONTAINER }, - { "cgroup", "/sys/fs/cgroup", "cgroup", "__DEVEL__sane_behavior", MS_NOSUID|MS_NOEXEC|MS_NODEV, + { "cgroup", "/sys/fs/cgroup", "cgroup2", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, cg_is_unified_wanted, MNT_FATAL|MNT_IN_CONTAINER }, { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER }, @@ -375,6 +375,7 @@ int mount_setup(bool loaded_policy) { before_relabel = now(CLOCK_MONOTONIC); nftw("/dev", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); + nftw("/dev/shm", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); nftw("/run", nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); after_relabel = now(CLOCK_MONOTONIC); diff --git a/src/core/mount.c b/src/core/mount.c index de1075d3a0..665a60bb55 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -86,6 +86,15 @@ static bool mount_is_network(const MountParameters *p) { return mount_needs_network(p->options, p->fstype); } +static bool mount_is_loop(const MountParameters *p) { + assert(p); + + if (fstab_test_option(p->options, "loop\0")) + return true; + + return false; +} + static bool mount_is_bind(const MountParameters *p) { assert(p); @@ -104,6 +113,28 @@ static bool mount_is_auto(const MountParameters *p) { return !fstab_test_option(p->options, "noauto\0"); } +static bool mount_is_automount(const MountParameters *p) { + assert(p); + + return fstab_test_option(p->options, + "comment=systemd.automount\0" + "x-systemd.automount\0"); +} + +static bool mount_state_active(MountState state) { + return IN_SET(state, + MOUNT_MOUNTING, + MOUNT_MOUNTING_DONE, + MOUNT_REMOUNTING, + MOUNT_UNMOUNTING, + MOUNT_MOUNTING_SIGTERM, + MOUNT_MOUNTING_SIGKILL, + MOUNT_UNMOUNTING_SIGTERM, + MOUNT_UNMOUNTING_SIGKILL, + MOUNT_REMOUNTING_SIGTERM, + MOUNT_REMOUNTING_SIGKILL); +} + static bool needs_quota(const MountParameters *p) { assert(p); @@ -261,12 +292,12 @@ static int mount_add_mount_links(Mount *m) { } /* Adds in links to other mount points that might be needed - * for the source path (if this is a bind mount) to be + * for the source path (if this is a bind mount or a loop mount) to be * available. */ pm = get_mount_parameters_fragment(m); if (pm && pm->what && path_is_absolute(pm->what) && - !mount_is_network(pm)) { + (mount_is_bind(pm) || mount_is_loop(pm) || !mount_is_network(pm))) { r = unit_require_mounts_for(UNIT(m), pm->what); if (r < 0) @@ -328,7 +359,7 @@ static int mount_add_device_links(Mount *m) { if (path_equal(m->where, "/")) return 0; - if (mount_is_auto(p) && UNIT(m)->manager->running_as == MANAGER_SYSTEM) + if (mount_is_auto(p) && !mount_is_automount(p) && MANAGER_IS_SYSTEM(UNIT(m)->manager)) device_wants_mount = true; r = unit_add_node_link(UNIT(m), p->what, device_wants_mount, m->from_fragment ? UNIT_BINDS_TO : UNIT_REQUIRES); @@ -344,7 +375,7 @@ static int mount_add_quota_links(Mount *m) { assert(m); - if (UNIT(m)->manager->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(UNIT(m)->manager)) return 0; p = get_mount_parameters_fragment(m); @@ -368,8 +399,8 @@ static int mount_add_quota_links(Mount *m) { static bool should_umount(Mount *m) { MountParameters *p; - if (path_equal(m->where, "/") || - path_equal(m->where, "/usr")) + if (PATH_IN_SET(m->where, "/", "/usr") || + path_startswith(m->where, "/run/initramfs")) return false; p = get_mount_parameters(m); @@ -390,16 +421,17 @@ static int mount_add_default_dependencies(Mount *m) { if (!UNIT(m)->default_dependencies) return 0; - if (UNIT(m)->manager->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(UNIT(m)->manager)) return 0; - /* We do not add any default dependencies to / and /usr, since - * they are guaranteed to stay mounted the whole time, since - * our system is on it. Also, don't bother with anything - * mounted below virtual file systems, it's also going to be - * virtual, and hence not worth the effort. */ - if (path_equal(m->where, "/") || - path_equal(m->where, "/usr") || + /* We do not add any default dependencies to /, /usr or + * /run/initramfs/, since they are guaranteed to stay + * mounted the whole time, since our system is on it. + * Also, don't bother with anything mounted below virtual + * file systems, it's also going to be virtual, and hence + * not worth the effort. */ + if (PATH_IN_SET(m->where, "/", "/usr") || + path_startswith(m->where, "/run/initramfs") || path_startswith(m->where, "/proc") || path_startswith(m->where, "/sys") || path_startswith(m->where, "/dev")) @@ -566,23 +598,6 @@ static int mount_load(Unit *u) { return mount_verify(m); } -static int mount_notify_automount(Mount *m, MountState old_state, MountState state) { - Unit *p; - int r; - Iterator i; - - assert(m); - - SET_FOREACH(p, UNIT(m)->dependencies[UNIT_TRIGGERED_BY], i) - if (p->type == UNIT_AUTOMOUNT) { - r = automount_update_mount(AUTOMOUNT(p), old_state, state); - if (r < 0) - return r; - } - - return 0; -} - static void mount_set_state(Mount *m, MountState state) { MountState old_state; assert(m); @@ -590,24 +605,13 @@ static void mount_set_state(Mount *m, MountState state) { old_state = m->state; m->state = state; - if (state != MOUNT_MOUNTING && - state != MOUNT_MOUNTING_DONE && - state != MOUNT_REMOUNTING && - state != MOUNT_UNMOUNTING && - state != MOUNT_MOUNTING_SIGTERM && - state != MOUNT_MOUNTING_SIGKILL && - state != MOUNT_UNMOUNTING_SIGTERM && - state != MOUNT_UNMOUNTING_SIGKILL && - state != MOUNT_REMOUNTING_SIGTERM && - state != MOUNT_REMOUNTING_SIGKILL) { + if (!mount_state_active(state)) { m->timer_event_source = sd_event_source_unref(m->timer_event_source); mount_unwatch_control_pid(m); m->control_command = NULL; m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID; } - mount_notify_automount(m, old_state, state); - if (state != old_state) log_unit_debug(UNIT(m), "Changed %s -> %s", mount_state_to_string(old_state), mount_state_to_string(state)); @@ -633,17 +637,7 @@ static int mount_coldplug(Unit *u) { if (m->control_pid > 0 && pid_is_unwaited(m->control_pid) && - IN_SET(new_state, - MOUNT_MOUNTING, - MOUNT_MOUNTING_DONE, - MOUNT_REMOUNTING, - MOUNT_UNMOUNTING, - MOUNT_MOUNTING_SIGTERM, - MOUNT_MOUNTING_SIGKILL, - MOUNT_UNMOUNTING_SIGTERM, - MOUNT_UNMOUNTING_SIGKILL, - MOUNT_REMOUNTING_SIGTERM, - MOUNT_REMOUNTING_SIGKILL)) { + mount_state_active(new_state)) { r = unit_watch_pid(UNIT(m), m->control_pid); if (r < 0) @@ -703,7 +697,6 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { .apply_permissions = true, .apply_chroot = true, .apply_tty_stdin = true, - .bus_endpoint_fd = -1, .stdin_fd = -1, .stdout_fd = -1, .stderr_fd = -1, @@ -967,6 +960,7 @@ fail: static int mount_start(Unit *u) { Mount *m = MOUNT(u); + int r; assert(m); @@ -985,6 +979,12 @@ static int mount_start(Unit *u) { assert(m->state == MOUNT_DEAD || m->state == MOUNT_FAILED); + r = unit_start_limit_test(u); + if (r < 0) { + mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT); + return r; + } + m->result = MOUNT_SUCCESS; m->reload_result = MOUNT_SUCCESS; m->reset_cpu_usage = true; @@ -1385,7 +1385,7 @@ static int mount_setup_unit( goto fail; } - if (m->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(m)) { const char* target; target = mount_needs_network(options, fstype) ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_LOCAL_FS_TARGET; @@ -1413,7 +1413,7 @@ static int mount_setup_unit( } } - if (m->running_as == MANAGER_SYSTEM && + if (MANAGER_IS_SYSTEM(m) && mount_needs_network(options, fstype)) { /* _netdev option may have shown up late, or on a * remount. Add remote-fs dependencies, even though @@ -1782,6 +1782,14 @@ static int mount_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { return unit_kill_common(u, who, signo, -1, MOUNT(u)->control_pid, error); } +static int mount_control_pid(Unit *u) { + Mount *m = MOUNT(u); + + assert(m); + + return m->control_pid; +} + static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = { [MOUNT_EXEC_MOUNT] = "ExecMount", [MOUNT_EXEC_UNMOUNT] = "ExecUnmount", @@ -1796,7 +1804,8 @@ static const char* const mount_result_table[_MOUNT_RESULT_MAX] = { [MOUNT_FAILURE_TIMEOUT] = "timeout", [MOUNT_FAILURE_EXIT_CODE] = "exit-code", [MOUNT_FAILURE_SIGNAL] = "signal", - [MOUNT_FAILURE_CORE_DUMP] = "core-dump" + [MOUNT_FAILURE_CORE_DUMP] = "core-dump", + [MOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit", }; DEFINE_STRING_TABLE_LOOKUP(mount_result, MountResult); @@ -1814,9 +1823,6 @@ const UnitVTable mount_vtable = { "Install\0", .private_section = "Mount", - .no_alias = true, - .no_instances = true, - .init = mount_init, .load = mount_load, .done = mount_done, @@ -1843,6 +1849,8 @@ const UnitVTable mount_vtable = { .reset_failed = mount_reset_failed, + .control_pid = mount_control_pid, + .bus_vtable = bus_mount_vtable, .bus_set_property = bus_mount_set_property, .bus_commit_properties = bus_mount_commit_properties, diff --git a/src/core/mount.h b/src/core/mount.h index 3b343c6b1f..da529c44f4 100644 --- a/src/core/mount.h +++ b/src/core/mount.h @@ -39,6 +39,7 @@ typedef enum MountResult { MOUNT_FAILURE_EXIT_CODE, MOUNT_FAILURE_SIGNAL, MOUNT_FAILURE_CORE_DUMP, + MOUNT_FAILURE_START_LIMIT_HIT, _MOUNT_RESULT_MAX, _MOUNT_RESULT_INVALID = -1 } MountResult; diff --git a/src/core/namespace.c b/src/core/namespace.c index 4fa381db5b..203d122810 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -44,6 +44,8 @@ #include "user-util.h" #include "util.h" +#define DEV_MOUNT_OPTIONS (MS_NOSUID|MS_STRICTATIME|MS_NOEXEC) + typedef enum MountMode { /* This is ordered by priority! */ INACCESSIBLE, @@ -51,7 +53,6 @@ typedef enum MountMode { PRIVATE_TMP, PRIVATE_VAR_TMP, PRIVATE_DEV, - PRIVATE_BUS_ENDPOINT, READWRITE } MountMode; @@ -154,7 +155,7 @@ static int mount_dev(BindMount *m) { dev = strjoina(temporary_mount, "/dev"); (void) mkdir(dev, 0755); - if (mount("tmpfs", dev, "tmpfs", MS_NOSUID|MS_STRICTATIME, "mode=755") < 0) { + if (mount("tmpfs", dev, "tmpfs", DEV_MOUNT_OPTIONS, "mode=755") < 0) { r = -errno; goto fail; } @@ -238,6 +239,8 @@ static int mount_dev(BindMount *m) { */ (void) mkdir_p_label(m->path, 0755); + /* Unmount everything in old /dev */ + umount_recursive(m->path, 0); if (mount(dev, m->path, NULL, MS_MOVE, NULL) < 0) { r = -errno; goto fail; @@ -268,78 +271,6 @@ fail: return r; } -static int mount_kdbus(BindMount *m) { - - char temporary_mount[] = "/tmp/kdbus-dev-XXXXXX"; - _cleanup_free_ char *basepath = NULL; - _cleanup_umask_ mode_t u; - char *busnode = NULL, *root; - struct stat st; - int r; - - assert(m); - - u = umask(0000); - - if (!mkdtemp(temporary_mount)) - return log_error_errno(errno, "Failed create temp dir: %m"); - - root = strjoina(temporary_mount, "/kdbus"); - (void) mkdir(root, 0755); - if (mount("tmpfs", root, "tmpfs", MS_NOSUID|MS_STRICTATIME, "mode=777") < 0) { - r = -errno; - goto fail; - } - - /* create a new /dev/null dev node copy so we have some fodder to - * bind-mount the custom endpoint over. */ - if (stat("/dev/null", &st) < 0) { - r = log_error_errno(errno, "Failed to stat /dev/null: %m"); - goto fail; - } - - busnode = strjoina(root, "/bus"); - if (mknod(busnode, (st.st_mode & ~07777) | 0600, st.st_rdev) < 0) { - r = log_error_errno(errno, "mknod() for %s failed: %m", - busnode); - goto fail; - } - - r = mount(m->path, busnode, NULL, MS_BIND, NULL); - if (r < 0) { - r = log_error_errno(errno, "bind mount of %s failed: %m", - m->path); - goto fail; - } - - basepath = dirname_malloc(m->path); - if (!basepath) { - r = -ENOMEM; - goto fail; - } - - if (mount(root, basepath, NULL, MS_MOVE, NULL) < 0) { - r = log_error_errno(errno, "bind mount of %s failed: %m", - basepath); - goto fail; - } - - rmdir(temporary_mount); - return 0; - -fail: - if (busnode) { - umount(busnode); - unlink(busnode); - } - - umount(root); - rmdir(root); - rmdir(temporary_mount); - - return r; -} - static int apply_mount( BindMount *m, const char *tmp_dir, @@ -379,9 +310,6 @@ static int apply_mount( case PRIVATE_DEV: return mount_dev(m); - case PRIVATE_BUS_ENDPOINT: - return mount_kdbus(m); - default: assert_not_reached("Unknown mode"); } @@ -404,9 +332,11 @@ static int make_read_only(BindMount *m) { if (IN_SET(m->mode, INACCESSIBLE, READONLY)) r = bind_remount_recursive(m->path, true); - else if (IN_SET(m->mode, READWRITE, PRIVATE_TMP, PRIVATE_VAR_TMP, PRIVATE_DEV)) + else if (IN_SET(m->mode, READWRITE, PRIVATE_TMP, PRIVATE_VAR_TMP, PRIVATE_DEV)) { r = bind_remount_recursive(m->path, false); - else + if (r == 0 && m->mode == PRIVATE_DEV) /* can be readonly but the submounts can't*/ + r = mount(NULL, m->path, NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL); + } else r = 0; if (m->ignore && r == -ENOENT) @@ -422,7 +352,6 @@ int setup_namespace( char** inaccessible_dirs, const char* tmp_dir, const char* var_tmp_dir, - const char* bus_endpoint_path, bool private_dev, ProtectHome protect_home, ProtectSystem protect_system, @@ -438,7 +367,7 @@ int setup_namespace( if (unshare(CLONE_NEWNS) < 0) return -errno; - n = !!tmp_dir + !!var_tmp_dir + !!bus_endpoint_path + + n = !!tmp_dir + !!var_tmp_dir + strv_length(read_write_dirs) + strv_length(read_only_dirs) + strv_length(inaccessible_dirs) + @@ -479,12 +408,6 @@ int setup_namespace( m++; } - if (bus_endpoint_path) { - m->path = prefix_roota(root_directory, bus_endpoint_path); - m->mode = PRIVATE_BUS_ENDPOINT; - m++; - } - if (protect_home != PROTECT_HOME_NO) { const char *home_dir, *run_user_dir, *root_dir; diff --git a/src/core/namespace.h b/src/core/namespace.h index 40bee74e2c..b54b7b47d6 100644 --- a/src/core/namespace.h +++ b/src/core/namespace.h @@ -45,7 +45,6 @@ int setup_namespace(const char *chroot, char **inaccessible_dirs, const char *tmp_dir, const char *var_tmp_dir, - const char *endpoint_path, bool private_dev, ProtectHome protect_home, ProtectSystem protect_system, diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf index 6a7a37ee92..3c64f20872 100644 --- a/src/core/org.freedesktop.systemd1.conf +++ b/src/core/org.freedesktop.systemd1.conf @@ -70,14 +70,26 @@ <allow send_destination="org.freedesktop.systemd1" send_interface="org.freedesktop.systemd1.Manager" + send_member="ListUnitsByPatterns"/> + + <allow send_destination="org.freedesktop.systemd1" + send_interface="org.freedesktop.systemd1.Manager" send_member="ListUnitFiles"/> <allow send_destination="org.freedesktop.systemd1" send_interface="org.freedesktop.systemd1.Manager" + send_member="ListUnitFilesByPatterns"/> + + <allow send_destination="org.freedesktop.systemd1" + send_interface="org.freedesktop.systemd1.Manager" send_member="GetUnitFileState"/> <allow send_destination="org.freedesktop.systemd1" send_interface="org.freedesktop.systemd1.Manager" + send_member="GetUnitProcesses"/> + + <allow send_destination="org.freedesktop.systemd1" + send_interface="org.freedesktop.systemd1.Manager" send_member="ListJobs"/> <allow send_destination="org.freedesktop.systemd1" @@ -144,6 +156,10 @@ <allow send_destination="org.freedesktop.systemd1" send_interface="org.freedesktop.systemd1.Manager" + send_member="ListUnitsByNames"/> + + <allow send_destination="org.freedesktop.systemd1" + send_interface="org.freedesktop.systemd1.Manager" send_member="StartTransientUnit"/> <allow send_destination="org.freedesktop.systemd1" @@ -176,6 +192,10 @@ <allow send_destination="org.freedesktop.systemd1" send_interface="org.freedesktop.systemd1.Manager" + send_member="RevertUnitFiles"/> + + <allow send_destination="org.freedesktop.systemd1" + send_interface="org.freedesktop.systemd1.Manager" send_member="PresetUnitFiles"/> <allow send_destination="org.freedesktop.systemd1" diff --git a/src/core/path.c b/src/core/path.c index 460c1d3bf2..0dd0d375d8 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -110,16 +110,14 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) { } else { exists = true; - /* Path exists, we don't need to watch parent - too closely. */ + /* Path exists, we don't need to watch parent too closely. */ if (oldslash) { char *cut2 = oldslash + (oldslash == s->path); char tmp2 = *cut2; *cut2 = '\0'; - inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF); - /* Error is ignored, the worst can happen is - we get spurious events. */ + (void) inotify_add_watch(s->inotify_fd, s->path, IN_MOVE_SELF); + /* Error is ignored, the worst can happen is we get spurious events. */ *cut2 = tmp2; } @@ -320,7 +318,7 @@ static int path_add_default_dependencies(Path *p) { if (r < 0) return r; - if (UNIT(p)->manager->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(UNIT(p)->manager)) { r = unit_add_two_dependencies_by_name(UNIT(p), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); if (r < 0) return r; @@ -476,7 +474,7 @@ static void path_enter_running(Path *p) { trigger = UNIT_TRIGGER(UNIT(p)); if (!trigger) { log_unit_error(UNIT(p), "Unit to trigger vanished."); - path_enter_dead(p, TIMER_FAILURE_RESOURCES); + path_enter_dead(p, PATH_FAILURE_RESOURCES); return; } @@ -562,6 +560,7 @@ static void path_mkdir(Path *p) { static int path_start(Unit *u) { Path *p = PATH(u); Unit *trigger; + int r; assert(p); assert(p->state == PATH_DEAD || p->state == PATH_FAILED); @@ -572,6 +571,12 @@ static int path_start(Unit *u) { return -ENOENT; } + r = unit_start_limit_test(u); + if (r < 0) { + path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT); + return r; + } + path_mkdir(p); p->result = PATH_SUCCESS; @@ -741,6 +746,7 @@ DEFINE_STRING_TABLE_LOOKUP(path_type, PathType); static const char* const path_result_table[_PATH_RESULT_MAX] = { [PATH_SUCCESS] = "success", [PATH_FAILURE_RESOURCES] = "resources", + [PATH_FAILURE_START_LIMIT_HIT] = "start-limit-hit", }; DEFINE_STRING_TABLE_LOOKUP(path_result, PathResult); diff --git a/src/core/path.h b/src/core/path.h index bbbcebd78e..4230c8fb99 100644 --- a/src/core/path.h +++ b/src/core/path.h @@ -62,6 +62,7 @@ static inline bool path_spec_owns_inotify_fd(PathSpec *s, int fd) { typedef enum PathResult { PATH_SUCCESS, PATH_FAILURE_RESOURCES, + PATH_FAILURE_START_LIMIT_HIT, _PATH_RESULT_MAX, _PATH_RESULT_INVALID = -1 } PathResult; diff --git a/src/core/scope.c b/src/core/scope.c index c5d0ecef04..238f63a729 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -50,8 +50,7 @@ static void scope_init(Unit *u) { assert(u->load_state == UNIT_STUB); s->timeout_stop_usec = u->manager->default_timeout_stop_usec; - - UNIT(s)->ignore_on_isolate = true; + u->ignore_on_isolate = true; } static void scope_done(Unit *u) { @@ -139,7 +138,7 @@ static int scope_verify(Scope *s) { return 0; if (set_isempty(UNIT(s)->pids) && - !manager_is_reloading_or_reexecuting(UNIT(s)->manager) && + !MANAGER_IS_RELOADING(UNIT(s)->manager) && !unit_has_name(UNIT(s), SPECIAL_INIT_SCOPE)) { log_unit_error(UNIT(s), "Scope has no PIDs. Refusing."); return -EINVAL; @@ -155,26 +154,27 @@ static int scope_load(Unit *u) { assert(s); assert(u->load_state == UNIT_STUB); - if (!u->transient && !manager_is_reloading_or_reexecuting(u->manager)) + if (!u->transient && !MANAGER_IS_RELOADING(u->manager)) + /* Refuse to load non-transient scope units, but allow them while reloading. */ return -ENOENT; - u->load_state = UNIT_LOADED; - - r = unit_load_dropin(u); + r = unit_load_fragment_and_dropin_optional(u); if (r < 0) return r; - r = unit_patch_contexts(u); - if (r < 0) - return r; + if (u->load_state == UNIT_LOADED) { + r = unit_patch_contexts(u); + if (r < 0) + return r; - r = unit_set_default_slice(u); - if (r < 0) - return r; + r = unit_set_default_slice(u); + if (r < 0) + return r; - r = scope_add_default_dependencies(s); - if (r < 0) - return r; + r = scope_add_default_dependencies(s); + if (r < 0) + return r; + } return scope_verify(s); } @@ -293,7 +293,7 @@ static int scope_start(Unit *u) { assert(s->state == SCOPE_DEAD); - if (!u->transient && !manager_is_reloading_or_reexecuting(u->manager)) + if (!u->transient && !MANAGER_IS_RELOADING(u->manager)) return -ENOENT; (void) unit_realize_cgroup(u); @@ -569,8 +569,6 @@ const UnitVTable scope_vtable = { "Install\0", .private_section = "Scope", - .no_alias = true, - .no_instances = true, .can_transient = true, .init = scope_init, diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c index 2cdfcf7b5d..cc287d602d 100644 --- a/src/core/selinux-access.c +++ b/src/core/selinux-access.c @@ -110,6 +110,7 @@ static int callback_type_to_priority(int type) { */ _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) { va_list ap; + const char *fmt2; #ifdef HAVE_AUDIT int fd; @@ -131,8 +132,10 @@ _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) { } #endif + fmt2 = strjoina("selinux: ", fmt); + va_start(ap, fmt); - log_internalv(LOG_AUTH | callback_type_to_priority(type), 0, __FILE__, __LINE__, __FUNCTION__, fmt, ap); + log_internalv(LOG_AUTH | callback_type_to_priority(type), 0, __FILE__, __LINE__, __FUNCTION__, fmt2, ap); va_end(ap); return 0; diff --git a/src/core/selinux-setup.c b/src/core/selinux-setup.c index 9a115a4387..4072df58e6 100644 --- a/src/core/selinux-setup.c +++ b/src/core/selinux-setup.c @@ -88,7 +88,7 @@ int mac_selinux_setup(bool *loaded_policy) { log_open(); log_error("Failed to compute init label, ignoring."); } else { - r = setcon(label); + r = setcon_raw(label); log_open(); if (r < 0) diff --git a/src/core/service.c b/src/core/service.c index ac7e41d777..7ebabca5d6 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -113,7 +113,6 @@ static void service_init(Unit *u) { s->runtime_max_usec = USEC_INFINITY; s->type = _SERVICE_TYPE_INVALID; s->socket_fd = -1; - s->bus_endpoint_fd = -1; s->stdin_fd = s->stdout_fd = s->stderr_fd = -1; s->guess_main_pid = true; @@ -181,20 +180,17 @@ static int service_set_main_pid(Service *s, pid_t pid) { return 0; } -static void service_close_socket_fd(Service *s) { +void service_close_socket_fd(Service *s) { assert(s); - s->socket_fd = asynchronous_close(s->socket_fd); -} - -static void service_connection_unref(Service *s) { - assert(s); + /* Undo the effect of service_set_socket_fd(). */ - if (!UNIT_ISSET(s->accept_socket)) - return; + s->socket_fd = asynchronous_close(s->socket_fd); - socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket))); - unit_ref_unset(&s->accept_socket); + if (UNIT_ISSET(s->accept_socket)) { + socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket))); + unit_ref_unset(&s->accept_socket); + } } static void service_stop_watchdog(Service *s) { @@ -321,9 +317,7 @@ static void service_done(Unit *u) { s->bus_name_owner = mfree(s->bus_name_owner); - s->bus_endpoint_fd = safe_close(s->bus_endpoint_fd); service_close_socket_fd(s); - service_connection_unref(s); unit_ref_unset(&s->accept_socket); @@ -525,7 +519,7 @@ static int service_add_default_dependencies(Service *s) { /* Add a number of automatic dependencies useful for the * majority of services. */ - if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(UNIT(s)->manager)) { /* First, pull in the really early boot stuff, and * require it, so that we fail if we can't acquire * it. */ @@ -834,7 +828,7 @@ static int service_load_pid_file(Service *s, bool may_warn) { return 0; } -static int service_search_main_pid(Service *s) { +static void service_search_main_pid(Service *s) { pid_t pid = 0; int r; @@ -843,30 +837,24 @@ static int service_search_main_pid(Service *s) { /* If we know it anyway, don't ever fallback to unreliable * heuristics */ if (s->main_pid_known) - return 0; + return; if (!s->guess_main_pid) - return 0; + return; assert(s->main_pid <= 0); - r = unit_search_main_pid(UNIT(s), &pid); - if (r < 0) - return r; + if (unit_search_main_pid(UNIT(s), &pid) < 0) + return; log_unit_debug(UNIT(s), "Main PID guessed: "PID_FMT, pid); - r = service_set_main_pid(s, pid); - if (r < 0) - return r; + if (service_set_main_pid(s, pid) < 0) + return; r = unit_watch_pid(UNIT(s), pid); - if (r < 0) { + if (r < 0) /* FIXME: we need to do something here */ log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" from: %m", pid); - return r; - } - - return 0; } static void service_set_state(Service *s, ServiceState state) { @@ -918,17 +906,15 @@ static void service_set_state(Service *s, ServiceState state) { SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL) && - !(state == SERVICE_DEAD && UNIT(s)->job)) { + !(state == SERVICE_DEAD && UNIT(s)->job)) service_close_socket_fd(s); - service_connection_unref(s); - } if (!IN_SET(state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) service_stop_watchdog(s); /* For the inactive states unit_notify() will trim the cgroup, * but for exit we have to do that ourselves... */ - if (state == SERVICE_EXITED && UNIT(s)->manager->n_reloading <= 0) + if (state == SERVICE_EXITED && !MANAGER_IS_RELOADING(UNIT(s)->manager)) unit_prune_cgroup(UNIT(s)); /* For remain_after_exit services, let's see if we can "release" the @@ -944,7 +930,7 @@ static void service_set_state(Service *s, ServiceState state) { if (ec && exec_context_may_touch_console(ec)) { Manager *m = UNIT(s)->manager; - m->n_on_console --; + m->n_on_console--; if (m->n_on_console == 0) /* unset no_console_output flag, since the console is free */ m->no_console_output = false; @@ -1157,7 +1143,6 @@ static int service_spawn( pid_t *_pid) { _cleanup_strv_free_ char **argv = NULL, **final_env = NULL, **our_env = NULL, **fd_names = NULL; - _cleanup_free_ char *bus_endpoint_path = NULL; _cleanup_free_ int *fds = NULL; unsigned n_fds = 0, n_env = 0; const char *path; @@ -1167,7 +1152,6 @@ static int service_spawn( .apply_permissions = apply_permissions, .apply_chroot = apply_chroot, .apply_tty_stdin = apply_tty_stdin, - .bus_endpoint_fd = -1, .stdin_fd = -1, .stdout_fd = -1, .stderr_fd = -1, @@ -1221,7 +1205,7 @@ static int service_spawn( if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid) < 0) return -ENOMEM; - if (UNIT(s)->manager->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(UNIT(s)->manager)) if (asprintf(our_env + n_env++, "MANAGERPID="PID_FMT, getpid()) < 0) return -ENOMEM; @@ -1267,18 +1251,6 @@ static int service_spawn( } else path = UNIT(s)->cgroup_path; - if (s->exec_context.bus_endpoint) { - r = bus_kernel_create_endpoint(UNIT(s)->manager->running_as == MANAGER_SYSTEM ? "system" : "user", - UNIT(s)->id, &bus_endpoint_path); - if (r < 0) - return r; - - /* Pass the fd to the exec_params so that the child process can upload the policy. - * Keep a reference to the fd in the service, so the endpoint is kept alive as long - * as the service is running. */ - exec_params.bus_endpoint_fd = s->bus_endpoint_fd = r; - } - exec_params.argv = argv; exec_params.fds = fds; exec_params.fd_names = fd_names; @@ -1290,7 +1262,6 @@ static int service_spawn( exec_params.cgroup_delegate = s->cgroup_context.delegate; exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager); exec_params.watchdog_usec = s->watchdog_usec; - exec_params.bus_endpoint_path = bus_endpoint_path; exec_params.selinux_context_net = s->socket_fd_selinux_context_net; if (s->type == SERVICE_IDLE) exec_params.idle_pipe = UNIT(s)->manager->idle_pipe; @@ -1986,6 +1957,7 @@ fail: static int service_start(Unit *u) { Service *s = SERVICE(u); + int r; assert(s); @@ -2012,6 +1984,13 @@ static int service_start(Unit *u) { assert(IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED)); + /* Make sure we don't enter a busy loop of some kind. */ + r = unit_start_limit_test(u); + if (r < 0) { + service_enter_dead(s, SERVICE_FAILURE_START_LIMIT_HIT, false); + return r; + } + s->result = SERVICE_SUCCESS; s->reload_result = SERVICE_SUCCESS; s->main_pid_known = false; @@ -2126,9 +2105,6 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { r = unit_serialize_item_fd(u, f, fds, "socket-fd", s->socket_fd); if (r < 0) return r; - r = unit_serialize_item_fd(u, f, fds, "endpoint-fd", s->bus_endpoint_fd); - if (r < 0) - return r; LIST_FOREACH(fd_store, fs, s->fd_store) { _cleanup_free_ char *c = NULL; @@ -2154,8 +2130,7 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { } } - if (dual_timestamp_is_set(&s->watchdog_timestamp)) - dual_timestamp_serialize(f, "watchdog-timestamp", &s->watchdog_timestamp); + dual_timestamp_serialize(f, "watchdog-timestamp", &s->watchdog_timestamp); unit_serialize_item(u, f, "forbid-restart", yes_no(s->forbid_restart)); @@ -2263,15 +2238,6 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, asynchronous_close(s->socket_fd); s->socket_fd = fdset_remove(fds, fd); } - } else if (streq(key, "endpoint-fd")) { - int fd; - - if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) - log_unit_debug(u, "Failed to parse endpoint-fd value: %s", value); - else { - safe_close(s->bus_endpoint_fd); - s->bus_endpoint_fd = fdset_remove(fds, fd); - } } else if (streq(key, "fd-store-fd")) { const char *fdv; size_t pf; @@ -2759,7 +2725,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { break; } } else - (void) service_search_main_pid(s); + service_search_main_pid(s); service_enter_start_post(s); break; @@ -2781,16 +2747,15 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { break; } } else - (void) service_search_main_pid(s); + service_search_main_pid(s); service_enter_running(s, SERVICE_SUCCESS); break; case SERVICE_RELOAD: - if (f == SERVICE_SUCCESS) { - service_load_pid_file(s, true); - (void) service_search_main_pid(s); - } + if (f == SERVICE_SUCCESS) + if (service_load_pid_file(s, true) < 0) + service_search_main_pid(s); s->reload_result = f; service_enter_running(s, SERVICE_SUCCESS); @@ -3176,9 +3141,8 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context assert(s); assert(fd >= 0); - /* This is called by the socket code when instantiating a new - * service for a stream socket and the socket needs to be - * configured. */ + /* This is called by the socket code when instantiating a new service for a stream socket and the socket needs + * to be configured. We take ownership of the passed fd on success. */ if (UNIT(s)->load_state != UNIT_LOADED) return -EINVAL; @@ -3206,12 +3170,15 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context return r; } + r = unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false); + if (r < 0) + return r; + s->socket_fd = fd; s->socket_fd_selinux_context_net = selinux_context_net; unit_ref_set(&s->accept_socket, UNIT(sock)); - - return unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false); + return 0; } static void service_reset_failed(Unit *u) { @@ -3232,6 +3199,22 @@ static int service_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { return unit_kill_common(u, who, signo, s->main_pid, s->control_pid, error); } +static int service_main_pid(Unit *u) { + Service *s = SERVICE(u); + + assert(s); + + return s->main_pid; +} + +static int service_control_pid(Unit *u) { + Service *s = SERVICE(u); + + assert(s); + + return s->control_pid; +} + static const char* const service_restart_table[_SERVICE_RESTART_MAX] = { [SERVICE_RESTART_NO] = "no", [SERVICE_RESTART_ON_SUCCESS] = "on-success", @@ -3291,6 +3274,7 @@ static const char* const service_result_table[_SERVICE_RESULT_MAX] = { [SERVICE_FAILURE_SIGNAL] = "signal", [SERVICE_FAILURE_CORE_DUMP] = "core-dump", [SERVICE_FAILURE_WATCHDOG] = "watchdog", + [SERVICE_FAILURE_START_LIMIT_HIT] = "start-limit-hit", }; DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult); @@ -3340,6 +3324,9 @@ const UnitVTable service_vtable = { .notify_cgroup_empty = service_notify_cgroup_empty_event, .notify_message = service_notify_message, + .main_pid = service_main_pid, + .control_pid = service_control_pid, + .bus_name_owner_change = service_bus_name_owner_change, .bus_vtable = bus_service_vtable, diff --git a/src/core/service.h b/src/core/service.h index d342e000bb..4af3d40439 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -80,12 +80,13 @@ typedef enum NotifyState { typedef enum ServiceResult { SERVICE_SUCCESS, - SERVICE_FAILURE_RESOURCES, + SERVICE_FAILURE_RESOURCES, /* a bit of a misnomer, just our catch-all error for errnos we didn't expect */ SERVICE_FAILURE_TIMEOUT, SERVICE_FAILURE_EXIT_CODE, SERVICE_FAILURE_SIGNAL, SERVICE_FAILURE_CORE_DUMP, SERVICE_FAILURE_WATCHDOG, + SERVICE_FAILURE_START_LIMIT_HIT, _SERVICE_RESULT_MAX, _SERVICE_RESULT_INVALID = -1 } ServiceResult; @@ -150,8 +151,6 @@ struct Service { int socket_fd; bool socket_fd_selinux_context_net; - int bus_endpoint_fd; - bool permissions_start_only; bool root_directory_start_only; bool remain_after_exit; @@ -200,6 +199,7 @@ struct Service { extern const UnitVTable service_vtable; int service_set_socket_fd(Service *s, int fd, struct Socket *socket, bool selinux_context_net); +void service_close_socket_fd(Service *s); const char* service_restart_to_string(ServiceRestart i) _const_; ServiceRestart service_restart_from_string(const char *s) _pure_; diff --git a/src/core/shutdown.c b/src/core/shutdown.c index 6296b4c94a..e14755d84e 100644 --- a/src/core/shutdown.c +++ b/src/core/shutdown.c @@ -202,7 +202,7 @@ int main(int argc, char *argv[]) { goto error; } - cg_get_root_path(&cgroup); + (void) cg_get_root_path(&cgroup); use_watchdog = !!getenv("WATCHDOG_USEC"); @@ -397,9 +397,14 @@ int main(int argc, char *argv[]) { if (!in_container) { _cleanup_free_ char *param = NULL; - if (read_one_line_file(REBOOT_PARAM_FILE, ¶m) >= 0) { + r = read_one_line_file("/run/systemd/reboot-param", ¶m); + if (r < 0) + log_warning_errno(r, "Failed to read reboot parameter file: %m"); + + if (!isempty(param)) { log_info("Rebooting with argument '%s'.", param); 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"); } } diff --git a/src/core/slice.c b/src/core/slice.c index d65364c6f4..c7700b8857 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -34,6 +34,13 @@ static const UnitActiveState state_translation_table[_SLICE_STATE_MAX] = { [SLICE_ACTIVE] = UNIT_ACTIVE }; +static void slice_init(Unit *u) { + assert(u); + assert(u->load_state == UNIT_STUB); + + u->ignore_on_isolate = true; +} + static void slice_set_state(Slice *t, SliceState state) { SliceState old_state; assert(t); @@ -128,6 +135,7 @@ static int slice_load(Unit *u) { int r; assert(s); + assert(u->load_state == UNIT_STUB); r = unit_load_fragment_and_dropin_optional(u); if (r < 0) @@ -301,10 +309,9 @@ const UnitVTable slice_vtable = { "Install\0", .private_section = "Slice", - .no_alias = true, - .no_instances = true, .can_transient = true, + .init = slice_init, .load = slice_load, .coldplug = slice_coldplug, diff --git a/src/core/smack-setup.c b/src/core/smack-setup.c index 0c26e85460..5a6d11cfa1 100644 --- a/src/core/smack-setup.c +++ b/src/core/smack-setup.c @@ -261,7 +261,7 @@ static int write_netlabel_rules(const char* srcdir) { } } - return r; + return r; } #endif diff --git a/src/core/socket.c b/src/core/socket.c index 976687af41..f6204d04bf 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -28,7 +28,6 @@ #include <unistd.h> #include <linux/sctp.h> -#include "sd-event.h" #include "alloc-util.h" #include "bus-error.h" #include "bus-util.h" @@ -38,6 +37,7 @@ #include "exit-status.h" #include "fd-util.h" #include "formats-util.h" +#include "io-util.h" #include "label.h" #include "log.h" #include "missing.h" @@ -99,6 +99,9 @@ static void socket_init(Unit *u) { s->exec_context.std_error = u->manager->default_std_error; s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; + + s->trigger_limit.interval = USEC_INFINITY; + s->trigger_limit.burst = (unsigned) -1; } static void socket_unwatch_control_pid(Socket *s) { @@ -227,7 +230,6 @@ int socket_instantiate_service(Socket *s) { if (r < 0) return r; - u->no_gc = true; unit_ref_set(&s->service, u); return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, u, false); @@ -301,7 +303,7 @@ static int socket_add_default_dependencies(Socket *s) { if (r < 0) return r; - if (UNIT(s)->manager->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(UNIT(s)->manager)) { r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); if (r < 0) return r; @@ -327,6 +329,25 @@ static int socket_add_extras(Socket *s) { assert(s); + /* Pick defaults for the trigger limit, if nothing was explicitly configured. We pick a relatively high limit + * in Accept=yes mode, and a lower limit for Accept=no. Reason: in Accept=yes mode we are invoking accept() + * ourselves before the trigger limit can hit, thus incoming connections are taken off the socket queue quickly + * and reliably. This is different for Accept=no, where the spawned service has to take the incoming traffic + * off the queues, which it might not necessarily do. Moreover, while Accept=no services are supposed to + * process whatever is queued in one go, and thus should normally never have to be started frequently. This is + * different for Accept=yes where each connection is processed by a new service instance, and thus frequent + * service starts are typical. */ + + if (s->trigger_limit.interval == USEC_INFINITY) + s->trigger_limit.interval = 2 * USEC_PER_SEC; + + if (s->trigger_limit.burst == (unsigned) -1) { + if (s->accept) + s->trigger_limit.burst = 200; + else + s->trigger_limit.burst = 20; + } + if (have_non_accept_socket(s)) { if (!UNIT_DEREF(s->service)) { @@ -619,8 +640,8 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { if (!isempty(s->user) || !isempty(s->group)) fprintf(f, - "%sOwnerUser: %s\n" - "%sOwnerGroup: %s\n", + "%sSocketUser: %s\n" + "%sSocketGroup: %s\n", prefix, strna(s->user), prefix, strna(s->group)); @@ -669,6 +690,12 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sListenFIFO: %s\n", prefix, p->path); } + fprintf(f, + "%sTriggerLimitIntervalSec: %s\n" + "%sTriggerLimitBurst: %u\n", + prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->trigger_limit.interval, USEC_PER_SEC), + prefix, s->trigger_limit.burst); + exec_context_dump(&s->exec_context, f, prefix); kill_context_dump(&s->kill_context, f, prefix); @@ -792,47 +819,45 @@ static void socket_close_fds(Socket *s) { assert(s); LIST_FOREACH(port, p, s->ports) { + bool was_open; - p->event_source = sd_event_source_unref(p->event_source); - - if (p->fd < 0) - continue; + was_open = p->fd >= 0; + p->event_source = sd_event_source_unref(p->event_source); p->fd = safe_close(p->fd); socket_cleanup_fd_list(p); - /* One little note: we should normally not delete any - * sockets in the file system here! After all some - * other process we spawned might still have a - * reference of this fd and wants to continue to use - * it. Therefore we delete sockets in the file system - * before we create a new one, not after we stopped - * using one! */ + /* One little note: we should normally not delete any sockets in the file system here! After all some + * other process we spawned might still have a reference of this fd and wants to continue to use + * it. Therefore we normally delete sockets in the file system before we create a new one, not after we + * stopped using one! That all said, if the user explicitly requested this, we'll delete them here + * anyway, but only then. */ - if (s->remove_on_stop) { - switch (p->type) { + if (!was_open || !s->remove_on_stop) + continue; - case SOCKET_FIFO: - unlink(p->path); - break; + switch (p->type) { - case SOCKET_MQUEUE: - mq_unlink(p->path); - break; + case SOCKET_FIFO: + (void) unlink(p->path); + break; - case SOCKET_SOCKET: - socket_address_unlink(&p->address); - break; + case SOCKET_MQUEUE: + (void) mq_unlink(p->path); + break; - default: - break; - } + case SOCKET_SOCKET: + (void) socket_address_unlink(&p->address); + break; + + default: + break; } } if (s->remove_on_stop) STRV_FOREACH(i, s->symlinks) - unlink(*i); + (void) unlink(*i); } static void socket_apply_socket_options(Socket *s, int fd) { @@ -1222,6 +1247,45 @@ fail: return r; } +static int socket_determine_selinux_label(Socket *s, char **ret) { + ExecCommand *c; + int r; + + assert(s); + assert(ret); + + if (s->selinux_context_from_net) { + /* If this is requested, get label from the network label */ + + r = mac_selinux_get_our_label(ret); + if (r == -EOPNOTSUPP) + goto no_label; + + } else { + /* Otherwise, get it from the executable we are about to start */ + r = socket_instantiate_service(s); + if (r < 0) + return r; + + if (!UNIT_ISSET(s->service)) + goto no_label; + + c = SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]; + if (!c) + goto no_label; + + r = mac_selinux_get_create_label_from_exe(c->path, ret); + if (r == -EPERM || r == -EOPNOTSUPP) + goto no_label; + } + + return r; + +no_label: + *ret = NULL; + return 0; +} + static int socket_open_fds(Socket *s) { _cleanup_(mac_selinux_freep) char *label = NULL; bool know_label = false; @@ -1240,46 +1304,28 @@ static int socket_open_fds(Socket *s) { case SOCKET_SOCKET: if (!know_label) { - /* Figure out label, if we don't it know - * yet. We do it once, for the first - * socket where we need this and - * remember it for the rest. */ - - if (s->selinux_context_from_net) { - /* Get it from the network label */ - - r = mac_selinux_get_our_label(&label); - if (r < 0 && r != -EOPNOTSUPP) - goto rollback; + /* Figure out label, if we don't it know yet. We do it once, for the first socket where + * we need this and remember it for the rest. */ - } else { - /* Get it from the executable we are about to start */ - - r = socket_instantiate_service(s); - if (r < 0) - goto rollback; - - if (UNIT_ISSET(s->service) && - SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]) { - r = mac_selinux_get_create_label_from_exe(SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]->path, &label); - if (r < 0 && r != -EPERM && r != -EOPNOTSUPP) - goto rollback; - } - } + r = socket_determine_selinux_label(s, &label); + if (r < 0) + goto rollback; know_label = true; } /* Apply the socket protocol */ - switch(p->address.type) { + switch (p->address.type) { + case SOCK_STREAM: case SOCK_SEQPACKET: - if (p->socket->socket_protocol == IPPROTO_SCTP) - p->address.protocol = p->socket->socket_protocol; + if (s->socket_protocol == IPPROTO_SCTP) + p->address.protocol = s->socket_protocol; break; + case SOCK_DGRAM: - if (p->socket->socket_protocol == IPPROTO_UDPLITE) - p->address.protocol = p->socket->socket_protocol; + if (s->socket_protocol == IPPROTO_UDPLITE) + p->address.protocol = s->socket_protocol; break; } @@ -1340,9 +1386,12 @@ static int socket_open_fds(Socket *s) { } break; - case SOCKET_USB_FUNCTION: + case SOCKET_USB_FUNCTION: { + _cleanup_free_ char *ep = NULL; - p->fd = usbffs_address_create(p->path); + ep = path_make_absolute("ep0", p->path); + + p->fd = usbffs_address_create(ep); if (p->fd < 0) { r = p->fd; goto rollback; @@ -1357,7 +1406,7 @@ static int socket_open_fds(Socket *s) { goto rollback; break; - + } default: assert_not_reached("Unknown port type"); } @@ -1420,6 +1469,34 @@ fail: return r; } +enum { + SOCKET_OPEN_NONE, + SOCKET_OPEN_SOME, + SOCKET_OPEN_ALL, +}; + +static int socket_check_open(Socket *s) { + bool have_open = false, have_closed = false; + SocketPort *p; + + assert(s); + + LIST_FOREACH(port, p, s->ports) { + if (p->fd < 0) + have_closed = true; + else + have_open = true; + + if (have_open && have_closed) + return SOCKET_OPEN_SOME; + } + + if (have_open) + return SOCKET_OPEN_ALL; + + return SOCKET_OPEN_NONE; +} + static void socket_set_state(Socket *s, SocketState state) { SocketState old_state; assert(s); @@ -1499,14 +1576,24 @@ static int socket_coldplug(Unit *u) { SOCKET_START_CHOWN, SOCKET_START_POST, SOCKET_LISTENING, - SOCKET_RUNNING, - SOCKET_STOP_PRE, - SOCKET_STOP_PRE_SIGTERM, - SOCKET_STOP_PRE_SIGKILL)) { - - r = socket_open_fds(s); - if (r < 0) - return r; + SOCKET_RUNNING)) { + + /* Originally, we used to simply reopen all sockets here that we didn't have file descriptors + * for. However, this is problematic, as we won't traverse throught the SOCKET_START_CHOWN state for + * them, and thus the UID/GID wouldn't be right. Hence, instead simply check if we have all fds open, + * and if there's a mismatch, warn loudly. */ + + r = socket_check_open(s); + if (r == SOCKET_OPEN_NONE) + log_unit_warning(UNIT(s), + "Socket unit configuration has changed while unit has been running, " + "no open socket file descriptor left. " + "The socket unit is not functional until restarted."); + else if (r == SOCKET_OPEN_SOME) + log_unit_warning(UNIT(s), + "Socket unit configuration has changed while unit has been running, " + "and some socket file descriptors have not been opened yet. " + "The socket unit is not fully functional until restarted."); } if (s->deserialized_state == SOCKET_LISTENING) { @@ -1527,7 +1614,6 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { .apply_permissions = true, .apply_chroot = true, .apply_tty_stdin = true, - .bus_endpoint_fd = -1, .stdin_fd = -1, .stdout_fd = -1, .stderr_fd = -1, @@ -1880,38 +1966,47 @@ fail: socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); } +static void flush_ports(Socket *s) { + SocketPort *p; + + /* Flush all incoming traffic, regardless if actual bytes or new connections, so that this socket isn't busy + * anymore */ + + LIST_FOREACH(port, p, s->ports) { + if (p->fd < 0) + continue; + + (void) flush_accept(p->fd); + (void) flush_fd(p->fd); + } +} + static void socket_enter_running(Socket *s, int cfd) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; + /* Note that this call takes possession of the connection fd passed. It either has to assign it somewhere or + * close it. */ + assert(s); - /* We don't take connections anymore if we are supposed to - * shut down anyway */ + /* We don't take connections anymore if we are supposed to shut down anyway */ if (unit_stop_pending(UNIT(s))) { log_unit_debug(UNIT(s), "Suppressing connection request since unit stop is scheduled."); if (cfd >= 0) - safe_close(cfd); - else { - /* Flush all sockets by closing and reopening them */ - socket_close_fds(s); - - r = socket_open_fds(s); - if (r < 0) { - log_unit_warning_errno(UNIT(s), r, "Failed to listen on sockets: %m"); - socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); - return; - } + cfd = safe_close(cfd); + else + flush_ports(s); - r = socket_watch_fds(s); - if (r < 0) { - log_unit_warning_errno(UNIT(s), r, "Failed to watch sockets: %m"); - socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); - } - } + return; + } + if (!ratelimit_test(&s->trigger_limit)) { + safe_close(cfd); + log_unit_warning(UNIT(s), "Trigger limit hit, refusing further activation."); + socket_enter_stop_pre(s, SOCKET_FAILURE_TRIGGER_LIMIT_HIT); return; } @@ -1946,7 +2041,7 @@ static void socket_enter_running(Socket *s, int cfd) { Service *service; if (s->n_connections >= s->max_connections) { - log_unit_warning(UNIT(s), "Too many incoming connections (%u)", s->n_connections); + log_unit_warning(UNIT(s), "Too many incoming connections (%u), refusing connection attempt.", s->n_connections); safe_close(cfd); return; } @@ -1962,6 +2057,7 @@ static void socket_enter_running(Socket *s, int cfd) { /* ENOTCONN is legitimate if TCP RST was received. * This connection is over, but the socket unit lives on. */ + log_unit_debug(UNIT(s), "Got ENOTCONN on incoming socket, assuming aborted connection attempt, ignoring."); safe_close(cfd); return; } @@ -1980,22 +2076,24 @@ static void socket_enter_running(Socket *s, int cfd) { service = SERVICE(UNIT_DEREF(s->service)); unit_ref_unset(&s->service); - s->n_accepted ++; - - UNIT(service)->no_gc = false; + s->n_accepted++; unit_choose_id(UNIT(service), name); r = service_set_socket_fd(service, cfd, s, s->selinux_context_from_net); if (r < 0) goto fail; - cfd = -1; - s->n_connections ++; + cfd = -1; /* We passed ownership of the fd to the service now. Forget it here. */ + s->n_connections++; r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL); - if (r < 0) + if (r < 0) { + /* We failed to activate the new service, but it still exists. Let's make sure the service + * closes and forgets the connection fd again, immediately. */ + service_close_socket_fd(service); goto fail; + } /* Notify clients about changed counters */ unit_add_to_dbus_queue(UNIT(s)); @@ -2042,6 +2140,7 @@ fail: static int socket_start(Unit *u) { Socket *s = SOCKET(u); + int r; assert(s); @@ -2086,6 +2185,12 @@ static int socket_start(Unit *u) { assert(s->state == SOCKET_DEAD || s->state == SOCKET_FAILED); + r = unit_start_limit_test(u); + if (r < 0) { + socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT); + return r; + } + s->result = SOCKET_SUCCESS; s->reset_cpu_usage = true; @@ -2720,17 +2825,26 @@ static void socket_trigger_notify(Unit *u, Unit *other) { assert(u); assert(other); - /* Don't propagate state changes from the service if we are - already down or accepting connections */ - if (!IN_SET(s->state, SOCKET_RUNNING, SOCKET_LISTENING) || s->accept) + /* Filter out invocations with bogus state */ + if (other->load_state != UNIT_LOADED || other->type != UNIT_SERVICE) + return; + + /* Don't propagate state changes from the service if we are already down */ + if (!IN_SET(s->state, SOCKET_RUNNING, SOCKET_LISTENING)) return; + /* We don't care for the service state if we are in Accept=yes mode */ + if (s->accept) + return; + + /* Propagate start limit hit state */ if (other->start_limit_hit) { socket_enter_stop_pre(s, SOCKET_FAILURE_SERVICE_START_LIMIT_HIT); return; } - if (other->load_state != UNIT_LOADED || other->type != UNIT_SERVICE) + /* Don't propagate anything if there's still a job queued */ + if (other->job) return; if (IN_SET(SERVICE(other)->state, @@ -2778,6 +2892,14 @@ char *socket_fdname(Socket *s) { return UNIT(s)->id; } +static int socket_control_pid(Unit *u) { + Socket *s = SOCKET(u); + + assert(s); + + return s->control_pid; +} + static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = { [SOCKET_EXEC_START_PRE] = "StartPre", [SOCKET_EXEC_START_CHOWN] = "StartChown", @@ -2795,6 +2917,8 @@ static const char* const socket_result_table[_SOCKET_RESULT_MAX] = { [SOCKET_FAILURE_EXIT_CODE] = "exit-code", [SOCKET_FAILURE_SIGNAL] = "signal", [SOCKET_FAILURE_CORE_DUMP] = "core-dump", + [SOCKET_FAILURE_START_LIMIT_HIT] = "start-limit-hit", + [SOCKET_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit", [SOCKET_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit" }; @@ -2843,6 +2967,8 @@ const UnitVTable socket_vtable = { .reset_failed = socket_reset_failed, + .control_pid = socket_control_pid, + .bus_vtable = bus_socket_vtable, .bus_set_property = bus_socket_set_property, .bus_commit_properties = bus_socket_commit_properties, diff --git a/src/core/socket.h b/src/core/socket.h index b537b026a7..0f1ac69c6f 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -52,6 +52,8 @@ typedef enum SocketResult { SOCKET_FAILURE_EXIT_CODE, SOCKET_FAILURE_SIGNAL, SOCKET_FAILURE_CORE_DUMP, + SOCKET_FAILURE_START_LIMIT_HIT, + SOCKET_FAILURE_TRIGGER_LIMIT_HIT, SOCKET_FAILURE_SERVICE_START_LIMIT_HIT, _SOCKET_RESULT_MAX, _SOCKET_RESULT_INVALID = -1 @@ -156,6 +158,8 @@ struct Socket { bool reset_cpu_usage:1; char *fdname; + + RateLimit trigger_limit; }; /* Called from the service code when collecting fds */ diff --git a/src/core/swap.c b/src/core/swap.c index 1bf0c0a808..a532b15be8 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -198,7 +198,7 @@ static int swap_add_device_links(Swap *s) { return 0; if (is_device_path(s->what)) - return unit_add_node_link(UNIT(s), s->what, UNIT(s)->manager->running_as == MANAGER_SYSTEM, UNIT_BINDS_TO); + return unit_add_node_link(UNIT(s), s->what, MANAGER_IS_SYSTEM(UNIT(s)->manager), UNIT_BINDS_TO); else /* File based swap devices need to be ordered after * systemd-remount-fs.service, since they might need a @@ -214,7 +214,7 @@ static int swap_add_default_dependencies(Swap *s) { if (!UNIT(s)->default_dependencies) return 0; - if (UNIT(s)->manager->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(UNIT(s)->manager)) return 0; if (detect_container() > 0) @@ -609,7 +609,6 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { .apply_permissions = true, .apply_chroot = true, .apply_tty_stdin = true, - .bus_endpoint_fd = -1, .stdin_fd = -1, .stdout_fd = -1, .stderr_fd = -1, @@ -815,6 +814,7 @@ fail: static int swap_start(Unit *u) { Swap *s = SWAP(u), *other; + int r; assert(s); @@ -843,6 +843,12 @@ static int swap_start(Unit *u) { if (UNIT(other)->job && UNIT(other)->job->state == JOB_RUNNING) return -EAGAIN; + r = unit_start_limit_test(u); + if (r < 0) { + swap_enter_dead(s, SWAP_FAILURE_START_LIMIT_HIT); + return r; + } + s->result = SWAP_SUCCESS; s->reset_cpu_usage = true; @@ -1427,6 +1433,14 @@ static bool swap_supported(void) { return supported; } +static int swap_control_pid(Unit *u) { + Swap *s = SWAP(u); + + assert(s); + + return s->control_pid; +} + static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = { [SWAP_EXEC_ACTIVATE] = "ExecActivate", [SWAP_EXEC_DEACTIVATE] = "ExecDeactivate", @@ -1440,7 +1454,8 @@ static const char* const swap_result_table[_SWAP_RESULT_MAX] = { [SWAP_FAILURE_TIMEOUT] = "timeout", [SWAP_FAILURE_EXIT_CODE] = "exit-code", [SWAP_FAILURE_SIGNAL] = "signal", - [SWAP_FAILURE_CORE_DUMP] = "core-dump" + [SWAP_FAILURE_CORE_DUMP] = "core-dump", + [SWAP_FAILURE_START_LIMIT_HIT] = "start-limit-hit", }; DEFINE_STRING_TABLE_LOOKUP(swap_result, SwapResult); @@ -1458,9 +1473,6 @@ const UnitVTable swap_vtable = { "Install\0", .private_section = "Swap", - .no_alias = true, - .no_instances = true, - .init = swap_init, .load = swap_load, .done = swap_done, @@ -1488,6 +1500,8 @@ const UnitVTable swap_vtable = { .reset_failed = swap_reset_failed, + .control_pid = swap_control_pid, + .bus_vtable = bus_swap_vtable, .bus_set_property = bus_swap_set_property, .bus_commit_properties = bus_swap_commit_properties, diff --git a/src/core/swap.h b/src/core/swap.h index ac7a63d81b..fbf66debdc 100644 --- a/src/core/swap.h +++ b/src/core/swap.h @@ -38,6 +38,7 @@ typedef enum SwapResult { SWAP_FAILURE_EXIT_CODE, SWAP_FAILURE_SIGNAL, SWAP_FAILURE_CORE_DUMP, + SWAP_FAILURE_START_LIMIT_HIT, _SWAP_RESULT_MAX, _SWAP_RESULT_INVALID = -1 } SwapResult; diff --git a/src/core/system.conf b/src/core/system.conf index e2ded27333..db8b7acd78 100644 --- a/src/core/system.conf +++ b/src/core/system.conf @@ -34,10 +34,11 @@ #DefaultTimeoutStartSec=90s #DefaultTimeoutStopSec=90s #DefaultRestartSec=100ms -#DefaultStartLimitInterval=10s +#DefaultStartLimitIntervalSec=10s #DefaultStartLimitBurst=5 #DefaultEnvironment= #DefaultCPUAccounting=no +#DefaultIOAccounting=no #DefaultBlockIOAccounting=no #DefaultMemoryAccounting=no #DefaultTasksAccounting=yes diff --git a/src/core/timer.c b/src/core/timer.c index 6f3e6a8db3..3206296f09 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -109,7 +109,7 @@ static int timer_add_default_dependencies(Timer *t) { if (r < 0) return r; - if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) { r = unit_add_two_dependencies_by_name(UNIT(t), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SYSINIT_TARGET, NULL, true); if (r < 0) return r; @@ -135,7 +135,7 @@ static int timer_setup_persistent(Timer *t) { if (!t->persistent) return 0; - if (UNIT(t)->manager->running_as == MANAGER_SYSTEM) { + if (MANAGER_IS_SYSTEM(UNIT(t)->manager)) { r = unit_require_mounts_for(UNIT(t), "/var/lib/systemd/timers"); if (r < 0) @@ -320,7 +320,7 @@ static usec_t monotonic_to_boottime(usec_t t) { if (t <= 0) return 0; - a = now(CLOCK_BOOTTIME); + a = now(clock_boottime_or_monotonic()); b = now(CLOCK_MONOTONIC); if (t + a > b) @@ -334,7 +334,7 @@ static void add_random(Timer *t, usec_t *v) { usec_t add; assert(t); - assert(*v); + assert(v); if (t->random_usec == 0) return; @@ -373,7 +373,7 @@ static void timer_enter_waiting(Timer *t, bool initial) { * rather than the monotonic clock. */ ts_realtime = now(CLOCK_REALTIME); - ts_monotonic = now(t->wake_system ? CLOCK_BOOTTIME : CLOCK_MONOTONIC); + ts_monotonic = now(t->wake_system ? clock_boottime_or_monotonic() : CLOCK_MONOTONIC); t->next_elapse_monotonic_or_boottime = t->next_elapse_realtime = 0; LIST_FOREACH(value, v, t->values) { @@ -599,6 +599,7 @@ static int timer_start(Unit *u) { Timer *t = TIMER(u); TimerValue *v; Unit *trigger; + int r; assert(t); assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED); @@ -609,6 +610,12 @@ static int timer_start(Unit *u) { return -ENOENT; } + r = unit_start_limit_test(u); + if (r < 0) { + timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT); + return r; + } + t->last_trigger = DUAL_TIMESTAMP_NULL; /* Reenable all timers that depend on unit activation time */ @@ -808,7 +815,8 @@ DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase); static const char* const timer_result_table[_TIMER_RESULT_MAX] = { [TIMER_SUCCESS] = "success", - [TIMER_FAILURE_RESOURCES] = "resources" + [TIMER_FAILURE_RESOURCES] = "resources", + [TIMER_FAILURE_START_LIMIT_HIT] = "start-limit-hit", }; DEFINE_STRING_TABLE_LOOKUP(timer_result, TimerResult); diff --git a/src/core/timer.h b/src/core/timer.h index 698e6da2f5..9c4b64f898 100644 --- a/src/core/timer.h +++ b/src/core/timer.h @@ -48,6 +48,7 @@ typedef struct TimerValue { typedef enum TimerResult { TIMER_SUCCESS, TIMER_FAILURE_RESOURCES, + TIMER_FAILURE_START_LIMIT_HIT, _TIMER_RESULT_MAX, _TIMER_RESULT_INVALID = -1 } TimerResult; diff --git a/src/core/transaction.c b/src/core/transaction.c index b28fc76785..e06a48a2f1 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -391,6 +391,7 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi if (delete) { + const char *status; /* logging for j not k here here to provide consistent narrative */ log_unit_warning(j->unit, "Breaking ordering cycle by deleting job %s/%s", @@ -399,7 +400,13 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi "Job %s/%s deleted to break ordering cycle starting with %s/%s", delete->unit->id, job_type_to_string(delete->type), j->unit->id, job_type_to_string(j->type)); - unit_status_printf(delete->unit, ANSI_HIGHLIGHT_RED " SKIP " ANSI_NORMAL, + + if (log_get_show_color()) + status = ANSI_HIGHLIGHT_RED " SKIP " ANSI_NORMAL; + else + status = " SKIP "; + + unit_status_printf(delete->unit, status, "Ordering cycle found, skipping %s"); transaction_delete_unit(tr, delete->unit); return -EAGAIN; @@ -590,7 +597,7 @@ static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) { /* Not invalidating recursively. Avoids triggering * OnFailure= actions of dependent jobs. Also avoids * invalidating our iterator. */ - job_finish_and_invalidate(j, JOB_CANCELED, false); + job_finish_and_invalidate(j, JOB_CANCELED, false, false); } } @@ -848,7 +855,7 @@ int transaction_add_job_and_dependencies( * This matters when jobs are spawned as part of coldplugging itself (see e. g. path_coldplug()). * This way, we "recursively" coldplug units, ensuring that we do not look at state of * not-yet-coldplugged units. */ - if (unit->manager->n_reloading > 0) + if (MANAGER_IS_RELOADING(unit->manager)) unit_coldplug(unit); /* log_debug("Pulling in %s/%s from %s/%s", */ @@ -932,7 +939,7 @@ int transaction_add_job_and_dependencies( if (r < 0) { /* unit masked, job type not applicable and unit not found are not considered as errors. */ log_unit_full(dep, - IN_SET(r, -ESHUTDOWN, -EBADR, -ENOENT) ? LOG_DEBUG : LOG_WARNING, + IN_SET(r, -ERFKILL, -EBADR, -ENOENT) ? LOG_DEBUG : LOG_WARNING, r, "Cannot add dependency job, ignoring: %s", bus_error_message(e, r)); sd_bus_error_free(e); diff --git a/src/core/triggers.systemd.in b/src/core/triggers.systemd.in index 9e18a39a67..0d8c303136 100644 --- a/src/core/triggers.systemd.in +++ b/src/core/triggers.systemd.in @@ -18,6 +18,8 @@ # along with systemd; If not, see <http://www.gnu.org/licenses/>. # The contents of this are an example to be copied into systemd.spec. +# +# Minimum rpm version supported: 4.13.0 %transfiletriggerin -P 900900 -p <lua> -- @systemunitdir@ /etc/systemd/system -- This script will run after any package is initially installed or diff --git a/src/core/umount.c b/src/core/umount.c index a458768e7d..c21a2be54e 100644 --- a/src/core/umount.c +++ b/src/core/umount.c @@ -412,6 +412,7 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e #ifndef HAVE_SPLIT_USR || path_equal(m->path, "/usr") #endif + || path_startswith(m->path, "/run/initramfs") ) continue; @@ -472,7 +473,7 @@ static int loopback_points_list_detach(MountPoint **head, bool *changed) { major(root_st.st_dev) != 0 && lstat(m->path, &loopback_st) >= 0 && root_st.st_dev == loopback_st.st_rdev) { - n_failed ++; + n_failed++; continue; } @@ -507,7 +508,7 @@ static int dm_points_list_detach(MountPoint **head, bool *changed) { if (k >= 0 && major(root_st.st_dev) != 0 && root_st.st_dev == m->devnum) { - n_failed ++; + n_failed++; continue; } diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c index fc057d965c..f11df42af3 100644 --- a/src/core/unit-printf.c +++ b/src/core/unit-printf.c @@ -140,14 +140,9 @@ static int specifier_runtime(char specifier, void *data, void *userdata, char ** assert(u); - if (u->manager->running_as == MANAGER_SYSTEM) - e = "/run"; - else { - e = getenv("XDG_RUNTIME_DIR"); - if (!e) - return -EOPNOTSUPP; - } - + e = manager_get_runtime_prefix(u->manager); + if (!e) + return -EOPNOTSUPP; n = strdup(e); if (!n) return -ENOMEM; diff --git a/src/core/unit.c b/src/core/unit.c index d39e3dcaeb..2fff3f2d8b 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -47,11 +47,13 @@ #include "path-util.h" #include "process-util.h" #include "set.h" +#include "signal-util.h" #include "special.h" #include "stat-util.h" #include "stdio-util.h" #include "string-util.h" #include "strv.h" +#include "umask-util.h" #include "unit-name.h" #include "unit.h" #include "user-util.h" @@ -130,6 +132,7 @@ static void unit_init(Unit *u) { * been initialized */ cc->cpu_accounting = u->manager->default_cpu_accounting; + cc->io_accounting = u->manager->default_io_accounting; cc->blockio_accounting = u->manager->default_blockio_accounting; cc->memory_accounting = u->manager->default_memory_accounting; cc->tasks_accounting = u->manager->default_tasks_accounting; @@ -191,7 +194,7 @@ int unit_add_name(Unit *u, const char *text) { if (r < 0) return r; - if (i && unit_vtable[t]->no_instances) + if (i && !unit_type_may_template(t)) return -EINVAL; /* Ensure that this unit is either instanced or not instanced, @@ -200,7 +203,7 @@ int unit_add_name(Unit *u, const char *text) { if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i) return -EINVAL; - if (unit_vtable[t]->no_alias && !set_isempty(u->names)) + if (!unit_type_may_alias(t) && !set_isempty(u->names)) return -EEXIST; if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES) @@ -364,7 +367,7 @@ void unit_add_to_gc_queue(Unit *u) { LIST_PREPEND(gc_queue, u->manager->gc_queue, u); u->in_gc_queue = true; - u->manager->n_in_gc_queue ++; + u->manager->n_in_gc_queue++; } void unit_add_to_dbus_queue(Unit *u) { @@ -418,13 +421,22 @@ static void unit_remove_transient(Unit *u) { (void) unlink(u->fragment_path); STRV_FOREACH(i, u->dropin_paths) { - _cleanup_free_ char *p = NULL; + _cleanup_free_ char *p = NULL, *pp = NULL; - (void) unlink(*i); + p = dirname_malloc(*i); /* Get the drop-in directory from the drop-in file */ + if (!p) + continue; + + pp = dirname_malloc(p); /* Get the config directory from the drop-in directory */ + if (!pp) + continue; - p = dirname_malloc(*i); - if (p) - (void) rmdir(p); + /* Only drop transient drop-ins */ + if (!path_equal(u->manager->lookup_paths.transient, pp)) + continue; + + (void) unlink(*i); + (void) rmdir(p); } } @@ -483,7 +495,10 @@ void unit_free(Unit *u) { assert(u); - if (u->manager->n_reloading <= 0) + if (u->transient_file) + fclose(u->transient_file); + + if (!MANAGER_IS_RELOADING(u->manager)) unit_remove_transient(u); bus_unit_send_removed_signal(u); @@ -706,6 +721,9 @@ int unit_merge(Unit *u, Unit *other) { if (!u->instance != !other->instance) return -EINVAL; + if (!unit_type_may_alias(u->type)) /* Merging only applies to unit names that support aliases */ + return -EEXIST; + if (other->load_state != UNIT_STUB && other->load_state != UNIT_NOT_FOUND) return -EEXIST; @@ -762,9 +780,9 @@ int unit_merge(Unit *u, Unit *other) { } int unit_merge_by_name(Unit *u, const char *name) { + _cleanup_free_ char *s = NULL; Unit *other; int r; - _cleanup_free_ char *s = NULL; assert(u); assert(name); @@ -814,7 +832,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) { return r; } - if (u->manager->running_as != MANAGER_SYSTEM) + if (!MANAGER_IS_SYSTEM(u->manager)) return 0; if (c->private_tmp) { @@ -888,7 +906,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { "%s\tInstance: %s\n" "%s\tUnit Load State: %s\n" "%s\tUnit Active State: %s\n" - "%s\nState Change Timestamp: %s\n" + "%s\tState Change Timestamp: %s\n" "%s\tInactive Exit Timestamp: %s\n" "%s\tActive Enter Timestamp: %s\n" "%s\tActive Exit Timestamp: %s\n" @@ -1196,6 +1214,7 @@ static int unit_add_startup_units(Unit *u) { return 0; if (c->startup_cpu_shares == CGROUP_CPU_SHARES_INVALID && + c->startup_io_weight == CGROUP_WEIGHT_INVALID && c->startup_blockio_weight == CGROUP_BLKIO_WEIGHT_INVALID) return 0; @@ -1222,6 +1241,17 @@ int unit_load(Unit *u) { if (u->load_state != UNIT_STUB) return 0; + if (u->transient_file) { + r = fflush_and_check(u->transient_file); + if (r < 0) + goto fail; + + fclose(u->transient_file); + u->transient_file = NULL; + + u->fragment_mtime = now(CLOCK_REALTIME); + } + if (UNIT_VTABLE(u)->load) { r = UNIT_VTABLE(u)->load(u); if (r < 0) @@ -1434,7 +1464,7 @@ void unit_status_emit_starting_stopping_reloading(Unit *u, JobType t) { unit_status_print_starting_stopping(u, t); } -static int unit_start_limit_test(Unit *u) { +int unit_start_limit_test(Unit *u) { assert(u); if (ratelimit_test(&u->start_limit)) { @@ -1460,7 +1490,6 @@ static int unit_start_limit_test(Unit *u) { int unit_start(Unit *u) { UnitActiveState state; Unit *following; - int r; assert(u); @@ -1472,11 +1501,6 @@ int unit_start(Unit *u) { if (UNIT_IS_ACTIVE_OR_RELOADING(state)) return -EALREADY; - /* Make sure we don't enter a busy loop of some kind. */ - r = unit_start_limit_test(u); - if (r < 0) - return r; - /* Units that aren't loaded cannot be started */ if (u->load_state != UNIT_LOADED) return -EINVAL; @@ -1834,7 +1858,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su m = u->manager; /* Update timestamps for state changes */ - if (m->n_reloading <= 0) { + if (!MANAGER_IS_RELOADING(m)) { dual_timestamp_get(&u->state_change_timestamp); if (UNIT_IS_INACTIVE_OR_FAILED(os) && !UNIT_IS_INACTIVE_OR_FAILED(ns)) @@ -1864,13 +1888,13 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su ec = unit_get_exec_context(u); if (ec && exec_context_may_touch_console(ec)) { if (UNIT_IS_INACTIVE_OR_FAILED(ns)) { - m->n_on_console --; + m->n_on_console--; if (m->n_on_console == 0) /* unset no_console_output flag, since the console is free */ m->no_console_output = false; } else - m->n_on_console ++; + m->n_on_console++; } } @@ -1894,12 +1918,12 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su case JOB_VERIFY_ACTIVE: if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) - job_finish_and_invalidate(u->job, JOB_DONE, true); + job_finish_and_invalidate(u->job, JOB_DONE, true, false); else if (u->job->state == JOB_RUNNING && ns != UNIT_ACTIVATING) { unexpected = true; if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true); + job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false); } break; @@ -1910,12 +1934,12 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su if (u->job->state == JOB_RUNNING) { if (ns == UNIT_ACTIVE) - job_finish_and_invalidate(u->job, reload_success ? JOB_DONE : JOB_FAILED, true); + job_finish_and_invalidate(u->job, reload_success ? JOB_DONE : JOB_FAILED, true, false); else if (ns != UNIT_ACTIVATING && ns != UNIT_RELOADING) { unexpected = true; if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true); + job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false); } } @@ -1926,10 +1950,10 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su case JOB_TRY_RESTART: if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - job_finish_and_invalidate(u->job, JOB_DONE, true); + job_finish_and_invalidate(u->job, JOB_DONE, true, false); else if (u->job->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) { unexpected = true; - job_finish_and_invalidate(u->job, JOB_FAILED, true); + job_finish_and_invalidate(u->job, JOB_FAILED, true, false); } break; @@ -1941,7 +1965,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su } else unexpected = true; - if (m->n_reloading <= 0) { + if (!MANAGER_IS_RELOADING(m)) { /* If this state change happened without being * requested by a job, then let's retroactively start @@ -1978,7 +2002,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su if (u->type == UNIT_SERVICE && !UNIT_IS_ACTIVE_OR_RELOADING(os) && - m->n_reloading <= 0) { + !MANAGER_IS_RELOADING(m)) { /* Write audit record if we have just finished starting up */ manager_send_unit_audit(m, u, AUDIT_SERVICE_START, true); u->in_audit = true; @@ -1995,7 +2019,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su if (u->type == UNIT_SERVICE && UNIT_IS_INACTIVE_OR_FAILED(ns) && !UNIT_IS_INACTIVE_OR_FAILED(os) && - m->n_reloading <= 0) { + !MANAGER_IS_RELOADING(m)) { /* Hmm, if there was no start record written * write it now, so that we always have a nice @@ -2016,7 +2040,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su manager_recheck_journal(m); unit_trigger_notify(u); - if (u->manager->n_reloading <= 0) { + if (!MANAGER_IS_RELOADING(u->manager)) { /* Maybe we finished startup and are now ready for * being stopped because unneeded? */ unit_check_unneeded(u); @@ -2378,9 +2402,11 @@ int unit_set_slice(Unit *u, Unit *slice) { if (UNIT_DEREF(u->slice) == slice) return 0; - if (UNIT_ISSET(u->slice)) + /* Disallow slice changes if @u is already bound to cgroups */ + if (UNIT_ISSET(u->slice) && u->cgroup_realized) return -EBUSY; + unit_ref_unset(&u->slice); unit_ref_set(&u->slice, slice); return 1; } @@ -2413,7 +2439,7 @@ int unit_set_default_slice(Unit *u) { if (!escaped) return -ENOMEM; - if (u->manager->running_as == MANAGER_SYSTEM) + if (MANAGER_IS_SYSTEM(u->manager)) b = strjoin("system-", escaped, ".slice", NULL); else b = strappend(escaped, ".slice"); @@ -2423,7 +2449,7 @@ int unit_set_default_slice(Unit *u) { slice_name = b; } else slice_name = - u->manager->running_as == MANAGER_SYSTEM && !unit_has_name(u, SPECIAL_INIT_SCOPE) + MANAGER_IS_SYSTEM(u->manager) && !unit_has_name(u, SPECIAL_INIT_SCOPE) ? SPECIAL_SYSTEM_SLICE : SPECIAL_ROOT_SLICE; @@ -2493,12 +2519,11 @@ int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name) { return -EBUSY; match = strjoina("type='signal'," - "sender='org.freedesktop.DBus'," - "path='/org/freedesktop/DBus'," - "interface='org.freedesktop.DBus'," - "member='NameOwnerChanged'," - "arg0='", name, "'", - NULL); + "sender='org.freedesktop.DBus'," + "path='/org/freedesktop/DBus'," + "interface='org.freedesktop.DBus'," + "member='NameOwnerChanged'," + "arg0='", name, "'"); return sd_bus_add_match(bus, &u->match_bus_slot, match, signal_name_owner_changed, u); } @@ -2884,7 +2909,7 @@ int unit_add_node_link(Unit *u, const char *what, bool wants, UnitDependency dep return r; r = unit_add_two_dependencies(u, UNIT_AFTER, - u->manager->running_as == MANAGER_SYSTEM ? dep : UNIT_WANTS, + MANAGER_IS_SYSTEM(u->manager) ? dep : UNIT_WANTS, device, true); if (r < 0) return r; @@ -2924,59 +2949,47 @@ int unit_coldplug(Unit *u) { return 0; } -bool unit_need_daemon_reload(Unit *u) { - _cleanup_strv_free_ char **t = NULL; - char **path; +static bool fragment_mtime_newer(const char *path, usec_t mtime) { struct stat st; - unsigned loaded_cnt, current_cnt; - assert(u); + if (!path) + return false; - if (u->fragment_path) { - zero(st); - if (stat(u->fragment_path, &st) < 0) - /* What, cannot access this anymore? */ - return true; + if (stat(path, &st) < 0) + /* What, cannot access this anymore? */ + return true; - if (u->fragment_mtime > 0 && - timespec_load(&st.st_mtim) != u->fragment_mtime) - return true; - } + if (mtime > 0) + /* For non-empty files check the mtime */ + return timespec_load(&st.st_mtim) > mtime; + else if (!null_or_empty(&st)) + /* For masked files check if they are still so */ + return true; - if (u->source_path) { - zero(st); - if (stat(u->source_path, &st) < 0) - return true; + return false; +} - if (u->source_mtime > 0 && - timespec_load(&st.st_mtim) != u->source_mtime) - return true; - } +bool unit_need_daemon_reload(Unit *u) { + _cleanup_strv_free_ char **t = NULL; + char **path; - (void) unit_find_dropin_paths(u, &t); - loaded_cnt = strv_length(t); - current_cnt = strv_length(u->dropin_paths); + assert(u); - if (loaded_cnt == current_cnt) { - if (loaded_cnt == 0) - return false; + if (fragment_mtime_newer(u->fragment_path, u->fragment_mtime)) + return true; - if (strv_overlap(u->dropin_paths, t)) { - STRV_FOREACH(path, u->dropin_paths) { - zero(st); - if (stat(*path, &st) < 0) - return true; + if (fragment_mtime_newer(u->source_path, u->source_mtime)) + return true; - if (u->dropin_mtime > 0 && - timespec_load(&st.st_mtim) > u->dropin_mtime) - return true; - } + (void) unit_find_dropin_paths(u, &t); + if (!strv_equal(u->dropin_paths, t)) + return true; - return false; - } else + STRV_FOREACH(path, u->dropin_paths) + if (fragment_mtime_newer(*path, u->dropin_mtime)) return true; - } else - return true; + + return false; } void unit_reset_failed(Unit *u) { @@ -3044,8 +3057,7 @@ bool unit_active_or_pending(Unit *u) { int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error) { assert(u); assert(w >= 0 && w < _KILL_WHO_MAX); - assert(signo > 0); - assert(signo < _NSIG); + assert(SIGNAL_VALID(signo)); if (!UNIT_VTABLE(u)->kill) return -EOPNOTSUPP; @@ -3162,7 +3174,7 @@ UnitFileState unit_get_unit_file_state(Unit *u) { if (u->unit_file_state < 0 && u->fragment_path) { r = unit_file_get_state( - u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, + u->manager->unit_file_scope, NULL, basename(u->fragment_path), &u->unit_file_state); @@ -3178,7 +3190,7 @@ int unit_get_unit_file_preset(Unit *u) { if (u->unit_file_preset < 0 && u->fragment_path) u->unit_file_preset = unit_file_query_preset( - u->manager->running_as == MANAGER_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, + u->manager->unit_file_scope, NULL, basename(u->fragment_path)); @@ -3203,6 +3215,10 @@ void unit_ref_unset(UnitRef *ref) { if (!ref->unit) return; + /* We are about to drop a reference to the unit, make sure the garbage collection has a look at it as it might + * be unreferenced now. */ + unit_add_to_gc_queue(ref->unit); + LIST_REMOVE(refs, ref->unit->refs, ref); ref->unit = NULL; } @@ -3229,7 +3245,7 @@ int unit_patch_contexts(Unit *u) { return -ENOMEM; } - if (u->manager->running_as == MANAGER_USER && + if (MANAGER_IS_USER(u->manager) && !ec->working_directory) { r = get_home_dir(&ec->working_directory); @@ -3241,7 +3257,7 @@ int unit_patch_contexts(Unit *u) { ec->working_directory_missing_ok = true; } - if (u->manager->running_as == MANAGER_USER && + if (MANAGER_IS_USER(u->manager) && (ec->syscall_whitelist || !set_isempty(ec->syscall_filter) || !set_isempty(ec->syscall_archs) || @@ -3319,59 +3335,62 @@ ExecRuntime *unit_get_exec_runtime(Unit *u) { return *(ExecRuntime**) ((uint8_t*) u + offset); } -static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient, char **dir) { +static const char* unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode) { assert(u); - if (u->manager->running_as == MANAGER_USER) { - int r; + if (!IN_SET(mode, UNIT_RUNTIME, UNIT_PERSISTENT)) + return NULL; - if (mode == UNIT_PERSISTENT && !transient) - r = user_config_home(dir); - else - r = user_runtime_dir(dir); - if (r == 0) - return -ENOENT; + if (u->transient) /* Redirect drop-ins for transient units always into the transient directory. */ + return u->manager->lookup_paths.transient; - return r; - } + if (mode == UNIT_RUNTIME) + return u->manager->lookup_paths.runtime_control; - if (mode == UNIT_PERSISTENT && !transient) - *dir = strdup("/etc/systemd/system"); - else - *dir = strdup("/run/systemd/system"); - if (!*dir) - return -ENOMEM; + if (mode == UNIT_PERSISTENT) + return u->manager->lookup_paths.persistent_control; - return 0; + return NULL; } int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) { - - _cleanup_free_ char *dir = NULL, *p = NULL, *q = NULL; + _cleanup_free_ char *p = NULL, *q = NULL; + const char *dir, *prefixed; int r; assert(u); + if (u->transient_file) { + /* When this is a transient unit file in creation, then let's not create a new drop-in but instead + * write to the transient unit file. */ + fputs(data, u->transient_file); + return 0; + } + if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) return 0; - r = unit_drop_in_dir(u, mode, u->transient, &dir); - if (r < 0) - return r; + dir = unit_drop_in_dir(u, mode); + if (!dir) + return -EINVAL; + + prefixed = strjoina("# This is a drop-in unit file extension, created via \"systemctl set-property\" or an equivalent operation. Do not edit.\n", + data); - r = write_drop_in(dir, u->id, 50, name, data); + r = drop_in_file(dir, u->id, 50, name, &p, &q); if (r < 0) return r; - r = drop_in_file(dir, u->id, 50, name, &p, &q); + (void) mkdir_p(p, 0755); + r = write_string_file_atomic_label(q, prefixed); if (r < 0) return r; - r = strv_extend(&u->dropin_paths, q); + r = strv_push(&u->dropin_paths, q); if (r < 0) return r; + q = NULL; - strv_sort(u->dropin_paths); strv_uniq(u->dropin_paths); u->dropin_mtime = now(CLOCK_REALTIME); @@ -3402,7 +3421,7 @@ int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *n } int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) { - _cleanup_free_ char *ndata = NULL; + const char *ndata; assert(u); assert(name); @@ -3414,9 +3433,7 @@ int unit_write_drop_in_private(Unit *u, UnitSetPropertiesMode mode, const char * if (!IN_SET(mode, UNIT_PERSISTENT, UNIT_RUNTIME)) return 0; - ndata = strjoin("[", UNIT_VTABLE(u)->private_section, "]\n", data, NULL); - if (!ndata) - return -ENOMEM; + ndata = strjoina("[", UNIT_VTABLE(u)->private_section, "]\n", data); return unit_write_drop_in(u, mode, name, ndata); } @@ -3444,24 +3461,51 @@ int unit_write_drop_in_private_format(Unit *u, UnitSetPropertiesMode mode, const } int unit_make_transient(Unit *u) { + FILE *f; + char *path; + assert(u); if (!UNIT_VTABLE(u)->can_transient) return -EOPNOTSUPP; - u->load_state = UNIT_STUB; - u->load_error = 0; - u->transient = true; + path = strjoin(u->manager->lookup_paths.transient, "/", u->id, NULL); + if (!path) + return -ENOMEM; + + /* Let's open the file we'll write the transient settings into. This file is kept open as long as we are + * creating the transient, and is closed in unit_load(), as soon as we start loading the file. */ + + RUN_WITH_UMASK(0022) { + f = fopen(path, "we"); + if (!f) { + free(path); + return -errno; + } + } + + if (u->transient_file) + fclose(u->transient_file); + u->transient_file = f; + + free(u->fragment_path); + u->fragment_path = path; - u->fragment_path = mfree(u->fragment_path); u->source_path = mfree(u->source_path); u->dropin_paths = strv_free(u->dropin_paths); u->fragment_mtime = u->source_mtime = u->dropin_mtime = 0; + u->load_state = UNIT_STUB; + u->load_error = 0; + u->transient = true; + unit_add_to_dbus_queue(u); unit_add_to_gc_queue(u); unit_add_to_load_queue(u); + fputs("# This is a transient unit file, created programmatically via the systemd API. Do not edit.\n", + u->transient_file); + return 0; } @@ -3553,7 +3597,7 @@ int unit_kill_context( * cases. It doesn't work at all in * containers, and outside of containers it * can be confused easily by left-over - * directories in the cgroup -- which however + * directories in the cgroup — which however * should not exist in non-delegated units. On * the unified hierarchy that's different, * there we get proper events. Hence rely on @@ -3754,3 +3798,21 @@ bool unit_is_pristine(Unit *u) { u->job || u->merged_into); } + +pid_t unit_control_pid(Unit *u) { + assert(u); + + if (UNIT_VTABLE(u)->control_pid) + return UNIT_VTABLE(u)->control_pid(u); + + return 0; +} + +pid_t unit_main_pid(Unit *u) { + assert(u); + + if (UNIT_VTABLE(u)->main_pid) + return UNIT_VTABLE(u)->main_pid(u); + + return 0; +} diff --git a/src/core/unit.h b/src/core/unit.h index 601e763ce2..08a927962d 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -95,6 +95,9 @@ struct Unit { usec_t source_mtime; usec_t dropin_mtime; + /* If this is a transient unit we are currently writing, this is where we are writing it to */ + FILE *transient_file; + /* If there is something to do with this unit, then this is the installed job for it */ Job *job; @@ -183,6 +186,7 @@ struct Unit { /* Counterparts in the cgroup filesystem */ char *cgroup_path; CGroupMask cgroup_realized_mask; + CGroupMask cgroup_enabled_mask; CGroupMask cgroup_subtree_mask; CGroupMask cgroup_members_mask; int cgroup_inotify_wd; @@ -387,6 +391,12 @@ struct UnitVTable { /* Returns the next timeout of a unit */ int (*get_timeout)(Unit *u, usec_t *timeout); + /* Returns the main PID if there is any defined, or 0. */ + pid_t (*main_pid)(Unit *u); + + /* Returns the main PID if there is any defined, or 0. */ + pid_t (*control_pid)(Unit *u); + /* This is called for each unit type and should be used to * enumerate existing devices and load them. However, * everything that is loaded here should still stay in @@ -407,12 +417,6 @@ struct UnitVTable { /* The strings to print in status messages */ UnitStatusMessageFormats status_message_formats; - /* Can units of this type have multiple names? */ - bool no_alias:1; - - /* Instances make no sense for this type */ - bool no_instances:1; - /* True if transient units of this type are OK */ bool can_transient:1; }; @@ -598,6 +602,9 @@ bool unit_type_supported(UnitType t); bool unit_is_pristine(Unit *u); +pid_t unit_control_pid(Unit *u); +pid_t unit_main_pid(Unit *u); + static inline bool unit_supported(Unit *u) { return unit_type_supported(u->type); } @@ -605,6 +612,8 @@ static inline bool unit_supported(Unit *u) { void unit_warn_if_dir_nonempty(Unit *u, const char* where); int unit_fail_if_symlink(Unit *u, const char* where); +int unit_start_limit_test(Unit *u); + /* Macros which append UNIT= or USER_UNIT= to the message */ #define log_unit_full(unit, level, error, ...) \ diff --git a/src/core/user.conf b/src/core/user.conf index 87c8164378..b427f1ef6d 100644 --- a/src/core/user.conf +++ b/src/core/user.conf @@ -23,7 +23,7 @@ #DefaultTimeoutStartSec=90s #DefaultTimeoutStopSec=90s #DefaultRestartSec=100ms -#DefaultStartLimitInterval=10s +#DefaultStartLimitIntervalSec=10s #DefaultStartLimitBurst=5 #DefaultEnvironment= #DefaultLimitCPU= diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 085909c20c..999de63900 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -49,6 +49,7 @@ #include "journald-native.h" #include "log.h" #include "macro.h" +#include "missing.h" #include "mkdir.h" #include "parse-util.h" #include "process-util.h" @@ -212,6 +213,10 @@ static int fix_xattr(int fd, const char *context[_CONTEXT_MAX]) { #define filename_escape(s) xescape((s), "./ ") +static inline const char *coredump_tmpfile_name(const char *s) { + return s ? s : "(unnamed temporary file)"; +} + static int fix_permissions( int fd, const char *filename, @@ -219,8 +224,9 @@ static int fix_permissions( const char *context[_CONTEXT_MAX], uid_t uid) { + int r; + assert(fd >= 0); - assert(filename); assert(target); assert(context); @@ -230,10 +236,11 @@ static int fix_permissions( (void) fix_xattr(fd, context); if (fsync(fd) < 0) - return log_error_errno(errno, "Failed to sync coredump %s: %m", filename); + return log_error_errno(errno, "Failed to sync coredump %s: %m", coredump_tmpfile_name(filename)); - if (rename(filename, target) < 0) - return log_error_errno(errno, "Failed to rename coredump %s -> %s: %m", filename, target); + r = link_tmpfile(fd, filename, target); + if (r < 0) + return log_error_errno(r, "Failed to move coredump %s into place: %m", target); return 0; } @@ -335,15 +342,11 @@ static int save_external_coredump( if (r < 0) return log_error_errno(r, "Failed to determine coredump file name: %m"); - r = tempfn_random(fn, NULL, &tmp); - if (r < 0) - return log_error_errno(r, "Failed to determine temporary file name: %m"); - mkdir_p_label("/var/lib/systemd/coredump", 0755); - fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640); + fd = open_tmpfile_linkable(fn, O_RDWR|O_CLOEXEC, &tmp); if (fd < 0) - return log_error_errno(errno, "Failed to create coredump file %s: %m", tmp); + return log_error_errno(fd, "Failed to create temporary file for coredump %s: %m", fn); r = copy_bytes(input_fd, fd, max_size, false); if (r == -EFBIG) { @@ -358,12 +361,12 @@ static int save_external_coredump( } if (fstat(fd, &st) < 0) { - log_error_errno(errno, "Failed to fstat coredump %s: %m", tmp); + log_error_errno(errno, "Failed to fstat coredump %s: %m", coredump_tmpfile_name(tmp)); goto fail; } if (lseek(fd, 0, SEEK_SET) == (off_t) -1) { - log_error_errno(errno, "Failed to seek on %s: %m", tmp); + log_error_errno(errno, "Failed to seek on %s: %m", coredump_tmpfile_name(tmp)); goto fail; } @@ -381,21 +384,15 @@ static int save_external_coredump( goto uncompressed; } - r = tempfn_random(fn_compressed, NULL, &tmp_compressed); - if (r < 0) { - log_error_errno(r, "Failed to determine temporary file name for %s: %m", fn_compressed); - goto uncompressed; - } - - fd_compressed = open(tmp_compressed, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640); + fd_compressed = open_tmpfile_linkable(fn_compressed, O_RDWR|O_CLOEXEC, &tmp_compressed); if (fd_compressed < 0) { - log_error_errno(errno, "Failed to create file %s: %m", tmp_compressed); + log_error_errno(fd_compressed, "Failed to create temporary file for coredump %s: %m", fn_compressed); goto uncompressed; } r = compress_stream(fd, fd_compressed, -1); if (r < 0) { - log_error_errno(r, "Failed to compress %s: %m", tmp_compressed); + log_error_errno(r, "Failed to compress %s: %m", coredump_tmpfile_name(tmp_compressed)); goto fail_compressed; } @@ -404,7 +401,8 @@ static int save_external_coredump( goto fail_compressed; /* OK, this worked, we can get rid of the uncompressed version now */ - unlink_noerrno(tmp); + if (tmp) + unlink_noerrno(tmp); *ret_filename = fn_compressed; /* compressed */ *ret_node_fd = fd_compressed; /* compressed */ @@ -417,7 +415,8 @@ static int save_external_coredump( return 0; fail_compressed: - (void) unlink(tmp_compressed); + if (tmp_compressed) + (void) unlink(tmp_compressed); } uncompressed: @@ -438,7 +437,8 @@ uncompressed: return 0; fail: - (void) unlink(tmp); + if (tmp) + (void) unlink(tmp); return r; } @@ -739,15 +739,16 @@ static int process_socket(int fd) { .msg_iovlen = 1, }; ssize_t n; - int l; + ssize_t l; if (!GREEDY_REALLOC(iovec, n_iovec_allocated, n_iovec + 3)) { r = log_oom(); goto finish; } - if (ioctl(fd, FIONREAD, &l) < 0) { - r = log_error_errno(errno, "FIONREAD failed: %m"); + l = next_datagram_size_fd(fd); + if (l < 0) { + r = log_error_errno(l, "Failed to determine datagram size to read: %m"); goto finish; } @@ -847,7 +848,7 @@ static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd) if (fd < 0) return log_error_errno(errno, "Failed to create coredump socket: %m"); - if (connect(fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)) < 0) + if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) return log_error_errno(errno, "Failed to connect to coredump service: %m"); for (i = 0; i < n_iovec; i++) { @@ -1095,7 +1096,7 @@ static int process_kernel(int argc, char* argv[]) { IOVEC_SET_STRING(iovec[n_iovec++], core_environ); } - core_timestamp = strjoina("COREDUMP_TIMESTAMP=", context[CONTEXT_TIMESTAMP], "000000", NULL); + core_timestamp = strjoina("COREDUMP_TIMESTAMP=", context[CONTEXT_TIMESTAMP], "000000"); IOVEC_SET_STRING(iovec[n_iovec++], core_timestamp); IOVEC_SET_STRING(iovec[n_iovec++], "MESSAGE_ID=fc2e22bc6ee647b6b90729ab34a250b1"); diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index 0034a1a0ac..27b1e0fb3f 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -54,7 +54,7 @@ static enum { } arg_action = ACTION_LIST; static const char* arg_field = NULL; static const char *arg_directory = NULL; -static int arg_no_pager = false; +static bool arg_no_pager = false; static int arg_no_legend = false; static int arg_one = false; static FILE* arg_output = NULL; @@ -664,7 +664,7 @@ static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) { #endif } else { if (r == -ENOENT) - log_error("Cannot retrieve coredump from journal nor disk."); + log_error("Cannot retrieve coredump from journal or disk."); else log_error_errno(r, "Failed to retrieve COREDUMP field: %m"); goto error; @@ -852,9 +852,7 @@ int main(int argc, char *argv[]) { case ACTION_LIST: case ACTION_INFO: - if (!arg_no_pager) - pager_open(false); - + pager_open(arg_no_pager, false); r = dump_list(j); break; diff --git a/src/coredump/stacktrace.c b/src/coredump/stacktrace.c index 68806992fc..cc4dad9465 100644 --- a/src/coredump/stacktrace.c +++ b/src/coredump/stacktrace.c @@ -91,7 +91,7 @@ static int frame_callback(Dwfl_Frame *frame, void *userdata) { } fprintf(c->f, "#%-2u 0x%016" PRIx64 " %s (%s)\n", c->n_frame, (uint64_t) pc, strna(symbol), strna(fname)); - c->n_frame ++; + c->n_frame++; return DWARF_CB_OK; } @@ -117,7 +117,7 @@ static int thread_callback(Dwfl_Thread *thread, void *userdata) { if (dwfl_thread_getframes(thread, frame_callback, c) < 0) return DWARF_CB_ABORT; - c->n_thread ++; + c->n_thread++; return DWARF_CB_OK; } diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 2ef966257a..9927621ea0 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -719,8 +719,12 @@ int main(int argc, char *argv[]) { int k; k = crypt_init_by_name(&cd, argv[2]); - if (k) { - log_error_errno(k, "crypt_init() failed: %m"); + if (k == -ENODEV) { + log_info("Volume %s already inactive.", argv[2]); + r = EXIT_SUCCESS; + goto finish; + } else if (k) { + log_error_errno(k, "crypt_init_by_name() failed: %m"); goto finish; } diff --git a/src/delta/delta.c b/src/delta/delta.c index a54fc89de6..f32744def2 100644 --- a/src/delta/delta.c +++ b/src/delta/delta.c @@ -85,14 +85,6 @@ static enum { (SHOW_MASKED | SHOW_EQUIVALENT | SHOW_REDIRECTED | SHOW_OVERRIDDEN | SHOW_EXTENDED) } arg_flags = 0; -static void pager_open_if_enabled(void) { - - if (arg_no_pager) - return; - - pager_open(false); -} - static int equivalent(const char *a, const char *b) { _cleanup_free_ char *x = NULL, *y = NULL; @@ -113,7 +105,7 @@ static int notify_override_masked(const char *top, const char *bottom) { printf("%s%s%s %s %s %s\n", ansi_highlight_red(), "[MASKED]", ansi_normal(), - top, draw_special_char(DRAW_ARROW), bottom); + top, special_glyph(ARROW), bottom); return 1; } @@ -123,7 +115,7 @@ static int notify_override_equivalent(const char *top, const char *bottom) { printf("%s%s%s %s %s %s\n", ansi_highlight_green(), "[EQUIVALENT]", ansi_normal(), - top, draw_special_char(DRAW_ARROW), bottom); + top, special_glyph(ARROW), bottom); return 1; } @@ -133,7 +125,7 @@ static int notify_override_redirected(const char *top, const char *bottom) { printf("%s%s%s %s %s %s\n", ansi_highlight(), "[REDIRECTED]", ansi_normal(), - top, draw_special_char(DRAW_ARROW), bottom); + top, special_glyph(ARROW), bottom); return 1; } @@ -143,7 +135,7 @@ static int notify_override_overridden(const char *top, const char *bottom) { printf("%s%s%s %s %s %s\n", ansi_highlight(), "[OVERRIDDEN]", ansi_normal(), - top, draw_special_char(DRAW_ARROW), bottom); + top, special_glyph(ARROW), bottom); return 1; } @@ -153,7 +145,7 @@ static int notify_override_extended(const char *top, const char *bottom) { printf("%s%s%s %s %s %s\n", ansi_highlight(), "[EXTENDED]", ansi_normal(), - top, draw_special_char(DRAW_ARROW), bottom); + top, special_glyph(ARROW), bottom); return 1; } @@ -255,7 +247,7 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const return -ENOMEM; d = p + strlen(toppath) + 1; - log_debug("Adding at top: %s %s %s", d, draw_special_char(DRAW_ARROW), p); + log_debug("Adding at top: %s %s %s", d, special_glyph(ARROW), p); k = hashmap_put(top, d, p); if (k >= 0) { p = strdup(p); @@ -267,7 +259,7 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const return k; } - log_debug("Adding at bottom: %s %s %s", d, draw_special_char(DRAW_ARROW), p); + log_debug("Adding at bottom: %s %s %s", d, special_glyph(ARROW), p); free(hashmap_remove(bottom, d)); k = hashmap_put(bottom, d, p); if (k < 0) { @@ -291,7 +283,7 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const return -ENOMEM; log_debug("Adding to drops: %s %s %s %s %s", - unit, draw_special_char(DRAW_ARROW), basename(p), draw_special_char(DRAW_ARROW), p); + unit, special_glyph(ARROW), basename(p), special_glyph(ARROW), p); k = hashmap_put(h, basename(p), p); if (k < 0) { free(p); @@ -342,7 +334,7 @@ static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const ch if (!p) return -ENOMEM; - log_debug("Adding at top: %s %s %s", basename(p), draw_special_char(DRAW_ARROW), p); + log_debug("Adding at top: %s %s %s", basename(p), special_glyph(ARROW), p); k = hashmap_put(top, basename(p), p); if (k >= 0) { p = strdup(p); @@ -353,7 +345,7 @@ static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const ch return k; } - log_debug("Adding at bottom: %s %s %s", basename(p), draw_special_char(DRAW_ARROW), p); + 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) { @@ -431,7 +423,7 @@ finish: hashmap_free_free(top); hashmap_free_free(bottom); - HASHMAP_FOREACH_KEY(h, key, drops, i){ + HASHMAP_FOREACH_KEY(h, key, drops, i) { hashmap_free_free(hashmap_remove(drops, key)); hashmap_remove(drops, key); free(key); @@ -610,7 +602,7 @@ int main(int argc, char *argv[]) { else if (arg_diff) arg_flags |= SHOW_OVERRIDDEN; - pager_open_if_enabled(); + pager_open(arg_no_pager, false); if (optind < argc) { int i; diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 7790ab865d..3df72460ef 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -160,7 +160,7 @@ static int prompt_loop(const char *text, char **l, bool (*is_valid)(const char * _cleanup_free_ char *p = NULL; unsigned u; - r = ask_string(&p, "%s %s (empty to skip): ", draw_special_char(DRAW_TRIANGULAR_BULLET), text); + r = ask_string(&p, "%s %s (empty to skip): ", special_glyph(TRIANGULAR_BULLET), text); if (r < 0) return log_error_errno(r, "Failed to query user: %m"); @@ -245,7 +245,7 @@ static int process_locale(void) { int r; etc_localeconf = prefix_roota(arg_root, "/etc/locale.conf"); - if (faccessat(AT_FDCWD, etc_localeconf, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) + if (laccess(etc_localeconf, F_OK) >= 0) return 0; if (arg_copy_locale && arg_root) { @@ -319,7 +319,7 @@ static int process_timezone(void) { int r; etc_localtime = prefix_roota(arg_root, "/etc/localtime"); - if (faccessat(AT_FDCWD, etc_localtime, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) + if (laccess(etc_localtime, F_OK) >= 0) return 0; if (arg_copy_timezone && arg_root) { @@ -371,7 +371,7 @@ static int prompt_hostname(void) { for (;;) { _cleanup_free_ char *h = NULL; - r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", draw_special_char(DRAW_TRIANGULAR_BULLET)); + r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", special_glyph(TRIANGULAR_BULLET)); if (r < 0) return log_error_errno(r, "Failed to query hostname: %m"); @@ -399,7 +399,7 @@ static int process_hostname(void) { int r; etc_hostname = prefix_roota(arg_root, "/etc/hostname"); - if (faccessat(AT_FDCWD, etc_hostname, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) + if (laccess(etc_hostname, F_OK) >= 0) return 0; r = prompt_hostname(); @@ -424,7 +424,7 @@ static int process_machine_id(void) { int r; etc_machine_id = prefix_roota(arg_root, "/etc/machine-id"); - if (faccessat(AT_FDCWD, etc_machine_id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) + if (laccess(etc_machine_id, F_OK) >= 0) return 0; if (sd_id128_equal(arg_machine_id, SD_ID128_NULL)) @@ -450,14 +450,14 @@ static int prompt_root_password(void) { return 0; etc_shadow = prefix_roota(arg_root, "/etc/shadow"); - if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) + if (laccess(etc_shadow, F_OK) >= 0) return 0; print_welcome(); putchar('\n'); - msg1 = strjoina(draw_special_char(DRAW_TRIANGULAR_BULLET), " Please enter a new root password (empty to skip): "); - msg2 = strjoina(draw_special_char(DRAW_TRIANGULAR_BULLET), " Please enter new root password again: "); + msg1 = strjoina(special_glyph(TRIANGULAR_BULLET), " Please enter a new root password (empty to skip): "); + msg2 = strjoina(special_glyph(TRIANGULAR_BULLET), " Please enter new root password again: "); for (;;) { _cleanup_string_free_erase_ char *a = NULL, *b = NULL; @@ -533,7 +533,7 @@ static int process_root_password(void) { int r; etc_shadow = prefix_roota(arg_root, "/etc/shadow"); - if (faccessat(AT_FDCWD, etc_shadow, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) + if (laccess(etc_shadow, F_OK) >= 0) return 0; mkdir_parents(etc_shadow, 0755); diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c index 6f56066da8..d32e1d923e 100644 --- a/src/fsck/fsck.c +++ b/src/fsck/fsck.c @@ -262,7 +262,7 @@ static int fsck_progress_socket(void) { if (fd < 0) return log_warning_errno(errno, "socket(): %m"); - if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) { + if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { r = log_full_errno(errno == ECONNREFUSED || errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno, "Failed to connect to progress socket %s, ignoring: %m", sa.un.sun_path); safe_close(fd); diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 97a48764ae..108522873e 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -336,8 +336,8 @@ static int add_mount( if (r < 0) return log_error_errno(r, "Failed to write unit file %s: %m", unit); - if (!noauto) { - lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL); + if (!noauto && !automount) { + lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", name, NULL); if (!lnk) return log_oom(); @@ -379,6 +379,7 @@ static int add_mount( } fprintf(f, + "\n" "[Automount]\n" "Where=%s\n", where); @@ -488,6 +489,7 @@ static int parse_fstab(bool initrd) { static int add_sysroot_mount(void) { _cleanup_free_ char *what = NULL; const char *opts; + int r; if (isempty(arg_root_what)) { log_debug("Could not find a root= entry on the kernel command line."); @@ -507,6 +509,13 @@ static int add_sysroot_mount(void) { opts = arg_root_options; log_debug("Found entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype)); + + if (is_device_path(what)) { + r = generator_write_initrd_root_device_deps(arg_dest, what); + if (r < 0) + return r; + } + return add_mount(what, "/sysroot", arg_root_fstype, diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index af96adec06..a4938a7c3a 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -956,6 +956,12 @@ static int add_root_mount(void) { * wait for a root device to show up. A udev rule will create * the link for us under the right name. */ + if (in_initrd()) { + r = generator_write_initrd_root_device_deps(arg_dest, "/dev/gpt-auto-root"); + if (r < 0) + return 0; + } + return add_mount( "root", "/dev/gpt-auto-root", diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index c37e32e96b..d11756e615 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -706,7 +706,7 @@ int main(int argc, char *argv[]) { log_open(); umask(0022); - mac_selinux_init("/etc"); + mac_selinux_init(); if (argc != 1) { log_error("This program takes no arguments."); diff --git a/src/import/aufs-util.c b/src/import/aufs-util.c deleted file mode 100644 index 44aa6e2170..0000000000 --- a/src/import/aufs-util.c +++ /dev/null @@ -1,73 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <ftw.h> - -#include "aufs-util.h" -#include "rm-rf.h" -#include "string-util.h" -#include "util.h" - -static int nftw_cb( - const char *fpath, - const struct stat *sb, - int flag, - struct FTW *ftwbuf) { - - const char *fn, *original; - char *p; - int r; - - fn = fpath + ftwbuf->base; - - /* We remove all whiteout files, and all whiteouts */ - - original = startswith(fn, ".wh."); - if (!original) - return FTW_CONTINUE; - - log_debug("Removing whiteout indicator %s.", fpath); - r = rm_rf(fpath, REMOVE_ROOT|REMOVE_PHYSICAL); - if (r < 0) - return FTW_STOP; - - if (!startswith(fn, ".wh..wh.")) { - - p = alloca(ftwbuf->base + strlen(original)); - strcpy(mempcpy(p, fpath, ftwbuf->base), original); - - log_debug("Removing deleted file %s.", p); - r = rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL); - if (r < 0) - return FTW_STOP; - } - - return FTW_CONTINUE; -} - -int aufs_resolve(const char *path) { - int r; - - errno = 0; - r = nftw(path, nftw_cb, 64, FTW_MOUNT|FTW_PHYS|FTW_ACTIONRETVAL); - if (r == FTW_STOP) - return errno > 0 ? -errno : -EIO; - - return 0; -} diff --git a/src/import/curl-util.c b/src/import/curl-util.c index a04c8c49ff..6990c47f48 100644 --- a/src/import/curl-util.c +++ b/src/import/curl-util.c @@ -137,7 +137,7 @@ static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, v if (sd_event_add_io(g->event, &io, fd, events, curl_glue_on_io, g) < 0) return -1; - sd_event_source_set_description(io, "curl-io"); + (void) sd_event_source_set_description(io, "curl-io"); r = hashmap_put(g->ios, FD_TO_PTR(s), io); if (r < 0) { @@ -204,7 +204,7 @@ static int curl_glue_timer_callback(CURLM *curl, long timeout_ms, void *userdata if (sd_event_add_time(g->event, &g->timer, clock_boottime_or_monotonic(), usec, 0, curl_glue_on_timer, g) < 0) return -1; - sd_event_source_set_description(g->timer, "curl-timer"); + (void) sd_event_source_set_description(g->timer, "curl-timer"); } return 0; diff --git a/src/import/import-common.c b/src/import/import-common.c index 18a30be36d..287a3382a1 100644 --- a/src/import/import-common.c +++ b/src/import/import-common.c @@ -136,7 +136,7 @@ int import_fork_tar_x(const char *path, pid_t *ret) { if (r < 0) log_error_errno(r, "Failed to drop capabilities, ignoring: %m"); - execlp("tar", "tar", "--numeric-owner", "-C", path, "-px", NULL); + execlp("tar", "tar", "--numeric-owner", "-C", path, "-px", "--xattrs", "--xattrs-include=*", NULL); log_error_errno(errno, "Failed to execute tar: %m"); _exit(EXIT_FAILURE); } @@ -210,7 +210,7 @@ int import_fork_tar_c(const char *path, pid_t *ret) { if (r < 0) log_error_errno(r, "Failed to drop capabilities, ignoring: %m"); - execlp("tar", "tar", "-C", path, "-c", ".", NULL); + execlp("tar", "tar", "-C", path, "-c", "--xattrs", "--xattrs-include=*", ".", NULL); log_error_errno(errno, "Failed to execute tar: %m"); _exit(EXIT_FAILURE); } diff --git a/src/import/importd.c b/src/import/importd.c index d2a5867a6e..956a82945c 100644 --- a/src/import/importd.c +++ b/src/import/importd.c @@ -677,7 +677,7 @@ static int manager_new(Manager **ret) { (void) mkdir_parents_label(sa.un.sun_path, 0755); (void) unlink(sa.un.sun_path); - if (bind(m->notify_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)) < 0) + if (bind(m->notify_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) return -errno; if (setsockopt(m->notify_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) diff --git a/src/import/pull-common.c b/src/import/pull-common.c index d301d4d79e..dc4e4667a9 100644 --- a/src/import/pull-common.c +++ b/src/import/pull-common.c @@ -330,7 +330,7 @@ int pull_verify(PullJob *main_job, _cleanup_close_ int sig_file = -1; const char *p, *line; char sig_file_path[] = "/tmp/sigXXXXXX", gpg_home[] = "/tmp/gpghomeXXXXXX"; - _cleanup_sigkill_wait_ pid_t pid = 0; + _cleanup_(sigkill_waitp) pid_t pid = 0; bool gpg_home_created = false; int r; diff --git a/src/import/pull-job.h b/src/import/pull-job.h index 998857035a..3a152a50e3 100644 --- a/src/import/pull-job.h +++ b/src/import/pull-job.h @@ -44,15 +44,6 @@ typedef enum PullJobState { #define PULL_JOB_IS_COMPLETE(j) (IN_SET((j)->state, PULL_JOB_DONE, PULL_JOB_FAILED)) -typedef enum PullJobCompression { - PULL_JOB_UNCOMPRESSED, - PULL_JOB_XZ, - PULL_JOB_GZIP, - PULL_JOB_BZIP2, - _PULL_JOB_COMPRESSION_MAX, - _PULL_JOB_COMPRESSION_INVALID = -1, -} PullJobCompression; - struct PullJob { PullJobState state; int error; diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c index 8a16602c3e..8993402821 100644 --- a/src/import/pull-raw.c +++ b/src/import/pull-raw.c @@ -355,10 +355,12 @@ static int raw_pull_make_local_copy(RawPull *i) { r = copy_file_atomic(i->settings_path, local_settings, 0644, i->force_local, 0); if (r == -EEXIST) log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings); - else if (r < 0 && r != -ENOENT) + else if (r == -ENOENT) + log_debug_errno(r, "Skipping creation of settings file, since none was found."); + else if (r < 0) log_warning_errno(r, "Failed to copy settings files %s, ignoring: %m", local_settings); else - log_info("Created new settings file '%s.nspawn'", i->local); + log_info("Created new settings file %s.", local_settings); } return 0; diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index afb13366f0..8c61c46f73 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -251,10 +251,12 @@ static int tar_pull_make_local_copy(TarPull *i) { r = copy_file_atomic(i->settings_path, local_settings, 0664, i->force_local, 0); if (r == -EEXIST) log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings); - else if (r < 0 && r != -ENOENT) + else if (r == -ENOENT) + log_debug_errno(r, "Skipping creation of settings file, since none was found."); + else if (r < 0) log_warning_errno(r, "Failed to copy settings files %s, ignoring: %m", local_settings); else - log_info("Created new settings file '%s.nspawn'", i->local); + log_info("Created new settings file %s.", local_settings); } return 0; diff --git a/src/initctl/initctl.c b/src/initctl/initctl.c index 3e57afb997..41b2237d16 100644 --- a/src/initctl/initctl.c +++ b/src/initctl/initctl.c @@ -314,7 +314,7 @@ static int server_init(Server *s, unsigned n_sockets) { f->fd = fd; LIST_PREPEND(fifo, s->fifos, f); f->server = s; - s->n_fifos ++; + s->n_fifos++; } r = bus_connect_system_systemd(&s->bus); diff --git a/src/journal-remote/browse.html b/src/journal-remote/browse.html index 3594f70c87..32848c7673 100644 --- a/src/journal-remote/browse.html +++ b/src/journal-remote/browse.html @@ -391,7 +391,7 @@ entry = document.getElementById("tableentry"); var buf = ""; - for (var key in d){ + for (var key in d) { var data = d[key]; if (data == null) diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index 60d897758b..4ad9184993 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -122,12 +122,14 @@ static int open_journal(RequestMeta *m) { } static int request_meta_ensure_tmp(RequestMeta *m) { + assert(m); + if (m->tmp) rewind(m->tmp); else { int fd; - fd = open_tmpfile("/tmp", O_RDWR|O_CLOEXEC); + fd = open_tmpfile_unlinkable("/tmp", O_RDWR|O_CLOEXEC); if (fd < 0) return fd; diff --git a/src/journal-remote/journal-remote-parse.c b/src/journal-remote/journal-remote-parse.c index 3864647eb7..9ba9ee3fc0 100644 --- a/src/journal-remote/journal-remote-parse.c +++ b/src/journal-remote/journal-remote-parse.c @@ -485,7 +485,7 @@ int process_source(RemoteSource *source, bool compress, bool seal) { } target = source->size; - while (target > 16 * LINE_CHUNK && remain < target / 2) + while (target > 16 * LINE_CHUNK && source->filled < target / 2) target /= 2; if (target < source->size) { char *tmp; diff --git a/src/journal-remote/journal-remote-parse.h b/src/journal-remote/journal-remote-parse.h index 0b8b6af736..1740a21f92 100644 --- a/src/journal-remote/journal-remote-parse.h +++ b/src/journal-remote/journal-remote-parse.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - #include "sd-event.h" #include "journal-remote-write.h" diff --git a/src/journal-remote/journal-remote-write.c b/src/journal-remote/journal-remote-write.c index 5fab74e5cc..7bba52566e 100644 --- a/src/journal-remote/journal-remote-write.c +++ b/src/journal-remote/journal-remote-write.c @@ -54,7 +54,7 @@ void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) { **********************************************************************/ static int do_rotate(JournalFile **f, bool compress, bool seal) { - int r = journal_file_rotate(f, compress, seal); + int r = journal_file_rotate(f, compress, seal, NULL); if (r < 0) { if (*f) log_error_errno(r, "Failed to rotate %s: %m", (*f)->path); diff --git a/src/journal-remote/journal-remote-write.h b/src/journal-remote/journal-remote-write.h index 6b645a353c..53ba45fc04 100644 --- a/src/journal-remote/journal-remote-write.h +++ b/src/journal-remote/journal-remote-write.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,9 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - - #include "journal-file.h" typedef struct RemoteServer RemoteServer; diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c index 3ce6fe27b3..35a1e55f9e 100644 --- a/src/journal-remote/journal-remote.c +++ b/src/journal-remote/journal-remote.c @@ -203,7 +203,7 @@ static int open_output(Writer *w, const char* host) { O_RDWR|O_CREAT, 0640, arg_compress, arg_seal, &w->metrics, - w->mmap, + w->mmap, NULL, NULL, &w->journal); if (r < 0) log_error_errno(r, "Failed to open output journal %s: %m", @@ -434,7 +434,7 @@ static int add_raw_socket(RemoteServer *s, int fd) { return r; fd_ = -1; - s->active ++; + s->active++; return 0; } @@ -742,7 +742,7 @@ static int setup_microhttpd_server(RemoteServer *s, goto error; } - s->active ++; + s->active++; return 0; error: diff --git a/src/journal-remote/journal-remote.h b/src/journal-remote/journal-remote.h index 6466a1c101..30ad7df996 100644 --- a/src/journal-remote/journal-remote.h +++ b/src/journal-remote/journal-remote.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,9 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - - #include "sd-event.h" #include "hashmap.h" diff --git a/src/journal-remote/journal-upload-journal.c b/src/journal-remote/journal-upload-journal.c index fc8f63c9e3..8ce8e1895e 100644 --- a/src/journal-remote/journal-upload-journal.c +++ b/src/journal-remote/journal-upload-journal.c @@ -25,6 +25,7 @@ #include "log.h" #include "utf8.h" #include "util.h" +#include "sd-daemon.h" /** * Write up to size bytes to buf. Return negative on error, and number of @@ -52,7 +53,7 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) { /* not enough space */ return pos; - u->entry_state ++; + u->entry_state++; if (pos + r == size) { /* exactly one character short, but we don't need it */ @@ -76,7 +77,7 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) { /* not enough space */ return pos; - u->entry_state ++; + u->entry_state++; if (r + pos == size) { /* exactly one character short, but we don't need it */ @@ -101,7 +102,7 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) { /* not enough space */ return pos; - u->entry_state ++; + u->entry_state++; if (r + pos == size) { /* exactly one character short, but we don't need it */ @@ -126,7 +127,7 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) { /* not enough space */ return pos; - u->entry_state ++; + u->entry_state++; if (r + pos == size) { /* exactly one character short, but we don't need it */ @@ -156,7 +157,7 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) { continue; } - u->entry_state ++; + u->entry_state++; } /* fall through */ case ENTRY_TEXT_FIELD: @@ -206,7 +207,7 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) { pos += len + 1; u->field_pos = len + 1; - u->entry_state ++; + u->entry_state++; } /* fall through */ case ENTRY_BINARY_FIELD_SIZE: { @@ -220,7 +221,7 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) { memcpy(buf + pos, &le64, 8); pos += 8; - u->entry_state ++; + u->entry_state++; continue; } @@ -230,8 +231,8 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) { return pos; buf[pos++] = '\n'; - u->entry_state ++; - u->entries_sent ++; + u->entry_state++; + u->entries_sent++; return pos; @@ -242,6 +243,22 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) { assert_not_reached("WTF?"); } +static inline void check_update_watchdog(Uploader *u) { + usec_t after; + usec_t elapsed_time; + + if (u->watchdog_usec <= 0) + return; + + after = now(CLOCK_MONOTONIC); + elapsed_time = usec_sub(after, u->watchdog_timestamp); + if (elapsed_time > u->watchdog_usec / 2) { + log_debug("Update watchdog timer"); + sd_notify(false, "WATCHDOG=1"); + u->watchdog_timestamp = after; + } +} + static size_t journal_input_callback(void *buf, size_t size, size_t nmemb, void *userp) { Uploader *u = userp; int r; @@ -252,6 +269,8 @@ static size_t journal_input_callback(void *buf, size_t size, size_t nmemb, void assert(u); assert(nmemb <= SSIZE_MAX / size); + check_update_watchdog(u); + j = u->journal; while (j && filled < size * nmemb) { diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c index 440563e7d3..4647cfdeb3 100644 --- a/src/journal-remote/journal-upload.c +++ b/src/journal-remote/journal-upload.c @@ -75,7 +75,7 @@ static void close_fd_input(Uploader *u); curl_easy_strerror(code)); \ cmd; \ } \ - } while(0) + } while (0) static size_t output_callback(char *buf, size_t size, @@ -463,6 +463,8 @@ static int setup_uploader(Uploader *u, const char *url, const char *state_file) if (r < 0) return log_error_errno(r, "Failed to set up signals: %m"); + (void) sd_watchdog_enabled(false, &u->watchdog_usec); + return load_cursor_state(u); } @@ -494,6 +496,7 @@ static int perform_upload(Uploader *u) { assert(u); + u->watchdog_timestamp = now(CLOCK_MONOTONIC); code = curl_easy_perform(u->easy); if (code) { if (u->error[0]) diff --git a/src/journal-remote/journal-upload.h b/src/journal-remote/journal-upload.h index b8cd04d527..5711905f86 100644 --- a/src/journal-remote/journal-upload.h +++ b/src/journal-remote/journal-upload.h @@ -4,6 +4,7 @@ #include "sd-event.h" #include "sd-journal.h" +#include "time-util.h" typedef enum { ENTRY_CURSOR = 0, /* Nothing actually written yet. */ @@ -48,6 +49,8 @@ typedef struct Uploader { size_t entries_sent; char *last_cursor, *current_cursor; + usec_t watchdog_timestamp; + usec_t watchdog_usec; } Uploader; #define JOURNAL_UPLOAD_POLL_TIMEOUT (10 * USEC_PER_SEC) diff --git a/src/journal-remote/microhttpd-util.h b/src/journal-remote/microhttpd-util.h index 70c4d29c0f..ea160f212b 100644 --- a/src/journal-remote/microhttpd-util.h +++ b/src/journal-remote/microhttpd-util.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - #include <microhttpd.h> #include <stdarg.h> diff --git a/src/journal/catalog.c b/src/journal/catalog.c index 164a3a15f2..886f6efd8b 100644 --- a/src/journal/catalog.c +++ b/src/journal/catalog.c @@ -164,14 +164,14 @@ static int finish_item( Hashmap *h, sd_id128_t id, const char *language, - char *payload) { + char *payload, size_t payload_size) { _cleanup_free_ CatalogItem *i = NULL; - _cleanup_free_ char *combined = NULL, *prev = NULL; - int r; + _cleanup_free_ char *prev = NULL, *combined = NULL; assert(h); assert(payload); + assert(payload_size > 0); i = new0(CatalogItem, 1); if (!i) @@ -184,23 +184,25 @@ static int finish_item( } prev = hashmap_get(h, i); - - /* Already have such an item, combine them */ if (prev) { + /* Already have such an item, combine them */ combined = combine_entries(payload, prev); if (!combined) return log_oom(); - r = hashmap_update(h, i, combined); - if (r < 0) - return r; - combined = NULL; - /* A new item */ + if (hashmap_update(h, i, combined) < 0) + return log_oom(); + combined = NULL; } else { - r = hashmap_put(h, i, payload); - if (r < 0) - return r; + /* A new item */ + combined = memdup(payload, payload_size + 1); + if (!combined) + return log_oom(); + + if (hashmap_put(h, i, combined) < 0) + return log_oom(); i = NULL; + combined = NULL; } return 0; @@ -215,7 +217,7 @@ int catalog_file_lang(const char* filename, char **lang) { beg = end - 1; while (beg > filename && *beg != '.' && *beg != '/' && end - beg < 32) - beg --; + beg--; if (*beg != '.' || end <= beg + 1) return 0; @@ -262,6 +264,7 @@ static int catalog_entry_lang(const char* filename, int line, int catalog_import_file(Hashmap *h, const char *path) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *payload = NULL; + size_t payload_size = 0, payload_allocated = 0; unsigned n = 0; sd_id128_t id; _cleanup_free_ char *deflang = NULL, *lang = NULL; @@ -283,8 +286,7 @@ int catalog_import_file(Hashmap *h, const char *path) { for (;;) { char line[LINE_MAX]; - size_t a, b, c; - char *t; + size_t line_len; if (!fgets(line, sizeof(line), f)) { if (feof(f)) @@ -323,17 +325,23 @@ int catalog_import_file(Hashmap *h, const char *path) { if (sd_id128_from_string(line + 2 + 1, &jd) >= 0) { if (got_id) { - r = finish_item(h, id, lang ?: deflang, payload); + if (payload_size == 0) { + log_error("[%s:%u] No payload text.", path, n); + return -EINVAL; + } + + r = finish_item(h, id, lang ?: deflang, payload, payload_size); if (r < 0) return r; - payload = NULL; lang = mfree(lang); + payload_size = 0; } if (with_language) { - t = strstrip(line + 2 + 1 + 32 + 1); + char *t; + t = strstrip(line + 2 + 1 + 32 + 1); r = catalog_entry_lang(path, n, t, deflang, &lang); if (r < 0) return r; @@ -343,9 +351,6 @@ int catalog_import_file(Hashmap *h, const char *path) { empty_line = false; id = jd; - if (payload) - payload[0] = '\0'; - continue; } } @@ -356,34 +361,30 @@ int catalog_import_file(Hashmap *h, const char *path) { return -EINVAL; } - a = payload ? strlen(payload) : 0; - b = strlen(line); - - c = a + (empty_line ? 1 : 0) + b + 1 + 1; - t = realloc(payload, c); - if (!t) + line_len = strlen(line); + if (!GREEDY_REALLOC(payload, payload_allocated, + payload_size + (empty_line ? 1 : 0) + line_len + 1 + 1)) return log_oom(); - if (empty_line) { - t[a] = '\n'; - memcpy(t + a + 1, line, b); - t[a+b+1] = '\n'; - t[a+b+2] = 0; - } else { - memcpy(t + a, line, b); - t[a+b] = '\n'; - t[a+b+1] = 0; - } + if (empty_line) + payload[payload_size++] = '\n'; + memcpy(payload + payload_size, line, line_len); + payload_size += line_len; + payload[payload_size++] = '\n'; + payload[payload_size] = '\0'; - payload = t; empty_line = false; } if (got_id) { - r = finish_item(h, id, lang ?: deflang, payload); + if (payload_size == 0) { + log_error("[%s:%u] No payload text.", path, n); + return -EINVAL; + } + + r = finish_item(h, id, lang ?: deflang, payload, payload_size); if (r < 0) return r; - payload = NULL; } return 0; diff --git a/src/journal/compress.c b/src/journal/compress.c index 1933b87b00..ba734b5561 100644 --- a/src/journal/compress.c +++ b/src/journal/compress.c @@ -17,6 +17,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <inttypes.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> @@ -111,7 +112,7 @@ int compress_blob_lz4(const void *src, uint64_t src_size, if (src_size < 9) return -ENOBUFS; - r = LZ4_compress_limitedOutput(src, dst + 8, src_size, (int) dst_alloc_size - 8); + r = LZ4_compress_limitedOutput(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8); if (r <= 0) return -ENOBUFS; @@ -175,7 +176,7 @@ int decompress_blob_xz(const void *src, uint64_t src_size, return -ENOMEM; s.avail_out = space - used; - s.next_out = *dst + used; + s.next_out = *(uint8_t**)dst + used; } *dst_size = space - s.avail_out; @@ -214,7 +215,7 @@ int decompress_blob_lz4(const void *src, uint64_t src_size, } else out = *dst; - r = LZ4_decompress_safe(src + 8, out, src_size - 8, size); + r = LZ4_decompress_safe((char*)src + 8, out, src_size - 8, size); if (r < 0 || r != size) return -EBADMSG; @@ -290,7 +291,7 @@ int decompress_startswith_xz(const void *src, uint64_t src_size, if (!(greedy_realloc(buffer, buffer_size, *buffer_size * 2, 1))) return -ENOMEM; - s.next_out = *buffer + *buffer_size - s.avail_out; + s.next_out = *(uint8_t**)buffer + *buffer_size - s.avail_out; } #else @@ -323,7 +324,7 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size, if (!(greedy_realloc(buffer, buffer_size, ALIGN_8(prefix_len + 1), 1))) return -ENOMEM; - r = LZ4_decompress_safe_partial(src + 8, *buffer, src_size - 8, + r = LZ4_decompress_safe_partial((char*)src + 8, *buffer, src_size - 8, prefix_len + 1, *buffer_size); if (r >= 0) size = (unsigned) r; @@ -498,7 +499,7 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) { total_out += n; if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) { - log_debug("Compressed stream longer than %zd bytes", max_bytes); + log_debug("Compressed stream longer than %"PRIu64" bytes", max_bytes); return -EFBIG; } @@ -649,7 +650,7 @@ int decompress_stream_lz4(int in, int out, uint64_t max_bytes) { total_out += produced; if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) { - log_debug("Decompressed stream longer than %zd bytes", max_bytes); + log_debug("Decompressed stream longer than %"PRIu64" bytes", max_bytes); r = -EFBIG; goto cleanup; } diff --git a/src/journal/fsprg.c b/src/journal/fsprg.c index 8f7e137e74..612b10f3a9 100644 --- a/src/journal/fsprg.c +++ b/src/journal/fsprg.c @@ -30,6 +30,7 @@ #include <string.h> #include "fsprg.h" +#include "gcrypt-util.h" #define ISVALID_SECPAR(secpar) (((secpar) % 16 == 0) && ((secpar) >= 16) && ((secpar) <= 16384)) #define VALIDATE_SECPAR(secpar) assert(ISVALID_SECPAR(secpar)); @@ -57,7 +58,7 @@ static gcry_mpi_t mpi_import(const void *buf, size_t buflen) { gcry_mpi_t h; unsigned len; - gcry_mpi_scan(&h, GCRYMPI_FMT_USG, buf, buflen, NULL); + assert_se(gcry_mpi_scan(&h, GCRYMPI_FMT_USG, buf, buflen, NULL) == 0); len = (gcry_mpi_get_nbits(h) + 7) / 8; assert(len <= buflen); assert(gcry_mpi_cmp_ui(h, 0) >= 0); @@ -206,20 +207,6 @@ static void CRT_compose(gcry_mpi_t *x, const gcry_mpi_t xp, const gcry_mpi_t xq, gcry_mpi_release(u); } -static void initialize_libgcrypt(void) { - const char *p; - if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) - return; - - p = gcry_check_version("1.4.5"); - assert(p); - - /* Turn off "secmem". Clients which whish to make use of this - * feature should initialize the library manually */ - gcry_control(GCRYCTL_DISABLE_SECMEM); - gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); -} - /******************************************************************************/ size_t FSPRG_mskinbytes(unsigned _secpar) { @@ -259,7 +246,7 @@ void FSPRG_GenMK(void *msk, void *mpk, const void *seed, size_t seedlen, unsigne VALIDATE_SECPAR(_secpar); secpar = _secpar; - initialize_libgcrypt(); + initialize_libgcrypt(false); if (!seed) { gcry_randomize(iseed, FSPRG_RECOMMENDED_SEEDLEN, GCRY_STRONG_RANDOM); @@ -295,7 +282,7 @@ void FSPRG_GenState0(void *state, const void *mpk, const void *seed, size_t seed gcry_mpi_t n, x; uint16_t secpar; - initialize_libgcrypt(); + initialize_libgcrypt(false); secpar = read_secpar(mpk + 0); n = mpi_import(mpk + 2, secpar / 8); @@ -314,7 +301,7 @@ void FSPRG_Evolve(void *state) { uint16_t secpar; uint64_t epoch; - initialize_libgcrypt(); + initialize_libgcrypt(false); secpar = read_secpar(state + 0); n = mpi_import(state + 2 + 0 * secpar / 8, secpar / 8); @@ -341,7 +328,7 @@ void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, gcry_mpi_t p, q, n, x, xp, xq, kp, kq, xm; uint16_t secpar; - initialize_libgcrypt(); + initialize_libgcrypt(false); secpar = read_secpar(msk + 0); p = mpi_import(msk + 2 + 0 * (secpar / 2) / 8, (secpar / 2) / 8); @@ -380,7 +367,7 @@ void FSPRG_Seek(void *state, uint64_t epoch, const void *msk, const void *seed, void FSPRG_GetKey(const void *state, void *key, size_t keylen, uint32_t idx) { uint16_t secpar; - initialize_libgcrypt(); + initialize_libgcrypt(false); secpar = read_secpar(state + 0); det_randomize(key, keylen, state + 2, 2 * secpar / 8 + 8, idx); diff --git a/src/journal/journal-authenticate.c b/src/journal/journal-authenticate.c index 49f3c8f0f4..d8af113d3f 100644 --- a/src/journal/journal-authenticate.c +++ b/src/journal/journal-authenticate.c @@ -22,6 +22,7 @@ #include "fd-util.h" #include "fsprg.h" +#include "gcrypt-util.h" #include "hexdecoct.h" #include "journal-authenticate.h" #include "journal-def.h" @@ -424,25 +425,13 @@ finish: return r; } -static void initialize_libgcrypt(void) { - const char *p; - - if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) - return; - - p = gcry_check_version("1.4.5"); - assert(p); - - gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); -} - int journal_file_hmac_setup(JournalFile *f) { gcry_error_t e; if (!f->seal) return 0; - initialize_libgcrypt(); + initialize_libgcrypt(true); e = gcry_md_open(&f->hmac, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); if (e != 0) diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 912eb94d0a..7504326bff 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -20,6 +20,7 @@ #include <errno.h> #include <fcntl.h> #include <linux/fs.h> +#include <pthread.h> #include <stddef.h> #include <sys/mman.h> #include <sys/statvfs.h> @@ -36,8 +37,10 @@ #include "journal-file.h" #include "lookup3.h" #include "parse-util.h" +#include "path-util.h" #include "random-util.h" #include "sd-event.h" +#include "set.h" #include "string-util.h" #include "xattr-util.h" @@ -86,33 +89,127 @@ /* The mmap context to use for the header we pick as one above the last defined typed */ #define CONTEXT_HEADER _OBJECT_TYPE_MAX -static int journal_file_set_online(JournalFile *f) { +/* This may be called from a separate thread to prevent blocking the caller for the duration of fsync(). + * As a result we use atomic operations on f->offline_state for inter-thread communications with + * journal_file_set_offline() and journal_file_set_online(). */ +static void journal_file_set_offline_internal(JournalFile *f) { assert(f); + assert(f->fd >= 0); + assert(f->header); - if (!f->writable) - return -EPERM; + for (;;) { + switch (f->offline_state) { + case OFFLINE_CANCEL: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_CANCEL, OFFLINE_DONE)) + continue; + return; - if (!(f->fd >= 0 && f->header)) - return -EINVAL; + case OFFLINE_AGAIN_FROM_SYNCING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_SYNCING, OFFLINE_SYNCING)) + continue; + break; + + case OFFLINE_AGAIN_FROM_OFFLINING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_OFFLINING, OFFLINE_SYNCING)) + continue; + break; + + case OFFLINE_SYNCING: + (void) fsync(f->fd); + + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_SYNCING, OFFLINE_OFFLINING)) + continue; + + f->header->state = f->archive ? STATE_ARCHIVED : STATE_OFFLINE; + (void) fsync(f->fd); + break; + + case OFFLINE_OFFLINING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_OFFLINING, OFFLINE_DONE)) + continue; + /* fall through */ + + case OFFLINE_DONE: + return; + + case OFFLINE_JOINED: + log_debug("OFFLINE_JOINED unexpected offline state for journal_file_set_offline_internal()"); + return; + } + } +} + +static void * journal_file_set_offline_thread(void *arg) { + JournalFile *f = arg; + + journal_file_set_offline_internal(f); + + return NULL; +} + +static int journal_file_set_offline_thread_join(JournalFile *f) { + int r; + + assert(f); + + if (f->offline_state == OFFLINE_JOINED) + return 0; + + r = pthread_join(f->offline_thread, NULL); + if (r) + return -r; + + f->offline_state = OFFLINE_JOINED; if (mmap_cache_got_sigbus(f->mmap, f->fd)) return -EIO; - switch (f->header->state) { - case STATE_ONLINE: - return 0; + return 0; +} - case STATE_OFFLINE: - f->header->state = STATE_ONLINE; - fsync(f->fd); - return 0; +/* Trigger a restart if the offline thread is mid-flight in a restartable state. */ +static bool journal_file_set_offline_try_restart(JournalFile *f) { + for (;;) { + switch (f->offline_state) { + case OFFLINE_AGAIN_FROM_SYNCING: + case OFFLINE_AGAIN_FROM_OFFLINING: + return true; + + case OFFLINE_CANCEL: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_CANCEL, OFFLINE_AGAIN_FROM_SYNCING)) + continue; + return true; + + case OFFLINE_SYNCING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_SYNCING, OFFLINE_AGAIN_FROM_SYNCING)) + continue; + return true; + + case OFFLINE_OFFLINING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_OFFLINING, OFFLINE_AGAIN_FROM_OFFLINING)) + continue; + return true; default: - return -EINVAL; + return false; + } } } -int journal_file_set_offline(JournalFile *f) { +/* Sets a journal offline. + * + * If wait is false then an offline is dispatched in a separate thread for a + * subsequent journal_file_set_offline() or journal_file_set_online() of the + * same journal to synchronize with. + * + * If wait is true, then either an existing offline thread will be restarted + * and joined, or if none exists the offline is simply performed in this + * context without involving another thread. + */ +int journal_file_set_offline(JournalFile *f, bool wait) { + bool restarted; + int r; + assert(f); if (!f->writable) @@ -121,22 +218,114 @@ int journal_file_set_offline(JournalFile *f) { if (!(f->fd >= 0 && f->header)) return -EINVAL; - if (f->header->state != STATE_ONLINE) + /* An offlining journal is implicitly online and may modify f->header->state, + * we must also join any potentially lingering offline thread when not online. */ + if (!journal_file_is_offlining(f) && f->header->state != STATE_ONLINE) + return journal_file_set_offline_thread_join(f); + + /* Restart an in-flight offline thread and wait if needed, or join a lingering done one. */ + restarted = journal_file_set_offline_try_restart(f); + if ((restarted && wait) || !restarted) { + r = journal_file_set_offline_thread_join(f); + if (r < 0) + return r; + } + + if (restarted) return 0; - fsync(f->fd); + /* Initiate a new offline. */ + f->offline_state = OFFLINE_SYNCING; - if (mmap_cache_got_sigbus(f->mmap, f->fd)) - return -EIO; + if (wait) /* Without using a thread if waiting. */ + journal_file_set_offline_internal(f); + else { + r = pthread_create(&f->offline_thread, NULL, journal_file_set_offline_thread, f); + if (r > 0) { + f->offline_state = OFFLINE_JOINED; + return -r; + } + } - f->header->state = STATE_OFFLINE; + return 0; +} + +static int journal_file_set_online(JournalFile *f) { + bool joined = false; + + assert(f); + + if (!f->writable) + return -EPERM; + + if (!(f->fd >= 0 && f->header)) + return -EINVAL; + + while (!joined) { + switch (f->offline_state) { + case OFFLINE_JOINED: + /* No offline thread, no need to wait. */ + joined = true; + break; + + case OFFLINE_SYNCING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_SYNCING, OFFLINE_CANCEL)) + continue; + /* Canceled syncing prior to offlining, no need to wait. */ + break; + + case OFFLINE_AGAIN_FROM_SYNCING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_SYNCING, OFFLINE_CANCEL)) + continue; + /* Canceled restart from syncing, no need to wait. */ + break; + + case OFFLINE_AGAIN_FROM_OFFLINING: + if (!__sync_bool_compare_and_swap(&f->offline_state, OFFLINE_AGAIN_FROM_OFFLINING, OFFLINE_CANCEL)) + continue; + /* Canceled restart from offlining, must wait for offlining to complete however. */ + + /* fall through to wait */ + default: { + int r; + + r = journal_file_set_offline_thread_join(f); + if (r < 0) + return r; + + joined = true; + break; + } + } + } if (mmap_cache_got_sigbus(f->mmap, f->fd)) return -EIO; - fsync(f->fd); + switch (f->header->state) { + case STATE_ONLINE: + return 0; - return 0; + case STATE_OFFLINE: + f->header->state = STATE_ONLINE; + (void) fsync(f->fd); + return 0; + + default: + return -EINVAL; + } +} + +bool journal_file_is_offlining(JournalFile *f) { + assert(f); + + __sync_synchronize(); + + if (f->offline_state == OFFLINE_DONE || + f->offline_state == OFFLINE_JOINED) + return false; + + return true; } JournalFile* journal_file_close(JournalFile *f) { @@ -159,7 +348,7 @@ JournalFile* journal_file_close(JournalFile *f) { sd_event_source_unref(f->post_change_timer); } - journal_file_set_offline(f); + journal_file_set_offline(f, true); if (f->mmap && f->fd >= 0) mmap_cache_close_fd(f->mmap, f->fd); @@ -176,7 +365,8 @@ JournalFile* journal_file_close(JournalFile *f) { (void) btrfs_defrag_fd(f->fd); } - safe_close(f->fd); + if (f->close_fd) + safe_close(f->fd); free(f->path); mmap_cache_unref(f->mmap); @@ -203,6 +393,15 @@ JournalFile* journal_file_close(JournalFile *f) { return NULL; } +void journal_file_close_set(Set *s) { + JournalFile *f; + + assert(s); + + while ((f = set_steal_first(s))) + (void) journal_file_close(f); +} + static int journal_file_init_header(JournalFile *f, JournalFile *template) { Header h = {}; ssize_t k; @@ -240,6 +439,39 @@ static int journal_file_init_header(JournalFile *f, JournalFile *template) { return 0; } +static int fsync_directory_of_file(int fd) { + _cleanup_free_ char *path = NULL, *dn = NULL; + _cleanup_close_ int dfd = -1; + struct stat st; + int r; + + if (fstat(fd, &st) < 0) + return -errno; + + if (!S_ISREG(st.st_mode)) + return -EBADFD; + + r = fd_get_path(fd, &path); + if (r < 0) + return r; + + if (!path_is_absolute(path)) + return -EINVAL; + + dn = dirname_malloc(path); + if (!dn) + return -ENOMEM; + + dfd = open(dn, O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (dfd < 0) + return -errno; + + if (fsync(dfd) < 0) + return -errno; + + return 0; +} + static int journal_file_refresh_header(JournalFile *f) { sd_id128_t boot_id; int r; @@ -263,7 +495,10 @@ static int journal_file_refresh_header(JournalFile *f) { r = journal_file_set_online(f); /* Sync the online state to disk */ - fsync(f->fd); + (void) fsync(f->fd); + + /* We likely just created a new file, also sync the directory this file is located in. */ + (void) fsync_directory_of_file(f->fd); return r; } @@ -508,7 +743,11 @@ int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset /* Objects may only be located at multiple of 64 bit */ if (!VALID64(offset)) - return -EFAULT; + return -EBADMSG; + + /* Object may not be located in the file header */ + if (offset < le64toh(f->header->header_size)) + return -EBADMSG; r = journal_file_move_to(f, type, false, offset, sizeof(ObjectHeader), &t); if (r < 0) @@ -1123,8 +1362,8 @@ static int journal_file_append_data( } #endif - if (compression == 0 && size > 0) - memcpy(o->data.payload, data, size); + if (compression == 0) + memcpy_safe(o->data.payload, data, size); r = journal_file_link_data(f, o, p, hash); if (r < 0) @@ -1389,7 +1628,7 @@ static int journal_file_append_entry_internal( return r; o->entry.seqnum = htole64(journal_file_entry_seqnum(f, seqnum)); - memcpy(o->entry.items, items, n_items * sizeof(EntryItem)); + memcpy_safe(o->entry.items, items, n_items * sizeof(EntryItem)); o->entry.realtime = htole64(ts->realtime); o->entry.monotonic = htole64(ts->monotonic); o->entry.xor_hash = htole64(xor_hash); @@ -1781,9 +2020,14 @@ static int generic_array_bisect( i = right - 1; lp = p = le64toh(array->entry_array.items[i]); if (p <= 0) - return -EBADMSG; - - r = test_object(f, p, needle); + r = -EBADMSG; + else + r = test_object(f, p, needle); + if (r == -EBADMSG) { + log_debug_errno(r, "Encountered invalid entry while bisecting, cutting algorithm short. (1)"); + n = i; + continue; + } if (r < 0) return r; @@ -1859,9 +2103,14 @@ static int generic_array_bisect( p = le64toh(array->entry_array.items[i]); if (p <= 0) - return -EBADMSG; - - r = test_object(f, p, needle); + r = -EBADMSG; + else + r = test_object(f, p, needle); + if (r == -EBADMSG) { + log_debug_errno(r, "Encountered invalid entry while bisecting, cutting algorithm short. (2)"); + right = n = i; + continue; + } if (r < 0) return r; @@ -1977,7 +2226,7 @@ static int generic_array_bisect_plus_one( goto found; if (r > 0 && idx) - (*idx) ++; + (*idx)++; return r; @@ -2266,13 +2515,18 @@ int journal_file_next_entry( le64toh(f->header->entry_array_offset), i, ret, &ofs); + if (r == -EBADMSG && direction == DIRECTION_DOWN) { + /* Special case: when we iterate throught the journal file linearly, and hit an entry we can't read, + * consider this the end of the journal file. */ + log_debug_errno(r, "Encountered entry we can't read while iterating through journal file. Considering this the end of the file."); + return 0; + } if (r <= 0) return r; if (p > 0 && (direction == DIRECTION_DOWN ? ofs <= p : ofs >= p)) { - log_debug("%s: entry array corrupted at entry %"PRIu64, - f->path, i); + log_debug("%s: entry array corrupted at entry %" PRIu64, f->path, i); return -EBADMSG; } @@ -2611,11 +2865,11 @@ void journal_file_print_header(JournalFile *f) { "Data Hash Table Size: %"PRIu64"\n" "Field Hash Table Size: %"PRIu64"\n" "Rotate Suggested: %s\n" - "Head Sequential Number: %"PRIu64"\n" - "Tail Sequential Number: %"PRIu64"\n" - "Head Realtime Timestamp: %s\n" - "Tail Realtime Timestamp: %s\n" - "Tail Monotonic Timestamp: %s\n" + "Head Sequential Number: %"PRIu64" (%"PRIx64")\n" + "Tail Sequential Number: %"PRIu64" (%"PRIx64")\n" + "Head Realtime Timestamp: %s (%"PRIx64")\n" + "Tail Realtime Timestamp: %s (%"PRIx64")\n" + "Tail Monotonic Timestamp: %s (%"PRIx64")\n" "Objects: %"PRIu64"\n" "Entry Objects: %"PRIu64"\n", f->path, @@ -2636,11 +2890,11 @@ void journal_file_print_header(JournalFile *f) { le64toh(f->header->data_hash_table_size) / sizeof(HashItem), le64toh(f->header->field_hash_table_size) / sizeof(HashItem), yes_no(journal_file_rotate_suggested(f, 0)), - le64toh(f->header->head_entry_seqnum), - le64toh(f->header->tail_entry_seqnum), - format_timestamp_safe(x, sizeof(x), le64toh(f->header->head_entry_realtime)), - format_timestamp_safe(y, sizeof(y), le64toh(f->header->tail_entry_realtime)), - format_timespan(z, sizeof(z), le64toh(f->header->tail_entry_monotonic), USEC_PER_MSEC), + le64toh(f->header->head_entry_seqnum), le64toh(f->header->head_entry_seqnum), + le64toh(f->header->tail_entry_seqnum), le64toh(f->header->tail_entry_seqnum), + format_timestamp_safe(x, sizeof(x), le64toh(f->header->head_entry_realtime)), le64toh(f->header->head_entry_realtime), + format_timestamp_safe(y, sizeof(y), le64toh(f->header->tail_entry_realtime)), le64toh(f->header->tail_entry_realtime), + format_timespan(z, sizeof(z), le64toh(f->header->tail_entry_monotonic), USEC_PER_MSEC), le64toh(f->header->tail_entry_monotonic), le64toh(f->header->n_objects), le64toh(f->header->n_entries)); @@ -2703,6 +2957,7 @@ static int journal_file_warn_btrfs(JournalFile *f) { } int journal_file_open( + int fd, const char *fname, int flags, mode_t mode, @@ -2710,6 +2965,7 @@ int journal_file_open( bool seal, JournalMetrics *metrics, MMapCache *mmap_cache, + Set *deferred_closes, JournalFile *template, JournalFile **ret) { @@ -2718,22 +2974,24 @@ int journal_file_open( void *h; int r; - assert(fname); assert(ret); + assert(fd >= 0 || fname); if ((flags & O_ACCMODE) != O_RDONLY && (flags & O_ACCMODE) != O_RDWR) return -EINVAL; - if (!endswith(fname, ".journal") && - !endswith(fname, ".journal~")) - return -EINVAL; + if (fname) { + if (!endswith(fname, ".journal") && + !endswith(fname, ".journal~")) + return -EINVAL; + } f = new0(JournalFile, 1); if (!f) return -ENOMEM; - f->fd = -1; + f->fd = fd; f->mode = mode; f->flags = flags; @@ -2758,7 +3016,10 @@ int journal_file_open( } } - f->path = strdup(fname); + if (fname) + f->path = strdup(fname); + else /* If we don't know the path, fill in something explanatory and vaguely useful */ + asprintf(&f->path, "/proc/self/%i", fd); if (!f->path) { r = -ENOMEM; goto fail; @@ -2770,10 +3031,15 @@ int journal_file_open( goto fail; } - f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode); if (f->fd < 0) { - r = -errno; - goto fail; + f->fd = open(f->path, f->flags|O_CLOEXEC, f->mode); + if (f->fd < 0) { + r = -errno; + goto fail; + } + + /* fds we opened here by us should also be closed by us. */ + f->close_fd = true; } r = journal_file_fstat(f); @@ -2829,6 +3095,9 @@ int journal_file_open( f->header = h; if (!newly_created) { + if (deferred_closes) + journal_file_close_set(deferred_closes); + r = journal_file_verify_header(f); if (r < 0) goto fail; @@ -2891,6 +3160,9 @@ int journal_file_open( goto fail; } + /* The file is opened now successfully, thus we take possession of any passed in fd. */ + f->close_fd = true; + *ret = f; return 0; @@ -2898,12 +3170,12 @@ fail: if (f->fd >= 0 && mmap_cache_got_sigbus(f->mmap, f->fd)) r = -EIO; - journal_file_close(f); + (void) journal_file_close(f); return r; } -int journal_file_rotate(JournalFile **f, bool compress, bool seal) { +int journal_file_rotate(JournalFile **f, bool compress, bool seal, Set *deferred_closes) { _cleanup_free_ char *p = NULL; size_t l; JournalFile *old_file, *new_file = NULL; @@ -2917,6 +3189,11 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal) { if (!old_file->writable) return -EINVAL; + /* Is this a journal file that was passed to us as fd? If so, we synthesized a path name for it, and we refuse + * rotation, since we don't know the actual path, and couldn't rename the file hence.*/ + if (path_startswith(old_file->path, "/proc/self/fd")) + return -EINVAL; + if (!endswith(old_file->path, ".journal")) return -EINVAL; @@ -2936,15 +3213,29 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal) { if (r < 0 && errno != ENOENT) return -errno; - old_file->header->state = STATE_ARCHIVED; + /* Sync the rename to disk */ + (void) fsync_directory_of_file(old_file->fd); + + /* Set as archive so offlining commits w/state=STATE_ARCHIVED. + * Previously we would set old_file->header->state to STATE_ARCHIVED directly here, + * but journal_file_set_offline() short-circuits when state != STATE_ONLINE, which + * would result in the rotated journal never getting fsync() called before closing. + * Now we simply queue the archive state by setting an archive bit, leaving the state + * as STATE_ONLINE so proper offlining occurs. */ + old_file->archive = true; /* Currently, btrfs is not very good with out write patterns * and fragments heavily. Let's defrag our journal files when * we archive them */ old_file->defrag_on_close = true; - r = journal_file_open(old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, old_file, &new_file); - journal_file_close(old_file); + r = journal_file_open(-1, old_file->path, old_file->flags, old_file->mode, compress, seal, NULL, old_file->mmap, deferred_closes, old_file, &new_file); + + if (deferred_closes && + set_put(deferred_closes, old_file) >= 0) + (void) journal_file_set_offline(old_file, false); + else + (void) journal_file_close(old_file); *f = new_file; return r; @@ -2958,6 +3249,7 @@ int journal_file_open_reliably( bool seal, JournalMetrics *metrics, MMapCache *mmap_cache, + Set *deferred_closes, JournalFile *template, JournalFile **ret) { @@ -2965,7 +3257,7 @@ int journal_file_open_reliably( size_t l; _cleanup_free_ char *p = NULL; - r = journal_file_open(fname, flags, mode, compress, seal, metrics, mmap_cache, template, ret); + r = journal_file_open(-1, fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret); if (!IN_SET(r, -EBADMSG, /* corrupted */ -ENODATA, /* truncated */ @@ -3001,12 +3293,12 @@ int journal_file_open_reliably( /* btrfs doesn't cope well with our write pattern and * fragments heavily. Let's defrag all files we rotate */ - (void) chattr_path(p, false, FS_NOCOW_FL); + (void) chattr_path(p, 0, FS_NOCOW_FL); (void) btrfs_defrag(p); log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname); - return journal_file_open(fname, flags, mode, compress, seal, metrics, mmap_cache, template, ret); + return journal_file_open(-1, fname, flags, mode, compress, seal, metrics, mmap_cache, deferred_closes, template, ret); } int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p, uint64_t *seqnum, Object **ret, uint64_t *offset) { diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index 07b9561b8a..564e1a8179 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -63,6 +63,16 @@ typedef enum LocationType { LOCATION_SEEK } LocationType; +typedef enum OfflineState { + OFFLINE_JOINED, + OFFLINE_SYNCING, + OFFLINE_OFFLINING, + OFFLINE_CANCEL, + OFFLINE_AGAIN_FROM_SYNCING, + OFFLINE_AGAIN_FROM_OFFLINING, + OFFLINE_DONE +} OfflineState; + typedef struct JournalFile { int fd; @@ -75,6 +85,8 @@ typedef struct JournalFile { bool compress_lz4:1; bool seal:1; bool defrag_on_close:1; + bool close_fd:1; + bool archive:1; bool tail_entry_monotonic_valid:1; @@ -105,6 +117,9 @@ typedef struct JournalFile { OrderedHashmap *chain_cache; + pthread_t offline_thread; + volatile OfflineState offline_state; + #if defined(HAVE_XZ) || defined(HAVE_LZ4) void *compress_buffer; size_t compress_buffer_size; @@ -129,6 +144,7 @@ typedef struct JournalFile { } JournalFile; int journal_file_open( + int fd, const char *fname, int flags, mode_t mode, @@ -136,11 +152,14 @@ int journal_file_open( bool seal, JournalMetrics *metrics, MMapCache *mmap_cache, + Set *deferred_closes, JournalFile *template, JournalFile **ret); -int journal_file_set_offline(JournalFile *f); +int journal_file_set_offline(JournalFile *f, bool wait); +bool journal_file_is_offlining(JournalFile *f); JournalFile* journal_file_close(JournalFile *j); +void journal_file_close_set(Set *s); int journal_file_open_reliably( const char *fname, @@ -150,6 +169,7 @@ int journal_file_open_reliably( bool seal, JournalMetrics *metrics, MMapCache *mmap_cache, + Set *deferred_closes, JournalFile *template, JournalFile **ret); @@ -223,7 +243,7 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6 void journal_file_dump(JournalFile *f); void journal_file_print_header(JournalFile *f); -int journal_file_rotate(JournalFile **f, bool compress, bool seal); +int journal_file_rotate(JournalFile **f, bool compress, bool seal, Set *deferred_closes); void journal_file_post_change(JournalFile *f); int journal_file_enable_post_change_timer(JournalFile *f, sd_event *e, usec_t t); diff --git a/src/journal/journal-internal.h b/src/journal/journal-internal.h index 7639325acf..34a48141f5 100644 --- a/src/journal/journal-internal.h +++ b/src/journal/journal-internal.h @@ -82,6 +82,8 @@ struct Directory { }; struct sd_journal { + int toplevel_fd; + char *path; char *prefix; @@ -117,6 +119,7 @@ struct sd_journal { bool on_network:1; bool no_new_files:1; + bool no_inotify:1; bool unique_file_lost:1; /* File we were iterating over got removed, and there were no more files, so sd_j_enumerate_unique diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c index c7d670f4ff..5e8a3e3200 100644 --- a/src/journal/journal-send.c +++ b/src/journal/journal-send.c @@ -50,7 +50,7 @@ *_f = alloca(_fl + 10); \ memcpy(*_f, "CODE_FUNC=", 10); \ memcpy(*_f + 10, _func, _fl); \ - } while(false) + } while (false) /* We open a single fd, and we'll share it with the current process, * all its threads, and all its subprocesses. This means we need to @@ -208,13 +208,13 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) { struct iovec *w; uint64_t *l; int i, j = 0; - struct sockaddr_un sa = { - .sun_family = AF_UNIX, - .sun_path = "/run/systemd/journal/socket", + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/socket", }; struct msghdr mh = { - .msg_name = &sa, - .msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(sa.sun_path), + .msg_name = (struct sockaddr*) &sa.sa, + .msg_namelen = SOCKADDR_UN_LEN(sa.un), }; ssize_t k; bool have_syslog_identifier = false; @@ -316,7 +316,7 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) { buffer_fd = memfd_new(NULL); if (buffer_fd < 0) { if (buffer_fd == -ENOSYS) { - buffer_fd = open_tmpfile("/dev/shm", O_RDWR | O_CLOEXEC); + buffer_fd = open_tmpfile_unlinkable("/dev/shm", O_RDWR | O_CLOEXEC); if (buffer_fd < 0) return buffer_fd; @@ -392,7 +392,7 @@ _public_ int sd_journal_perror(const char *message) { } _public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) { - union sockaddr_union sa = { + static const union sockaddr_union sa = { .un.sun_family = AF_UNIX, .un.sun_path = "/run/systemd/journal/stdout", }; @@ -408,7 +408,7 @@ _public_ int sd_journal_stream_fd(const char *identifier, int priority, int leve if (fd < 0) return -errno; - r = connect(fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)); + r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) return -errno; diff --git a/src/journal/journal-vacuum.c b/src/journal/journal-vacuum.c index 05e97620ae..f09dc66e03 100644 --- a/src/journal/journal-vacuum.c +++ b/src/journal/journal-vacuum.c @@ -239,13 +239,13 @@ int journal_directory_vacuum( /* Vacuum corrupted files */ if (q < 1 + 16 + 1 + 16 + 8 + 1) { - n_active_files ++; + n_active_files++; continue; } if (de->d_name[q-1-8-16-1] != '-' || de->d_name[q-1-8-16-1-16-1] != '@') { - n_active_files ++; + n_active_files++; continue; } @@ -256,7 +256,7 @@ int journal_directory_vacuum( } if (sscanf(de->d_name + q-1-8-16-1-16, "%16llx-%16llx.journal~", &realtime, &tmp) != 2) { - n_active_files ++; + n_active_files++; continue; } @@ -302,7 +302,7 @@ int journal_directory_vacuum( list[n_list].realtime = realtime; list[n_list].seqnum_id = seqnum_id; list[n_list].have_seqnum = have_seqnum; - n_list ++; + n_list++; p = NULL; sum += size; diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index b968e89bb8..26572ddd76 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -97,20 +97,20 @@ static void flush_progress(void) { fflush(stdout); } -#define debug(_offset, _fmt, ...) do{ \ +#define debug(_offset, _fmt, ...) do { \ flush_progress(); \ log_debug(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \ - } while(0) + } while (0) -#define warning(_offset, _fmt, ...) do{ \ +#define warning(_offset, _fmt, ...) do { \ flush_progress(); \ log_warning(OFSfmt": " _fmt, _offset, ##__VA_ARGS__); \ - } while(0) + } while (0) -#define error(_offset, _fmt, ...) do{ \ +#define error(_offset, _fmt, ...) do { \ flush_progress(); \ log_error(OFSfmt": " _fmt, (uint64_t)_offset, ##__VA_ARGS__); \ - } while(0) + } while (0) static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o) { uint64_t i; @@ -838,19 +838,19 @@ int journal_file_verify( } else if (f->seal) return -ENOKEY; - data_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC); + data_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC); if (data_fd < 0) { r = log_error_errno(data_fd, "Failed to create data file: %m"); goto fail; } - entry_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC); + entry_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC); if (entry_fd < 0) { r = log_error_errno(entry_fd, "Failed to create entry file: %m"); goto fail; } - entry_array_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC); + entry_array_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC); if (entry_array_fd < 0) { r = log_error_errno(entry_array_fd, "Failed to create entry array file: %m"); @@ -894,7 +894,7 @@ int journal_file_verify( goto fail; } - n_objects ++; + n_objects++; r = journal_file_object_verify(f, p, o); if (r < 0) { @@ -991,7 +991,7 @@ int journal_file_verify( entry_realtime = le64toh(o->entry.realtime); entry_realtime_set = true; - n_entries ++; + n_entries++; break; case OBJECT_DATA_HASH_TABLE: @@ -1131,11 +1131,11 @@ int journal_file_verify( last_epoch = le64toh(o->tag.epoch); - n_tags ++; + n_tags++; break; default: - n_weird ++; + n_weird++; } if (p == le64toh(f->header->tail_object_offset)) { diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 273242bea6..f67c556783 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -95,11 +95,13 @@ static bool arg_boot = false; static sd_id128_t arg_boot_id = {}; static int arg_boot_offset = 0; static bool arg_dmesg = false; +static bool arg_no_hostname = false; static const char *arg_cursor = NULL; static const char *arg_after_cursor = NULL; static bool arg_show_cursor = false; static const char *arg_directory = NULL; static char **arg_file = NULL; +static bool arg_file_stdin = false; static int arg_priorities = 0xFF; static const char *arg_verify_key = NULL; #ifdef HAVE_GCRYPT @@ -225,14 +227,6 @@ static int add_matches_for_device(sd_journal *j, const char *devpath) { return 0; } -static void pager_open_if_enabled(void) { - - if (arg_no_pager) - return; - - pager_open(arg_pager_end); -} - static char *format_timestamp_maybe_utc(char *buf, size_t l, usec_t t) { if (arg_utc) @@ -278,7 +272,7 @@ static int parse_boot_descriptor(const char *x, sd_id128_t *boot_id, int *offset static void help(void) { - pager_open_if_enabled(); + pager_open(arg_no_pager, arg_pager_end); printf("%s [OPTIONS...] [MATCHES...]\n\n" "Query the journal.\n\n" @@ -312,6 +306,7 @@ static void help(void) { " -a --all Show all fields, including long and unprintable\n" " -q --quiet Do not show info messages and privilege warning\n" " --no-pager Do not pipe output into a pager\n" + " --no-hostname Suppress output of hostname field\n" " -m --merge Show entries from all available journals\n" " -D --directory=PATH Show journal files from directory\n" " --file=PATH Show journal file\n" @@ -378,6 +373,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_VACUUM_SIZE, ARG_VACUUM_FILES, ARG_VACUUM_TIME, + ARG_NO_HOSTNAME, }; static const struct option options[] = { @@ -435,6 +431,7 @@ static int parse_argv(int argc, char *argv[]) { { "vacuum-size", required_argument, NULL, ARG_VACUUM_SIZE }, { "vacuum-files", required_argument, NULL, ARG_VACUUM_FILES }, { "vacuum-time", required_argument, NULL, ARG_VACUUM_TIME }, + { "no-hostname", no_argument, NULL, ARG_NO_HOSTNAME }, {} }; @@ -596,9 +593,17 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_FILE: - r = glob_extend(&arg_file, optarg); - if (r < 0) - return log_error_errno(r, "Failed to add paths: %m"); + if (streq(optarg, "-")) + /* An undocumented feature: we can read journal files from STDIN. We don't document + * this though, since after all we only support this for mmap-able, seekable files, and + * not for example pipes which are probably the primary usecase for reading things from + * STDIN. To avoid confusion we hence don't document this feature. */ + arg_file_stdin = true; + else { + r = glob_extend(&arg_file, optarg); + if (r < 0) + return log_error_errno(r, "Failed to add paths: %m"); + } break; case ARG_ROOT: @@ -788,6 +793,10 @@ static int parse_argv(int argc, char *argv[]) { arg_action = ACTION_LIST_FIELD_NAMES; break; + case ARG_NO_HOSTNAME: + arg_no_hostname = true; + break; + case 'x': arg_catalog = true; break; @@ -864,6 +873,18 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } + if (!strv_isempty(arg_system_units) && (arg_journal_type == SD_JOURNAL_CURRENT_USER)) { + + /* Specifying --user and --unit= at the same time makes no sense (as the former excludes the user + * journal, but the latter excludes the system journal, thus resulting in empty output). Let's be nice + * to users, and automatically turn --unit= into --user-unit= if combined with --user. */ + r = strv_extend_strv(&arg_user_units, arg_system_units, true); + if (r < 0) + return -ENOMEM; + + arg_system_units = strv_free(arg_system_units); + } + return 1; } @@ -988,18 +1009,18 @@ static void boot_id_free_all(BootId *l) { } } -static int discover_next_boot( - sd_journal *j, - BootId **boot, +static int discover_next_boot(sd_journal *j, + sd_id128_t previous_boot_id, bool advance_older, - bool read_realtime) { + BootId **ret) { - int r; - char match[9+32+1] = "_BOOT_ID="; _cleanup_free_ BootId *next_boot = NULL; + char match[9+32+1] = "_BOOT_ID="; + sd_id128_t boot_id; + int r; assert(j); - assert(boot); + assert(ret); /* We expect the journal to be on the last position of a boot * (in relation to the direction we are going), so that the next @@ -1012,29 +1033,40 @@ static int discover_next_boot( * we can actually advance to a *different* boot. */ sd_journal_flush_matches(j); - if (advance_older) - r = sd_journal_previous(j); - else - r = sd_journal_next(j); - if (r < 0) - return r; - else if (r == 0) - return 0; /* End of journal, yay. */ + do { + if (advance_older) + r = sd_journal_previous(j); + else + r = sd_journal_next(j); + if (r < 0) + return r; + else if (r == 0) + return 0; /* End of journal, yay. */ + + r = sd_journal_get_monotonic_usec(j, NULL, &boot_id); + if (r < 0) + return r; + + /* We iterate through this in a loop, until the boot ID differs from the previous one. Note that + * normally, this will only require a single iteration, as we seeked to the last entry of the previous + * boot entry already. However, it might happen that the per-journal-field entry arrays are less + * complete than the main entry array, and hence might reference an entry that's not actually the last + * one of the boot ID as last one. Let's hence use the per-field array is initial seek position to + * speed things up, but let's not trust that it is complete, and hence, manually advance as + * necessary. */ + + } while (sd_id128_equal(boot_id, previous_boot_id)); next_boot = new0(BootId, 1); if (!next_boot) return -ENOMEM; - r = sd_journal_get_monotonic_usec(j, NULL, &next_boot->id); + next_boot->id = boot_id; + + r = sd_journal_get_realtime_usec(j, &next_boot->first); if (r < 0) return r; - if (read_realtime) { - r = sd_journal_get_realtime_usec(j, &next_boot->first); - if (r < 0) - return r; - } - /* Now seek to the last occurrence of this boot ID. */ sd_id128_to_string(next_boot->id, match + 9); r = sd_journal_add_match(j, match, sizeof(match) - 1); @@ -1057,13 +1089,11 @@ static int discover_next_boot( else if (r == 0) return -ENODATA; /* This shouldn't happen. We just came from this very boot ID. */ - if (read_realtime) { - r = sd_journal_get_realtime_usec(j, &next_boot->last); - if (r < 0) - return r; - } + r = sd_journal_get_realtime_usec(j, &next_boot->last); + if (r < 0) + return r; - *boot = next_boot; + *ret = next_boot; next_boot = NULL; return 0; @@ -1072,47 +1102,48 @@ static int discover_next_boot( static int get_boots( sd_journal *j, BootId **boots, - BootId *query_ref_boot, + sd_id128_t *query_ref_boot, int ref_boot_offset) { bool skip_once; int r, count = 0; BootId *head = NULL, *tail = NULL; const bool advance_older = query_ref_boot && ref_boot_offset <= 0; + sd_id128_t previous_boot_id; assert(j); /* Adjust for the asymmetry that offset 0 is * the last (and current) boot, while 1 is considered the * (chronological) first boot in the journal. */ - skip_once = query_ref_boot && sd_id128_is_null(query_ref_boot->id) && ref_boot_offset < 0; + skip_once = query_ref_boot && sd_id128_is_null(*query_ref_boot) && ref_boot_offset < 0; /* Advance to the earliest/latest occurrence of our reference * boot ID (taking our lookup direction into account), so that * discover_next_boot() can do its job. * If no reference is given, the journal head/tail will do, * they're "virtual" boots after all. */ - if (query_ref_boot && !sd_id128_is_null(query_ref_boot->id)) { + if (query_ref_boot && !sd_id128_is_null(*query_ref_boot)) { char match[9+32+1] = "_BOOT_ID="; sd_journal_flush_matches(j); - sd_id128_to_string(query_ref_boot->id, match + 9); + sd_id128_to_string(*query_ref_boot, match + 9); r = sd_journal_add_match(j, match, sizeof(match) - 1); if (r < 0) return r; if (advance_older) - r = sd_journal_seek_head(j); + r = sd_journal_seek_head(j); /* seek to oldest */ else - r = sd_journal_seek_tail(j); + r = sd_journal_seek_tail(j); /* seek to newest */ if (r < 0) return r; if (advance_older) - r = sd_journal_next(j); + r = sd_journal_next(j); /* read the oldest entry */ else - r = sd_journal_previous(j); + r = sd_journal_previous(j); /* read the most recently added entry */ if (r < 0) return r; else if (r == 0) @@ -1121,21 +1152,31 @@ static int get_boots( count = 1; goto finish; } + + /* At this point the read pointer is positioned at the oldest/newest occurence of the reference boot + * ID. After flushing the matches, one more invocation of _previous()/_next() will hence place us at + * the following entry, which must then have an older/newer boot ID */ } else { + if (advance_older) - r = sd_journal_seek_tail(j); + r = sd_journal_seek_tail(j); /* seek to newest */ else - r = sd_journal_seek_head(j); + r = sd_journal_seek_head(j); /* seek to oldest */ if (r < 0) return r; - /* No sd_journal_next/previous here. */ + /* No sd_journal_next()/_previous() here. + * + * At this point the read pointer is positioned after the newest/before the oldest entry in the whole + * journal. The next invocation of _previous()/_next() will hence position us at the newest/oldest + * entry we have. */ } + previous_boot_id = SD_ID128_NULL; for (;;) { _cleanup_free_ BootId *current = NULL; - r = discover_next_boot(j, ¤t, advance_older, !query_ref_boot); + r = discover_next_boot(j, previous_boot_id, advance_older, ¤t); if (r < 0) { boot_id_free_all(head); return r; @@ -1144,6 +1185,8 @@ static int get_boots( if (!current) break; + previous_boot_id = current->id; + if (query_ref_boot) { if (!skip_once) ref_boot_offset += advance_older ? 1 : -1; @@ -1151,7 +1194,7 @@ static int get_boots( if (ref_boot_offset == 0) { count = 1; - query_ref_boot->id = current->id; + *query_ref_boot = current->id; break; } } else { @@ -1183,7 +1226,7 @@ static int list_boots(sd_journal *j) { if (count == 0) return count; - pager_open_if_enabled(); + pager_open(arg_no_pager, arg_pager_end); /* numbers are one less, but we need an extra char for the sign */ w = DECIMAL_STR_WIDTH(count - 1) + 1; @@ -1207,8 +1250,8 @@ static int list_boots(sd_journal *j) { static int add_boot(sd_journal *j) { char match[9+32+1] = "_BOOT_ID="; + sd_id128_t ref_boot_id; int r; - BootId ref_boot_id = {}; assert(j); @@ -1218,7 +1261,7 @@ static int add_boot(sd_journal *j) { if (arg_boot_offset == 0 && sd_id128_equal(arg_boot_id, SD_ID128_NULL)) return add_match_this_boot(j, arg_machine); - ref_boot_id.id = arg_boot_id; + ref_boot_id = arg_boot_id; r = get_boots(j, NULL, &ref_boot_id, arg_boot_offset); assert(r <= 1); if (r <= 0) { @@ -1234,7 +1277,7 @@ static int add_boot(sd_journal *j) { return r == 0 ? -ENODATA : r; } - sd_id128_to_string(ref_boot_id.id, match + 9); + sd_id128_to_string(ref_boot_id, match + 9); r = sd_journal_add_match(j, match, sizeof(match) - 1); if (r < 0) @@ -1363,7 +1406,7 @@ static int add_units(sd_journal *j) { r = sd_journal_add_disjunction(j); if (r < 0) return r; - count ++; + count++; } } @@ -1383,7 +1426,7 @@ static int add_units(sd_journal *j) { r = sd_journal_add_disjunction(j); if (r < 0) return r; - count ++; + count++; } } @@ -1408,7 +1451,7 @@ static int add_units(sd_journal *j) { r = sd_journal_add_disjunction(j); if (r < 0) return r; - count ++; + count++; } } @@ -1428,7 +1471,7 @@ static int add_units(sd_journal *j) { r = sd_journal_add_disjunction(j); if (r < 0) return r; - count ++; + count++; } } @@ -1847,7 +1890,7 @@ static int access_check(sd_journal *j) { break; default: - log_warning_errno(err, "An error was encountered while opening journal file %s, ignoring file.", path); + log_warning_errno(err, "An error was encountered while opening journal file or directory %s, ignoring file: %m", path); break; } } @@ -2061,7 +2104,7 @@ int main(int argc, char *argv[]) { } else { bool oneline = arg_action == ACTION_LIST_CATALOG; - pager_open_if_enabled(); + pager_open(arg_no_pager, arg_pager_end); if (optind < argc) r = catalog_list_items(stdout, database, oneline, argv + optind); @@ -2103,11 +2146,61 @@ int main(int argc, char *argv[]) { if (arg_directory) r = sd_journal_open_directory(&j, arg_directory, arg_journal_type); - else if (arg_file) + else if (arg_file_stdin) { + int ifd = STDIN_FILENO; + r = sd_journal_open_files_fd(&j, &ifd, 1, 0); + } else if (arg_file) r = sd_journal_open_files(&j, (const char**) arg_file, 0); - else if (arg_machine) - r = sd_journal_open_container(&j, arg_machine, 0); - else + else if (arg_machine) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int fd; + + if (geteuid() != 0) { + /* The file descriptor returned by OpenMachineRootDirectory() will be owned by users/groups of + * the container, thus we need root privileges to override them. */ + log_error("Using the --machine= switch requires root privileges."); + r = -EPERM; + goto finish; + } + + r = sd_bus_open_system(&bus); + if (r < 0) { + log_error_errno(r, "Failed to open system bus: %m"); + goto finish; + } + + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "OpenMachineRootDirectory", + &error, + &reply, + "s", arg_machine); + if (r < 0) { + log_error_errno(r, "Failed to open root directory: %s", bus_error_message(&error, r)); + goto finish; + } + + r = sd_bus_message_read(reply, "h", &fd); + if (r < 0) { + bus_log_parse_error(r); + goto finish; + } + + fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (fd < 0) { + r = log_error_errno(errno, "Failed to duplicate file descriptor: %m"); + goto finish; + } + + r = sd_journal_open_directory_fd(&j, fd, SD_JOURNAL_OS_ROOT); + if (r < 0) + safe_close(fd); + } else r = sd_journal_open(&j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type); if (r < 0) { log_error_errno(r, "Failed to open %s: %m", arg_directory ?: arg_file ? "files" : "journal"); @@ -2181,7 +2274,7 @@ int main(int argc, char *argv[]) { SD_JOURNAL_FOREACH_FIELD(j, field) { printf("%s\n", field); - n_shown ++; + n_shown++; } r = 0; @@ -2273,7 +2366,7 @@ int main(int argc, char *argv[]) { else printf("%.*s\n", (int) size, (const char*) data); - n_shown ++; + n_shown++; } r = 0; @@ -2283,6 +2376,10 @@ int main(int argc, char *argv[]) { /* Opening the fd now means the first sd_journal_wait() will actually wait */ if (arg_follow) { r = sd_journal_get_fd(j); + if (r == -EMEDIUMTYPE) { + log_error_errno(r, "The --follow switch is not supported in conjunction with reading from STDIN."); + goto finish; + } if (r < 0) { log_error_errno(r, "Failed to get journal fd: %m"); goto finish; @@ -2368,7 +2465,7 @@ int main(int argc, char *argv[]) { } if (!arg_follow) - pager_open_if_enabled(); + pager_open(arg_no_pager, arg_pager_end); if (!arg_quiet) { usec_t start, end; @@ -2452,7 +2549,8 @@ int main(int argc, char *argv[]) { arg_full * OUTPUT_FULL_WIDTH | colors_enabled() * OUTPUT_COLOR | arg_catalog * OUTPUT_CATALOG | - arg_utc * OUTPUT_UTC; + arg_utc * OUTPUT_UTC | + arg_no_hostname * OUTPUT_NO_HOSTNAME; r = output_journal(stdout, j, arg_output, 0, flags, &ellipsized); need_seek = true; diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c index b2eb8a33ef..a433c91c54 100644 --- a/src/journal/journald-audit.c +++ b/src/journal/journald-audit.c @@ -63,7 +63,7 @@ static int map_simple_field(const char *field, const char **p, struct iovec **io (*iov)[*n_iov].iov_base = c; (*iov)[*n_iov].iov_len = l; - (*n_iov) ++; + (*n_iov)++; *p = e; c = NULL; @@ -142,7 +142,7 @@ static int map_string_field_internal(const char *field, const char **p, struct i (*iov)[*n_iov].iov_base = c; (*iov)[*n_iov].iov_len = l; - (*n_iov) ++; + (*n_iov)++; *p = e; c = NULL; @@ -200,7 +200,7 @@ static int map_generic_field(const char *prefix, const char **p, struct iovec ** } strcpy(t, "="); - e ++; + e++; r = map_simple_field(c, &e, iov, n_iov_allocated, n_iov); if (r < 0) diff --git a/src/journal/journald-gperf.gperf b/src/journal/journald-gperf.gperf index c154610c54..7fecd7a964 100644 --- a/src/journal/journald-gperf.gperf +++ b/src/journal/journald-gperf.gperf @@ -19,7 +19,9 @@ Journal.Storage, config_parse_storage, 0, offsetof(Server, storage Journal.Compress, config_parse_bool, 0, offsetof(Server, compress) Journal.Seal, config_parse_bool, 0, offsetof(Server, seal) Journal.SyncIntervalSec, config_parse_sec, 0, offsetof(Server, sync_interval_usec) +# The following is a legacy name for compatibility Journal.RateLimitInterval, config_parse_sec, 0, offsetof(Server, rate_limit_interval) +Journal.RateLimitIntervalSec,config_parse_sec, 0, offsetof(Server, rate_limit_interval) Journal.RateLimitBurst, config_parse_unsigned, 0, offsetof(Server, rate_limit_burst) Journal.SystemMaxUse, config_parse_iec_uint64, 0, offsetof(Server, system_metrics.max_use) Journal.SystemMaxFileSize, config_parse_iec_uint64, 0, offsetof(Server, system_metrics.max_size) diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c index eb1ac90e98..f64abdd431 100644 --- a/src/journal/journald-kmsg.c +++ b/src/journal/journald-kmsg.c @@ -201,7 +201,7 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) { if (*k != ' ') break; - k ++, l --; + k++, l--; e = memchr(k, '\n', l); if (!e) diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c index 3d8f05996b..0a1ce205c2 100644 --- a/src/journal/journald-native.c +++ b/src/journal/journald-native.c @@ -206,7 +206,7 @@ void server_process_native_message( allow_object_pid(ucred)) { char buf[DECIMAL_STR_MAX(pid_t)]; memcpy(buf, p + strlen("OBJECT_PID="), l - strlen("OBJECT_PID=")); - char_array_0(buf); + buf[l-strlen("OBJECT_PID=")] = '\0'; /* ignore error */ parse_pid(buf, &object_pid); @@ -448,24 +448,24 @@ void server_process_native_file( } int server_open_native_socket(Server*s) { + + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/socket", + }; static const int one = 1; int r; assert(s); if (s->native_fd < 0) { - union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/journal/socket", - }; - s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (s->native_fd < 0) return log_error_errno(errno, "socket() failed: %m"); - unlink(sa.un.sun_path); + (void) unlink(sa.un.sun_path); - r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)); + r = bind(s->native_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); diff --git a/src/journal/journald-rate-limit.c b/src/journal/journald-rate-limit.c index 6f6a90fe4e..fce799a6ce 100644 --- a/src/journal/journald-rate-limit.c +++ b/src/journal/journald-rate-limit.c @@ -104,7 +104,7 @@ static void journal_rate_limit_group_free(JournalRateLimitGroup *g) { LIST_REMOVE(lru, g->parent->lru, g); LIST_REMOVE(bucket, g->parent->buckets[g->hash % BUCKETS_MAX], g); - g->parent->n_groups --; + g->parent->n_groups--; } free(g->id); @@ -168,7 +168,7 @@ static JournalRateLimitGroup* journal_rate_limit_group_new(JournalRateLimit *r, LIST_PREPEND(lru, r->lru, g); if (!g->lru_next) r->lru_tail = g; - r->n_groups ++; + r->n_groups++; g->parent = r; return g; diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index ee2db8d29f..8f82d2a838 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -251,15 +251,15 @@ static int open_journal( assert(ret); if (reliably) - r = journal_file_open_reliably(fname, flags, 0640, s->compress, seal, metrics, s->mmap, NULL, &f); + r = journal_file_open_reliably(fname, flags, 0640, s->compress, seal, metrics, s->mmap, s->deferred_closes, NULL, &f); else - r = journal_file_open(fname, flags, 0640, s->compress, seal, metrics, s->mmap, NULL, &f); + r = journal_file_open(-1, fname, flags, 0640, s->compress, seal, metrics, s->mmap, s->deferred_closes, NULL, &f); if (r < 0) return r; r = journal_file_enable_post_change_timer(f, s->event, POST_CHANGE_TIMER_INTERVAL_USEC); if (r < 0) { - journal_file_close(f); + (void) journal_file_close(f); return r; } @@ -302,7 +302,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) { /* Too many open? Then let's close one */ f = ordered_hashmap_steal_first(s->user_journals); assert(f); - journal_file_close(f); + (void) journal_file_close(f); } r = open_journal(s, true, p, O_RDWR|O_CREAT, s->seal, &s->system_metrics, &f); @@ -313,7 +313,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) { r = ordered_hashmap_put(s->user_journals, UID_TO_PTR(uid), f); if (r < 0) { - journal_file_close(f); + (void) journal_file_close(f); return s->system_journal; } @@ -333,7 +333,7 @@ static int do_rotate( if (!*f) return -EINVAL; - r = journal_file_rotate(f, s->compress, seal); + r = journal_file_rotate(f, s->compress, seal, s->deferred_closes); if (r < 0) if (*f) log_error_errno(r, "Failed to rotate %s: %m", (*f)->path); @@ -364,6 +364,13 @@ void server_rotate(Server *s) { /* Old file has been closed and deallocated */ ordered_hashmap_remove(s->user_journals, k); } + + /* Perform any deferred closes which aren't still offlining. */ + SET_FOREACH(f, s->deferred_closes, i) + if (!journal_file_is_offlining(f)) { + (void) set_remove(s->deferred_closes, f); + (void) journal_file_close(f); + } } void server_sync(Server *s) { @@ -372,13 +379,13 @@ void server_sync(Server *s) { int r; if (s->system_journal) { - r = journal_file_set_offline(s->system_journal); + r = journal_file_set_offline(s->system_journal, false); if (r < 0) log_warning_errno(r, "Failed to sync system journal, ignoring: %m"); } ORDERED_HASHMAP_FOREACH(f, s->user_journals, i) { - r = journal_file_set_offline(f); + r = journal_file_set_offline(f, false); if (r < 0) log_warning_errno(r, "Failed to sync user journal, ignoring: %m"); } @@ -485,38 +492,36 @@ static void server_cache_hostname(Server *s) { } static bool shall_try_append_again(JournalFile *f, int r) { - - /* -E2BIG Hit configured limit - -EFBIG Hit fs limit - -EDQUOT Quota limit hit - -ENOSPC Disk full - -EIO I/O error of some kind (mmap) - -EHOSTDOWN Other machine - -EBUSY Unclean shutdown - -EPROTONOSUPPORT Unsupported feature - -EBADMSG Corrupted - -ENODATA Truncated - -ESHUTDOWN Already archived - -EIDRM Journal file has been deleted */ - - if (r == -E2BIG || r == -EFBIG || r == -EDQUOT || r == -ENOSPC) + switch(r) { + case -E2BIG: /* Hit configured limit */ + case -EFBIG: /* Hit fs limit */ + case -EDQUOT: /* Quota limit hit */ + case -ENOSPC: /* Disk full */ log_debug("%s: Allocation limit reached, rotating.", f->path); - else if (r == -EHOSTDOWN) + return true; + case -EIO: /* I/O error of some kind (mmap) */ + log_warning("%s: IO error, rotating.", f->path); + return true; + case -EHOSTDOWN: /* Other machine */ log_info("%s: Journal file from other machine, rotating.", f->path); - else if (r == -EBUSY) + return true; + case -EBUSY: /* Unclean shutdown */ log_info("%s: Unclean shutdown, rotating.", f->path); - else if (r == -EPROTONOSUPPORT) + return true; + case -EPROTONOSUPPORT: /* Unsupported feature */ log_info("%s: Unsupported feature, rotating.", f->path); - else if (r == -EBADMSG || r == -ENODATA || r == ESHUTDOWN) + return true; + case -EBADMSG: /* Corrupted */ + case -ENODATA: /* Truncated */ + case -ESHUTDOWN: /* Already archived */ log_warning("%s: Journal file corrupted, rotating.", f->path); - else if (r == -EIO) - log_warning("%s: IO error, rotating.", f->path); - else if (r == -EIDRM) + return true; + case -EIDRM: /* Journal file has been deleted */ log_warning("%s: Journal file has been deleted, rotating.", f->path); - else + return true; + default: return false; - - return true; + } } static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned n, int priority) { @@ -1400,7 +1405,7 @@ static int server_parse_proc_cmdline(Server *s) { } p = line; - for(;;) { + for (;;) { _cleanup_free_ char *word = NULL; r = extract_first_word(&p, &word, NULL, 0); @@ -1655,7 +1660,7 @@ static int server_connect_notify(Server *s) { it. Specifically: given that PID 1 might block on dbus-daemon during IPC, and dbus-daemon is logging to us, and might hence block on us, we might end up in a deadlock - if we block on sending PID 1 notification messages -- by + if we block on sending PID 1 notification messages — by generating a full blocking circle. To avoid this, let's create a non-blocking socket, and connect it to the notification socket, and then wait for POLLOUT before we @@ -1691,7 +1696,7 @@ static int server_connect_notify(Server *s) { if (sa.un.sun_path[0] == '@') sa.un.sun_path[0] = 0; - r = connect(s->notify_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(e)); + r = connect(s->notify_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) return log_error_errno(errno, "Failed to connect to notify socket: %m"); @@ -1765,6 +1770,10 @@ int server_init(Server *s) { if (!s->mmap) return log_oom(); + s->deferred_closes = set_new(NULL); + if (!s->deferred_closes) + return log_oom(); + r = sd_event_default(&s->event); if (r < 0) return log_error_errno(r, "Failed to create event loop: %m"); @@ -1918,17 +1927,22 @@ void server_done(Server *s) { JournalFile *f; assert(s); + if (s->deferred_closes) { + journal_file_close_set(s->deferred_closes); + set_free(s->deferred_closes); + } + while (s->stdout_streams) stdout_stream_free(s->stdout_streams); if (s->system_journal) - journal_file_close(s->system_journal); + (void) journal_file_close(s->system_journal); if (s->runtime_journal) - journal_file_close(s->runtime_journal); + (void) journal_file_close(s->runtime_journal); while ((f = ordered_hashmap_steal_first(s->user_journals))) - journal_file_close(f); + (void) journal_file_close(f); ordered_hashmap_free(s->user_journals); diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h index b9551dda1b..e025a4cf90 100644 --- a/src/journal/journald-server.h +++ b/src/journal/journald-server.h @@ -130,6 +130,8 @@ struct Server { MMapCache *mmap; + Set *deferred_closes; + struct udev *udev; uint64_t *kernel_seqnum; diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index 6e8b405b53..4ad16ee41c 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -96,7 +96,7 @@ void stdout_stream_free(StdoutStream *s) { if (s->server) { assert(s->server->n_stdout_streams > 0); - s->server->n_stdout_streams --; + s->server->n_stdout_streams--; LIST_REMOVE(stdout_stream, s->server->stdout_streams, s); if (s->in_notify_queue) @@ -511,7 +511,7 @@ static int stdout_stream_install(Server *s, int fd, StdoutStream **ret) { stream->server = s; LIST_PREPEND(stdout_stream, s->stdout_streams, stream); - s->n_stdout_streams ++; + s->n_stdout_streams++; if (ret) *ret = stream; @@ -700,23 +700,22 @@ fail: } int server_open_stdout_socket(Server *s) { + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/stdout", + }; int r; assert(s); if (s->stdout_fd < 0) { - union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/journal/stdout", - }; - s->stdout_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (s->stdout_fd < 0) return log_error_errno(errno, "socket() failed: %m"); - unlink(sa.un.sun_path); + (void) unlink(sa.un.sun_path); - r = bind(s->stdout_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)); + r = bind(s->stdout_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c index 5153fd0cce..0609b4b694 100644 --- a/src/journal/journald-syslog.c +++ b/src/journal/journald-syslog.c @@ -52,8 +52,7 @@ static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned .msg_iov = (struct iovec *) iovec, .msg_iovlen = n_iovec, .msg_name = (struct sockaddr*) &sa.sa, - .msg_namelen = offsetof(union sockaddr_union, un.sun_path) - + strlen("/run/systemd/journal/syslog"), + .msg_namelen = SOCKADDR_UN_LEN(sa.un), }; struct cmsghdr *cmsg; union { @@ -316,12 +315,12 @@ static void syslog_skip_date(char **buf) { } void server_process_syslog_message( - Server *s, - const char *buf, - const struct ucred *ucred, - const struct timeval *tv, - const char *label, - size_t label_len) { + Server *s, + const char *buf, + const struct ucred *ucred, + const struct timeval *tv, + const char *label, + size_t label_len) { char syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)], syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)]; @@ -365,14 +364,12 @@ void server_process_syslog_message( if (identifier) { syslog_identifier = strjoina("SYSLOG_IDENTIFIER=", identifier); - if (syslog_identifier) - IOVEC_SET_STRING(iovec[n++], syslog_identifier); + IOVEC_SET_STRING(iovec[n++], syslog_identifier); } if (pid) { syslog_pid = strjoina("SYSLOG_PID=", pid); - if (syslog_pid) - IOVEC_SET_STRING(iovec[n++], syslog_pid); + IOVEC_SET_STRING(iovec[n++], syslog_pid); } message = strjoina("MESSAGE=", buf); @@ -383,24 +380,24 @@ void server_process_syslog_message( } int server_open_syslog_socket(Server *s) { + + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/dev-log", + }; static const int one = 1; int r; assert(s); if (s->syslog_fd < 0) { - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/journal/dev-log", - }; - s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (s->syslog_fd < 0) return log_error_errno(errno, "socket() failed: %m"); - unlink(sa.un.sun_path); + (void) unlink(sa.un.sun_path); - r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)); + r = bind(s->syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); @@ -437,6 +434,7 @@ int server_open_syslog_socket(Server *s) { void server_maybe_warn_forward_syslog_missed(Server *s) { usec_t n; + assert(s); if (s->n_forward_syslog_missed <= 0) diff --git a/src/journal/journald.conf b/src/journal/journald.conf index 7beb96c671..2541b949be 100644 --- a/src/journal/journald.conf +++ b/src/journal/journald.conf @@ -17,7 +17,7 @@ #Seal=yes #SplitMode=uid #SyncIntervalSec=5m -#RateLimitInterval=30s +#RateLimitIntervalSec=30s #RateLimitBurst=1000 #SystemMaxUse= #SystemKeepFree= diff --git a/src/journal/mmap-cache.c b/src/journal/mmap-cache.c index 9c0ce8ccbf..6bcd9b6ac8 100644 --- a/src/journal/mmap-cache.c +++ b/src/journal/mmap-cache.c @@ -107,7 +107,7 @@ MMapCache* mmap_cache_ref(MMapCache *m) { assert(m); assert(m->n_ref > 0); - m->n_ref ++; + m->n_ref++; return m; } @@ -361,7 +361,7 @@ MMapCache* mmap_cache_unref(MMapCache *m) { assert(m->n_ref > 0); - m->n_ref --; + m->n_ref--; if (m->n_ref == 0) mmap_cache_free(m); @@ -598,14 +598,14 @@ int mmap_cache_get( /* Check whether the current context is the right one already */ r = try_context(m, fd, prot, context, keep_always, offset, size, ret); if (r != 0) { - m->n_hit ++; + m->n_hit++; return r; } /* Search for a matching mmap */ r = find_mmap(m, fd, prot, context, keep_always, offset, size, ret); if (r != 0) { - m->n_hit ++; + m->n_hit++; return r; } diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 5a2a28a8d4..1cea68ad42 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -19,6 +19,7 @@ #include <errno.h> #include <fcntl.h> +#include <inttypes.h> #include <linux/magic.h> #include <poll.h> #include <stddef.h> @@ -1063,7 +1064,7 @@ _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) { if (r < 0) return r; - for(;;) { + for (;;) { _cleanup_free_ char *item = NULL; unsigned long long ll; sd_id128_t id; @@ -1232,14 +1233,37 @@ static bool file_type_wanted(int flags, const char *filename) { return false; } -static int add_any_file(sd_journal *j, const char *path) { +static bool path_has_prefix(sd_journal *j, const char *path, const char *prefix) { + assert(j); + assert(path); + assert(prefix); + + if (j->toplevel_fd >= 0) + return false; + + return path_startswith(path, prefix); +} + +static const char *skip_slash(const char *p) { + + if (!p) + return NULL; + + while (*p == '/') + p++; + + return p; +} + +static int add_any_file(sd_journal *j, int fd, const char *path) { JournalFile *f = NULL; + bool close_fd = false; int r, k; assert(j); - assert(path); + assert(fd >= 0 || path); - if (ordered_hashmap_get(j->files, path)) + if (path && ordered_hashmap_get(j->files, path)) return 0; if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) { @@ -1248,8 +1272,24 @@ static int add_any_file(sd_journal *j, const char *path) { goto fail; } - r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f); + if (fd < 0 && j->toplevel_fd >= 0) { + + /* If there's a top-level fd defined, open the file relative to this now. (Make the path relative, + * explicitly, since otherwise openat() ignores the first argument.) */ + + fd = openat(j->toplevel_fd, skip_slash(path), O_RDONLY|O_CLOEXEC); + if (fd < 0) { + r = log_debug_errno(errno, "Failed to open journal file %s: %m", path); + goto fail; + } + + close_fd = true; + } + + r = journal_file_open(fd, path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, NULL, &f); if (r < 0) { + if (close_fd) + safe_close(fd); log_debug_errno(r, "Failed to open journal file %s: %m", path); goto fail; } @@ -1258,15 +1298,21 @@ static int add_any_file(sd_journal *j, const char *path) { r = ordered_hashmap_put(j->files, f->path, f); if (r < 0) { - journal_file_close(f); + f->close_fd = close_fd; + (void) journal_file_close(f); goto fail; } + if (!j->has_runtime_files && path_has_prefix(j, f->path, "/run")) + j->has_runtime_files = true; + else if (!j->has_persistent_files && path_has_prefix(j, f->path, "/var")) + j->has_persistent_files = true; + log_debug("File %s added.", f->path); check_network(j, f->fd); - j->current_invalidate_counter ++; + j->current_invalidate_counter++; return 0; @@ -1285,18 +1331,14 @@ static int add_file(sd_journal *j, const char *prefix, const char *filename) { assert(prefix); assert(filename); - if (j->no_new_files || - !file_type_wanted(j->flags, filename)) + if (j->no_new_files) return 0; - path = strjoina(prefix, "/", filename); - - if (!j->has_runtime_files && path_startswith(path, "/run/log/journal")) - j->has_runtime_files = true; - else if (!j->has_persistent_files && path_startswith(path, "/var/log/journal")) - j->has_persistent_files = true; + if (!file_type_wanted(j->flags, filename)) + return 0; - return add_any_file(j, path); + path = strjoina(prefix, "/", filename); + return add_any_file(j, -1, path); } static void remove_file(sd_journal *j, const char *prefix, const char *filename) { @@ -1343,9 +1385,9 @@ static void remove_file_real(sd_journal *j, JournalFile *f) { j->fields_file_lost = true; } - journal_file_close(f); + (void) journal_file_close(f); - j->current_invalidate_counter ++; + j->current_invalidate_counter++; } static int dirname_is_machine_id(const char *fn) { @@ -1372,21 +1414,33 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) assert(j); assert(prefix); - assert(dirname); - log_debug("Considering %s/%s.", prefix, dirname); - - if ((j->flags & SD_JOURNAL_LOCAL_ONLY) && - !(dirname_is_machine_id(dirname) > 0 || path_startswith(prefix, "/run"))) - return 0; + /* Adds a journal file directory to watch. If the directory is already tracked this updates the inotify watch + * and reenumerates directory contents */ - path = strjoin(prefix, "/", dirname, NULL); + if (dirname) + path = strjoin(prefix, "/", dirname, NULL); + else + path = strdup(prefix); if (!path) { r = -ENOMEM; goto fail; } - d = opendir(path); + log_debug("Considering directory %s.", path); + + /* We consider everything local that is in a directory for the local machine ID, or that is stored in /run */ + if ((j->flags & SD_JOURNAL_LOCAL_ONLY) && + !((dirname && dirname_is_machine_id(dirname) > 0) || path_has_prefix(j, path, "/run"))) + return 0; + + + if (j->toplevel_fd < 0) + d = opendir(path); + else + /* Open the specified directory relative to the the toplevel fd. Enforce that the path specified is + * relative, by dropping the initial slash */ + d = xopendirat(j->toplevel_fd, skip_slash(path), 0); if (!d) { r = log_debug_errno(errno, "Failed to open directory %s: %m", path); goto fail; @@ -1410,7 +1464,7 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) } path = NULL; /* avoid freeing in cleanup */ - j->current_invalidate_counter ++; + j->current_invalidate_counter++; log_debug("Directory %s added.", m->path); @@ -1418,17 +1472,18 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) return 0; if (m->wd <= 0 && j->inotify_fd >= 0) { + /* Watch this directory, if it not being watched yet. */ - m->wd = inotify_add_watch(j->inotify_fd, m->path, - IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| - IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM| - IN_ONLYDIR); + m->wd = inotify_add_watch_fd(j->inotify_fd, dirfd(d), + IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| + IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT|IN_MOVED_FROM| + IN_ONLYDIR); if (m->wd > 0 && hashmap_put(j->directories_by_wd, INT_TO_PTR(m->wd), m) < 0) inotify_rm_watch(j->inotify_fd, m->wd); } - FOREACH_DIRENT_ALL(de, d, return log_debug_errno(errno, "Failed to read directory %s: %m", m->path)) { + FOREACH_DIRENT_ALL(de, d, r = log_debug_errno(errno, "Failed to read directory %s: %m", m->path); goto fail) { if (dirent_is_file_with_suffix(de, ".journal") || dirent_is_file_with_suffix(de, ".journal~")) @@ -1440,7 +1495,7 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) return 0; fail: - k = journal_put_error(j, r, path ?: dirname); + k = journal_put_error(j, r, path ?: prefix); if (k < 0) return k; @@ -1448,28 +1503,62 @@ fail: } static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) { + _cleanup_closedir_ DIR *d = NULL; struct dirent *de; Directory *m; int r, k; assert(j); - assert(p); - if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) && - !path_startswith(p, "/run")) - return -EINVAL; + /* Adds a root directory to our set of directories to use. If the root directory is already in the set, we + * update the inotify logic, and renumerate the directory entries. This call may hence be called to initially + * populate the set, as well as to update it later. */ - if (j->prefix) - p = strjoina(j->prefix, p); + if (p) { + /* If there's a path specified, use it. */ - d = opendir(p); - if (!d) { - if (errno == ENOENT && missing_ok) - return 0; + if ((j->flags & SD_JOURNAL_RUNTIME_ONLY) && + !path_has_prefix(j, p, "/run")) + return -EINVAL; - r = log_debug_errno(errno, "Failed to open root directory %s: %m", p); - goto fail; + if (j->prefix) + p = strjoina(j->prefix, p); + + if (j->toplevel_fd < 0) + d = opendir(p); + else + d = xopendirat(j->toplevel_fd, skip_slash(p), 0); + + if (!d) { + if (errno == ENOENT && missing_ok) + return 0; + + r = log_debug_errno(errno, "Failed to open root directory %s: %m", p); + goto fail; + } + } else { + int dfd; + + /* If there's no path specified, then we use the top-level fd itself. We duplicate the fd here, since + * opendir() will take possession of the fd, and close it, which we don't want. */ + + p = "."; /* store this as "." in the directories hashmap */ + + dfd = fcntl(j->toplevel_fd, F_DUPFD_CLOEXEC, 3); + if (dfd < 0) { + r = -errno; + goto fail; + } + + d = fdopendir(dfd); + if (!d) { + r = -errno; + safe_close(dfd); + goto fail; + } + + rewinddir(d); } m = hashmap_get(j->directories_by_path, p); @@ -1481,6 +1570,7 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) { } m->is_root = true; + m->path = strdup(p); if (!m->path) { free(m); @@ -1495,7 +1585,7 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) { goto fail; } - j->current_invalidate_counter ++; + j->current_invalidate_counter++; log_debug("Root directory %s added.", m->path); @@ -1504,7 +1594,7 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) { if (m->wd <= 0 && j->inotify_fd >= 0) { - m->wd = inotify_add_watch(j->inotify_fd, m->path, + m->wd = inotify_add_watch_fd(j->inotify_fd, dirfd(d), IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB|IN_DELETE| IN_ONLYDIR); @@ -1515,7 +1605,7 @@ static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) { if (j->no_new_files) return 0; - FOREACH_DIRENT_ALL(de, d, return log_debug_errno(errno, "Failed to read directory %s: %m", m->path)) { + FOREACH_DIRENT_ALL(de, d, r = log_debug_errno(errno, "Failed to read directory %s: %m", m->path); goto fail) { sd_id128_t id; if (dirent_is_file_with_suffix(de, ".journal") || @@ -1584,8 +1674,7 @@ static int add_current_paths(sd_journal *j) { assert(j); assert(j->no_new_files); - /* Simply adds all directories for files we have open as - * "root" directories. We don't expect errors here, so we + /* Simply adds all directories for files we have open as directories. We don't expect errors here, so we * treat them as fatal. */ ORDERED_HASHMAP_FOREACH(f, j->files, i) { @@ -1596,7 +1685,7 @@ static int add_current_paths(sd_journal *j) { if (!dir) return -ENOMEM; - r = add_root_directory(j, dir, true); + r = add_directory(j, dir, NULL); if (r < 0) return r; } @@ -1613,13 +1702,7 @@ static int allocate_inotify(sd_journal *j) { return -errno; } - if (!j->directories_by_wd) { - j->directories_by_wd = hashmap_new(NULL); - if (!j->directories_by_wd) - return -ENOMEM; - } - - return 0; + return hashmap_ensure_allocated(&j->directories_by_wd, NULL); } static sd_journal *journal_new(int flags, const char *path) { @@ -1630,6 +1713,7 @@ static sd_journal *journal_new(int flags, const char *path) { return NULL; j->original_pid = getpid(); + j->toplevel_fd = -1; j->inotify_fd = -1; j->flags = flags; j->data_threshold = DEFAULT_DATA_THRESHOLD; @@ -1683,6 +1767,9 @@ _public_ int sd_journal_open_container(sd_journal **ret, const char *machine, in char *p; int r; + /* This is pretty much deprecated, people should use machined's OpenMachineRootDirectory() call instead in + * combination with sd_journal_open_directory_fd(). */ + assert_return(machine, -EINVAL); assert_return(ret, -EINVAL); assert_return((flags & ~(SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM)) == 0, -EINVAL); @@ -1725,13 +1812,16 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f assert_return(ret, -EINVAL); assert_return(path, -EINVAL); - assert_return(flags == 0, -EINVAL); + assert_return((flags & ~SD_JOURNAL_OS_ROOT) == 0, -EINVAL); j = journal_new(flags, path); if (!j) return -ENOMEM; - r = add_root_directory(j, path, false); + if (flags & SD_JOURNAL_OS_ROOT) + r = add_search_paths(j); + else + r = add_root_directory(j, path, false); if (r < 0) goto fail; @@ -1740,7 +1830,6 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f fail: sd_journal_close(j); - return r; } @@ -1757,7 +1846,7 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla return -ENOMEM; STRV_FOREACH(path, paths) { - r = add_any_file(j, *path); + r = add_any_file(j, -1, *path); if (r < 0) goto fail; } @@ -1769,7 +1858,96 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla fail: sd_journal_close(j); + return r; +} + +_public_ int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags) { + sd_journal *j; + struct stat st; + int r; + + assert_return(ret, -EINVAL); + assert_return(fd >= 0, -EBADF); + assert_return((flags & ~SD_JOURNAL_OS_ROOT) == 0, -EINVAL); + + if (fstat(fd, &st) < 0) + return -errno; + + if (!S_ISDIR(st.st_mode)) + return -EBADFD; + + j = journal_new(flags, NULL); + if (!j) + return -ENOMEM; + + j->toplevel_fd = fd; + + if (flags & SD_JOURNAL_OS_ROOT) + r = add_search_paths(j); + else + r = add_root_directory(j, NULL, false); + if (r < 0) + goto fail; + *ret = j; + return 0; + +fail: + sd_journal_close(j); + return r; +} + +_public_ int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags) { + Iterator iterator; + JournalFile *f; + sd_journal *j; + unsigned i; + int r; + + assert_return(ret, -EINVAL); + assert_return(n_fds > 0, -EBADF); + assert_return(flags == 0, -EINVAL); + + j = journal_new(flags, NULL); + if (!j) + return -ENOMEM; + + for (i = 0; i < n_fds; i++) { + struct stat st; + + if (fds[i] < 0) { + r = -EBADF; + goto fail; + } + + if (fstat(fds[i], &st) < 0) { + r = -errno; + goto fail; + } + + if (!S_ISREG(st.st_mode)) { + r = -EBADFD; + goto fail; + } + + r = add_any_file(j, fds[i], NULL); + if (r < 0) + goto fail; + } + + j->no_new_files = true; + j->no_inotify = true; + + *ret = j; + return 0; + +fail: + /* If we fail, make sure we don't take possession of the files we managed to make use of successfully, and they + * remain open */ + ORDERED_HASHMAP_FOREACH(f, j->files, iterator) + f->close_fd = false; + + sd_journal_close(j); return r; } @@ -1784,7 +1962,7 @@ _public_ void sd_journal_close(sd_journal *j) { sd_journal_flush_matches(j); while ((f = ordered_hashmap_steal_first(j->files))) - journal_file_close(f); + (void) journal_file_close(f); ordered_hashmap_free(j->files); @@ -1957,7 +2135,7 @@ _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void ** &f->compress_buffer, &f->compress_buffer_size, field, field_length, '='); if (r < 0) - log_debug_errno(r, "Cannot decompress %s object of length %zu at offset "OFSfmt": %m", + log_debug_errno(r, "Cannot decompress %s object of length %"PRIu64" at offset "OFSfmt": %m", object_compressed_to_string(compression), l, p); else if (r > 0) { @@ -2078,7 +2256,7 @@ _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t if (r < 0) return r; - j->current_field ++; + j->current_field++; return 1; } @@ -2096,6 +2274,9 @@ _public_ int sd_journal_get_fd(sd_journal *j) { assert_return(j, -EINVAL); assert_return(!journal_pid_changed(j), -ECHILD); + if (j->no_inotify) + return -EMEDIUMTYPE; + if (j->inotify_fd >= 0) return j->inotify_fd; @@ -2103,10 +2284,14 @@ _public_ int sd_journal_get_fd(sd_journal *j) { if (r < 0) return r; + log_debug("Reiterating files to get inotify watches established"); + /* Iterate through all dirs again, to add them to the * inotify */ if (j->no_new_files) r = add_current_paths(j); + else if (j->toplevel_fd >= 0) + r = add_root_directory(j, NULL, false); else if (j->path) r = add_root_directory(j, j->path, true); else diff --git a/src/journal/test-catalog.c b/src/journal/test-catalog.c index da6fcbca4d..898c876450 100644 --- a/src/journal/test-catalog.c +++ b/src/journal/test-catalog.c @@ -103,6 +103,8 @@ static void test_catalog_import_one(void) { assert_se(hashmap_size(h) == 1); HASHMAP_FOREACH(payload, h, j) { + printf("expect: %s\n", expect); + printf("actual: %s\n", payload); assert_se(streq(expect, payload)); } } diff --git a/src/journal/test-compress-benchmark.c b/src/journal/test-compress-benchmark.c index 5b2d130cd6..6f6d71435d 100644 --- a/src/journal/test-compress-benchmark.c +++ b/src/journal/test-compress-benchmark.c @@ -105,6 +105,8 @@ static void test_compress_decompress(const char* label, const char* type, int r; size = permute(i); + if (size == 0) + continue; log_debug("%s %zu %zu", type, i, size); @@ -162,7 +164,7 @@ int main(int argc, char *argv[]) { arg_duration = x * USEC_PER_SEC; } if (argc == 3) - (void) safe_atolu(argv[2], &arg_start); + (void) safe_atozu(argv[2], &arg_start); else arg_start = getpid(); diff --git a/src/journal/test-journal-enum.c b/src/journal/test-journal-enum.c index e5e9d9dcb3..354c2c3c00 100644 --- a/src/journal/test-journal-enum.c +++ b/src/journal/test-journal-enum.c @@ -44,7 +44,7 @@ int main(int argc, char *argv[]) { printf("%.*s\n", (int) l, (char*) d); - n ++; + n++; if (n >= 10) break; } diff --git a/src/journal/test-journal-flush.c b/src/journal/test-journal-flush.c index 7bd9c40366..ba8b20b228 100644 --- a/src/journal/test-journal-flush.c +++ b/src/journal/test-journal-flush.c @@ -38,7 +38,7 @@ int main(int argc, char *argv[]) { assert_se(mkdtemp(dn)); fn = strappend(dn, "/test.journal"); - r = journal_file_open(fn, O_CREAT|O_RDWR, 0644, false, false, NULL, NULL, NULL, &new_journal); + r = journal_file_open(-1, fn, O_CREAT|O_RDWR, 0644, false, false, NULL, NULL, NULL, NULL, &new_journal); assert_se(r >= 0); r = sd_journal_open(&j, 0); @@ -66,7 +66,7 @@ int main(int argc, char *argv[]) { sd_journal_close(j); - journal_file_close(new_journal); + (void) journal_file_close(new_journal); unlink(fn); assert_se(rmdir(dn) == 0); diff --git a/src/journal/test-journal-interleaving.c b/src/journal/test-journal-interleaving.c index 7f94990888..5e063f4d04 100644 --- a/src/journal/test-journal-interleaving.c +++ b/src/journal/test-journal-interleaving.c @@ -52,12 +52,12 @@ noreturn static void log_assert_errno(const char *text, int eno, const char *fil static JournalFile *test_open(const char *name) { JournalFile *f; - assert_ret(journal_file_open(name, O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, &f)); + assert_ret(journal_file_open(-1, name, O_RDWR|O_CREAT, 0644, true, false, NULL, NULL, NULL, NULL, &f)); return f; } static void test_close(JournalFile *f) { - journal_file_close (f); + (void) journal_file_close (f); } static void append_number(JournalFile *f, int n, uint64_t *seqnum) { @@ -216,8 +216,8 @@ static void test_sequence_numbers(void) { assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); - assert_se(journal_file_open("one.journal", O_RDWR|O_CREAT, 0644, - true, false, NULL, NULL, NULL, &one) == 0); + assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0644, + true, false, NULL, NULL, NULL, NULL, &one) == 0); append_number(one, 1, &seqnum); printf("seqnum=%"PRIu64"\n", seqnum); @@ -233,8 +233,8 @@ static void test_sequence_numbers(void) { memcpy(&seqnum_id, &one->header->seqnum_id, sizeof(sd_id128_t)); - assert_se(journal_file_open("two.journal", O_RDWR|O_CREAT, 0644, - true, false, NULL, NULL, one, &two) == 0); + assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0644, + true, false, NULL, NULL, NULL, one, &two) == 0); assert_se(two->header->state == STATE_ONLINE); assert_se(!sd_id128_equal(two->header->file_id, one->header->file_id)); @@ -264,8 +264,8 @@ static void test_sequence_numbers(void) { /* restart server */ seqnum = 0; - assert_se(journal_file_open("two.journal", O_RDWR, 0, - true, false, NULL, NULL, NULL, &two) == 0); + assert_se(journal_file_open(-1, "two.journal", O_RDWR, 0, + true, false, NULL, NULL, NULL, NULL, &two) == 0); assert_se(sd_id128_equal(two->header->seqnum_id, seqnum_id)); diff --git a/src/journal/test-journal-stream.c b/src/journal/test-journal-stream.c index 4e6f8c0f7b..7e5a980719 100644 --- a/src/journal/test-journal-stream.c +++ b/src/journal/test-journal-stream.c @@ -92,9 +92,9 @@ int main(int argc, char *argv[]) { assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); - assert_se(journal_file_open("one.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, &one) == 0); - assert_se(journal_file_open("two.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, &two) == 0); - assert_se(journal_file_open("three.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, &three) == 0); + assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &one) == 0); + assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &two) == 0); + assert_se(journal_file_open(-1, "three.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &three) == 0); for (i = 0; i < N_ENTRIES; i++) { char *p, *q; @@ -133,9 +133,9 @@ int main(int argc, char *argv[]) { free(q); } - journal_file_close(one); - journal_file_close(two); - journal_file_close(three); + (void) journal_file_close(one); + (void) journal_file_close(two); + (void) journal_file_close(three); assert_se(sd_journal_open_directory(&j, t, 0) >= 0); diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c index a26c624f41..3d2312fc55 100644 --- a/src/journal/test-journal-verify.c +++ b/src/journal/test-journal-verify.c @@ -55,12 +55,12 @@ static int raw_verify(const char *fn, const char *verification_key) { JournalFile *f; int r; - r = journal_file_open(fn, O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, &f); + r = journal_file_open(-1, fn, O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f); if (r < 0) return r; r = journal_file_verify(f, verification_key, NULL, NULL, NULL, false); - journal_file_close(f); + (void) journal_file_close(f); return r; } @@ -88,7 +88,7 @@ int main(int argc, char *argv[]) { log_info("Generating..."); - assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, true, !!verification_key, NULL, NULL, NULL, &f) == 0); + assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0); for (n = 0; n < N_ENTRIES; n++) { struct iovec iovec; @@ -107,11 +107,11 @@ int main(int argc, char *argv[]) { free(test); } - journal_file_close(f); + (void) journal_file_close(f); log_info("Verifying..."); - assert_se(journal_file_open("test.journal", O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, &f) == 0); + assert_se(journal_file_open(-1, "test.journal", O_RDONLY, 0666, true, !!verification_key, NULL, NULL, NULL, NULL, &f) == 0); /* journal_file_print_header(f); */ journal_file_dump(f); @@ -123,7 +123,7 @@ int main(int argc, char *argv[]) { format_timestamp(b, sizeof(b), to), format_timespan(c, sizeof(c), total > to ? total - to : 0, 0)); - journal_file_close(f); + (void) journal_file_close(f); if (verification_key) { log_info("Toggling bits..."); diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c index 0334b1cd1a..2543d64b5b 100644 --- a/src/journal/test-journal.c +++ b/src/journal/test-journal.c @@ -42,7 +42,7 @@ static void test_non_empty(void) { assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); - assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, &f) == 0); + assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f) == 0); dual_timestamp_get(&ts); @@ -104,10 +104,10 @@ static void test_non_empty(void) { assert_se(journal_file_move_to_entry_by_seqnum(f, 10, DIRECTION_DOWN, &o, NULL) == 0); - journal_file_rotate(&f, true, true); - journal_file_rotate(&f, true, true); + journal_file_rotate(&f, true, true, NULL); + journal_file_rotate(&f, true, true, NULL); - journal_file_close(f); + (void) journal_file_close(f); log_info("Done..."); @@ -131,13 +131,13 @@ static void test_empty(void) { assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); - assert_se(journal_file_open("test.journal", O_RDWR|O_CREAT, 0666, false, false, NULL, NULL, NULL, &f1) == 0); + assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, false, false, NULL, NULL, NULL, NULL, &f1) == 0); - assert_se(journal_file_open("test-compress.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, &f2) == 0); + assert_se(journal_file_open(-1, "test-compress.journal", O_RDWR|O_CREAT, 0666, true, false, NULL, NULL, NULL, NULL, &f2) == 0); - assert_se(journal_file_open("test-seal.journal", O_RDWR|O_CREAT, 0666, false, true, NULL, NULL, NULL, &f3) == 0); + assert_se(journal_file_open(-1, "test-seal.journal", O_RDWR|O_CREAT, 0666, false, true, NULL, NULL, NULL, NULL, &f3) == 0); - assert_se(journal_file_open("test-seal-compress.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, &f4) == 0); + assert_se(journal_file_open(-1, "test-seal-compress.journal", O_RDWR|O_CREAT, 0666, true, true, NULL, NULL, NULL, NULL, &f4) == 0); journal_file_print_header(f1); puts(""); @@ -158,10 +158,10 @@ static void test_empty(void) { assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); } - journal_file_close(f1); - journal_file_close(f2); - journal_file_close(f3); - journal_file_close(f4); + (void) journal_file_close(f1); + (void) journal_file_close(f2); + (void) journal_file_close(f3); + (void) journal_file_close(f4); } int main(int argc, char *argv[]) { diff --git a/src/libsystemd-network/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c index 1d9ec7be82..a21efc4d06 100644 --- a/src/libsystemd-network/dhcp-identifier.c +++ b/src/libsystemd-network/dhcp-identifier.c @@ -31,6 +31,37 @@ #define SYSTEMD_PEN 43793 #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09) +int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len) { + struct duid d; + + assert_cc(sizeof(d.raw) >= MAX_DUID_LEN); + if (duid_len > MAX_DUID_LEN) + return -EINVAL; + + switch (duid_type) { + case DUID_TYPE_LLT: + if (duid_len <= sizeof(d.llt)) + return -EINVAL; + break; + case DUID_TYPE_EN: + if (duid_len != sizeof(d.en)) + return -EINVAL; + break; + case DUID_TYPE_LL: + if (duid_len <= sizeof(d.ll)) + return -EINVAL; + break; + case DUID_TYPE_UUID: + if (duid_len != sizeof(d.uuid)) + return -EINVAL; + break; + default: + /* accept unknown type in order to be forward compatible */ + break; + } + return 0; +} + int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) { sd_id128_t machine_id; uint64_t hash; @@ -43,7 +74,7 @@ int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) { if (r < 0) return r; - unaligned_write_be16(&duid->type, DHCP6_DUID_EN); + unaligned_write_be16(&duid->type, DUID_TYPE_EN); unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN); *len = sizeof(duid->type) + sizeof(duid->en); diff --git a/src/libsystemd-network/dhcp-identifier.h b/src/libsystemd-network/dhcp-identifier.h index 93f06f5938..1cc0f9fb71 100644 --- a/src/libsystemd-network/dhcp-identifier.h +++ b/src/libsystemd-network/dhcp-identifier.h @@ -25,32 +25,42 @@ #include "sparse-endian.h" #include "unaligned.h" +typedef enum DUIDType { + DUID_TYPE_LLT = 1, + DUID_TYPE_EN = 2, + DUID_TYPE_LL = 3, + DUID_TYPE_UUID = 4, + _DUID_TYPE_MAX, + _DUID_TYPE_INVALID = -1, +} DUIDType; + /* RFC 3315 section 9.1: * A DUID can be no more than 128 octets long (not including the type code). */ #define MAX_DUID_LEN 128 +/* https://tools.ietf.org/html/rfc3315#section-9.1 */ struct duid { - uint16_t type; + be16_t type; union { struct { - /* DHCP6_DUID_LLT */ + /* DUID_TYPE_LLT */ uint16_t htype; uint32_t time; uint8_t haddr[0]; } _packed_ llt; struct { - /* DHCP6_DUID_EN */ + /* DUID_TYPE_EN */ uint32_t pen; uint8_t id[8]; } _packed_ en; struct { - /* DHCP6_DUID_LL */ + /* DUID_TYPE_LL */ int16_t htype; uint8_t haddr[0]; } _packed_ ll; struct { - /* DHCP6_DUID_UUID */ + /* DUID_TYPE_UUID */ sd_id128_t uuid; } _packed_ uuid; struct { @@ -59,5 +69,6 @@ struct duid { }; } _packed_; +int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len); int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len); int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id); diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h index a3b842cda3..4662b0d847 100644 --- a/src/libsystemd-network/dhcp-internal.h +++ b/src/libsystemd-network/dhcp-internal.h @@ -42,10 +42,10 @@ int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_t overload, uint8_t code, size_t optlen, const void *optval); -typedef int (*dhcp_option_cb_t)(uint8_t code, uint8_t len, +typedef int (*dhcp_option_callback_t)(uint8_t code, uint8_t len, const void *option, void *userdata); -int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_cb_t cb, void *userdata, char **error_message); +int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **error_message); int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, uint8_t type, uint16_t arp_type, size_t optlen, diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c index df1996c8ce..c105196334 100644 --- a/src/libsystemd-network/dhcp-option.c +++ b/src/libsystemd-network/dhcp-option.c @@ -34,7 +34,7 @@ static int option_append(uint8_t options[], size_t size, size_t *offset, if (code != SD_DHCP_OPTION_END) /* always make sure there is space for an END option */ - size --; + size--; switch (code) { @@ -54,12 +54,7 @@ static int option_append(uint8_t options[], size_t size, size_t *offset, options[*offset] = code; options[*offset + 1] = optlen; - if (optlen) { - assert(optval); - - memcpy(&options[*offset + 2], optval, optlen); - } - + memcpy_safe(&options[*offset + 2], optval, optlen); *offset += optlen + 2; break; @@ -140,7 +135,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, } static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload, - uint8_t *message_type, char **error_message, dhcp_option_cb_t cb, + uint8_t *message_type, char **error_message, dhcp_option_callback_t cb, void *userdata) { uint8_t code, len; const uint8_t *option; @@ -226,7 +221,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo return 0; } -int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_cb_t cb, void *userdata, char **_error_message) { +int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **_error_message) { _cleanup_free_ char *error_message = NULL; uint8_t overload = 0; uint8_t message_type = 0; diff --git a/src/libsystemd-network/dhcp-packet.c b/src/libsystemd-network/dhcp-packet.c index 8d75d49691..8be774061d 100644 --- a/src/libsystemd-network/dhcp-packet.c +++ b/src/libsystemd-network/dhcp-packet.c @@ -66,7 +66,7 @@ uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len) { /* wrap around in one's complement */ sum++; - buf_64 ++; + buf_64++; } if (len % sizeof(uint64_t)) { diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h index bf123f1439..0c76956fad 100644 --- a/src/libsystemd-network/dhcp-server-internal.h +++ b/src/libsystemd-network/dhcp-server-internal.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -18,8 +20,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - #include "sd-dhcp-server.h" #include "sd-event.h" @@ -63,6 +63,8 @@ struct sd_dhcp_server { struct in_addr *ntp, *dns; unsigned n_ntp, n_dns; + bool emit_router; + Hashmap *leases_by_client_id; DHCPLease **bound_leases; DHCPLease invalid_lease; diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 749086d33a..945c3b9721 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -55,7 +55,8 @@ struct DHCP6IA { typedef struct DHCP6IA DHCP6IA; -#define log_dhcp6_client(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__) +#define log_dhcp6_client_errno(p, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__) +#define log_dhcp6_client(p, fmt, ...) log_dhcp6_client_errno(p, 0, fmt, ##__VA_ARGS__) int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, size_t optlen, const void *optval); diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index b073906660..5462e03476 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -71,8 +71,7 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, if (r < 0) return r; - if (optval) - memcpy(*buf, optval, optlen); + memcpy_safe(*buf, optval, optlen); *buf += optlen; *buflen -= optlen; diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h index ee4bdfb07f..2487c470ab 100644 --- a/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libsystemd-network/dhcp6-protocol.h @@ -62,13 +62,6 @@ enum { #define DHCP6_REB_TIMEOUT 10 * USEC_PER_SEC #define DHCP6_REB_MAX_RT 600 * USEC_PER_SEC -enum { - DHCP6_DUID_LLT = 1, - DHCP6_DUID_EN = 2, - DHCP6_DUID_LL = 3, - DHCP6_DUID_UUID = 4, -}; - enum DHCP6State { DHCP6_STATE_STOPPED = 0, DHCP6_STATE_INFORMATION_REQUEST = 1, diff --git a/src/libsystemd-network/lldp-internal.c b/src/libsystemd-network/lldp-internal.c deleted file mode 100644 index c8740ce5f0..0000000000 --- a/src/libsystemd-network/lldp-internal.c +++ /dev/null @@ -1,360 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 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 "sd-lldp.h" - -#include "alloc-util.h" -#include "lldp-internal.h" - -/* We store maximum 1K chassis entries */ -#define LLDP_MIB_MAX_CHASSIS 1024 - -/* Maximum Ports can be attached to any chassis */ -#define LLDP_MIB_MAX_PORT_PER_CHASSIS 32 - -/* 10.5.5.2.2 mibUpdateObjects () - * The mibUpdateObjects () procedure updates the MIB objects corresponding to - * the TLVs contained in the received LLDPDU for the LLDP remote system - * indicated by the LLDP remote systems update process defined in 10.3.5 */ - -int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv) { - lldp_neighbour_port *p; - uint16_t length, ttl; - uint8_t *data; - uint8_t type; - int r; - - assert_return(c, -EINVAL); - assert_return(tlv, -EINVAL); - - r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length); - if (r < 0) - return r; - - /* Update the packet if we already have */ - LIST_FOREACH(port, p, c->ports) { - - if ((p->type == type && p->length == length && !memcmp(p->data, data, p->length))) { - - r = sd_lldp_packet_read_ttl(tlv, &ttl); - if (r < 0) - return r; - - p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic()); - - sd_lldp_packet_unref(p->packet); - p->packet = tlv; - - prioq_reshuffle(p->c->by_expiry, p, &p->prioq_idx); - - return 0; - } - } - - return -1; -} - -int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv) { - lldp_neighbour_port *p, *q; - uint8_t *data; - uint16_t length; - uint8_t type; - int r; - - assert_return(c, -EINVAL); - assert_return(tlv, -EINVAL); - - r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length); - if (r < 0) - return r; - - LIST_FOREACH_SAFE(port, p, q, c->ports) { - - /* Find the port */ - if (p->type == type && p->length == length && !memcmp(p->data, data, p->length)) { - lldp_neighbour_port_remove_and_free(p); - break; - } - } - - return 0; -} - -int lldp_mib_add_objects(Prioq *by_expiry, - Hashmap *neighbour_mib, - tlv_packet *tlv) { - _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL; - _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL; - lldp_chassis_id chassis_id; - bool new_chassis = false; - uint8_t subtype, *data; - uint16_t ttl, length; - int r; - - assert_return(by_expiry, -EINVAL); - assert_return(neighbour_mib, -EINVAL); - assert_return(tlv, -EINVAL); - - r = sd_lldp_packet_read_chassis_id(tlv, &subtype, &data, &length); - if (r < 0) - goto drop; - - r = sd_lldp_packet_read_ttl(tlv, &ttl); - if (r < 0) - goto drop; - - /* Make hash key */ - chassis_id.type = subtype; - chassis_id.length = length; - chassis_id.data = data; - - /* Try to find the Chassis */ - c = hashmap_get(neighbour_mib, &chassis_id); - if (!c) { - - /* Don't create chassis if ttl 0 is received . Silently drop it */ - if (ttl == 0) { - log_lldp("TTL value 0 received. Skiping Chassis creation."); - goto drop; - } - - /* Admission Control: Can we store this packet ? */ - if (hashmap_size(neighbour_mib) >= LLDP_MIB_MAX_CHASSIS) { - - log_lldp("Exceeding number of chassie: %d. Dropping ...", - hashmap_size(neighbour_mib)); - goto drop; - } - - r = lldp_chassis_new(tlv, by_expiry, neighbour_mib, &c); - if (r < 0) - goto drop; - - new_chassis = true; - - r = hashmap_put(neighbour_mib, &c->chassis_id, c); - if (r < 0) - goto drop; - - } else { - - /* When the TTL field is set to zero, the receiving LLDP agent is notified all - * system information associated with the LLDP agent/port is to be deleted */ - if (ttl == 0) { - log_lldp("TTL value 0 received . Deleting associated Port ..."); - - lldp_mib_remove_objects(c, tlv); - - c = NULL; - goto drop; - } - - /* if we already have this port just update it */ - r = lldp_mib_update_objects(c, tlv); - if (r >= 0) { - c = NULL; - return r; - } - - /* Admission Control: Can this port attached to the existing chassis ? */ - if (c->n_ref >= LLDP_MIB_MAX_PORT_PER_CHASSIS) { - log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...", c->n_ref); - - c = NULL; - goto drop; - } - } - - /* This is a new port */ - r = lldp_neighbour_port_new(c, tlv, &p); - if (r < 0) - goto drop; - - r = prioq_put(c->by_expiry, p, &p->prioq_idx); - if (r < 0) - goto drop; - - /* Attach new port to chassis */ - LIST_PREPEND(port, c->ports, p); - c->n_ref ++; - - p = NULL; - c = NULL; - - return 0; - - drop: - sd_lldp_packet_unref(tlv); - - if (new_chassis) - hashmap_remove(neighbour_mib, &c->chassis_id); - - return r; -} - -void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p) { - lldp_chassis *c; - - assert(p); - assert(p->c); - - c = p->c; - - prioq_remove(c->by_expiry, p, &p->prioq_idx); - - LIST_REMOVE(port, c->ports, p); - lldp_neighbour_port_free(p); - - /* Drop the Chassis if no port is attached */ - c->n_ref --; - if (c->n_ref <= 1) { - hashmap_remove(c->neighbour_mib, &c->chassis_id); - lldp_chassis_free(c); - } -} - -void lldp_neighbour_port_free(lldp_neighbour_port *p) { - - if(!p) - return; - - sd_lldp_packet_unref(p->packet); - - free(p->data); - free(p); -} - -int lldp_neighbour_port_new(lldp_chassis *c, - tlv_packet *tlv, - lldp_neighbour_port **ret) { - _cleanup_lldp_neighbour_port_free_ lldp_neighbour_port *p = NULL; - uint16_t length, ttl; - uint8_t *data; - uint8_t type; - int r; - - assert(tlv); - - r = sd_lldp_packet_read_port_id(tlv, &type, &data, &length); - if (r < 0) - return r; - - r = sd_lldp_packet_read_ttl(tlv, &ttl); - if (r < 0) - return r; - - p = new0(lldp_neighbour_port, 1); - if (!p) - return -ENOMEM; - - p->c = c; - p->type = type; - p->length = length; - p->packet = tlv; - p->prioq_idx = PRIOQ_IDX_NULL; - p->until = ttl * USEC_PER_SEC + now(clock_boottime_or_monotonic()); - - p->data = memdup(data, length); - if (!p->data) - return -ENOMEM; - - *ret = p; - p = NULL; - - return 0; -} - -void lldp_chassis_free(lldp_chassis *c) { - - if (!c) - return; - - if (c->n_ref > 1) - return; - - free(c->chassis_id.data); - free(c); -} - -int lldp_chassis_new(tlv_packet *tlv, - Prioq *by_expiry, - Hashmap *neighbour_mib, - lldp_chassis **ret) { - _cleanup_lldp_chassis_free_ lldp_chassis *c = NULL; - uint16_t length; - uint8_t *data; - uint8_t type; - int r; - - assert(tlv); - - r = sd_lldp_packet_read_chassis_id(tlv, &type, &data, &length); - if (r < 0) - return r; - - c = new0(lldp_chassis, 1); - if (!c) - return -ENOMEM; - - c->n_ref = 1; - c->chassis_id.type = type; - c->chassis_id.length = length; - - c->chassis_id.data = memdup(data, length); - if (!c->chassis_id.data) - return -ENOMEM; - - LIST_HEAD_INIT(c->ports); - - c->by_expiry = by_expiry; - c->neighbour_mib = neighbour_mib; - - *ret = c; - c = NULL; - - return 0; -} - -int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - _cleanup_(sd_lldp_packet_unrefp) tlv_packet *packet = NULL; - tlv_packet *p; - uint16_t length; - int r; - - assert(fd); - assert(userdata); - - r = tlv_packet_new(&packet); - if (r < 0) - return r; - - length = read(fd, &packet->pdu, sizeof(packet->pdu)); - - /* Silently drop the packet */ - if ((size_t) length > ETHER_MAX_LEN) - return 0; - - packet->userdata = userdata; - - p = packet; - packet = NULL; - - return lldp_handle_packet(p, (uint16_t) length); -} diff --git a/src/libsystemd-network/lldp-internal.h b/src/libsystemd-network/lldp-internal.h index 15b4a11b15..7592bc4305 100644 --- a/src/libsystemd-network/lldp-internal.h +++ b/src/libsystemd-network/lldp-internal.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -18,74 +20,34 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - #include "sd-event.h" +#include "sd-lldp.h" -#include "list.h" -#include "lldp-tlv.h" +#include "hashmap.h" #include "log.h" #include "prioq.h" -typedef struct lldp_neighbour_port lldp_neighbour_port; -typedef struct lldp_chassis lldp_chassis; -typedef struct lldp_chassis_id lldp_chassis_id; -typedef struct lldp_agent_statistics lldp_agent_statistics; +struct sd_lldp { + int ifindex; + int fd; -struct lldp_neighbour_port { - uint8_t type; - uint8_t *data; + sd_event *event; + int64_t event_priority; + sd_event_source *io_event_source; + sd_event_source *timer_event_source; - uint16_t length; - usec_t until; + Prioq *neighbor_by_expiry; + Hashmap *neighbor_by_id; - unsigned prioq_idx; + uint64_t neighbors_max; - lldp_chassis *c; - tlv_packet *packet; + sd_lldp_callback_t callback; + void *userdata; - LIST_FIELDS(lldp_neighbour_port, port); -}; + uint16_t capability_mask; -int lldp_neighbour_port_new(lldp_chassis *c, tlv_packet *tlv, lldp_neighbour_port **ret); -void lldp_neighbour_port_free(lldp_neighbour_port *p); -void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p); - -DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_neighbour_port *, lldp_neighbour_port_free); -#define _cleanup_lldp_neighbour_port_free_ _cleanup_(lldp_neighbour_port_freep) - -struct lldp_chassis_id { - uint8_t type; - uint16_t length; - - uint8_t *data; + struct ether_addr filter_address; }; -struct lldp_chassis { - unsigned n_ref; - - lldp_chassis_id chassis_id; - - Prioq *by_expiry; - Hashmap *neighbour_mib; - - LIST_HEAD(lldp_neighbour_port, ports); -}; - -int lldp_chassis_new(tlv_packet *tlv, - Prioq *by_expiry, - Hashmap *neighbour_mib, - lldp_chassis **ret); - -void lldp_chassis_free(lldp_chassis *c); - -DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_chassis *, lldp_chassis_free); -#define _cleanup_lldp_chassis_free_ _cleanup_(lldp_chassis_freep) - -int lldp_mib_update_objects(lldp_chassis *c, tlv_packet *tlv); -int lldp_mib_add_objects(Prioq *by_expiry, Hashmap *neighbour_mib, tlv_packet *tlv); -int lldp_mib_remove_objects(lldp_chassis *c, tlv_packet *tlv); - -int lldp_handle_packet(tlv_packet *m, uint16_t length); -int lldp_receive_packet(sd_event_source *s, int fd, uint32_t revents, void *userdata); -#define log_lldp(fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__) +#define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__) +#define log_lldp(fmt, ...) log_lldp_errno(0, fmt, ##__VA_ARGS__) diff --git a/src/libsystemd-network/lldp-neighbor.c b/src/libsystemd-network/lldp-neighbor.c new file mode 100644 index 0000000000..6a716430e3 --- /dev/null +++ b/src/libsystemd-network/lldp-neighbor.c @@ -0,0 +1,794 @@ +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "alloc-util.h" +#include "escape.h" +#include "ether-addr-util.h" +#include "hexdecoct.h" +#include "in-addr-util.h" +#include "lldp-internal.h" +#include "lldp-neighbor.h" +#include "unaligned.h" + +static void lldp_neighbor_id_hash_func(const void *p, struct siphash *state) { + const LLDPNeighborID *id = p; + + siphash24_compress(id->chassis_id, id->chassis_id_size, state); + siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state); + siphash24_compress(id->port_id, id->port_id_size, state); + siphash24_compress(&id->port_id_size, sizeof(id->port_id_size), state); +} + +static int lldp_neighbor_id_compare_func(const void *a, const void *b) { + const LLDPNeighborID *x = a, *y = b; + int r; + + r = memcmp(x->chassis_id, y->chassis_id, MIN(x->chassis_id_size, y->chassis_id_size)); + if (r != 0) + return r; + + if (x->chassis_id_size < y->chassis_id_size) + return -1; + + if (x->chassis_id_size > y->chassis_id_size) + return 1; + + r = memcmp(x->port_id, y->port_id, MIN(x->port_id_size, y->port_id_size)); + if (r != 0) + return r; + + if (x->port_id_size < y->port_id_size) + return -1; + if (x->port_id_size > y->port_id_size) + return 1; + + return 0; +} + +const struct hash_ops lldp_neighbor_id_hash_ops = { + .hash = lldp_neighbor_id_hash_func, + .compare = lldp_neighbor_id_compare_func +}; + +int lldp_neighbor_prioq_compare_func(const void *a, const void *b) { + const sd_lldp_neighbor *x = a, *y = b; + + if (x->until < y->until) + return -1; + + if (x->until > y->until) + return 1; + + return 0; +} + +_public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) { + if (!n) + return NULL; + + assert(n->n_ref > 0 || n->lldp); + n->n_ref++; + + return n; +} + +static void lldp_neighbor_free(sd_lldp_neighbor *n) { + assert(n); + + free(n->id.port_id); + free(n->id.chassis_id); + free(n->port_description); + free(n->system_name); + free(n->system_description); + free(n->chassis_id_as_string); + free(n->port_id_as_string); + free(n); +} + +_public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) { + + /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from + * the sd_lldp object. */ + + if (!n) + return NULL; + + assert(n->n_ref > 0); + n->n_ref--; + + if (n->n_ref <= 0 && !n->lldp) + lldp_neighbor_free(n); + + return NULL; +} + +sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) { + + /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */ + + if (!n) + return NULL; + + if (!n->lldp) + return NULL; + + assert_se(hashmap_remove(n->lldp->neighbor_by_id, &n->id) == n); + assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0); + + n->lldp = NULL; + + if (n->n_ref <= 0) + lldp_neighbor_free(n); + + return NULL; +} + +sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) { + sd_lldp_neighbor *n; + + n = malloc0(ALIGN(sizeof(sd_lldp_neighbor)) + raw_size); + if (!n) + return NULL; + + n->raw_size = raw_size; + n->n_ref = 1; + + return n; +} + +static int parse_string(char **s, const void *q, size_t n) { + const char *p = q; + char *k; + + assert(s); + assert(p || n == 0); + + if (*s) { + log_lldp("Found duplicate string, ignoring field."); + return 0; + } + + /* Strip trailing NULs, just to be nice */ + while (n > 0 && p[n-1] == 0) + n--; + + if (n <= 0) /* Ignore empty strings */ + return 0; + + /* Look for inner NULs */ + if (memchr(p, 0, n)) { + log_lldp("Found inner NUL in string, ignoring field."); + return 0; + } + + /* Let's escape weird chars, for security reasons */ + k = cescape_length(p, n); + if (!k) + return -ENOMEM; + + free(*s); + *s = k; + + return 1; +} + +int lldp_neighbor_parse(sd_lldp_neighbor *n) { + struct ether_header h; + const uint8_t *p; + size_t left; + int r; + + assert(n); + + if (n->raw_size < sizeof(struct ether_header)) { + log_lldp("Recieved truncated packet, ignoring."); + return -EBADMSG; + } + + memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h)); + + if (h.ether_type != htobe16(ETHERTYPE_LLDP)) { + log_lldp("Received packet with wrong type, ignoring."); + return -EBADMSG; + } + + if (h.ether_dhost[0] != 0x01 || + h.ether_dhost[1] != 0x80 || + h.ether_dhost[2] != 0xc2 || + h.ether_dhost[3] != 0x00 || + h.ether_dhost[4] != 0x00 || + !IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) { + log_lldp("Received packet with wrong destination address, ignoring."); + return -EBADMSG; + } + + memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr)); + memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr)); + + p = (const uint8_t*) LLDP_NEIGHBOR_RAW(n) + sizeof(struct ether_header); + left = n->raw_size - sizeof(struct ether_header); + + for (;;) { + uint8_t type; + uint16_t length; + + if (left < 2) { + log_lldp("TLV lacks header, ignoring."); + return -EBADMSG; + } + + type = p[0] >> 1; + length = p[1] + (((uint16_t) (p[0] & 1)) << 8); + p += 2, left -= 2; + + if (left < length) { + log_lldp("TLV truncated, ignoring datagram."); + return -EBADMSG; + } + + switch (type) { + + case SD_LLDP_TYPE_END: + if (length != 0) { + 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; + } + + goto end_marker; + + case SD_LLDP_TYPE_CHASSIS_ID: + if (length < 2 || length > 256) { /* includes the chassis subtype, hence one extra byte */ + log_lldp("Chassis ID field size out of range, ignoring datagram."); + return -EBADMSG; + } + if (n->id.chassis_id) { + log_lldp("Duplicate chassis ID field, ignoring datagram."); + return -EBADMSG; + } + + n->id.chassis_id = memdup(p, length); + if (!n->id.chassis_id) + return -ENOMEM; + + n->id.chassis_id_size = length; + break; + + case SD_LLDP_TYPE_PORT_ID: + if (length < 2 || length > 256) { /* includes the port subtype, hence one extra byte */ + log_lldp("Port ID field size out of range, ignoring datagram."); + return -EBADMSG; + } + if (n->id.port_id) { + log_lldp("Duplicate port ID field, ignoring datagram."); + return -EBADMSG; + } + + n->id.port_id = memdup(p, length); + if (!n->id.port_id) + return -ENOMEM; + + n->id.port_id_size = length; + break; + + case SD_LLDP_TYPE_TTL: + if (length != 2) { + log_lldp("TTL field has wrong size, ignoring datagram."); + return -EBADMSG; + } + + if (n->has_ttl) { + log_lldp("Duplicate TTL field, ignoring datagram."); + return -EBADMSG; + } + + n->ttl = unaligned_read_be16(p); + n->has_ttl = true; + break; + + case SD_LLDP_TYPE_PORT_DESCRIPTION: + r = parse_string(&n->port_description, p, length); + if (r < 0) + return r; + break; + + case SD_LLDP_TYPE_SYSTEM_NAME: + r = parse_string(&n->system_name, p, length); + if (r < 0) + return r; + break; + + case SD_LLDP_TYPE_SYSTEM_DESCRIPTION: + r = parse_string(&n->system_description, p, length); + if (r < 0) + return r; + break; + + case SD_LLDP_TYPE_SYSTEM_CAPABILITIES: + if (length != 4) + log_lldp("System capabilities field has wrong size, ignoring."); + else { + n->system_capabilities = unaligned_read_be16(p); + n->enabled_capabilities = unaligned_read_be16(p + 2); + n->has_capabilities = true; + } + + break; + + case SD_LLDP_TYPE_PRIVATE: + if (length < 4) + log_lldp("Found private TLV that is too short, ignoring."); + + break; + } + + + p += length, left -= length; + } + +end_marker: + if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) { + log_lldp("One or more mandatory TLV missing in datagram. Ignoring."); + return -EBADMSG; + + } + + n->rindex = sizeof(struct ether_header); + + return 0; +} + +void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) { + assert(n); + + if (n->ttl > 0) + n->until = usec_add(now(clock_boottime_or_monotonic()), n->ttl * USEC_PER_SEC); + else + n->until = 0; + + if (n->lldp) + prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx); +} + +bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) { + if (a == b) + return true; + + if (!a || !b) + return false; + + if (a->raw_size != b->raw_size) + return false; + + return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0; +} + +_public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) { + assert_return(n, -EINVAL); + assert_return(address, -EINVAL); + + *address = n->source_address; + return 0; +} + +_public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) { + assert_return(n, -EINVAL); + assert_return(address, -EINVAL); + + *address = n->destination_address; + return 0; +} + +_public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) { + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + assert_return(size, -EINVAL); + + *ret = LLDP_NEIGHBOR_RAW(n); + *size = n->raw_size; + + return 0; +} + +_public_ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) { + assert_return(n, -EINVAL); + assert_return(type, -EINVAL); + assert_return(ret, -EINVAL); + assert_return(size, -EINVAL); + + assert(n->id.chassis_id_size > 0); + + *type = *(uint8_t*) n->id.chassis_id; + *ret = (uint8_t*) n->id.chassis_id + 1; + *size = n->id.chassis_id_size - 1; + + return 0; +} + +static int format_mac_address(const void *data, size_t sz, char **ret) { + struct ether_addr a; + char *k; + + assert(data || sz <= 0); + + if (sz != 7) + return 0; + + memcpy(&a, (uint8_t*) data + 1, sizeof(a)); + + k = new(char, ETHER_ADDR_TO_STRING_MAX); + if (!k) + return -ENOMEM; + + *ret = ether_addr_to_string(&a, k); + return 1; +} + +static int format_network_address(const void *data, size_t sz, char **ret) { + union in_addr_union a; + int family, r; + + if (sz == 6 && ((uint8_t*) data)[1] == 1) { + memcpy(&a.in, (uint8_t*) data + 2, sizeof(a.in)); + family = AF_INET; + } else if (sz == 18 && ((uint8_t*) data)[1] == 2) { + memcpy(&a.in6, (uint8_t*) data + 2, sizeof(a.in6)); + family = AF_INET6; + } else + return 0; + + r = in_addr_to_string(family, &a, ret); + if (r < 0) + return r; + return 1; +} + +_public_ int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) { + char *k; + int r; + + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + + if (n->chassis_id_as_string) { + *ret = n->chassis_id_as_string; + return 0; + } + + assert(n->id.chassis_id_size > 0); + + switch (*(uint8_t*) n->id.chassis_id) { + + case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT: + case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS: + case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT: + case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME: + case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED: + k = cescape_length((char*) n->id.chassis_id + 1, n->id.chassis_id_size - 1); + if (!k) + return -ENOMEM; + + goto done; + + case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS: + r = format_mac_address(n->id.chassis_id, n->id.chassis_id_size, &k); + if (r < 0) + return r; + if (r > 0) + goto done; + + break; + + case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS: + r = format_network_address(n->id.chassis_id, n->id.chassis_id_size, &k); + if (r < 0) + return r; + if (r > 0) + goto done; + + break; + } + + /* Generic fallback */ + k = hexmem(n->id.chassis_id, n->id.chassis_id_size); + if (!k) + return -ENOMEM; + +done: + *ret = n->chassis_id_as_string = k; + return 0; +} + +_public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) { + assert_return(n, -EINVAL); + assert_return(type, -EINVAL); + assert_return(ret, -EINVAL); + assert_return(size, -EINVAL); + + assert(n->id.port_id_size > 0); + + *type = *(uint8_t*) n->id.port_id; + *ret = (uint8_t*) n->id.port_id + 1; + *size = n->id.port_id_size - 1; + + return 0; +} + +_public_ int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) { + char *k; + int r; + + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + + if (n->port_id_as_string) { + *ret = n->port_id_as_string; + return 0; + } + + assert(n->id.port_id_size > 0); + + switch (*(uint8_t*) n->id.port_id) { + + case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS: + case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT: + case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME: + case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED: + k = cescape_length((char*) n->id.port_id + 1, n->id.port_id_size - 1); + if (!k) + return -ENOMEM; + + goto done; + + case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS: + r = format_mac_address(n->id.port_id, n->id.port_id_size, &k); + if (r < 0) + return r; + if (r > 0) + goto done; + + break; + + case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS: + r = format_network_address(n->id.port_id, n->id.port_id_size, &k); + if (r < 0) + return r; + if (r > 0) + goto done; + + break; + } + + /* Generic fallback */ + k = hexmem(n->id.port_id, n->id.port_id_size); + if (!k) + return -ENOMEM; + +done: + *ret = n->port_id_as_string = k; + return 0; +} + +_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret) { + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + + *ret = n->ttl; + return 0; +} + +_public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) { + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + + if (!n->system_name) + return -ENODATA; + + *ret = n->system_name; + return 0; +} + +_public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) { + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + + if (!n->system_description) + return -ENODATA; + + *ret = n->system_description; + return 0; +} + +_public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) { + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + + if (!n->port_description) + return -ENODATA; + + *ret = n->port_description; + return 0; +} + +_public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) { + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + + if (!n->has_capabilities) + return -ENODATA; + + *ret = n->system_capabilities; + return 0; +} + +_public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) { + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + + if (!n->has_capabilities) + return -ENODATA; + + *ret = n->enabled_capabilities; + return 0; +} + +int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) { + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; + int r; + + assert_return(ret, -EINVAL); + assert_return(raw || raw_size <= 0, -EINVAL); + + n = lldp_neighbor_new(raw_size); + if (!n) + return -ENOMEM; + + memcpy(LLDP_NEIGHBOR_RAW(n), raw, raw_size); + r = lldp_neighbor_parse(n); + if (r < 0) + return r; + + *ret = n; + n = 0; + + return r; +} + +_public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) { + assert_return(n, -EINVAL); + + assert(n->raw_size >= sizeof(struct ether_header)); + n->rindex = sizeof(struct ether_header); + + return 0; +} + +_public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) { + size_t length; + + assert_return(n, -EINVAL); + + if (n->rindex == n->raw_size) /* EOF */ + return -ESPIPE; + + if (n->rindex + 2 > n->raw_size) /* Truncated message */ + return -EBADMSG; + + length = LLDP_NEIGHBOR_LENGTH(n); + if (n->rindex + 2 + length > n->raw_size) + return -EBADMSG; + + n->rindex += 2 + length; + return n->rindex < n->raw_size; +} + +_public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) { + assert_return(n, -EINVAL); + assert_return(type, -EINVAL); + + if (n->rindex == n->raw_size) /* EOF */ + return -ESPIPE; + + if (n->rindex + 2 > n->raw_size) + return -EBADMSG; + + *type = LLDP_NEIGHBOR_TYPE(n); + return 0; +} + +_public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) { + uint8_t k; + int r; + + assert_return(n, -EINVAL); + + r = sd_lldp_neighbor_tlv_get_type(n, &k); + if (r < 0) + return r; + + return type == k; +} + +_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], uint8_t *subtype) { + const uint8_t *d; + size_t length; + int r; + + assert_return(n, -EINVAL); + assert_return(oui, -EINVAL); + assert_return(subtype, -EINVAL); + + r = sd_lldp_neighbor_tlv_is_type(n, SD_LLDP_TYPE_PRIVATE); + if (r < 0) + return r; + if (r == 0) + return -ENXIO; + + length = LLDP_NEIGHBOR_LENGTH(n); + if (length < 4) + return -EBADMSG; + + if (n->rindex + 2 + length > n->raw_size) + return -EBADMSG; + + d = LLDP_NEIGHBOR_DATA(n); + memcpy(oui, d, 3); + *subtype = d[3]; + + return 0; +} + +_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[3], uint8_t subtype) { + uint8_t k[3], st; + int r; + + r = sd_lldp_neighbor_tlv_get_oui(n, k, &st); + if (r == -ENXIO) + return 0; + if (r < 0) + return r; + + return memcmp(k, oui, 3) == 0 && st == subtype; +} + +_public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) { + size_t length; + + assert_return(n, -EINVAL); + assert_return(ret, -EINVAL); + assert_return(size, -EINVAL); + + /* Note that this returns the full TLV, including the TLV header */ + + if (n->rindex + 2 > n->raw_size) + return -EBADMSG; + + length = LLDP_NEIGHBOR_LENGTH(n); + + if (n->rindex + 2 + length > n->raw_size) + return -EBADMSG; + + *ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex; + *size = length + 2; + + return 0; +} diff --git a/src/libsystemd-network/lldp-neighbor.h b/src/libsystemd-network/lldp-neighbor.h new file mode 100644 index 0000000000..f203bfa604 --- /dev/null +++ b/src/libsystemd-network/lldp-neighbor.h @@ -0,0 +1,106 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <inttypes.h> +#include <stdbool.h> +#include <sys/types.h> + +#include "sd-lldp.h" + +#include "hash-funcs.h" +#include "lldp-internal.h" +#include "time-util.h" + +typedef struct LLDPNeighborID { + /* The spec calls this an "MSAP identifier" */ + void *chassis_id; + size_t chassis_id_size; + + void *port_id; + size_t port_id_size; +} LLDPNeighborID; + +struct sd_lldp_neighbor { + /* Neighbor objects stay around as long as they are linked into an "sd_lldp" object or n_ref > 0. */ + sd_lldp *lldp; + unsigned n_ref; + + usec_t until; + unsigned prioq_idx; + + struct ether_addr source_address; + struct ether_addr destination_address; + + LLDPNeighborID id; + + /* The raw packet size. The data is appended to the object, accessible via LLDP_NEIGHBOR_RAW() */ + size_t raw_size; + + /* The current read index for the iterative TLV interface */ + size_t rindex; + + /* And a couple of fields parsed out. */ + bool has_ttl:1; + bool has_capabilities:1; + bool has_port_vlan_id:1; + + uint16_t ttl; + + uint16_t system_capabilities; + uint16_t enabled_capabilities; + + char *port_description; + char *system_name; + char *system_description; + + uint16_t port_vlan_id; + + char *chassis_id_as_string; + char *port_id_as_string; +}; + +static inline void *LLDP_NEIGHBOR_RAW(const sd_lldp_neighbor *n) { + return (uint8_t*) n + ALIGN(sizeof(sd_lldp_neighbor)); +} + +static inline uint8_t LLDP_NEIGHBOR_TYPE(const sd_lldp_neighbor *n) { + return ((uint8_t*) LLDP_NEIGHBOR_RAW(n))[n->rindex] >> 1; +} + +static inline size_t LLDP_NEIGHBOR_LENGTH(const sd_lldp_neighbor *n) { + uint8_t *p; + + p = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex; + return p[1] + (((size_t) (p[0] & 1)) << 8); +} + +static inline void* LLDP_NEIGHBOR_DATA(const sd_lldp_neighbor *n) { + return ((uint8_t*) LLDP_NEIGHBOR_RAW(n)) + n->rindex + 2; +} + +extern const struct hash_ops lldp_neighbor_id_hash_ops; +int lldp_neighbor_prioq_compare_func(const void *a, const void *b); + +sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n); +sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size); +int lldp_neighbor_parse(sd_lldp_neighbor *n); +void lldp_neighbor_start_ttl(sd_lldp_neighbor *n); +bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b); diff --git a/src/libsystemd-network/lldp-network.c b/src/libsystemd-network/lldp-network.c index 42058c4449..f031760351 100644 --- a/src/libsystemd-network/lldp-network.c +++ b/src/libsystemd-network/lldp-network.c @@ -19,65 +19,58 @@ ***/ #include <linux/filter.h> -#include <linux/if_ether.h> +#include <netinet/if_ether.h> #include "fd-util.h" -#include "lldp-internal.h" #include "lldp-network.h" -#include "lldp-tlv.h" #include "socket-util.h" int lldp_network_bind_raw_socket(int ifindex) { - typedef struct LLDPFrame { - struct ethhdr hdr; - uint8_t tlvs[0]; - } LLDPFrame; - struct sock_filter filter[] = { - BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(LLDPFrame, hdr.h_dest)), /* A <- 4 bytes of destination MAC */ + static const struct sock_filter filter[] = { + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ethhdr, h_dest)), /* A <- 4 bytes of destination MAC */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0), /* A != 01:80:c2:00 */ BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(LLDPFrame, hdr.h_dest) + 4), /* A <- remaining 2 bytes of destination MAC */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_dest) + 4), /* A <- remaining 2 bytes of destination MAC */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0000, 3, 0), /* A != 00:00 */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0003, 2, 0), /* A != 00:03 */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x000e, 1, 0), /* A != 00:0e */ BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(LLDPFrame, hdr.h_proto)), /* A <- protocol */ + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ethhdr, h_proto)), /* A <- protocol */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_LLDP, 1, 0), /* A != ETHERTYPE_LLDP */ BPF_STMT(BPF_RET + BPF_K, 0), /* drop packet */ BPF_STMT(BPF_RET + BPF_K, (uint32_t) -1), /* accept packet */ }; - struct sock_fprog fprog = { + static const struct sock_fprog fprog = { .len = ELEMENTSOF(filter), - .filter = filter + .filter = (struct sock_filter*) filter, }; - _cleanup_close_ int s = -1; - union sockaddr_union saddrll = { .ll.sll_family = AF_PACKET, .ll.sll_ifindex = ifindex, }; + _cleanup_close_ int fd = -1; int r; assert(ifindex > 0); - s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); - if (s < 0) + fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, htons(ETHERTYPE_LLDP)); + if (fd < 0) return -errno; - r = setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); + r = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); if (r < 0) return -errno; - r = bind(s, &saddrll.sa, sizeof(saddrll.ll)); + r = bind(fd, &saddrll.sa, sizeof(saddrll.ll)); if (r < 0) return -errno; - r = s; - s = -1; + r = fd; + fd = -1; return r; } diff --git a/src/libsystemd-network/lldp-network.h b/src/libsystemd-network/lldp-network.h index dcf31faa95..c4cf8c79f1 100644 --- a/src/libsystemd-network/lldp-network.h +++ b/src/libsystemd-network/lldp-network.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -18,8 +20,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - #include "sd-event.h" int lldp_network_bind_raw_socket(int ifindex); diff --git a/src/libsystemd-network/lldp-port.c b/src/libsystemd-network/lldp-port.c deleted file mode 100644 index c86f62a6c2..0000000000 --- a/src/libsystemd-network/lldp-port.c +++ /dev/null @@ -1,116 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 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 "alloc-util.h" -#include "async.h" -#include "lldp-internal.h" -#include "lldp-network.h" -#include "lldp-port.h" - -int lldp_port_start(lldp_port *p) { - int r; - - assert_return(p, -EINVAL); - - r = lldp_network_bind_raw_socket(p->ifindex); - if (r < 0) - return r; - - p->rawfd = r; - - r = sd_event_add_io(p->event, &p->lldp_port_rx, - p->rawfd, EPOLLIN, lldp_receive_packet, p); - if (r < 0) { - log_debug_errno(r, "Failed to allocate event source: %m"); - goto fail; - } - - r = sd_event_source_set_priority(p->lldp_port_rx, p->event_priority); - if (r < 0) { - log_debug_errno(r, "Failed to set event priority: %m"); - goto fail; - } - - r = sd_event_source_set_description(p->lldp_port_rx, "lldp-port-rx"); - if (r < 0) { - log_debug_errno(r, "Failed to set event name: %m"); - goto fail; - } - - return 0; - -fail: - lldp_port_stop(p); - - return r; -} - -int lldp_port_stop(lldp_port *p) { - - assert_return(p, -EINVAL); - - p->rawfd = asynchronous_close(p->rawfd); - p->lldp_port_rx = sd_event_source_unref(p->lldp_port_rx); - - return 0; -} - -void lldp_port_free(lldp_port *p) { - if (!p) - return; - - lldp_port_stop(p); - - free(p->ifname); - free(p); -} - -int lldp_port_new(int ifindex, - const char *ifname, - const struct ether_addr *addr, - void *userdata, - lldp_port **ret) { - _cleanup_free_ lldp_port *p = NULL; - - assert_return(ifindex, -EINVAL); - assert_return(ifname, -EINVAL); - assert_return(addr, -EINVAL); - - p = new0(lldp_port, 1); - if (!p) - return -ENOMEM; - - p->rawfd = -1; - p->ifindex = ifindex; - - p->ifname = strdup(ifname); - if (!p->ifname) - return -ENOMEM; - - memcpy(&p->mac, addr, ETH_ALEN); - - p->userdata = userdata; - - *ret = p; - - p = NULL; - - return 0; -} diff --git a/src/libsystemd-network/lldp-port.h b/src/libsystemd-network/lldp-port.h deleted file mode 100644 index 96092f8df9..0000000000 --- a/src/libsystemd-network/lldp-port.h +++ /dev/null @@ -1,69 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 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/>. -***/ - -#pragma once - -#include <net/ethernet.h> - -#include "sd-event.h" -#include "sd-lldp.h" - -#include "util.h" - -typedef struct lldp_port lldp_port; - -typedef enum LLDPPortStatus { - LLDP_PORT_STATUS_NONE, - LLDP_PORT_STATUS_ENABLED, - LLDP_PORT_STATUS_DISABLED, - _LLDP_PORT_STATUS_MAX, - _LLDP_PORT_STATUS_INVALID = -1, -} LLDPPortStatus; - -struct lldp_port { - LLDPPortStatus status; - - int ifindex; - char *ifname; - - struct ether_addr mac; - - int rawfd; - - sd_event *event; - sd_event_source *lldp_port_rx; - - int event_priority; - - void *userdata; -}; - -int lldp_port_new(int ifindex, - const char *ifname, - const struct ether_addr *addr, - void *userdata, - lldp_port **ret); -void lldp_port_free(lldp_port *p); - -DEFINE_TRIVIAL_CLEANUP_FUNC(lldp_port*, lldp_port_free); -#define _cleanup_lldp_port_free_ _cleanup_(lldp_port_freep) - -int lldp_port_start(lldp_port *p); -int lldp_port_stop(lldp_port *p); diff --git a/src/libsystemd-network/lldp-tlv.c b/src/libsystemd-network/lldp-tlv.c deleted file mode 100644 index 9170b50691..0000000000 --- a/src/libsystemd-network/lldp-tlv.c +++ /dev/null @@ -1,638 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 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 <arpa/inet.h> -#include <net/ethernet.h> - -#include "alloc-util.h" -#include "lldp-tlv.h" -#include "macro.h" - -int tlv_section_new(tlv_section **ret) { - tlv_section *s; - - s = new0(tlv_section, 1); - if (!s) - return -ENOMEM; - - *ret = s; - - return 0; -} - -void tlv_section_free(tlv_section *m) { - - if (!m) - return; - - free(m); -} - -int tlv_packet_new(tlv_packet **ret) { - tlv_packet *m; - - m = new0(tlv_packet, 1); - if (!m) - return -ENOMEM; - - LIST_HEAD_INIT(m->sections); - m->n_ref = 1; - - *ret = m; - - return 0; -} - -tlv_packet *sd_lldp_packet_ref(tlv_packet *m) { - - if (!m) - return NULL; - - assert(m->n_ref > 0); - m->n_ref++; - - return m; -} - -tlv_packet *sd_lldp_packet_unref(tlv_packet *m) { - tlv_section *s, *n; - - if (!m) - return NULL; - - assert(m->n_ref > 0); - m->n_ref--; - - if (m->n_ref > 0) - return m; - - LIST_FOREACH_SAFE(section, s, n, m->sections) - tlv_section_free(s); - - free(m); - return NULL; -} - -int tlv_packet_append_bytes(tlv_packet *m, const void *data, size_t data_length) { - uint8_t *p; - - assert_return(m, -EINVAL); - assert_return(data, -EINVAL); - assert_return(data_length, -EINVAL); - - if (m->length + data_length > ETHER_MAX_LEN) - return -ENOMEM; - - p = m->pdu + m->length; - memcpy(p, data, data_length); - m->length += data_length; - - return 0; -} - -int tlv_packet_append_u8(tlv_packet *m, uint8_t data) { - - assert_return(m, -EINVAL); - - return tlv_packet_append_bytes(m, &data, sizeof(uint8_t)); -} - -int tlv_packet_append_u16(tlv_packet *m, uint16_t data) { - uint16_t type; - - assert_return(m, -EINVAL); - - type = htons(data); - - return tlv_packet_append_bytes(m, &type, sizeof(uint16_t)); -} - -int tlv_packet_append_u32(tlv_packet *m, uint32_t data) { - uint32_t type; - - assert_return(m, -EINVAL); - - type = htonl(data); - - return tlv_packet_append_bytes(m, &type, sizeof(uint32_t)); -} - -int tlv_packet_append_string(tlv_packet *m, char *data, uint16_t size) { - - assert_return(m, -EINVAL); - - return tlv_packet_append_bytes(m, data, size); -} - -int lldp_tlv_packet_open_container(tlv_packet *m, uint16_t type) { - - assert_return(m, -EINVAL); - - m->container_pos = m->pdu + m->length; - - return tlv_packet_append_u16(m, type << 9); -} - -int lldp_tlv_packet_close_container(tlv_packet *m) { - uint16_t type; - - assert_return(m, -EINVAL); - assert_return(m->container_pos, -EINVAL); - - memcpy(&type, m->container_pos, sizeof(uint16_t)); - - type |= htons(((m->pdu + m->length) - (m->container_pos + 2)) & 0x01ff); - memcpy(m->container_pos, &type, sizeof(uint16_t)); - - return 0; -} - -static inline int tlv_packet_read_internal(tlv_section *m, void **data) { - - assert_return(m->read_pos, -EINVAL); - - *data = m->read_pos; - - return 0; -} - -int tlv_packet_read_u8(tlv_packet *m, uint8_t *data) { - void *val = NULL; - int r; - - assert_return(m, -EINVAL); - - r = tlv_packet_read_internal(m->container, &val); - if (r < 0) - return r; - - memcpy(data, val, sizeof(uint8_t)); - - m->container->read_pos ++; - - return 0; -} - -int tlv_packet_read_u16(tlv_packet *m, uint16_t *data) { - uint16_t t; - void *val = NULL; - int r; - - assert_return(m, -EINVAL); - - r = tlv_packet_read_internal(m->container, &val); - if (r < 0) - return r; - - memcpy(&t, val, sizeof(uint16_t)); - *data = ntohs(t); - - m->container->read_pos += 2; - - return 0; -} - -int tlv_packet_read_u32(tlv_packet *m, uint32_t *data) { - uint32_t t; - void *val; - int r; - - assert_return(m, -EINVAL); - - r = tlv_packet_read_internal(m->container, &val); - if (r < 0) - return r; - - memcpy(&t, val, sizeof(uint32_t)); - *data = ntohl(t); - - m->container->read_pos += 4; - - return r; -} - -int tlv_packet_read_string(tlv_packet *m, char **data, uint16_t *data_length) { - void *val = NULL; - int r; - - assert_return(m, -EINVAL); - - r = tlv_packet_read_internal(m->container, &val); - if (r < 0) - return r; - - *data = (char *) val; - *data_length = m->container->data + m->container->length - m->container->read_pos; - - m->container->read_pos += *data_length; - - return 0; -} - -int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length) { - void *val = NULL; - int r; - - assert_return(m, -EINVAL); - - r = tlv_packet_read_internal(m->container, &val); - if (r < 0) - return r; - - *data = (uint8_t *) val; - *data_length = m->container->data + m->container->length - m->container->read_pos; - - m->container->read_pos += *data_length; - - return 0; -} - -/* parse raw TLV packet */ -int tlv_packet_parse_pdu(tlv_packet *m, uint16_t size) { - tlv_section *section, *tail; - uint16_t t, l; - uint8_t *p; - int r; - - assert_return(m, -EINVAL); - assert_return(size, -EINVAL); - - p = m->pdu; - - /* extract Ethernet header */ - memcpy(&m->mac, p, ETH_ALEN); - p += sizeof(struct ether_header); - - for (l = 0; l <= size; ) { - r = tlv_section_new(§ion); - if (r < 0) - return r; - - memcpy(&t, p, sizeof(uint16_t)); - - section->type = ntohs(t) >> 9; - section->length = ntohs(t) & 0x01ff; - - if (section->type == LLDP_TYPE_END || section->type >=_LLDP_TYPE_MAX) { - tlv_section_free(section); - break; - } - - p += 2; - - if (section->type == LLDP_TYPE_PRIVATE && - section->length >= LLDP_OUI_LEN + 1) { - section->oui = p; - p += LLDP_OUI_LEN; - section->subtype = *p++; - - section->length -= LLDP_OUI_LEN + 1; - l += LLDP_OUI_LEN + 1; - } - - section->data = p; - - LIST_FIND_TAIL(section, m->sections, tail); - LIST_INSERT_AFTER(section, m->sections, tail, section); - - p += section->length; - l += (section->length + 2); - } - - return 0; -} - -int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type) { - tlv_section *s; - - assert_return(m, -EINVAL); - assert_return(type != LLDP_TYPE_PRIVATE, -EINVAL); - - LIST_FOREACH(section, s, m->sections) - if (s->type == type) - break; - if (!s) - return -1; - - m->container = s; - - m->container->read_pos = s->data; - if (!m->container->read_pos) { - m->container = NULL; - return -1; - } - - return 0; -} - -int lldp_tlv_packet_enter_container_oui(tlv_packet *m, const uint8_t *oui, uint8_t subtype) { - tlv_section *s; - - assert_return(m, -EINVAL); - assert_return(oui, -EINVAL); - - LIST_FOREACH(section, s, m->sections) { - if (s->type == LLDP_TYPE_PRIVATE && - s->oui && - s->subtype == subtype && - !memcmp(s->oui, oui, LLDP_OUI_LEN)) - break; - } - - if (!s) - return -1; - - m->container = s; - - m->container->read_pos = s->data; - if (!m->container->read_pos) { - m->container = NULL; - return -1; - } - - return 0; -} - -int lldp_tlv_packet_exit_container(tlv_packet *m) { - assert_return(m, -EINVAL); - - m->container = 0; - - return 0; -} - -static int lldp_tlv_packet_read_u16_tlv(tlv_packet *tlv, uint16_t type, uint16_t *value) { - int r, r2; - - assert_return(tlv, -EINVAL); - - r = lldp_tlv_packet_enter_container(tlv, type); - if (r < 0) - return r; - - r = tlv_packet_read_u16(tlv, value); - r2 = lldp_tlv_packet_exit_container(tlv); - - return r < 0 ? r : r2; -} - -static int lldp_tlv_packet_read_string_tlv(tlv_packet *tlv, uint16_t type, char **data, uint16_t *length) { - char *s; - int r, r2; - - assert_return(tlv, -EINVAL); - - r = lldp_tlv_packet_enter_container(tlv, type); - if (r < 0) - return r; - - r = tlv_packet_read_string(tlv, &s, length); - if (r < 0) - goto out; - - *data = (char *) s; - - out: - r2 = lldp_tlv_packet_exit_container(tlv); - - return r < 0 ? r : r2; -} - -int sd_lldp_packet_read_chassis_id(tlv_packet *tlv, - uint8_t *type, - uint8_t **data, - uint16_t *length) { - uint8_t subtype; - int r, r2; - - assert_return(tlv, -EINVAL); - - r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_CHASSIS_ID); - if (r < 0) - return r; - - r = tlv_packet_read_u8(tlv, &subtype); - if (r < 0) - goto out; - - switch (subtype) { - case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS: - - r = tlv_packet_read_bytes(tlv, data, length); - if (r < 0) - goto out; - - break; - default: - r = -EOPNOTSUPP; - break; - } - - *type = subtype; - - out: - r2 = lldp_tlv_packet_exit_container(tlv); - - return r < 0 ? r : r2; -} - -int sd_lldp_packet_read_port_id(tlv_packet *tlv, - uint8_t *type, - uint8_t **data, - uint16_t *length) { - uint8_t subtype; - char *s; - int r, r2; - - assert_return(tlv, -EINVAL); - - r = lldp_tlv_packet_enter_container(tlv, LLDP_TYPE_PORT_ID); - if (r < 0) - return r; - - r = tlv_packet_read_u8(tlv, &subtype); - if (r < 0) - goto out; - - switch (subtype) { - case LLDP_PORT_SUBTYPE_PORT_COMPONENT: - case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS: - case LLDP_PORT_SUBTYPE_INTERFACE_NAME: - case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED: - - r = tlv_packet_read_string(tlv, &s, length); - if (r < 0) - goto out; - - *data = (uint8_t *) s; - - break; - case LLDP_PORT_SUBTYPE_MAC_ADDRESS: - - r = tlv_packet_read_bytes(tlv, data, length); - if (r < 0) - goto out; - - break; - default: - r = -EOPNOTSUPP; - break; - } - - *type = subtype; - - out: - r2 = lldp_tlv_packet_exit_container(tlv); - - return r < 0 ? r : r2; -} - -int sd_lldp_packet_read_ttl(tlv_packet *tlv, uint16_t *ttl) { - return lldp_tlv_packet_read_u16_tlv(tlv, LLDP_TYPE_TTL, ttl); -} - -int sd_lldp_packet_read_system_name(tlv_packet *tlv, - char **data, - uint16_t *length) { - return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_SYSTEM_NAME, data, length); -} - -int sd_lldp_packet_read_system_description(tlv_packet *tlv, - char **data, - uint16_t *length) { - return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_SYSTEM_DESCRIPTION, data, length); -} - -int sd_lldp_packet_read_port_description(tlv_packet *tlv, - char **data, - uint16_t *length) { - return lldp_tlv_packet_read_string_tlv(tlv, LLDP_TYPE_PORT_DESCRIPTION, data, length); -} - -int sd_lldp_packet_read_system_capability(tlv_packet *tlv, uint16_t *data) { - return lldp_tlv_packet_read_u16_tlv(tlv, LLDP_TYPE_SYSTEM_CAPABILITIES, data); -} - -int sd_lldp_packet_read_port_vlan_id(tlv_packet *tlv, uint16_t *id) { - int r, r2; - - assert_return(tlv, -EINVAL); - - r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID); - if (r < 0) - return r; - - r = tlv_packet_read_u16(tlv, id); - r2 = lldp_tlv_packet_exit_container(tlv); - - return r < 0 ? r : r2; -} - -int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet *tlv, uint8_t *flags, uint16_t *id) { - int r, r2; - - assert_return(tlv, -EINVAL); - - r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID); - if (r < 0) - return r; - - r = tlv_packet_read_u8(tlv, flags); - if (r >= 0) - r = tlv_packet_read_u16(tlv, id); - - r2 = lldp_tlv_packet_exit_container(tlv); - - return r < 0 ? r : r2; -} - -int sd_lldp_packet_read_vlan_name(tlv_packet *tlv, uint16_t *vlan_id, char **name, uint16_t *length) { - int r, r2; - uint8_t len = 0; - - assert_return(tlv, -EINVAL); - - r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_VLAN_NAME); - if (r < 0) - return r; - - r = tlv_packet_read_u16(tlv, vlan_id); - if (r >= 0) - r = tlv_packet_read_u8(tlv, &len); - if (r >= 0) - r = tlv_packet_read_string(tlv, name, length); - - if (r >= 0 && len < *length) - *length = len; - - r2 = lldp_tlv_packet_exit_container(tlv); - - return r < 0 ? r : r2; -} - -int sd_lldp_packet_read_management_vid(tlv_packet *tlv, uint16_t *id) { - int r, r2; - - assert_return(tlv, -EINVAL); - - r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID); - if (r < 0) - return r; - - r = tlv_packet_read_u16(tlv, id); - r2 = lldp_tlv_packet_exit_container(tlv); - - return r < 0 ? r : r2; -} - -int sd_lldp_packet_read_link_aggregation(sd_lldp_packet *tlv, uint8_t *status, uint32_t *id) { - int r, r2; - - assert_return(tlv, -EINVAL); - - r = lldp_tlv_packet_enter_container_oui(tlv, LLDP_OUI_802_1, LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION); - if (r < 0) - return r; - - r = tlv_packet_read_u8(tlv, status); - if (r >= 0) - r = tlv_packet_read_u32(tlv, id); - - r2 = lldp_tlv_packet_exit_container(tlv); - - return r < 0 ? r : r2; -} - -int sd_lldp_packet_get_destination_type(tlv_packet *tlv, int *dest) { - assert_return(tlv, -EINVAL); - assert_return(dest, -EINVAL); - - /* 802.1AB-2009, Table 7-1 */ - if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_BRIDGE, ETH_ALEN)) - *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE; - else if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_NON_TPMR_BRIDGE, ETH_ALEN)) - *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE; - else if (!memcmp(&tlv->mac, LLDP_MAC_NEAREST_CUSTOMER_BRIDGE, ETH_ALEN)) - *dest = SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE; - else - return -EINVAL; - - return 0; -} diff --git a/src/libsystemd-network/lldp-tlv.h b/src/libsystemd-network/lldp-tlv.h deleted file mode 100644 index 8e7706c612..0000000000 --- a/src/libsystemd-network/lldp-tlv.h +++ /dev/null @@ -1,94 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 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/>. -***/ - -#pragma once - -#include <net/ethernet.h> - -#include "sd-lldp.h" - -#include "list.h" -#include "lldp.h" -#include "util.h" - -typedef struct sd_lldp_packet tlv_packet; -typedef struct sd_lldp_section tlv_section; - -#define LLDP_OUI_LEN 3 - -struct sd_lldp_section { - uint16_t type; - uint16_t length; - uint8_t *oui; - uint8_t subtype; - - uint8_t *read_pos; - uint8_t *data; - - LIST_FIELDS(tlv_section, section); -}; - -#define LLDP_MAC_NEAREST_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e } -#define LLDP_MAC_NEAREST_NON_TPMR_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 } -#define LLDP_MAC_NEAREST_CUSTOMER_BRIDGE (uint8_t[]) { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 } - -int tlv_section_new(tlv_section **ret); -void tlv_section_free(tlv_section *ret); - -struct sd_lldp_packet { - unsigned n_ref; - - uint16_t type; - uint16_t length; - usec_t ts; - - uint8_t *container_pos; - uint8_t pdu[ETHER_MAX_LEN]; - - void *userdata; - - struct ether_addr mac; - tlv_section *container; - - LIST_HEAD(tlv_section, sections); -}; - -int tlv_packet_new(tlv_packet **ret); - -int lldp_tlv_packet_open_container(tlv_packet *m, uint16_t type); -int lldp_tlv_packet_close_container(tlv_packet *m); - -int tlv_packet_append_bytes(tlv_packet *m, const void *data, size_t data_length); -int tlv_packet_append_u8(tlv_packet *m, uint8_t data); -int tlv_packet_append_u16(tlv_packet *m, uint16_t data); -int tlv_packet_append_u32(tlv_packet *m, uint32_t data); -int tlv_packet_append_string(tlv_packet *m, char *data, uint16_t size); - -int lldp_tlv_packet_enter_container(tlv_packet *m, uint16_t type); -int lldp_tlv_packet_enter_container_oui(tlv_packet *m, const uint8_t *oui, uint8_t subtype); -int lldp_tlv_packet_exit_container(tlv_packet *m); - -int tlv_packet_read_bytes(tlv_packet *m, uint8_t **data, uint16_t *data_length); -int tlv_packet_read_string(tlv_packet *m, char **data, uint16_t *data_length); -int tlv_packet_read_u8(tlv_packet *m, uint8_t *data); -int tlv_packet_read_u16(tlv_packet *m, uint16_t *data); -int tlv_packet_read_u32(tlv_packet *m, uint32_t *data); - -int tlv_packet_parse_pdu(tlv_packet *t, uint16_t size); diff --git a/src/libsystemd-network/lldp.h b/src/libsystemd-network/lldp.h deleted file mode 100644 index d2c7164633..0000000000 --- a/src/libsystemd-network/lldp.h +++ /dev/null @@ -1,126 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 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/>. -***/ - -#pragma once - -#define LLDP_MULTICAST_ADDR { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e } - -#define ETHERTYPE_LLDP 0x88cc - -/* IEEE 802.3AB Clause 9: TLV Types */ -typedef enum LLDPTypes { - LLDP_TYPE_END = 0, - LLDP_TYPE_CHASSIS_ID = 1, - LLDP_TYPE_PORT_ID = 2, - LLDP_TYPE_TTL = 3, - LLDP_TYPE_PORT_DESCRIPTION = 4, - LLDP_TYPE_SYSTEM_NAME = 5, - LLDP_TYPE_SYSTEM_DESCRIPTION = 6, - LLDP_TYPE_SYSTEM_CAPABILITIES = 7, - LLDP_TYPE_MGMT_ADDRESS = 8, - LLDP_TYPE_PRIVATE = 127, - _LLDP_TYPE_MAX, - _LLDP_TYPE_INVALID = -1, -} LLDPTypes; - -/* IEEE 802.3AB Clause 9.5.2: Chassis subtypes */ -typedef enum LLDPChassisSubtypes { - LLDP_CHASSIS_SUBTYPE_RESERVED = 0, - LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT = 1, - LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS = 2, - LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT = 3, - LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS = 4, - LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS = 5, - LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME = 6, - LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED = 7, - _LLDP_CHASSIS_SUBTYPE_MAX, - _LLDP_CHASSIS_SUBTYPE_INVALID = -1, -} LLDPChassisSubtypes; - -/* IEEE 802.3AB Clause 9.5.3: Port subtype */ -typedef enum LLDPPortSubtypes { - LLDP_PORT_SUBTYPE_RESERVED = 0, - LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1, - LLDP_PORT_SUBTYPE_PORT_COMPONENT = 2, - LLDP_PORT_SUBTYPE_MAC_ADDRESS = 3, - LLDP_PORT_SUBTYPE_NETWORK = 4, - LLDP_PORT_SUBTYPE_INTERFACE_NAME = 5, - LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6, - LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7, - _LLDP_PORT_SUBTYPE_MAX, - _LLDP_PORT_SUBTYPE_INVALID = -1 -} LLDPPortSubtypes; - -typedef enum LLDPSystemCapabilities { - LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0, - LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1, - LLDP_SYSTEM_CAPABILITIES_BRIDGE = 1 << 2, - LLDP_SYSTEM_CAPABILITIES_WLAN_AP = 1 << 3, - LLDP_SYSTEM_CAPABILITIES_ROUTER = 1 << 4, - LLDP_SYSTEM_CAPABILITIES_PHONE = 1 << 5, - LLDP_SYSTEM_CAPABILITIES_DOCSIS = 1 << 6, - LLDP_SYSTEM_CAPABILITIES_STATION = 1 << 7, - LLDP_SYSTEM_CAPABILITIES_CVLAN = 1 << 8, - LLDP_SYSTEM_CAPABILITIES_SVLAN = 1 << 9, - LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10, - _LLDP_SYSTEM_CAPABILITIES_MAX, - _LLDP_SYSTEM_CAPABILITIES_INVALID = -1, -} LLDPSystemCapabilities; - -typedef enum LLDPMedSubtype { - LLDP_MED_SUBTYPE_RESERVED = 0, - LLDP_MED_SUBTYPE_CAPABILITIES = 1, - LLDP_MED_SUBTYPE_NETWORK_POLICY = 2, - LLDP_MED_SUBTYPE_LOCATION_ID = 3, - LLDP_MED_SUBTYPE_EXTENDED_PVMDI = 4, - LLDP_MED_SUBTYPE_INV_HWREV = 5, - LLDP_MED_SUBTYPE_INV_FWREV = 6, - LLDP_MED_SUBTYPE_INV_SWREV = 7, - LLDP_MED_SUBTYPE_INV_SERIAL = 8, - LLDP_MED_SUBTYPE_INV_MANUFACTURER = 9, - LLDP_MED_SUBTYPE_INV_MODELNAME = 10, - LLDP_MED_SUBTYPE_INV_ASSETID = 11, - _LLDP_MED_SUBTYPE_MAX, - _LLDP_MED_SUBTYPE_INVALID = -1, -} LLDPMedSubtype; - -typedef enum LLDPMedCapability { - LLDP_MED_CAPABILITY_CAPAPILITIES = 1 << 0, - LLDP_MED_CAPABILITY_NETWORK_POLICY = 1 << 1, - LLDP_MED_CAPABILITY_LOCATION_ID = 1 << 2, - LLDP_MED_CAPABILITY_EXTENDED_PSE = 1 << 3, - LLDP_MED_CAPABILITY_EXTENDED_PD = 1 << 4, - LLDP_MED_CAPABILITY_INVENTORY = 1 << 5, - LLDP_MED_CAPABILITY_MAX, - LLDP_MED_CAPABILITY_INVALID = -1, -} LLDPMedCapability; - -#define LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 } -#define LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f } - -enum { - LLDP_OUI_SUBTYPE_802_1_PORT_VLAN_ID = 1, - LLDP_OUI_SUBTYPE_802_1_PORT_PROTOCOL_VLAN_ID = 2, - LLDP_OUI_SUBTYPE_802_1_VLAN_NAME = 3, - LLDP_OUI_SUBTYPE_802_1_PROTOCOL_IDENTITY = 4, - LLDP_OUI_SUBTYPE_802_1_VID_USAGE_DIGEST = 5, - LLDP_OUI_SUBTYPE_802_1_MANAGEMENT_VID = 6, - LLDP_OUI_SUBTYPE_802_1_LINK_AGGREGATION = 7, -}; diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index fdafcd84d8..046b0f9393 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -27,11 +27,13 @@ #include "condition.h" #include "conf-parser.h" #include "dhcp-lease-internal.h" +#include "ether-addr-util.c" #include "hexdecoct.h" #include "log.h" #include "network-internal.h" #include "parse-util.h" #include "siphash24.h" +#include "socket-util.h" #include "string-util.h" #include "strv.h" #include "utf8.h" @@ -175,58 +177,19 @@ int config_parse_net_condition(const char *unit, return 0; } -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) { - - char **s = data; - _cleanup_free_ char *n = NULL; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - n = strdup(rvalue); - if (!n) - return log_oom(); - - if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue); - return 0; - } - - free(*s); - if (*n) { - *s = n; - n = NULL; - } else - *s = NULL; - - return 0; -} - -int config_parse_ifnames(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_ifnames( + 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) { char ***sv = data; - const char *word, *state; - size_t l; int r; assert(filename); @@ -234,22 +197,27 @@ int config_parse_ifnames(const char *unit, assert(rvalue); assert(data); - FOREACH_WORD(word, l, rvalue, state) { - char *n; + for (;;) { + _cleanup_free_ char *word = NULL; - n = strndup(word, l); - if (!n) - return log_oom(); + r = extract_first_word(&rvalue, &word, NULL, 0); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse interface name list: %s", rvalue); + return 0; + } + if (r == 0) + break; - if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue); - free(n); + if (!ifname_valid(word)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue); return 0; } - r = strv_consume(sv, n); + r = strv_push(sv, word); if (r < 0) return log_oom(); + + word = NULL; } return 0; @@ -305,6 +273,8 @@ int config_parse_hwaddr(const char *unit, void *userdata) { struct ether_addr **hwaddr = data; struct ether_addr *n; + const char *start; + size_t offset; int r; assert(filename); @@ -316,14 +286,10 @@ int config_parse_hwaddr(const char *unit, if (!n) return log_oom(); - r = sscanf(rvalue, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", - &n->ether_addr_octet[0], - &n->ether_addr_octet[1], - &n->ether_addr_octet[2], - &n->ether_addr_octet[3], - &n->ether_addr_octet[4], - &n->ether_addr_octet[5]); - if (r != 6) { + start = rvalue + strspn(rvalue, WHITESPACE); + r = ether_addr_from_string(start, n, &offset); + + if (r || (start[offset + strspn(start + offset, WHITESPACE)] != '\0')) { log_syntax(unit, LOG_ERR, filename, line, 0, "Not a valid MAC address, ignoring assignment: %s", rvalue); free(n); return 0; @@ -335,6 +301,36 @@ int config_parse_hwaddr(const char *unit, return 0; } +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) { + uint32_t iaid; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = safe_atou32(rvalue, &iaid); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Unable to read IAID, ignoring assignment: %s", rvalue); + return 0; + } + + *((uint32_t *)data) = iaid; + + return 0; +} + void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size) { unsigned i; @@ -350,32 +346,32 @@ void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size) { int deserialize_in_addrs(struct in_addr **ret, const char *string) { _cleanup_free_ struct in_addr *addresses = NULL; int size = 0; - const char *word, *state; - size_t len; assert(ret); assert(string); - FOREACH_WORD(word, len, string, state) { - _cleanup_free_ char *addr_str = NULL; + for (;;) { + _cleanup_free_ char *word = NULL; struct in_addr *new_addresses; int r; + r = extract_first_word(&string, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; + new_addresses = realloc(addresses, (size + 1) * sizeof(struct in_addr)); if (!new_addresses) return -ENOMEM; else addresses = new_addresses; - addr_str = strndup(word, len); - if (!addr_str) - return -ENOMEM; - - r = inet_pton(AF_INET, addr_str, &(addresses[size])); + r = inet_pton(AF_INET, word, &(addresses[size])); if (r <= 0) continue; - size ++; + size++; } *ret = addresses; @@ -401,28 +397,28 @@ void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, int deserialize_in6_addrs(struct in6_addr **ret, const char *string) { _cleanup_free_ struct in6_addr *addresses = NULL; int size = 0; - const char *word, *state; - size_t len; assert(ret); assert(string); - FOREACH_WORD(word, len, string, state) { - _cleanup_free_ char *addr_str = NULL; + for (;;) { + _cleanup_free_ char *word = NULL; struct in6_addr *new_addresses; int r; + r = extract_first_word(&string, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; + new_addresses = realloc(addresses, (size + 1) * sizeof(struct in6_addr)); if (!new_addresses) return -ENOMEM; else addresses = new_addresses; - addr_str = strndup(word, len); - if (!addr_str) - return -ENOMEM; - - r = inet_pton(AF_INET6, addr_str, &(addresses[size])); + r = inet_pton(AF_INET6, word, &(addresses[size])); if (r <= 0) continue; @@ -463,29 +459,29 @@ void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, siz int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string) { _cleanup_free_ struct sd_dhcp_route *routes = NULL; size_t size = 0, allocated = 0; - const char *word, *state; - size_t len; assert(ret); assert(ret_size); assert(ret_allocated); assert(string); - FOREACH_WORD(word, len, string, state) { - /* WORD FORMAT: dst_ip/dst_prefixlen,gw_ip */ - _cleanup_free_ char* entry = NULL; + /* WORD FORMAT: dst_ip/dst_prefixlen,gw_ip */ + for (;;) { + _cleanup_free_ char *word = NULL; char *tok, *tok_end; unsigned n; int r; - if (!GREEDY_REALLOC(routes, allocated, size + 1)) - return -ENOMEM; + r = extract_first_word(&string, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; - entry = strndup(word, len); - if(!entry) + if (!GREEDY_REALLOC(routes, allocated, size + 1)) return -ENOMEM; - tok = entry; + tok = word; /* get the subnet */ tok_end = strchr(tok, '/'); diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h index c8a531ab0f..5bcd577167 100644 --- a/src/libsystemd-network/network-internal.h +++ b/src/libsystemd-network/network-internal.h @@ -50,10 +50,6 @@ int config_parse_hwaddr(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_ifnames(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); @@ -62,6 +58,10 @@ int config_parse_ifalias(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_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 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/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index cad1a52c09..ad79c6cc2c 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -82,7 +82,7 @@ struct sd_dhcp_client { } _packed_ ll; struct { /* 255: Node-specific (RFC 4361) */ - uint32_t iaid; + be32_t iaid; struct duid duid; } _packed_ ns; struct { @@ -101,7 +101,7 @@ struct sd_dhcp_client { sd_event_source *timeout_t1; sd_event_source *timeout_t2; sd_event_source *timeout_expire; - sd_dhcp_client_cb_t cb; + sd_dhcp_client_callback_t cb; void *userdata; sd_dhcp_lease *lease; usec_t start_delay; @@ -115,14 +115,22 @@ static const uint8_t default_req_opts[] = { SD_DHCP_OPTION_DOMAIN_NAME_SERVER, }; -static int client_receive_message_raw(sd_event_source *s, int fd, - uint32_t revents, void *userdata); -static int client_receive_message_udp(sd_event_source *s, int fd, - uint32_t revents, void *userdata); +static int client_receive_message_raw( + sd_event_source *s, + int fd, + uint32_t revents, + void *userdata); +static int client_receive_message_udp( + sd_event_source *s, + int fd, + uint32_t revents, + void *userdata); static void client_stop(sd_dhcp_client *client, int error); -int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb, - void *userdata) { +int sd_dhcp_client_set_callback( + sd_dhcp_client *client, + sd_dhcp_client_callback_t cb, + void *userdata) { assert_return(client, -EINVAL); client->cb = cb; @@ -171,8 +179,9 @@ int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option) { return 0; } -int sd_dhcp_client_set_request_address(sd_dhcp_client *client, - const struct in_addr *last_addr) { +int sd_dhcp_client_set_request_address( + sd_dhcp_client *client, + const struct in_addr *last_addr) { assert_return(client, -EINVAL); assert_return (IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED), -EBUSY); @@ -196,8 +205,12 @@ int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index) { return 0; } -int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr, - size_t addr_len, uint16_t arp_type) { +int sd_dhcp_client_set_mac( + sd_dhcp_client *client, + const uint8_t *addr, + size_t addr_len, + uint16_t arp_type) { + DHCP_CLIENT_DONT_DESTROY(client); bool need_restart = false; @@ -234,8 +247,11 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr, return 0; } -int sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type, - const uint8_t **data, size_t *data_len) { +int sd_dhcp_client_get_client_id( + sd_dhcp_client *client, + uint8_t *type, + const uint8_t **data, + size_t *data_len) { assert_return(client, -EINVAL); assert_return(type, -EINVAL); @@ -254,8 +270,12 @@ int sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type, return 0; } -int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type, - const uint8_t *data, size_t data_len) { +int sd_dhcp_client_set_client_id( + sd_dhcp_client *client, + uint8_t type, + const uint8_t *data, + size_t data_len) { + DHCP_CLIENT_DONT_DESTROY(client); bool need_restart = false; @@ -298,8 +318,71 @@ int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type, return 0; } -int sd_dhcp_client_set_hostname(sd_dhcp_client *client, - const char *hostname) { +/** + * Sets IAID and DUID. If duid is non-null, the DUID is set to duid_type + duid + * without further modification. Otherwise, if duid_type is supported, DUID + * is set based on that type. Otherwise, an error is returned. + */ +int sd_dhcp_client_set_iaid_duid( + sd_dhcp_client *client, + uint32_t iaid, + uint16_t duid_type, + const void *duid, + size_t duid_len) { + + DHCP_CLIENT_DONT_DESTROY(client); + int r; + size_t len; + + assert_return(client, -EINVAL); + assert_return(duid_len == 0 || duid != NULL, -EINVAL); + + if (duid != NULL) { + r = dhcp_validate_duid_len(duid_type, duid_len); + if (r < 0) + return r; + } + + zero(client->client_id); + client->client_id.type = 255; + + /* If IAID is not configured, generate it. */ + if (iaid == 0) { + r = dhcp_identifier_set_iaid(client->index, client->mac_addr, + client->mac_addr_len, + &client->client_id.ns.iaid); + if (r < 0) + return r; + } else + client->client_id.ns.iaid = htobe32(iaid); + + if (duid != NULL) { + client->client_id.ns.duid.type = htobe16(duid_type); + memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len); + len = sizeof(client->client_id.ns.duid.type) + duid_len; + } else if (duid_type == DUID_TYPE_EN) { + r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len); + if (r < 0) + return r; + } else + return -EOPNOTSUPP; + + client->client_id_len = sizeof(client->client_id.type) + len + + sizeof(client->client_id.ns.iaid); + + if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { + log_dhcp_client(client, "Configured IAID+DUID, restarting."); + client_stop(client, SD_DHCP_CLIENT_EVENT_STOP); + sd_dhcp_client_start(client); + } + + return 0; +} + +int sd_dhcp_client_set_hostname( + sd_dhcp_client *client, + const char *hostname) { + char *new_hostname = NULL; assert_return(client, -EINVAL); @@ -322,8 +405,10 @@ int sd_dhcp_client_set_hostname(sd_dhcp_client *client, return 0; } -int sd_dhcp_client_set_vendor_class_identifier(sd_dhcp_client *client, - const char *vci) { +int sd_dhcp_client_set_vendor_class_identifier( + sd_dhcp_client *client, + const char *vci) { + char *new_vci = NULL; assert_return(client, -EINVAL); @@ -406,9 +491,14 @@ static void client_stop(sd_dhcp_client *client, int error) { client_initialize(client); } -static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, - uint8_t type, size_t *_optlen, size_t *_optoffset) { - _cleanup_free_ DHCPPacket *packet; +static int client_message_init( + sd_dhcp_client *client, + DHCPPacket **ret, + uint8_t type, + size_t *_optlen, + size_t *_optoffset) { + + _cleanup_free_ DHCPPacket *packet = NULL; size_t optlen, optoffset, size; be16_t max_size; usec_t time_now; @@ -548,8 +638,12 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, return 0; } -static int client_append_fqdn_option(DHCPMessage *message, size_t optlen, size_t *optoffset, - const char *fqdn) { +static int client_append_fqdn_option( + DHCPMessage *message, + size_t optlen, + size_t *optoffset, + const char *fqdn) { + uint8_t buffer[3 + DHCP_MAX_FQDN_LENGTH]; int r; @@ -566,8 +660,11 @@ static int client_append_fqdn_option(DHCPMessage *message, size_t optlen, size_t return r; } -static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet, - size_t len) { +static int dhcp_client_send_raw( + sd_dhcp_client *client, + DHCPPacket *packet, + size_t len) { + dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT, INADDR_BROADCAST, DHCP_PORT_SERVER, len); @@ -774,8 +871,11 @@ static int client_send_request(sd_dhcp_client *client) { static int client_start(sd_dhcp_client *client); -static int client_timeout_resend(sd_event_source *s, uint64_t usec, - void *userdata) { +static int client_timeout_resend( + sd_event_source *s, + uint64_t usec, + void *userdata) { + sd_dhcp_client *client = userdata; DHCP_CLIENT_DONT_DESTROY(client); usec_t next_timeout = 0; @@ -919,8 +1019,10 @@ error: return 0; } -static int client_initialize_io_events(sd_dhcp_client *client, - sd_event_io_handler_t io_callback) { +static int client_initialize_io_events( + sd_dhcp_client *client, + sd_event_io_handler_t io_callback) { + int r; assert(client); @@ -958,7 +1060,7 @@ static int client_initialize_time_events(sd_dhcp_client *client) { client->timeout_resend = sd_event_source_unref(client->timeout_resend); if (client->start_delay) { - sd_event_now(client->event, clock_boottime_or_monotonic(), &usec); + assert_se(sd_event_now(client->event, clock_boottime_or_monotonic(), &usec) >= 0); usec += client->start_delay; } @@ -987,8 +1089,7 @@ error: } -static int client_initialize_events(sd_dhcp_client *client, - sd_event_io_handler_t io_callback) { +static int client_initialize_events(sd_dhcp_client *client, sd_event_io_handler_t io_callback) { client_initialize_io_events(client, io_callback); client_initialize_time_events(client); @@ -1028,8 +1129,7 @@ static int client_start(sd_dhcp_client *client) { return client_start_delayed(client); } -static int client_timeout_expire(sd_event_source *s, uint64_t usec, - void *userdata) { +static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userdata) { sd_dhcp_client *client = userdata; DHCP_CLIENT_DONT_DESTROY(client); @@ -1069,8 +1169,7 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) return client_initialize_events(client, client_receive_message_raw); } -static int client_timeout_t1(sd_event_source *s, uint64_t usec, - void *userdata) { +static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) { sd_dhcp_client *client = userdata; DHCP_CLIENT_DONT_DESTROY(client); @@ -1080,8 +1179,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, return client_initialize_time_events(client); } -static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, - size_t len) { +static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_t len) { _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; int r; @@ -1132,8 +1230,7 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, return 0; } -static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force, - size_t len) { +static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force, size_t len) { int r; r = dhcp_option_parse(force, len, NULL, NULL, NULL); @@ -1145,8 +1242,7 @@ static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force, return 0; } -static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, - size_t len) { +static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t len) { _cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL; _cleanup_free_ char *error_message = NULL; int r; @@ -1374,8 +1470,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { return 0; } -static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, - int len) { +static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, int len) { DHCP_CLIENT_DONT_DESTROY(client); char time_string[FORMAT_TIMESPAN_MAX]; int r = 0, notify_event = 0; @@ -1521,30 +1616,31 @@ error: return r; } -static int client_receive_message_udp(sd_event_source *s, int fd, - uint32_t revents, void *userdata) { +static int client_receive_message_udp( + sd_event_source *s, + int fd, + uint32_t revents, + void *userdata) { + sd_dhcp_client *client = userdata; _cleanup_free_ DHCPMessage *message = NULL; - int buflen = 0, len, r; const struct ether_addr zero_mac = { { 0, 0, 0, 0, 0, 0 } }; const struct ether_addr *expected_chaddr = NULL; uint8_t expected_hlen = 0; + ssize_t len, buflen; assert(s); assert(client); - r = ioctl(fd, FIONREAD, &buflen); - if (r < 0) - return -errno; - else if (buflen < 0) - /* this can't be right */ - return -EIO; + buflen = next_datagram_size_fd(fd); + if (buflen < 0) + return buflen; message = malloc0(buflen); if (!message) return -ENOMEM; - len = read(fd, message, buflen); + len = recv(fd, message, buflen, 0); if (len < 0) { if (errno == EAGAIN || errno == EINTR) return 0; @@ -1602,8 +1698,12 @@ static int client_receive_message_udp(sd_event_source *s, int fd, return client_handle_message(client, message, len); } -static int client_receive_message_raw(sd_event_source *s, int fd, - uint32_t revents, void *userdata) { +static int client_receive_message_raw( + sd_event_source *s, + int fd, + uint32_t revents, + void *userdata) { + sd_dhcp_client *client = userdata; _cleanup_free_ DHCPPacket *packet = NULL; uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))]; @@ -1616,17 +1716,15 @@ static int client_receive_message_raw(sd_event_source *s, int fd, }; struct cmsghdr *cmsg; bool checksum = true; - int buflen = 0, len, r; + ssize_t buflen, len; + int r; assert(s); assert(client); - r = ioctl(fd, FIONREAD, &buflen); - if (r < 0) - return -errno; - else if (buflen < 0) - /* this can't be right */ - return -EIO; + buflen = next_datagram_size_fd(fd); + if (buflen < 0) + return buflen; packet = malloc0(buflen); if (!packet) @@ -1696,8 +1794,7 @@ int sd_dhcp_client_stop(sd_dhcp_client *client) { return 0; } -int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, - int priority) { +int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int64_t priority) { int r; assert_return(client, -EINVAL); diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index 7a119fd488..ef50ed17a1 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -825,7 +825,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len); if (r >= 0) { - _cleanup_free_ char *client_id_hex; + _cleanup_free_ char *client_id_hex = NULL; client_id_hex = hexmem(client_id, client_id_len); if (!client_id_hex) { diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index ad3a37b722..fb335337c4 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -208,8 +208,7 @@ int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) { return 0; } -int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, - int priority) { +int sd_dhcp_server_attach_event(sd_dhcp_server *server, sd_event *event, int64_t priority) { int r; assert_return(server, -EINVAL); @@ -281,10 +280,11 @@ static int dhcp_server_send_unicast_raw(sd_dhcp_server *server, } static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination, + uint16_t destination_port, DHCPMessage *message, size_t len) { union sockaddr_union dest = { .in.sin_family = AF_INET, - .in.sin_port = htobe16(DHCP_PORT_CLIENT), + .in.sin_port = htobe16(destination_port), .in.sin_addr.s_addr = destination, }; struct iovec iov = { @@ -343,6 +343,7 @@ int dhcp_server_send_packet(sd_dhcp_server *server, DHCPRequest *req, DHCPPacket *packet, int type, size_t optoffset) { be32_t destination = INADDR_ANY; + uint16_t destination_port = DHCP_PORT_CLIENT; int r; assert(server); @@ -387,17 +388,19 @@ int dhcp_server_send_packet(sd_dhcp_server *server, */ if (req->message->giaddr) { destination = req->message->giaddr; + destination_port = DHCP_PORT_SERVER; if (type == DHCP_NAK) packet->dhcp.flags = htobe16(0x8000); } else if (req->message->ciaddr && type != DHCP_NAK) destination = req->message->ciaddr; if (destination != INADDR_ANY) - return dhcp_server_send_udp(server, destination, &packet->dhcp, + return dhcp_server_send_udp(server, destination, + destination_port, &packet->dhcp, sizeof(DHCPMessage) + optoffset); else if (requested_broadcast(req) || type == DHCP_NAK) return dhcp_server_send_udp(server, INADDR_BROADCAST, - &packet->dhcp, + destination_port, &packet->dhcp, sizeof(DHCPMessage) + optoffset); else /* we cannot send UDP packet to specific MAC address when the @@ -465,10 +468,12 @@ static int server_send_offer(sd_dhcp_server *server, DHCPRequest *req, if (r < 0) return r; - r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, - SD_DHCP_OPTION_ROUTER, 4, &server->address); - if (r < 0) - return r; + if (server->emit_router) { + r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, + SD_DHCP_OPTION_ROUTER, 4, &server->address); + if (r < 0) + return r; + } r = dhcp_server_send_packet(server, req, packet, DHCP_OFFER, offset); if (r < 0) @@ -502,10 +507,12 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, if (r < 0) return r; - r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, - SD_DHCP_OPTION_ROUTER, 4, &server->address); - if (r < 0) - return r; + if (server->emit_router) { + r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, + SD_DHCP_OPTION_ROUTER, 4, &server->address); + if (r < 0) + return r; + } if (server->n_dns > 0) { r = dhcp_option_append( @@ -580,7 +587,8 @@ static int server_send_forcerenew(sd_dhcp_server *server, be32_t address, memcpy(&packet->dhcp.chaddr, chaddr, ETH_ALEN); - r = dhcp_server_send_udp(server, address, &packet->dhcp, + r = dhcp_server_send_udp(server, address, DHCP_PORT_CLIENT, + &packet->dhcp, sizeof(DHCPMessage) + optoffset); if (r < 0) return r; @@ -955,14 +963,13 @@ static int server_receive_message(sd_event_source *s, int fd, .msg_controllen = sizeof(cmsgbuf), }; struct cmsghdr *cmsg; - int buflen = 0, len; + ssize_t buflen, len; assert(server); - if (ioctl(fd, FIONREAD, &buflen) < 0) - return -errno; - else if (buflen < 0) - return -EIO; + buflen = next_datagram_size_fd(fd); + if (buflen < 0) + return buflen; message = malloc(buflen); if (!message) @@ -1155,3 +1162,14 @@ int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], u return 1; } + +int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled) { + assert_return(server, -EINVAL); + + if (enabled == server->emit_router) + return 0; + + server->emit_router = enabled; + + return 1; +} diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 5b6b9cbcac..05972e01c9 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -33,6 +33,7 @@ #include "in-addr-util.h" #include "network-internal.h" #include "random-util.h" +#include "socket-util.h" #include "string-table.h" #include "util.h" @@ -63,7 +64,7 @@ struct sd_dhcp6_client { uint8_t retransmit_count; sd_event_source *timeout_resend; sd_event_source *timeout_resend_expire; - sd_dhcp6_client_cb_t cb; + sd_dhcp6_client_callback_t cb; void *userdata; struct duid duid; size_t duid_len; @@ -110,7 +111,10 @@ DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int); static int client_start(sd_dhcp6_client *client, enum DHCP6State state); -int sd_dhcp6_client_set_callback(sd_dhcp6_client *client, sd_dhcp6_client_cb_t cb, void *userdata) { +int sd_dhcp6_client_set_callback( + sd_dhcp6_client *client, + sd_dhcp6_client_callback_t cb, + void *userdata) { assert_return(client, -EINVAL); client->cb = cb; @@ -130,7 +134,10 @@ int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index) { return 0; } -int sd_dhcp6_client_set_local_address(sd_dhcp6_client *client, const struct in6_addr *local_address) { +int sd_dhcp6_client_set_local_address( + sd_dhcp6_client *client, + const struct in6_addr *local_address) { + assert_return(client, -EINVAL); assert_return(local_address, -EINVAL); assert_return(in_addr_is_link_local(AF_INET6, (const union in_addr_union *) local_address) > 0, -EINVAL); @@ -179,41 +186,47 @@ static int client_ensure_duid(sd_dhcp6_client *client) { return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); } +/** + * Sets DUID. If duid is non-null, the DUID is set to duid_type + duid + * without further modification. Otherwise, if duid_type is supported, DUID + * is set based on that type. Otherwise, an error is returned. + */ int sd_dhcp6_client_set_duid( sd_dhcp6_client *client, - uint16_t type, - uint8_t *duid, size_t duid_len) { - assert_return(client, -EINVAL); - assert_return(duid, -EINVAL); - assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL); + uint16_t duid_type, + const void *duid, + size_t duid_len) { + int r; + assert_return(client, -EINVAL); + assert_return(duid_len == 0 || duid != NULL, -EINVAL); assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); - switch (type) { - case DHCP6_DUID_LLT: - if (duid_len <= sizeof(client->duid.llt)) - return -EINVAL; - break; - case DHCP6_DUID_EN: - if (duid_len != sizeof(client->duid.en)) - return -EINVAL; - break; - case DHCP6_DUID_LL: - if (duid_len <= sizeof(client->duid.ll)) - return -EINVAL; - break; - case DHCP6_DUID_UUID: - if (duid_len != sizeof(client->duid.uuid)) - return -EINVAL; - break; - default: - /* accept unknown type in order to be forward compatible */ - break; + if (duid != NULL) { + r = dhcp_validate_duid_len(duid_type, duid_len); + if (r < 0) + return r; } - client->duid.type = htobe16(type); - memcpy(&client->duid.raw.data, duid, duid_len); - client->duid_len = duid_len + sizeof(client->duid.type); + if (duid != NULL) { + client->duid.type = htobe16(duid_type); + memcpy(&client->duid.raw.data, duid, duid_len); + client->duid_len = sizeof(client->duid.type) + duid_len; + } else if (duid_type == DUID_TYPE_EN) { + r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); + if (r < 0) + return r; + } else + return -EOPNOTSUPP; + + return 0; +} + +int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { + assert_return(client, -EINVAL); + assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); + + client->ia_na.id = htobe32(iaid); return 0; } @@ -438,8 +451,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return 0; } -static int client_timeout_t2(sd_event_source *s, uint64_t usec, - void *userdata) { +static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) { sd_dhcp6_client *client = userdata; assert_return(s, -EINVAL); @@ -456,8 +468,7 @@ static int client_timeout_t2(sd_event_source *s, uint64_t usec, return 0; } -static int client_timeout_t1(sd_event_source *s, uint64_t usec, - void *userdata) { +static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) { sd_dhcp6_client *client = userdata; assert_return(s, -EINVAL); @@ -474,8 +485,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, return 0; } -static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, - void *userdata) { +static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) { sd_dhcp6_client *client = userdata; DHCP6_CLIENT_DONT_DESTROY(client); enum DHCP6State state; @@ -501,8 +511,7 @@ static usec_t client_timeout_compute_random(usec_t val) { (random_u32() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC; } -static int client_timeout_resend(sd_event_source *s, uint64_t usec, - void *userdata) { +static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) { int r = 0; sd_dhcp6_client *client = userdata; usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0; @@ -669,9 +678,11 @@ static int client_ensure_iaid(sd_dhcp6_client *client) { return 0; } -static int client_parse_message(sd_dhcp6_client *client, - DHCP6Message *message, size_t len, - sd_dhcp6_lease *lease) { +static int client_parse_message( + sd_dhcp6_client *client, + DHCP6Message *message, + size_t len, + sd_dhcp6_lease *lease) { int r; uint8_t *optval, *option, *id = NULL; uint16_t optcode, status; @@ -891,31 +902,28 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, sd_dhcp6_client *client = userdata; DHCP6_CLIENT_DONT_DESTROY(client); _cleanup_free_ DHCP6Message *message = NULL; - int r, buflen, len; + ssize_t buflen, len; + int r = 0; assert(s); assert(client); assert(client->event); - r = ioctl(fd, FIONREAD, &buflen); - if (r < 0) - return -errno; - else if (buflen < 0) - /* This really should not happen */ - return -EIO; + buflen = next_datagram_size_fd(fd); + if (buflen < 0) + return buflen; message = malloc(buflen); if (!message) return -ENOMEM; - len = read(fd, message, buflen); + len = recv(fd, message, buflen, 0); if (len < 0) { if (errno == EAGAIN || errno == EINTR) return 0; - log_dhcp6_client(client, "Could not receive message from UDP socket: %m"); + return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m"); - return -errno; } else if ((size_t)len < sizeof(DHCP6Message)) return 0; @@ -938,8 +946,7 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, break; default: - log_dhcp6_client(client, "unknown message type %d", - message->type); + log_dhcp6_client(client, "Unknown message type %d", message->type); return 0; } @@ -998,10 +1005,9 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, return 0; } - if (r >= 0) { + if (r >= 0) log_dhcp6_client(client, "Recv %s", dhcp6_message_type_to_string(message->type)); - } return 0; } @@ -1054,7 +1060,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { if (client->lease->ia.lifetime_t1 == 0xffffffff || client->lease->ia.lifetime_t2 == 0xffffffff) { - log_dhcp6_client(client, "infinite T1 0x%08x or T2 0x%08x", + log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x", be32toh(client->lease->ia.lifetime_t1), be32toh(client->lease->ia.lifetime_t2)); @@ -1170,8 +1176,13 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { return r; r = dhcp6_network_bind_udp_socket(client->index, &client->local_address); - if (r < 0) - return r; + if (r < 0) { + _cleanup_free_ char *p = NULL; + + (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p); + return log_dhcp6_client_errno(client, r, + "Failed to bind to UDP socket at address %s: %m", strna(p)); + } client->fd = r; @@ -1187,7 +1198,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { goto error; r = sd_event_source_set_description(client->receive_message, - "dhcp6-receive-message"); + "dhcp6-receive-message"); if (r < 0) goto error; @@ -1195,8 +1206,8 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { state = DHCP6_STATE_INFORMATION_REQUEST; log_dhcp6_client(client, "Started in %s mode", - client->information_request? "Information request": - "Managed"); + client->information_request? "Information request": + "Managed"); return client_start(client, state); @@ -1205,7 +1216,7 @@ error: return r; } -int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int priority) { +int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) { int r; assert_return(client, -EINVAL); diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c index f7880a891c..cc7436db6b 100644 --- a/src/libsystemd-network/sd-ipv4acd.c +++ b/src/libsystemd-network/sd-ipv4acd.c @@ -92,7 +92,7 @@ struct sd_ipv4acd { struct ether_addr mac_addr; sd_event *event; int event_priority; - sd_ipv4acd_cb_t cb; + sd_ipv4acd_callback_t cb; void* userdata; }; @@ -428,7 +428,7 @@ int sd_ipv4acd_detach_event(sd_ipv4acd *ll) { return 0; } -int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority) { +int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int64_t priority) { int r; assert_return(ll, -EINVAL); @@ -447,7 +447,7 @@ int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority) { return 0; } -int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata) { +int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_callback_t cb, void *userdata) { assert_return(ll, -EINVAL); ll->cb = cb; @@ -456,7 +456,7 @@ int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata) return 0; } -int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address){ +int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address) { assert_return(ll, -EINVAL); assert_return(address, -EINVAL); assert_return(ll->state == IPV4ACD_STATE_INIT, -EBUSY); diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c index db6cf22aaa..2a06418c53 100644 --- a/src/libsystemd-network/sd-ipv4ll.c +++ b/src/libsystemd-network/sd-ipv4ll.c @@ -52,7 +52,7 @@ struct sd_ipv4ll { /* External */ be32_t claimed_address; - sd_ipv4ll_cb_t cb; + sd_ipv4ll_callback_t cb; void* userdata; }; @@ -160,7 +160,7 @@ int sd_ipv4ll_detach_event(sd_ipv4ll *ll) { return sd_ipv4acd_detach_event(ll->acd); } -int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) { +int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority) { int r; assert_return(ll, -EINVAL); @@ -172,7 +172,7 @@ int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority) { return 0; } -int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata) { +int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata) { assert_return(ll, -EINVAL); ll->cb = cb; @@ -181,7 +181,7 @@ int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata) { return 0; } -int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address){ +int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address) { assert_return(ll, -EINVAL); assert_return(address, -EINVAL); diff --git a/src/libsystemd-network/sd-lldp.c b/src/libsystemd-network/sd-lldp.c index 885ca62425..9d4587c80e 100644 --- a/src/libsystemd-network/sd-lldp.c +++ b/src/libsystemd-network/sd-lldp.c @@ -1,21 +1,21 @@ /*** - This file is part of systemd. + This file is part of systemd. - Copyright (C) 2014 Tom Gundersen - Copyright (C) 2014 Susant Sahani + Copyright (C) 2014 Tom Gundersen + Copyright (C) 2014 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 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. + 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/>. + 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 <arpa/inet.h> @@ -24,733 +24,473 @@ #include "alloc-util.h" #include "fd-util.h" -#include "fileio.h" -#include "hashmap.h" #include "lldp-internal.h" -#include "lldp-port.h" -#include "lldp-tlv.h" -#include "prioq.h" -#include "siphash24.h" -#include "string-util.h" - -typedef enum LLDPAgentRXState { - LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL = 4, - LLDP_AGENT_RX_DELETE_AGED_INFO, - LLDP_AGENT_RX_LLDP_INITIALIZE, - LLDP_AGENT_RX_WAIT_FOR_FRAME, - LLDP_AGENT_RX_RX_FRAME, - LLDP_AGENT_RX_DELETE_INFO, - LLDP_AGENT_RX_UPDATE_INFO, - _LLDP_AGENT_RX_STATE_MAX, - _LLDP_AGENT_RX_INVALID = -1, -} LLDPAgentRXState; - -/* Section 10.5.2.2 Reception counters */ -struct lldp_agent_statistics { - uint64_t stats_ageouts_total; - uint64_t stats_frames_discarded_total; - uint64_t stats_frames_in_errors_total; - uint64_t stats_frames_in_total; - uint64_t stats_tlvs_discarded_total; - uint64_t stats_tlvs_unrecognized_total; -}; - -struct sd_lldp { - lldp_port *port; - - Prioq *by_expiry; - Hashmap *neighbour_mib; - - sd_lldp_cb_t cb; - - void *userdata; - - LLDPAgentRXState rx_state; - lldp_agent_statistics statistics; -}; - -static void chassis_id_hash_func(const void *p, struct siphash *state) { - const lldp_chassis_id *id = p; - - assert(id); - assert(id->data); - - siphash24_compress(&id->length, sizeof(id->length), state); - siphash24_compress(id->data, id->length, state); -} +#include "lldp-neighbor.h" +#include "lldp-network.h" +#include "socket-util.h" +#include "ether-addr-util.h" -static int chassis_id_compare_func(const void *_a, const void *_b) { - const lldp_chassis_id *a, *b; +#define LLDP_DEFAULT_NEIGHBORS_MAX 128U - a = _a; - b = _b; +static void lldp_flush_neighbors(sd_lldp *lldp) { + sd_lldp_neighbor *n; - assert(!a->length || a->data); - assert(!b->length || b->data); + assert(lldp); - if (a->type != b->type) - return -1; + while ((n = hashmap_first(lldp->neighbor_by_id))) + lldp_neighbor_unlink(n); +} - if (a->length != b->length) - return a->length < b->length ? -1 : 1; +static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) { + assert(lldp); + assert(n); - return memcmp(a->data, b->data, a->length); -} + log_lldp("Invoking callback for '%c'.", event); -static const struct hash_ops chassis_id_hash_ops = { - .hash = chassis_id_hash_func, - .compare = chassis_id_compare_func -}; + if (!lldp->callback) + return; -static void lldp_mib_delete_objects(sd_lldp *lldp); -static void lldp_set_state(sd_lldp *lldp, LLDPAgentRXState state); -static void lldp_run_state_machine(sd_lldp *ll); + lldp->callback(lldp, event, n, lldp->userdata); +} -static int lldp_receive_frame(sd_lldp *lldp, tlv_packet *tlv) { - int r; +static int lldp_make_space(sd_lldp *lldp, size_t extra) { + usec_t t = USEC_INFINITY; + bool changed = false; assert(lldp); - assert(tlv); - - /* Remove expired packets */ - if (prioq_size(lldp->by_expiry) > 0) { - lldp_set_state(lldp, LLDP_AGENT_RX_DELETE_INFO); + /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries + * are free. */ - lldp_mib_delete_objects(lldp); - } + for (;;) { + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; - r = lldp_mib_add_objects(lldp->by_expiry, lldp->neighbour_mib, tlv); - if (r < 0) - goto out; + n = prioq_peek(lldp->neighbor_by_expiry); + if (!n) + break; - lldp_set_state(lldp, LLDP_AGENT_RX_UPDATE_INFO); + sd_lldp_neighbor_ref(n); - log_lldp("Packet added. MIB size: %d , PQ size: %d", - hashmap_size(lldp->neighbour_mib), - prioq_size(lldp->by_expiry)); + if (hashmap_size(lldp->neighbor_by_id) > LESS_BY(lldp->neighbors_max, extra)) + goto remove_one; - lldp->statistics.stats_frames_in_total ++; + if (t == USEC_INFINITY) + t = now(clock_boottime_or_monotonic()); - out: - if (r < 0) - log_lldp("Receive frame failed: %s", strerror(-r)); + if (n->until > t) + break; - lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME); + remove_one: + lldp_neighbor_unlink(n); + lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n); + changed = true; + } - return 0; + return changed; } -/* 10.3.2 LLDPDU validation: rxProcessFrame() */ -int lldp_handle_packet(tlv_packet *tlv, uint16_t length) { - bool system_description = false, system_name = false, chassis_id = false; - bool malformed = false, port_id = false, ttl = false, end = false; - uint16_t type, len, i, l, t; - lldp_port *port; - uint8_t *p, *q; - sd_lldp *lldp; - int r; - - assert(tlv); - assert(length > 0); - - port = (lldp_port *) tlv->userdata; - lldp = (sd_lldp *) port->userdata; - - if (lldp->port->status == LLDP_PORT_STATUS_DISABLED) { - log_lldp("Port: %s is disabled. Dropping.", lldp->port->ifname); - goto out; - } +static bool lldp_keep_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) { + assert(lldp); + assert(n); - lldp_set_state(lldp, LLDP_AGENT_RX_RX_FRAME); + /* Don't keep data with a zero TTL */ + if (n->ttl <= 0) + return false; - p = tlv->pdu; - p += sizeof(struct ether_header); + /* Filter out data from the filter address */ + if (!ether_addr_is_null(&lldp->filter_address) && + ether_addr_equal(&lldp->filter_address, &n->source_address)) + return false; - for (i = 1, l = 0; l <= length; i++) { + /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with + * no caps field set. */ + if (n->has_capabilities && + (n->enabled_capabilities & lldp->capability_mask) == 0) + return false; - memcpy(&t, p, sizeof(uint16_t)); + /* Keep everything else */ + return true; +} - type = ntohs(t) >> 9; - len = ntohs(t) & 0x01ff; +static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor); - if (type == LLDP_TYPE_END) { - if (len != 0) { - log_lldp("TLV type end must be length 0 (not %d). Dropping.", len); +static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) { + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL; + bool keep; + int r; - malformed = true; - goto out; - } + assert(lldp); + assert(n); + assert(!n->lldp); - end = true; + keep = lldp_keep_neighbor(lldp, n); - break; - } else if (type >=_LLDP_TYPE_MAX) { - log_lldp("TLV type: %d not recognized. Dropping.", type); + /* First retrieve the old entry for this MSAP */ + old = hashmap_get(lldp->neighbor_by_id, &n->id); + if (old) { + sd_lldp_neighbor_ref(old); - malformed = true; - goto out; + if (!keep) { + lldp_neighbor_unlink(old); + lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old); + return 0; } - /* skip type and length encoding */ - p += 2; - q = p; - - p += len; - l += (len + 2); + if (lldp_neighbor_equal(n, old)) { + /* Is this equal, then restart the TTL counter, but don't do anyting else. */ + lldp_start_timer(lldp, old); + lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old); + return 0; + } - if (i <= 3) { - if (i != type) { - log_lldp("TLV missing or out of order. Dropping."); + /* Data changed, remove the old entry, and add a new one */ + lldp_neighbor_unlink(old); - malformed = true; - goto out; - } - } + } else if (!keep) + return 0; - switch(type) { - case LLDP_TYPE_CHASSIS_ID: + /* Then, make room for at least one new neighbor */ + lldp_make_space(lldp, 1); - if (len < 2) { - log_lldp("Received malformed Chassis ID TLV length: %d. Dropping.", len); + r = hashmap_put(lldp->neighbor_by_id, &n->id, n); + if (r < 0) + goto finish; - malformed = true; - goto out; - } + r = prioq_put(lldp->neighbor_by_expiry, n, &n->prioq_idx); + if (r < 0) { + assert_se(hashmap_remove(lldp->neighbor_by_id, &n->id) == n); + goto finish; + } - if (chassis_id) { - log_lldp("Duplicate Chassis ID TLV found. Dropping."); + n->lldp = lldp; - malformed = true; - goto out; - } + lldp_start_timer(lldp, n); + lldp_callback(lldp, old ? SD_LLDP_EVENT_UPDATED : SD_LLDP_EVENT_ADDED, n); - /* Look what subtype it has */ - if (*q == LLDP_CHASSIS_SUBTYPE_RESERVED || *q > LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED) { - log_lldp("Unknown subtype: %d found in Chassis ID TLV. Dropping.", *q); + return 1; - malformed = true; - goto out; +finish: + if (old) + lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n); - } + return r; +} - chassis_id = true; +static int lldp_handle_datagram(sd_lldp *lldp, sd_lldp_neighbor *n) { + int r; - break; - case LLDP_TYPE_PORT_ID: + assert(lldp); + assert(n); - if (len < 2) { - log_lldp("Received malformed Port ID TLV length: %d. Dropping.", len); + r = lldp_neighbor_parse(n); + if (r == -EBADMSG) /* Ignore bad messages */ + return 0; + if (r < 0) + return r; - malformed = true; - goto out; - } + r = lldp_add_neighbor(lldp, n); + if (r < 0) { + log_lldp_errno(r, "Failed to add datagram. Ignoring."); + return 0; + } - if (port_id) { - log_lldp("Duplicate Port ID TLV found. Dropping."); + log_lldp("Successfully processed LLDP datagram."); + return 0; +} - malformed = true; - goto out; - } +static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; + ssize_t space, length; + sd_lldp *lldp = userdata; - /* Look what subtype it has */ - if (*q == LLDP_PORT_SUBTYPE_RESERVED || *q > LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED) { - log_lldp("Unknown subtype: %d found in Port ID TLV. Dropping.", *q); + assert(fd >= 0); + assert(lldp); - malformed = true; - goto out; + space = next_datagram_size_fd(fd); + if (space < 0) + return log_lldp_errno(space, "Failed to determine datagram size to read: %m"); - } + n = lldp_neighbor_new(space); + if (!n) + return -ENOMEM; - port_id = true; + length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT); + if (length < 0) + return log_lldp_errno(errno, "Failed to read LLDP datagram: %m"); - break; - case LLDP_TYPE_TTL: + if ((size_t) length != n->raw_size) { + log_lldp("Packet size mismatch."); + return -EINVAL; + } - if(len != 2) { - log_lldp("Received invalid TTL TLV lenth: %d. Dropping.", len); + return lldp_handle_datagram(lldp, n); +} - malformed = true; - goto out; - } +_public_ int sd_lldp_start(sd_lldp *lldp) { + int r; - if (ttl) { - log_lldp("Duplicate TTL TLV found. Dropping."); + assert_return(lldp, -EINVAL); - malformed = true; - goto out; - } + if (lldp->fd >= 0) + return 0; - ttl = true; + assert(!lldp->io_event_source); - break; - case LLDP_TYPE_SYSTEM_NAME: + lldp->fd = lldp_network_bind_raw_socket(lldp->ifindex); + if (lldp->fd < 0) + return lldp->fd; - /* According to RFC 1035 the length of a FQDN is limited to 255 characters */ - if (len > 255) { - log_lldp("Received invalid system name length: %d. Dropping.", len); - malformed = true; - goto out; - } + if (lldp->event) { + r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp); + if (r < 0) + goto fail; - if (system_name) { - log_lldp("Duplicate system name found. Dropping."); - malformed = true; - goto out; - } + r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority); + if (r < 0) + goto fail; - system_name = true; + (void) sd_event_source_set_description(lldp->io_event_source, "lldp-io"); + } - break; - case LLDP_TYPE_SYSTEM_DESCRIPTION: - - /* 0 <= n <= 255 octets */ - if (len > 255) { - log_lldp("Received invalid system description length: %d. Dropping.", len); - malformed = true; - goto out; - } - - if (system_description) { - log_lldp("Duplicate system description found. Dropping."); - malformed = true; - goto out; - } - - system_description = true; - break; - default: + return 1; - if (len == 0) { - log_lldp("TLV type: %d length 0 received. Dropping.", type); +fail: + lldp->io_event_source = sd_event_source_unref(lldp->io_event_source); + lldp->fd = safe_close(lldp->fd); - malformed = true; - goto out; - } - break; - } - } + return r; +} - if(!chassis_id || !port_id || !ttl || !end) { - log_lldp("One or more mandatory TLV missing. Dropping."); +_public_ int sd_lldp_stop(sd_lldp *lldp) { + assert_return(lldp, -EINVAL); - malformed = true; - goto out; + if (lldp->fd < 0) + return 0; - } + lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source); + lldp->io_event_source = sd_event_source_unref(lldp->io_event_source); + lldp->fd = safe_close(lldp->fd); - r = tlv_packet_parse_pdu(tlv, length); - if (r < 0) { - log_lldp("Failed to parse the TLV. Dropping."); + lldp_flush_neighbors(lldp); - malformed = true; - goto out; - } + return 1; +} - return lldp_receive_frame(lldp, tlv); +_public_ int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority) { + int r; - out: - lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME); + assert_return(lldp, -EINVAL); + assert_return(lldp->fd < 0, -EBUSY); + assert_return(!lldp->event, -EBUSY); - if (malformed) { - lldp->statistics.stats_frames_discarded_total ++; - lldp->statistics.stats_frames_in_errors_total ++; + if (event) + lldp->event = sd_event_ref(event); + else { + r = sd_event_default(&lldp->event); + if (r < 0) + return r; } - sd_lldp_packet_unref(tlv); + lldp->event_priority = priority; return 0; } -static int ttl_expiry_item_prioq_compare_func(const void *a, const void *b) { - const lldp_neighbour_port *p = a, *q = b; +_public_ int sd_lldp_detach_event(sd_lldp *lldp) { - if (p->until < q->until) - return -1; - - if (p->until > q->until) - return 1; + assert_return(lldp, -EINVAL); + assert_return(lldp->fd < 0, -EBUSY); + lldp->event = sd_event_unref(lldp->event); return 0; } -static void lldp_set_state(sd_lldp *lldp, LLDPAgentRXState state) { - - assert(lldp); - assert(state < _LLDP_AGENT_RX_STATE_MAX); +_public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata) { + assert_return(lldp, -EINVAL); - lldp->rx_state = state; + lldp->callback = cb; + lldp->userdata = userdata; - lldp_run_state_machine(lldp); + return 0; } -static void lldp_run_state_machine(sd_lldp *lldp) { - if (!lldp->cb) - return; - - switch (lldp->rx_state) { - case LLDP_AGENT_RX_UPDATE_INFO: - lldp->cb(lldp, SD_LLDP_EVENT_UPDATE_INFO, lldp->userdata); - break; - default: - break; - } -} +_public_ sd_lldp* sd_lldp_unref(sd_lldp *lldp) { -/* 10.5.5.2.1 mibDeleteObjects () - * The mibDeleteObjects () procedure deletes all information in the LLDP remote - * systems MIB associated with the MSAP identifier if an LLDPDU is received with - * an rxTTL value of zero (see 10.3.2) or the timing counter rxInfoTTL expires. */ + if (!lldp) + return NULL; -static void lldp_mib_delete_objects(sd_lldp *lldp) { - lldp_neighbour_port *p; - usec_t t = 0; + lldp_flush_neighbors(lldp); - /* Remove all entries that are past their TTL */ - for (;;) { + hashmap_free(lldp->neighbor_by_id); + prioq_free(lldp->neighbor_by_expiry); - if (prioq_size(lldp->by_expiry) <= 0) - break; + sd_event_source_unref(lldp->io_event_source); + sd_event_source_unref(lldp->timer_event_source); + sd_event_unref(lldp->event); + safe_close(lldp->fd); - p = prioq_peek(lldp->by_expiry); - if (!p) - break; + free(lldp); - if (t <= 0) - t = now(clock_boottime_or_monotonic()); + return NULL; +} - if (p->until > t) - break; +_public_ int sd_lldp_new(sd_lldp **ret, int ifindex) { + _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL; + int r; - lldp_neighbour_port_remove_and_free(p); + assert_return(ret, -EINVAL); + assert_return(ifindex > 0, -EINVAL); - lldp->statistics.stats_ageouts_total ++; - } -} + lldp = new0(sd_lldp, 1); + if (!lldp) + return -ENOMEM; -static void lldp_mib_objects_flush(sd_lldp *lldp) { - lldp_neighbour_port *p, *q; - lldp_chassis *c; + lldp->fd = -1; + lldp->ifindex = ifindex; + lldp->neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX; + lldp->capability_mask = (uint16_t) -1; - assert(lldp); - assert(lldp->neighbour_mib); - assert(lldp->by_expiry); + lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_id_hash_ops); + if (!lldp->neighbor_by_id) + return -ENOMEM; - /* Drop all packets */ - while ((c = hashmap_steal_first(lldp->neighbour_mib))) { + r = prioq_ensure_allocated(&lldp->neighbor_by_expiry, lldp_neighbor_prioq_compare_func); + if (r < 0) + return r; - LIST_FOREACH_SAFE(port, p, q, c->ports) { - lldp_neighbour_port_remove_and_free(p); - } - } + *ret = lldp; + lldp = NULL; - assert(hashmap_size(lldp->neighbour_mib) == 0); - assert(prioq_size(lldp->by_expiry) == 0); + return 0; } -int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) { - _cleanup_free_ char *temp_path = NULL; - _cleanup_fclose_ FILE *f = NULL; - uint8_t *mac, *port_id, type; - lldp_neighbour_port *p; - uint16_t data = 0, length = 0; - char buf[LINE_MAX]; - lldp_chassis *c; - usec_t time; - Iterator i; - int r; +static int neighbor_compare_func(const void *a, const void *b) { + const sd_lldp_neighbor * const*x = a, * const *y = b; - assert(lldp); - assert(lldp_file); + return lldp_neighbor_id_hash_ops.compare(&(*x)->id, &(*y)->id); +} - r = fopen_temporary(lldp_file, &f, &temp_path); - if (r < 0) - goto fail; - - fchmod(fileno(f), 0644); - - HASHMAP_FOREACH(c, lldp->neighbour_mib, i) { - LIST_FOREACH(port, p, c->ports) { - _cleanup_free_ char *s = NULL; - char *k, *t; - - r = sd_lldp_packet_read_chassis_id(p->packet, &type, &mac, &length); - if (r < 0) - continue; - - sprintf(buf, "'_Chassis=%02x:%02x:%02x:%02x:%02x:%02x' '_CType=%d' ", - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], type); - - s = strdup(buf); - if (!s) { - r = -ENOMEM; - goto fail; - } - - r = sd_lldp_packet_read_port_id(p->packet, &type, &port_id, &length); - if (r < 0) - continue; - - if (type != LLDP_PORT_SUBTYPE_MAC_ADDRESS) { - k = strndup((char *) port_id, length -1); - if (!k) { - r = -ENOMEM; - goto fail; - } - - sprintf(buf, "'_Port=%s' '_PType=%d' ", k , type); - free(k); - } else { - mac = port_id; - sprintf(buf, "'_Port=%02x:%02x:%02x:%02x:%02x:%02x' '_PType=%d' ", - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], type); - } - - k = strappend(s, buf); - if (!k) { - r = -ENOMEM; - goto fail; - } - - free(s); - s = k; - - time = now(clock_boottime_or_monotonic()); - - /* Don't write expired packets */ - if (time - p->until <= 0) - continue; - - sprintf(buf, "'_TTL="USEC_FMT"' ", p->until); - - k = strappend(s, buf); - if (!k) { - r = -ENOMEM; - goto fail; - } - - free(s); - s = k; - - r = sd_lldp_packet_read_system_name(p->packet, &k, &length); - if (r < 0) - k = strappend(s, "'_NAME=N/A' "); - else { - t = strndup(k, length); - if (!t) { - r = -ENOMEM; - goto fail; - } - - k = strjoin(s, "'_NAME=", t, "' ", NULL); - free(t); - } - - if (!k) { - r = -ENOMEM; - goto fail; - } - - free(s); - s = k; - - (void) sd_lldp_packet_read_system_capability(p->packet, &data); - - sprintf(buf, "'_CAP=%x'", data); - - k = strappend(s, buf); - if (!k) { - r = -ENOMEM; - goto fail; - } - - free(s); - s = k; - - fprintf(f, "%s\n", s); - } - } +static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) { + sd_lldp *lldp = userdata; + int r, q; - r = fflush_and_check(f); + r = lldp_make_space(lldp, 0); if (r < 0) - goto fail; + return log_lldp_errno(r, "Failed to make space: %m"); - if (rename(temp_path, lldp_file) < 0) { - r = -errno; - goto fail; - } + q = lldp_start_timer(lldp, NULL); + if (q < 0) + return log_lldp_errno(q, "Failed to restart timer: %m"); return 0; - - fail: - if (temp_path) - (void) unlink(temp_path); - - return log_error_errno(r, "Failed to save lldp data %s: %m", lldp_file); } -int sd_lldp_start(sd_lldp *lldp) { +static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) { + sd_lldp_neighbor *n; int r; - assert_return(lldp, -EINVAL); - assert_return(lldp->port, -EINVAL); + assert(lldp); - lldp->port->status = LLDP_PORT_STATUS_ENABLED; + if (neighbor) + lldp_neighbor_start_ttl(neighbor); - lldp_set_state(lldp, LLDP_AGENT_RX_LLDP_INITIALIZE); + n = prioq_peek(lldp->neighbor_by_expiry); + if (!n) { - r = lldp_port_start(lldp->port); - if (r < 0) { - log_lldp("Failed to start Port : %s , %s", - lldp->port->ifname, - strerror(-r)); + if (lldp->timer_event_source) + return sd_event_source_set_enabled(lldp->timer_event_source, SD_EVENT_OFF); - lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL); - - return r; + return 0; } - lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME); - - return 0; -} - -int sd_lldp_stop(sd_lldp *lldp) { - int r; + if (lldp->timer_event_source) { + r = sd_event_source_set_time(lldp->timer_event_source, n->until); + if (r < 0) + return r; - assert_return(lldp, -EINVAL); - assert_return(lldp->port, -EINVAL); + return sd_event_source_set_enabled(lldp->timer_event_source, SD_EVENT_ONESHOT); + } - lldp->port->status = LLDP_PORT_STATUS_DISABLED; + if (!lldp->event) + return 0; - r = lldp_port_stop(lldp->port); + r = sd_event_add_time(lldp->event, &lldp->timer_event_source, clock_boottime_or_monotonic(), n->until, 0, on_timer_event, lldp); if (r < 0) return r; - lldp_mib_objects_flush(lldp); + r = sd_event_source_set_priority(lldp->timer_event_source, lldp->event_priority); + if (r < 0) + return r; + (void) sd_event_source_set_description(lldp->timer_event_source, "lldp-timer"); return 0; } -int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int priority) { - int r; +_public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) { + sd_lldp_neighbor **l = NULL, *n; + Iterator i; + int k = 0, r; assert_return(lldp, -EINVAL); - assert_return(!lldp->port->event, -EBUSY); + assert_return(ret, -EINVAL); - if (event) - lldp->port->event = sd_event_ref(event); - else { - r = sd_event_default(&lldp->port->event); - if (r < 0) - return r; + if (hashmap_isempty(lldp->neighbor_by_id)) { /* Special shortcut */ + *ret = NULL; + return 0; } - lldp->port->event_priority = priority; + l = new0(sd_lldp_neighbor*, hashmap_size(lldp->neighbor_by_id)); + if (!l) + return -ENOMEM; - return 0; -} + r = lldp_start_timer(lldp, NULL); + if (r < 0) { + free(l); + return r; + } -int sd_lldp_detach_event(sd_lldp *lldp) { + HASHMAP_FOREACH(n, lldp->neighbor_by_id, i) + l[k++] = sd_lldp_neighbor_ref(n); - assert_return(lldp, -EINVAL); + assert((size_t) k == hashmap_size(lldp->neighbor_by_id)); - lldp->port->event = sd_event_unref(lldp->port->event); + /* Return things in a stable order */ + qsort(l, k, sizeof(sd_lldp_neighbor*), neighbor_compare_func); + *ret = l; - return 0; + return k; } -int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_cb_t cb, void *userdata) { +_public_ int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t m) { assert_return(lldp, -EINVAL); + assert_return(m <= 0, -EINVAL); - lldp->cb = cb; - lldp->userdata = userdata; + lldp->neighbors_max = m; + lldp_make_space(lldp, 0); return 0; } -sd_lldp* sd_lldp_unref(sd_lldp *lldp) { - - if (!lldp) - return NULL; - - /* Drop all packets */ - lldp_mib_objects_flush(lldp); - - lldp_port_free(lldp->port); - - hashmap_free(lldp->neighbour_mib); - prioq_free(lldp->by_expiry); - - free(lldp); - return NULL; -} - -int sd_lldp_new(int ifindex, - const char *ifname, - const struct ether_addr *mac, - sd_lldp **ret) { - _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL; - int r; - - assert_return(ret, -EINVAL); - assert_return(ifindex > 0, -EINVAL); - assert_return(ifname, -EINVAL); - assert_return(mac, -EINVAL); - - lldp = new0(sd_lldp, 1); - if (!lldp) - return -ENOMEM; - - r = lldp_port_new(ifindex, ifname, mac, lldp, &lldp->port); - if (r < 0) - return r; - - lldp->neighbour_mib = hashmap_new(&chassis_id_hash_ops); - if (!lldp->neighbour_mib) - return -ENOMEM; - - r = prioq_ensure_allocated(&lldp->by_expiry, - ttl_expiry_item_prioq_compare_func); - if (r < 0) - return r; - - lldp->rx_state = LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL; +_public_ int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask) { + assert_return(lldp, -EINVAL); + assert_return(mask != 0, -EINVAL); - *ret = lldp; - lldp = NULL; + lldp->capability_mask = mask; return 0; } -int sd_lldp_get_packets(sd_lldp *lldp, sd_lldp_packet ***tlvs) { - lldp_neighbour_port *p; - lldp_chassis *c; - Iterator iter; - unsigned count = 0, i; - +_public_ int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *addr) { assert_return(lldp, -EINVAL); - assert_return(tlvs, -EINVAL); - HASHMAP_FOREACH(c, lldp->neighbour_mib, iter) { - LIST_FOREACH(port, p, c->ports) - count++; - } + /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so + * that our own can be filtered out here. */ - if (!count) { - *tlvs = NULL; + if (!addr) { + zero(lldp->filter_address); return 0; } - *tlvs = new(sd_lldp_packet *, count); - if (!*tlvs) - return -ENOMEM; - - i = 0; - HASHMAP_FOREACH(c, lldp->neighbour_mib, iter) { - LIST_FOREACH(port, p, c->ports) - (*tlvs)[i++] = sd_lldp_packet_ref(p->packet); - } - - return count; + lldp->filter_address = *addr; + return 0; } diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c index 519d2aa36b..fb4ef55673 100644 --- a/src/libsystemd-network/sd-ndisc.c +++ b/src/libsystemd-network/sd-ndisc.c @@ -166,7 +166,7 @@ int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr) { } -int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int priority) { +int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority) { int r; assert_return(nd, -EINVAL); @@ -491,19 +491,16 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r struct cmsghdr *cmsg; struct in6_addr *gw; unsigned lifetime; - ssize_t len; - int r, pref, stateful, buflen = 0; + ssize_t len, buflen; + int r, pref, stateful; assert(s); assert(nd); assert(nd->event); - r = ioctl(fd, FIONREAD, &buflen); - if (r < 0) - return -errno; - else if (buflen < 0) - /* This really should not happen */ - return -EIO; + buflen = next_datagram_size_fd(fd); + if (buflen < 0) + return buflen; iov.iov_len = buflen; diff --git a/src/libsystemd-network/test-dhcp-option.c b/src/libsystemd-network/test-dhcp-option.c index 7d8a957227..d84859c053 100644 --- a/src/libsystemd-network/test-dhcp-option.c +++ b/src/libsystemd-network/test-dhcp-option.c @@ -110,14 +110,9 @@ static DHCPMessage *create_message(uint8_t *options, uint16_t optlen, message = malloc0(len); assert_se(message); - if (options && optlen) - memcpy(&message->options, options, optlen); - - if (file && filelen <= 128) - memcpy(&message->file, file, filelen); - - if (sname && snamelen <= 64) - memcpy(&message->sname, sname, snamelen); + memcpy_safe(&message->options, options, optlen); + memcpy_safe(&message->file, file, filelen); + memcpy_safe(&message->sname, sname, snamelen); return message; } diff --git a/src/libsystemd-network/test-lldp.c b/src/libsystemd-network/test-lldp.c index b8490073dd..1aae2253c0 100644 --- a/src/libsystemd-network/test-lldp.c +++ b/src/libsystemd-network/test-lldp.c @@ -22,6 +22,7 @@ #include <net/ethernet.h> #include <stdio.h> #include <string.h> +#include <unistd.h> #include "sd-event.h" #include "sd-lldp.h" @@ -29,8 +30,6 @@ #include "alloc-util.h" #include "fd-util.h" #include "lldp-network.h" -#include "lldp-tlv.h" -#include "lldp.h" #include "macro.h" #include "string-util.h" @@ -38,211 +37,8 @@ #define TEST_LLDP_TYPE_SYSTEM_NAME "systemd-lldp" #define TEST_LLDP_TYPE_SYSTEM_DESC "systemd-lldp-desc" -static int test_fd[2]; - -static struct ether_addr mac_addr = { - .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'} -}; - -static int lldp_build_tlv_packet(tlv_packet **ret) { - _cleanup_(sd_lldp_packet_unrefp) tlv_packet *m = NULL; - const uint8_t lldp_dst[] = LLDP_MULTICAST_ADDR; - struct ether_header ether = { - .ether_type = htons(ETHERTYPE_LLDP), - }; - - /* Append Ethernet header */ - memcpy(ðer.ether_dhost, lldp_dst, ETHER_ADDR_LEN); - memcpy(ðer.ether_shost, &mac_addr, ETHER_ADDR_LEN); - - assert_se(tlv_packet_new(&m) >= 0); - - assert_se(tlv_packet_append_bytes(m, ðer, sizeof(struct ether_header)) >= 0); - - assert_se(lldp_tlv_packet_open_container(m, LLDP_TYPE_CHASSIS_ID) >= 0); - - assert_se(tlv_packet_append_u8(m, LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS) >= 0); - assert_se(tlv_packet_append_bytes(m, &mac_addr, ETHER_ADDR_LEN) >= 0); - - assert_se(lldp_tlv_packet_close_container(m) >= 0); - - /* port name */ - assert_se(lldp_tlv_packet_open_container(m, LLDP_TYPE_PORT_ID) >= 0); - - assert_se(tlv_packet_append_u8(m, LLDP_PORT_SUBTYPE_INTERFACE_NAME) >= 0); - assert_se(tlv_packet_append_bytes(m, TEST_LLDP_PORT, strlen(TEST_LLDP_PORT) + 1) >= 0); - - assert_se(lldp_tlv_packet_close_container(m) >= 0); - - /* ttl */ - assert_se(lldp_tlv_packet_open_container(m, LLDP_TYPE_TTL) >= 0); - - assert_se(tlv_packet_append_u16(m, 170) >= 0); - - assert_se(lldp_tlv_packet_close_container(m) >= 0); - - /* system name */ - assert_se(lldp_tlv_packet_open_container(m, LLDP_TYPE_SYSTEM_NAME) >= 0); - - assert_se(tlv_packet_append_bytes(m, TEST_LLDP_TYPE_SYSTEM_NAME, - strlen(TEST_LLDP_TYPE_SYSTEM_NAME)) >= 0); - assert_se(lldp_tlv_packet_close_container(m) >= 0); - - /* system descrition */ - assert_se(lldp_tlv_packet_open_container(m, LLDP_TYPE_SYSTEM_DESCRIPTION) >= 0); - - assert_se(tlv_packet_append_bytes(m, TEST_LLDP_TYPE_SYSTEM_DESC, - strlen(TEST_LLDP_TYPE_SYSTEM_DESC)) >= 0); - - assert_se(lldp_tlv_packet_close_container(m) >= 0); - - /* Mark end of packet */ - assert_se(lldp_tlv_packet_open_container(m, LLDP_TYPE_END) >= 0); - assert_se(lldp_tlv_packet_close_container(m) >= 0); - - *ret = m; - - m = NULL; - - return 0; -} - -static int lldp_parse_chassis_tlv(tlv_packet *m, uint8_t *type) { - uint8_t *p, subtype; - uint16_t length; - - assert_se(lldp_tlv_packet_enter_container(m, LLDP_TYPE_CHASSIS_ID) >= 0); - assert_se(tlv_packet_read_u8(m, &subtype) >= 0); - - switch (subtype) { - case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS: - - *type = LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS; - assert_se(tlv_packet_read_bytes(m, &p, &length) >= 0); - - assert_se(memcmp(p, &mac_addr.ether_addr_octet, ETHER_ADDR_LEN) == 0); - - break; - default: - assert_not_reached("Unhandled option"); - } - - assert_se(lldp_tlv_packet_exit_container(m) >= 0); - - return 0; -} - -static int lldp_parse_port_id_tlv(tlv_packet *m) { - _cleanup_free_ char *p = NULL; - char *str = NULL; - uint16_t length; - uint8_t subtype; - - assert_se(lldp_tlv_packet_enter_container(m, LLDP_TYPE_PORT_ID) >= 0); - - assert_se(tlv_packet_read_u8(m, &subtype) >= 0); - - switch (subtype) { - case LLDP_PORT_SUBTYPE_INTERFACE_NAME: - assert_se(tlv_packet_read_string(m, &str, &length) >= 0); - - p = strndup(str, length-1); - assert_se(p); - - assert_se(streq(p, TEST_LLDP_PORT) == 1); - break; - default: - assert_not_reached("Unhandled option"); - } - - assert_se(lldp_tlv_packet_exit_container(m) >= 0); - - return 0; -} - -static int lldp_parse_system_name_tlv(tlv_packet *m) { - _cleanup_free_ char *p = NULL; - char *str = NULL; - uint16_t length; - - assert_se(lldp_tlv_packet_enter_container(m, LLDP_TYPE_SYSTEM_NAME) >= 0); - assert_se(tlv_packet_read_string(m, &str, &length) >= 0); - - p = strndup(str, length); - assert_se(p); - - assert_se(streq(p, TEST_LLDP_TYPE_SYSTEM_NAME) == 1); - - assert_se(lldp_tlv_packet_exit_container(m) >= 0); - - return 1; -} - -static int lldp_parse_system_desc_tlv(tlv_packet *m) { - _cleanup_free_ char *p = NULL; - char *str = NULL; - uint16_t length; - - assert_se(lldp_tlv_packet_enter_container(m, LLDP_TYPE_SYSTEM_DESCRIPTION) >= 0); - assert_se(tlv_packet_read_string(m, &str, &length) >= 0); - - p = strndup(str, length); - assert_se(p); - - assert_se(streq(p, TEST_LLDP_TYPE_SYSTEM_DESC) == 1); - - assert_se(lldp_tlv_packet_exit_container(m) >= 0); - - return 0; -} - -static int lldp_parse_ttl_tlv(tlv_packet *m) { - uint16_t ttl; - - assert_se(lldp_tlv_packet_enter_container(m, LLDP_TYPE_TTL) >= 0); - assert_se(tlv_packet_read_u16(m, &ttl) >= 0); - - assert_se(ttl == 170); - - assert_se(lldp_tlv_packet_exit_container(m) >= 0); - - return 0; -} - -static int lldp_get_destination_type(tlv_packet *m) { - int dest; - - assert_se(sd_lldp_packet_get_destination_type(m, &dest) >= 0); - assert_se(dest == SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE); - - return 0; -} - -static int lldp_parse_tlv_packet(tlv_packet *m, int len) { - uint8_t subtype; - - assert_se(tlv_packet_parse_pdu(m, len) >= 0); - assert_se(lldp_parse_chassis_tlv(m, &subtype) >= 0); - assert_se(lldp_parse_port_id_tlv(m) >= 0); - assert_se(lldp_parse_system_name_tlv(m) >= 0); - assert_se(lldp_parse_ttl_tlv(m) >= 0); - assert_se(lldp_parse_system_desc_tlv(m) >= 0); - - assert_se(lldp_get_destination_type(m) >= 0); - - return 0; -} - -static void test_parser(void) { - _cleanup_(sd_lldp_packet_unrefp) tlv_packet *tlv = NULL; - - /* form a packet */ - lldp_build_tlv_packet(&tlv); - /* parse the packet */ - tlv_packet_parse_pdu(tlv, tlv->length); - /* verify */ - lldp_parse_tlv_packet(tlv, tlv->length); -} +static int test_fd[2] = { -1, -1 }; +static int lldp_handler_calls; int lldp_network_bind_raw_socket(int ifindex) { if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, test_fd) < 0) @@ -251,28 +47,27 @@ int lldp_network_bind_raw_socket(int ifindex) { return test_fd[0]; } -static int lldp_handler_calls; -static void lldp_handler (sd_lldp *lldp, int event, void *userdata) { +static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata) { lldp_handler_calls++; } -static int start_lldp(sd_lldp **lldp, sd_event *e, sd_lldp_cb_t cb, void *cb_data) { +static int start_lldp(sd_lldp **lldp, sd_event *e, sd_lldp_callback_t cb, void *cb_data) { int r; - r = sd_lldp_new(42, "dummy", &mac_addr, lldp); - if (r) + r = sd_lldp_new(lldp, 42); + if (r < 0) return r; r = sd_lldp_attach_event(*lldp, e, 0); - if (r) + if (r < 0) return r; r = sd_lldp_set_callback(*lldp, cb, cb_data); - if (r) + if (r < 0) return r; r = sd_lldp_start(*lldp); - if (r) + if (r < 0) return r; return 0; @@ -282,11 +77,11 @@ static int stop_lldp(sd_lldp *lldp) { int r; r = sd_lldp_stop(lldp); - if (r) + if (r < 0) return r; r = sd_lldp_detach_event(lldp); - if (r) + if (r < 0) return r; sd_lldp_unref(lldp); @@ -296,13 +91,8 @@ static int stop_lldp(sd_lldp *lldp) { } static void test_receive_basic_packet(sd_event *e) { - sd_lldp *lldp; - sd_lldp_packet **packets; - uint8_t type, *data; - uint16_t length, ttl; - int dest_type; - char *str; - uint8_t frame[] = { + + static const uint8_t frame[] = { /* Ethernet header */ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC*/ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, /* Source MAC */ @@ -319,51 +109,53 @@ static void test_receive_basic_packet(sd_event *e) { 0x00, 0x00 /* End Of LLDPDU */ }; + sd_lldp *lldp; + sd_lldp_neighbor **neighbors; + uint8_t type; + const void *data; + uint16_t ttl; + size_t length; + const char *str; + lldp_handler_calls = 0; assert_se(start_lldp(&lldp, e, lldp_handler, NULL) == 0); assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame)); sd_event_run(e, 0); assert_se(lldp_handler_calls == 1); - assert_se(sd_lldp_get_packets(lldp, &packets) == 1); + assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 1); - assert_se(sd_lldp_packet_read_chassis_id(packets[0], &type, &data, &length) == 0); - assert_se(type == LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS); + assert_se(sd_lldp_neighbor_get_chassis_id(neighbors[0], &type, &data, &length) == 0); + assert_se(type == SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS); assert_se(length == ETH_ALEN); assert_se(!memcmp(data, "\x00\x01\x02\x03\x04\x05", ETH_ALEN)); - assert_se(sd_lldp_packet_read_port_id(packets[0], &type, &data, &length) == 0); - assert_se(type == LLDP_PORT_SUBTYPE_INTERFACE_NAME); + assert_se(sd_lldp_neighbor_get_port_id(neighbors[0], &type, &data, &length) == 0); + assert_se(type == SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME); assert_se(length == 3); assert_se(strneq((char *) data, "1/3", 3)); - assert_se(sd_lldp_packet_read_port_description(packets[0], &str, &length) == 0); - assert_se(length == 4); - assert_se(strneq(str, "Port", 4)); + assert_se(sd_lldp_neighbor_get_port_description(neighbors[0], &str) == 0); + assert_se(streq(str, "Port")); - assert_se(sd_lldp_packet_read_system_name(packets[0], &str, &length) == 0); - assert_se(length == 3); - assert_se(strneq(str, "SYS", 3)); + assert_se(sd_lldp_neighbor_get_system_name(neighbors[0], &str) == 0); + assert_se(streq(str, "SYS")); - assert_se(sd_lldp_packet_read_system_description(packets[0], &str, &length) == 0); - assert_se(length == 4); /* This is the real length in the TLV packet */ - assert_se(strneq(str, "foo", 3)); + assert_se(sd_lldp_neighbor_get_system_description(neighbors[0], &str) == 0); + assert_se(streq(str, "foo")); - assert_se(sd_lldp_packet_read_ttl(packets[0], &ttl) == 0); + assert_se(sd_lldp_neighbor_get_ttl(neighbors[0], &ttl) == 0); assert_se(ttl == 120); - assert_se(sd_lldp_packet_get_destination_type(packets[0], &dest_type) == 0); - assert_se(dest_type == SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE); - - sd_lldp_packet_unref(packets[0]); - free(packets); + sd_lldp_neighbor_unref(neighbors[0]); + free(neighbors); assert_se(stop_lldp(lldp) == 0); } static void test_receive_incomplete_packet(sd_event *e) { sd_lldp *lldp; - sd_lldp_packet **packets; + sd_lldp_neighbor **neighbors; uint8_t frame[] = { /* Ethernet header */ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC*/ @@ -383,18 +175,14 @@ static void test_receive_incomplete_packet(sd_event *e) { assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame)); sd_event_run(e, 0); assert_se(lldp_handler_calls == 0); - assert_se(sd_lldp_get_packets(lldp, &packets) == 0); + assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 0); assert_se(stop_lldp(lldp) == 0); } static void test_receive_oui_packet(sd_event *e) { sd_lldp *lldp; - sd_lldp_packet **packets; - uint32_t id32; - uint16_t id16, len; - uint8_t flags; - char *str; + sd_lldp_neighbor **neighbors; uint8_t frame[] = { /* Ethernet header */ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, /* Destination MAC*/ @@ -426,29 +214,30 @@ static void test_receive_oui_packet(sd_event *e) { assert_se(write(test_fd[1], frame, sizeof(frame)) == sizeof(frame)); sd_event_run(e, 0); assert_se(lldp_handler_calls == 1); - assert_se(sd_lldp_get_packets(lldp, &packets) == 1); - - assert_se(sd_lldp_packet_read_port_vlan_id(packets[0], &id16) == 0); - assert_se(id16 == 0x1234); - - assert_se(sd_lldp_packet_read_port_protocol_vlan_id(packets[0], &flags, &id16) == 0); - assert_se(flags == 1); - assert_se(id16 == 0x7788); - - assert_se(sd_lldp_packet_read_vlan_name(packets[0], &id16, &str, &len) == 0); - assert_se(id16 == 0x1234); - assert_se(len == 6); - assert_se(strneq(str, "Vlan51", 6)); - - assert_se(sd_lldp_packet_read_management_vid(packets[0], &id16) == 0); - assert_se(id16 == 0x0102); - - assert_se(sd_lldp_packet_read_link_aggregation(packets[0], &flags, &id32) == 0); - assert_se(flags == 1); - assert_se(id32 == 0x00140012); - - sd_lldp_packet_unref(packets[0]); - free(packets); + assert_se(sd_lldp_get_neighbors(lldp, &neighbors) == 1); + + assert_se(sd_lldp_neighbor_tlv_rewind(neighbors[0]) >= 0); + assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_CHASSIS_ID) > 0); + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); + assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_PORT_ID) > 0); + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); + assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_TTL) > 0); + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); + assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID) > 0); + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); + assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID) > 0); + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); + assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME) > 0); + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); + assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID) > 0); + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); + assert_se(sd_lldp_neighbor_tlv_is_oui(neighbors[0], SD_LLDP_OUI_802_1, SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION) > 0); + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) > 0); + assert_se(sd_lldp_neighbor_tlv_is_type(neighbors[0], SD_LLDP_TYPE_END) > 0); + assert_se(sd_lldp_neighbor_tlv_next(neighbors[0]) == 0); + + sd_lldp_neighbor_unref(neighbors[0]); + free(neighbors); assert_se(stop_lldp(lldp) == 0); } @@ -456,7 +245,7 @@ static void test_receive_oui_packet(sd_event *e) { int main(int argc, char *argv[]) { _cleanup_(sd_event_unrefp) sd_event *e = NULL; - test_parser(); + log_set_max_level(LOG_DEBUG); /* LLDP reception tests */ assert_se(sd_event_new(&e) == 0); diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index 4ab637b686..0b3a1708dc 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -489,3 +489,9 @@ global: sd_journal_enumerate_fields; sd_journal_restart_fields; } LIBSYSTEMD_227; + +LIBSYSTEMD_230 { +global: + sd_journal_open_directory_fd; + sd_journal_open_files_fd; +} LIBSYSTEMD_229; diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c index 3c19f2b108..02e3bf904c 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ b/src/libsystemd/sd-bus/bus-common-errors.c @@ -38,6 +38,8 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, EDEADLK), SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, EDEADLK), SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_MASKED, ESHUTDOWN), + SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_GENERATED, EADDRNOTAVAIL), + SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_LINKED, ELOOP), SD_BUS_ERROR_MAP(BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, EBADR), SD_BUS_ERROR_MAP(BUS_ERROR_NO_ISOLATION, EPERM), SD_BUS_ERROR_MAP(BUS_ERROR_SHUTTING_DOWN, ECANCELED), @@ -68,10 +70,8 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { SD_BUS_ERROR_MAP(BUS_ERROR_NO_NAME_SERVERS, ESRCH), SD_BUS_ERROR_MAP(BUS_ERROR_INVALID_REPLY, EINVAL), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_RR, ENOENT), - SD_BUS_ERROR_MAP(BUS_ERROR_NO_RESOURCES, ENOMEM), SD_BUS_ERROR_MAP(BUS_ERROR_CNAME_LOOP, EDEADLK), SD_BUS_ERROR_MAP(BUS_ERROR_ABORTED, ECANCELED), - SD_BUS_ERROR_MAP(BUS_ERROR_CONNECTION_FAILURE, ECONNREFUSED), SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SERVICE, EUNATCH), SD_BUS_ERROR_MAP(BUS_ERROR_DNSSEC_FAILED, EHOSTUNREACH), SD_BUS_ERROR_MAP(BUS_ERROR_NO_TRUST_ANCHOR, EHOSTUNREACH), diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index fab8748f46..c8f369cb78 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -34,6 +34,8 @@ #define BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC "org.freedesktop.systemd1.TransactionOrderIsCyclic" #define BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE "org.freedesktop.systemd1.TransactionIsDestructive" #define BUS_ERROR_UNIT_MASKED "org.freedesktop.systemd1.UnitMasked" +#define BUS_ERROR_UNIT_GENERATED "org.freedesktop.systemd1.UnitGenerated" +#define BUS_ERROR_UNIT_LINKED "org.freedesktop.systemd1.UnitLinked" #define BUS_ERROR_JOB_TYPE_NOT_APPLICABLE "org.freedesktop.systemd1.JobTypeNotApplicable" #define BUS_ERROR_NO_ISOLATION "org.freedesktop.systemd1.NoIsolation" #define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown" @@ -67,10 +69,8 @@ #define BUS_ERROR_NO_NAME_SERVERS "org.freedesktop.resolve1.NoNameServers" #define BUS_ERROR_INVALID_REPLY "org.freedesktop.resolve1.InvalidReply" #define BUS_ERROR_NO_SUCH_RR "org.freedesktop.resolve1.NoSuchRR" -#define BUS_ERROR_NO_RESOURCES "org.freedesktop.resolve1.NoResources" #define BUS_ERROR_CNAME_LOOP "org.freedesktop.resolve1.CNameLoop" #define BUS_ERROR_ABORTED "org.freedesktop.resolve1.Aborted" -#define BUS_ERROR_CONNECTION_FAILURE "org.freedesktop.resolve1.ConnectionFailure" #define BUS_ERROR_NO_SUCH_SERVICE "org.freedesktop.resolve1.NoSuchService" #define BUS_ERROR_DNSSEC_FAILED "org.freedesktop.resolve1.DnssecFailed" #define BUS_ERROR_NO_TRUST_ANCHOR "org.freedesktop.resolve1.NoTrustAnchor" diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c index 05222b8d30..52128e7b5c 100644 --- a/src/libsystemd/sd-bus/bus-control.c +++ b/src/libsystemd/sd-bus/bus-control.c @@ -678,6 +678,7 @@ int bus_get_name_creds_kdbus( (mask & (SD_BUS_CREDS_PPID| SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|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_TID_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| @@ -795,6 +796,7 @@ static int bus_get_name_creds_dbus1( ((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| @@ -947,6 +949,7 @@ static int bus_get_owner_creds_kdbus(sd_bus *bus, uint64_t mask, sd_bus_creds ** (mask & (SD_BUS_CREDS_PPID| SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|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_TID_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| @@ -1131,8 +1134,7 @@ static int add_name_change_match(sd_bus *bus, item->name_change.old_id.id = old_owner_id; item->name_change.new_id.id = new_owner_id; - if (name) - memcpy(item->name_change.name, name, l); + memcpy_safe(item->name_change.name, name, l); /* If the old name is unset or empty, then * this can match against added names */ diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c index 7c81e7a25d..21a6b20a11 100644 --- a/src/libsystemd/sd-bus/bus-dump.c +++ b/src/libsystemd/sd-bus/bus-dump.c @@ -74,7 +74,7 @@ int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags) { "%s%s%s Type=%s%s%s Endian=%c Flags=%u Version=%u Priority=%"PRIi64, m->header->type == SD_BUS_MESSAGE_METHOD_ERROR ? ansi_highlight_red() : m->header->type == SD_BUS_MESSAGE_METHOD_RETURN ? ansi_highlight_green() : - m->header->type != SD_BUS_MESSAGE_SIGNAL ? ansi_highlight() : "", draw_special_char(DRAW_TRIANGULAR_BULLET), ansi_normal(), + m->header->type != SD_BUS_MESSAGE_SIGNAL ? ansi_highlight() : "", special_glyph(TRIANGULAR_BULLET), ansi_normal(), ansi_highlight(), bus_message_type_to_string(m->header->type), ansi_normal(), m->header->endian, m->header->flags, @@ -198,7 +198,7 @@ int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags) { else if (type == SD_BUS_TYPE_DICT_ENTRY) fprintf(f, "%sDICT_ENTRY \"%s\" {\n", prefix, contents); - level ++; + level++; continue; } diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c index 0896eeb177..59398b841d 100644 --- a/src/libsystemd/sd-bus/bus-kernel.c +++ b/src/libsystemd/sd-bus/bus-kernel.c @@ -1693,50 +1693,6 @@ int bus_kernel_open_bus_fd(const char *bus, char **path) { return fd; } -int bus_kernel_create_endpoint(const char *bus_name, const char *ep_name, char **ep_path) { - _cleanup_free_ char *path = NULL; - struct kdbus_cmd *make; - struct kdbus_item *n; - const char *name; - int fd; - - fd = bus_kernel_open_bus_fd(bus_name, &path); - if (fd < 0) - return fd; - - make = alloca0_align(ALIGN8(offsetof(struct kdbus_cmd, items)) + - ALIGN8(offsetof(struct kdbus_item, str) + DECIMAL_STR_MAX(uid_t) + 1 + strlen(ep_name) + 1), - 8); - make->size = ALIGN8(offsetof(struct kdbus_cmd, items)); - make->flags = KDBUS_MAKE_ACCESS_WORLD; - - n = make->items; - sprintf(n->str, UID_FMT "-%s", getuid(), ep_name); - n->size = offsetof(struct kdbus_item, str) + strlen(n->str) + 1; - n->type = KDBUS_ITEM_MAKE_NAME; - make->size += ALIGN8(n->size); - name = n->str; - - if (ioctl(fd, KDBUS_CMD_ENDPOINT_MAKE, make) < 0) { - safe_close(fd); - return -errno; - } - - if (ep_path) { - char *p; - - p = strjoin(dirname(path), "/", name, NULL); - if (!p) { - safe_close(fd); - return -ENOMEM; - } - - *ep_path = p; - } - - return fd; -} - int bus_kernel_try_close(sd_bus *bus) { struct kdbus_cmd byebye = { .size = sizeof(byebye) }; diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c index 7be28c509b..b8958ec7bb 100644 --- a/src/libsystemd/sd-bus/bus-message.c +++ b/src/libsystemd/sd-bus/bus-message.c @@ -1131,10 +1131,7 @@ _public_ int sd_bus_message_set_expect_reply(sd_bus_message *m, int b) { assert_return(!m->sealed, -EPERM); assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EPERM); - if (b) - m->header->flags &= ~BUS_MESSAGE_NO_REPLY_EXPECTED; - else - m->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED; + SET_FLAG(m->header->flags, BUS_MESSAGE_NO_REPLY_EXPECTED, !b); return 0; } @@ -1143,10 +1140,7 @@ _public_ int sd_bus_message_set_auto_start(sd_bus_message *m, int b) { assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); - if (b) - m->header->flags &= ~BUS_MESSAGE_NO_AUTO_START; - else - m->header->flags |= BUS_MESSAGE_NO_AUTO_START; + SET_FLAG(m->header->flags, BUS_MESSAGE_NO_AUTO_START, !b); return 0; } @@ -1155,10 +1149,7 @@ _public_ int sd_bus_message_set_allow_interactive_authorization(sd_bus_message * assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); - if (b) - m->header->flags |= BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION; - else - m->header->flags &= ~BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION; + SET_FLAG(m->header->flags, BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION, b); return 0; } @@ -1198,7 +1189,7 @@ struct bus_body_part *message_append_part(sd_bus_message *m) { part->memfd = -1; m->body_end = part; - m->n_body_parts ++; + m->n_body_parts++; return part; } @@ -1643,7 +1634,7 @@ int message_append_basic(sd_bus_message *m, char type, const void *p, const void } if (type == SD_BUS_TYPE_UNIX_FD) - m->n_fds ++; + m->n_fds++; if (c->enclosing != SD_BUS_TYPE_ARRAY) c->index++; @@ -2387,9 +2378,9 @@ int bus_message_append_ap( t = types; if (n_array != (unsigned) -1) - n_array --; + n_array--; else { - types ++; + types++; n_struct--; } @@ -2631,8 +2622,7 @@ _public_ int sd_bus_message_append_array( if (r < 0) return r; - if (size > 0) - memcpy(p, ptr, size); + memcpy_safe(p, ptr, size); return 0; } @@ -3867,7 +3857,7 @@ static int build_struct_offsets( if (r < 0) return r; if (r == 0 && p[n] != 0) /* except the last item */ - n_variable ++; + n_variable++; n_total++; p += n; @@ -4467,9 +4457,9 @@ static int message_read_ap( t = types; if (n_array != (unsigned) -1) - n_array --; + n_array--; else { - types ++; + types++; n_struct--; } diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c index 1f285ae8a6..9bd07ffcab 100644 --- a/src/libsystemd/sd-bus/bus-objects.c +++ b/src/libsystemd/sd-bus/bus-objects.c @@ -145,7 +145,7 @@ static int add_enumerated_to_set( continue; } - if (!object_path_is_valid(*k)){ + if (!object_path_is_valid(*k)) { free(*k); r = -EINVAL; continue; @@ -337,7 +337,7 @@ static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, if (cap == 0) cap = CAP_SYS_ADMIN; else - cap --; + cap--; r = sd_bus_query_sender_privilege(m, cap); if (r < 0) diff --git a/src/libsystemd/sd-bus/bus-slot.c b/src/libsystemd/sd-bus/bus-slot.c index a8c74011bf..8e9074c7df 100644 --- a/src/libsystemd/sd-bus/bus-slot.c +++ b/src/libsystemd/sd-bus/bus-slot.c @@ -206,7 +206,7 @@ _public_ sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) { assert(slot->n_ref > 0); if (slot->n_ref > 1) { - slot->n_ref --; + slot->n_ref--; return NULL; } diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c index a2fb391239..f1e2a06050 100644 --- a/src/libsystemd/sd-bus/bus-socket.c +++ b/src/libsystemd/sd-bus/bus-socket.c @@ -60,7 +60,7 @@ static void iovec_advance(struct iovec iov[], unsigned *idx, size_t size) { i->iov_base = NULL; i->iov_len = 0; - (*idx) ++; + (*idx)++; } } @@ -350,7 +350,7 @@ static int bus_socket_auth_write(sd_bus *b, const char *t) { if (!p) return -ENOMEM; - memcpy(p, b->auth_iovec[0].iov_base, b->auth_iovec[0].iov_len); + memcpy_safe(p, b->auth_iovec[0].iov_base, b->auth_iovec[0].iov_len); memcpy(p + b->auth_iovec[0].iov_len, t, l); b->auth_iovec[0].iov_base = p; @@ -787,7 +787,7 @@ int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) { n = m->n_iovec * sizeof(struct iovec); iov = alloca(n); - memcpy(iov, m->iovec, n); + memcpy_safe(iov, m->iovec, n); j = 0; iovec_advance(iov, &j, *idx); @@ -998,7 +998,7 @@ int bus_socket_read_message(sd_bus *bus) { return -ENOMEM; } - memcpy(f + bus->n_fds, CMSG_DATA(cmsg), n * sizeof(int)); + memcpy_safe(f + bus->n_fds, CMSG_DATA(cmsg), n * sizeof(int)); bus->fds = f; bus->n_fds += n; } else diff --git a/src/libsystemd/sd-bus/bus-track.c b/src/libsystemd/sd-bus/bus-track.c index bdbf7d4a85..1f436fe560 100644 --- a/src/libsystemd/sd-bus/bus-track.c +++ b/src/libsystemd/sd-bus/bus-track.c @@ -129,7 +129,7 @@ _public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) { assert(track->n_ref > 0); if (track->n_ref > 1) { - track->n_ref --; + track->n_ref--; return NULL; } diff --git a/src/libsystemd/sd-bus/busctl.c b/src/libsystemd/sd-bus/busctl.c index 35fabf038c..bfe967bfb0 100644 --- a/src/libsystemd/sd-bus/busctl.c +++ b/src/libsystemd/sd-bus/busctl.c @@ -62,15 +62,6 @@ static bool arg_allow_interactive_authorization = true; static bool arg_augment_creds = true; static usec_t arg_timeout = 0; -static void pager_open_if_enabled(void) { - - /* Cache result before we open the pager */ - if (arg_no_pager) - return; - - pager_open(false); -} - #define NAME_IS_ACQUIRED INT_TO_PTR(1) #define NAME_IS_ACTIVATABLE INT_TO_PTR(2) @@ -95,7 +86,7 @@ static int list_bus_names(sd_bus *bus, char **argv) { if (r < 0) return log_error_errno(r, "Failed to list names: %m"); - pager_open_if_enabled(); + pager_open(arg_no_pager, false); names = hashmap_new(&string_hash_ops); if (!names) @@ -258,8 +249,8 @@ static void print_subtree(const char *prefix, const char *path, char **l) { l++; } - vertical = strjoina(prefix, draw_special_char(DRAW_TREE_VERTICAL)); - space = strjoina(prefix, draw_special_char(DRAW_TREE_SPACE)); + vertical = strjoina(prefix, special_glyph(TREE_VERTICAL)); + space = strjoina(prefix, special_glyph(TREE_SPACE)); for (;;) { bool has_more = false; @@ -280,7 +271,7 @@ static void print_subtree(const char *prefix, const char *path, char **l) { n++; } - printf("%s%s%s\n", prefix, draw_special_char(has_more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT), *l); + printf("%s%s%s\n", prefix, special_glyph(has_more ? TREE_BRANCH : TREE_RIGHT), *l); print_subtree(has_more ? vertical : space, *l, l); l = n; @@ -289,7 +280,7 @@ static void print_subtree(const char *prefix, const char *path, char **l) { static void print_tree(const char *prefix, char **l) { - pager_open_if_enabled(); + pager_open(arg_no_pager, false); prefix = strempty(prefix); @@ -409,7 +400,7 @@ static int tree_one(sd_bus *bus, const char *service, const char *prefix, bool m p = NULL; } - pager_open_if_enabled(); + pager_open(arg_no_pager, false); l = set_get_strv(done); if (!l) @@ -438,7 +429,7 @@ static int tree(sd_bus *bus, char **argv) { if (r < 0) return log_error_errno(r, "Failed to get name list: %m"); - pager_open_if_enabled(); + pager_open(arg_no_pager, false); STRV_FOREACH(i, names) { int q; @@ -468,7 +459,7 @@ static int tree(sd_bus *bus, char **argv) { printf("\n"); if (argv[2]) { - pager_open_if_enabled(); + pager_open(arg_no_pager, false); printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_normal()); } @@ -501,8 +492,10 @@ static int format_cmdline(sd_bus_message *m, FILE *f, bool needs_space) { } basic; r = sd_bus_message_peek_type(m, &type, &contents); - if (r <= 0) + if (r < 0) return r; + if (r == 0) + return needs_space; if (bus_type_is_container(type) > 0) { @@ -533,18 +526,23 @@ static int format_cmdline(sd_bus_message *m, FILE *f, bool needs_space) { fputc(' ', f); fprintf(f, "%u", n); + needs_space = true; + } else if (type == SD_BUS_TYPE_VARIANT) { if (needs_space) fputc(' ', f); fprintf(f, "%s", contents); + needs_space = true; } - r = format_cmdline(m, f, needs_space || IN_SET(type, SD_BUS_TYPE_ARRAY, SD_BUS_TYPE_VARIANT)); + r = format_cmdline(m, f, needs_space); if (r < 0) return r; + needs_space = r > 0; + r = sd_bus_message_exit_container(m); if (r < 0) return r; @@ -985,7 +983,7 @@ static int introspect(sd_bus *bus, char **argv) { return bus_log_parse_error(r); } - pager_open_if_enabled(); + pager_open(arg_no_pager, false); name_width = strlen("NAME"); type_width = strlen("TYPE"); @@ -1081,10 +1079,21 @@ static int message_pcap(sd_bus_message *m, FILE *f) { } static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FILE *f)) { - bool added_something = false; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; char **i; + uint32_t flags = 0; int r; + /* upgrade connection; it's not used for anything else after this call */ + r = sd_bus_message_new_method_call(bus, &message, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus.Monitoring", "BecomeMonitor"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(message, 'a', "s"); + if (r < 0) + return bus_log_create_error(r); + STRV_FOREACH(i, argv+1) { _cleanup_free_ char *m = NULL; @@ -1097,34 +1106,38 @@ static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FIL if (!m) return log_oom(); - r = sd_bus_add_match(bus, NULL, m, NULL, NULL); + r = sd_bus_message_append_basic(message, 's', m); if (r < 0) - return log_error_errno(r, "Failed to add match: %m"); + return bus_log_create_error(r); free(m); m = strjoin("destination='", *i, "'", NULL); if (!m) return log_oom(); - r = sd_bus_add_match(bus, NULL, m, NULL, NULL); + r = sd_bus_message_append_basic(message, 's', m); if (r < 0) - return log_error_errno(r, "Failed to add match: %m"); - - added_something = true; + return bus_log_create_error(r); } STRV_FOREACH(i, arg_matches) { - r = sd_bus_add_match(bus, NULL, *i, NULL, NULL); + r = sd_bus_message_append_basic(message, 's', *i); if (r < 0) - return log_error_errno(r, "Failed to add match: %m"); - - added_something = true; + return bus_log_create_error(r); } - if (!added_something) { - r = sd_bus_add_match(bus, NULL, "", NULL, NULL); - if (r < 0) - return log_error_errno(r, "Failed to add match: %m"); + r = sd_bus_message_close_container(message); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(message, 'u', &flags); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, message, arg_timeout, &error, NULL); + if (r < 0) { + log_error("%s", bus_error_message(&error, r)); + return r; } log_info("Monitoring bus message stream."); @@ -1552,7 +1565,7 @@ static int call(sd_bus *bus, char *argv[]) { if (r == 0 && !arg_quiet) { if (arg_verbose) { - pager_open_if_enabled(); + pager_open(arg_no_pager, false); r = bus_message_dump(reply, stdout, 0); if (r < 0) @@ -1607,7 +1620,7 @@ static int get_property(sd_bus *bus, char *argv[]) { return bus_log_parse_error(r); if (arg_verbose) { - pager_open_if_enabled(); + pager_open(arg_no_pager, false); r = bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY); if (r < 0) diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index c6f626d8aa..ed5f94e136 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -313,10 +313,7 @@ _public_ int sd_bus_negotiate_creds(sd_bus *bus, int b, uint64_t mask) { assert_return(!IN_SET(bus->state, BUS_CLOSING, BUS_CLOSED), -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); - if (b) - bus->creds_mask |= mask; - else - bus->creds_mask &= ~mask; + SET_FLAG(bus->creds_mask, mask, b); /* The well knowns we need unconditionally, so that matches can work */ bus->creds_mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME; @@ -530,7 +527,7 @@ static void skip_address_key(const char **p) { *p += strcspn(*p, ","); if (**p == ',') - (*p) ++; + (*p)++; } static int parse_unix_address(sd_bus *b, const char **p, char **guid) { @@ -695,7 +692,7 @@ static int parse_exec_address(sd_bus *b, const char **p, char **guid) { goto fail; } - (*p) ++; + (*p)++; if (ul >= n_argv) { if (!GREEDY_REALLOC0(argv, allocated, ul + 2)) { @@ -839,7 +836,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 = offsetof(struct sockaddr_un, sun_path) + strlen("/var/run/dbus/system_bus_socket"); + b->sockaddr_size = SOCKADDR_UN_LEN(b->sockaddr.un); return 0; } @@ -1668,7 +1665,7 @@ static int dispatch_wqueue(sd_bus *bus) { * it got full, then all bets are off * anyway. */ - bus->wqueue_size --; + bus->wqueue_size--; sd_bus_message_unref(bus->wqueue[0]); memmove(bus->wqueue, bus->wqueue + 1, sizeof(sd_bus_message*) * bus->wqueue_size); bus->windex = 0; @@ -1717,7 +1714,7 @@ static int dispatch_rqueue(sd_bus *bus, bool hint_priority, int64_t priority, sd /* Dispatch a queued message */ *m = bus->rqueue[0]; - bus->rqueue_size --; + bus->rqueue_size--; memmove(bus->rqueue, bus->rqueue + 1, sizeof(sd_bus_message*) * bus->rqueue_size); return 1; } @@ -1781,7 +1778,7 @@ static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie, r = bus_write_message(bus, m, hint_sync_call, &idx); if (r < 0) { - if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { bus_enter_closing(bus); return -ECONNRESET; } @@ -1809,7 +1806,7 @@ static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie, if (!GREEDY_REALLOC(bus->wqueue, bus->wqueue_allocated, bus->wqueue_size + 1)) return -ENOMEM; - bus->wqueue[bus->wqueue_size ++] = sd_bus_message_ref(m); + bus->wqueue[bus->wqueue_size++] = sd_bus_message_ref(m); } finish: @@ -2086,7 +2083,7 @@ _public_ int sd_bus_call( r = bus_read_message(bus, false, 0); if (r < 0) { - if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { bus_enter_closing(bus); r = -ECONNRESET; } @@ -2119,7 +2116,7 @@ _public_ int sd_bus_call( r = dispatch_wqueue(bus); if (r < 0) { - if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { bus_enter_closing(bus); r = -ECONNRESET; } @@ -2257,7 +2254,7 @@ static int process_timeout(sd_bus *bus) { slot = container_of(c, sd_bus_slot, reply_callback); - bus->iteration_counter ++; + bus->iteration_counter++; bus->current_message = m; bus->current_slot = sd_bus_slot_ref(slot); @@ -2769,7 +2766,7 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit case BUS_OPENING: r = bus_socket_process_opening(bus); - if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { bus_enter_closing(bus); r = 1; } else if (r < 0) @@ -2780,7 +2777,7 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit case BUS_AUTHENTICATING: r = bus_socket_process_authenticating(bus); - if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { bus_enter_closing(bus); r = 1; } else if (r < 0) @@ -2794,7 +2791,7 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit case BUS_RUNNING: case BUS_HELLO: r = process_running(bus, hint_priority, priority, ret); - if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { bus_enter_closing(bus); r = 1; @@ -2917,7 +2914,7 @@ _public_ int sd_bus_flush(sd_bus *bus) { for (;;) { r = dispatch_wqueue(bus); if (r < 0) { - if (r == -ENOTCONN || r == -ECONNRESET || r == -EPIPE || r == -ESHUTDOWN) { + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { bus_enter_closing(bus); return -ECONNRESET; } diff --git a/src/libsystemd/sd-bus/test-bus-error.c b/src/libsystemd/sd-bus/test-bus-error.c index 46d18abd29..66a3874f10 100644 --- a/src/libsystemd/sd-bus/test-bus-error.c +++ b/src/libsystemd/sd-bus/test-bus-error.c @@ -146,7 +146,7 @@ static void dump_mapping_table(void) { } printf("%s -> %i/%s\n", strna(m->name), m->code, strna(errno_to_name(m->code))); - m ++; + m++; } printf("---------------------------\n"); } diff --git a/src/libsystemd/sd-bus/test-bus-proxy.c b/src/libsystemd/sd-bus/test-bus-proxy.c deleted file mode 100644 index 45d0a5ffce..0000000000 --- a/src/libsystemd/sd-bus/test-bus-proxy.c +++ /dev/null @@ -1,117 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2015 David Herrmann <dh.herrmann@gmail.com> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <errno.h> -#include <fcntl.h> -#include <stdlib.h> - -#include "sd-bus.h" - -#include "alloc-util.h" -#include "bus-dump.h" -#include "bus-kernel.h" -#include "bus-util.h" -#include "log.h" -#include "util.h" - -typedef struct { - const char *sender; - int matched_acquired; -} TestProxyMatch; - -static int test_proxy_acquired(sd_bus_message *m, void *userdata, sd_bus_error *error) { - TestProxyMatch *match = userdata; - const char *name; - int r; - - r = sd_bus_message_read(m, "s", &name); - assert_se(r >= 0); - - if (!streq_ptr(match->sender, name)) - return 0; - - ++match->matched_acquired; - return 1; -} - -static void test_proxy_matched(void) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *a = NULL; - _cleanup_free_ char *matchstr = NULL; - TestProxyMatch match = {}; - const char *me; - int r; - - /* open bus 'a' */ - - r = sd_bus_new(&a); - assert_se(r >= 0); - - r = sd_bus_set_address(a, "unix:path=/var/run/dbus/system_bus_socket"); - assert_se(r >= 0); - - r = sd_bus_set_bus_client(a, true); - assert_se(r >= 0); - - r = sd_bus_start(a); - assert_se(r >= 0); - - r = sd_bus_get_unique_name(a, &me); - assert_se(r >= 0); - - matchstr = strjoin("type='signal'," - "member='NameAcquired'," - "destination='", - me, - "'", - NULL); - assert_se(matchstr); - r = sd_bus_add_match(a, NULL, matchstr, test_proxy_acquired, &match); - assert_se(r >= 0); - - r = sd_bus_get_unique_name(a, &match.sender); - assert_se(r >= 0); - - /* barrier to guarantee proxy/dbus-daemon handled the previous data */ - r = sd_bus_call_method(a, - "org.freedesktop.DBus", - "/org/freedesktop/DBus", - "org.freedesktop.DBus", - "GetId", - NULL, NULL, NULL); - assert_se(r >= 0); - - /* now we can be sure the Name* signals were sent */ - do { - r = sd_bus_process(a, NULL); - } while (r > 0); - assert_se(r == 0); - - assert_se(match.matched_acquired == 1); -} - -int main(int argc, char **argv) { - if (access("/var/run/dbus/system_bus_socket", F_OK) < 0) - return EXIT_TEST_SKIP; - - log_parse_environment(); - - test_proxy_matched(); - - return EXIT_SUCCESS; -} diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c index 4e50b61979..4da9dbfd63 100644 --- a/src/libsystemd/sd-daemon/sd-daemon.c +++ b/src/libsystemd/sd-daemon/sd-daemon.c @@ -458,14 +458,12 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char if (sockaddr.un.sun_path[0] == '@') sockaddr.un.sun_path[0] = 0; - msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e); - if (msghdr.msg_namelen > sizeof(struct sockaddr_un)) - msghdr.msg_namelen = sizeof(struct sockaddr_un); + msghdr.msg_namelen = SOCKADDR_UN_LEN(sockaddr.un); have_pid = pid != 0 && pid != getpid(); if (n_fds > 0 || have_pid) { - /* CMSG_SPACE(0) may return value different then zero, which results in miscalculated controllen. */ + /* CMSG_SPACE(0) may return value different than zero, which results in miscalculated controllen. */ msghdr.msg_controllen = (n_fds > 0 ? CMSG_SPACE(sizeof(int) * n_fds) : 0) + (have_pid ? CMSG_SPACE(sizeof(struct ucred)) : 0); diff --git a/src/libsystemd/sd-device/device-internal.h b/src/libsystemd/sd-device/device-internal.h index b96441de56..ab222e27de 100644 --- a/src/libsystemd/sd-device/device-internal.h +++ b/src/libsystemd/sd-device/device-internal.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -18,8 +20,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - #include "hashmap.h" #include "set.h" diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c index f2af3ab3ae..9082d377f4 100644 --- a/src/libsystemd/sd-device/device-private.c +++ b/src/libsystemd/sd-device/device-private.c @@ -890,7 +890,7 @@ void device_cleanup_tags(sd_device *device) { set_free_free(device->tags); device->tags = NULL; device->property_tags_outdated = true; - device->tags_generation ++; + device->tags_generation++; } void device_cleanup_devlinks(sd_device *device) { @@ -899,7 +899,7 @@ void device_cleanup_devlinks(sd_device *device) { set_free_free(device->devlinks); device->devlinks = NULL; device->property_devlinks_outdated = true; - device->devlinks_generation ++; + device->devlinks_generation++; } void device_remove_tag(sd_device *device, const char *tag) { @@ -908,7 +908,7 @@ void device_remove_tag(sd_device *device, const char *tag) { free(set_remove(device->tags, tag)); device->property_tags_outdated = true; - device->tags_generation ++; + device->tags_generation++; } static int device_tag(sd_device *device, const char *tag, bool add) { diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index 9633e46ce0..b1c3d5f228 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -136,7 +136,7 @@ int device_add_property_aux(sd_device *device, const char *_key, const char *_va } if (!db) { - device->properties_generation ++; + device->properties_generation++; device->properties_buf_outdated = true; } @@ -309,7 +309,7 @@ _public_ int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *s if (name[len] == '/') name[len] = '!'; - len ++; + len++; } syspath = strjoina("/sys/subsystem/", subsystem, "/devices/", name); @@ -669,7 +669,7 @@ _public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) { return -EINVAL; sysname[0] = '\0'; - sysname ++; + sysname++; return sd_device_new_from_subsystem_sysname(ret, subsys, sysname); } @@ -971,7 +971,7 @@ static int device_set_sysname(sd_device *device) { pos = strrchr(device->devpath, '/'); if (!pos) return -EINVAL; - pos ++; + pos++; /* devpath is not a root directory */ if (*pos == '\0' || pos <= device->devpath) @@ -986,7 +986,7 @@ static int device_set_sysname(sd_device *device) { if (sysname[len] == '!') sysname[len] = '/'; - len ++; + len++; } /* trailing number */ @@ -1066,7 +1066,7 @@ int device_add_tag(sd_device *device, const char *tag) { if (r < 0) return r; - device->tags_generation ++; + device->tags_generation++; device->property_tags_outdated = true; return 0; @@ -1086,7 +1086,7 @@ int device_add_devlink(sd_device *device, const char *devlink) { if (r < 0) return r; - device->devlinks_generation ++; + device->devlinks_generation++; device->property_devlinks_outdated = true; return 0; @@ -1212,19 +1212,19 @@ int device_get_id_filename(sd_device *device, const char **ret) { if (major(devnum) > 0) { assert(subsystem); - /* use dev_t -- b259:131072, c254:0 */ + /* use dev_t — b259:131072, c254:0 */ r = asprintf(&id, "%c%u:%u", streq(subsystem, "block") ? 'b' : 'c', major(devnum), minor(devnum)); if (r < 0) return -ENOMEM; } else if (ifindex > 0) { - /* use netdev ifindex -- n3 */ + /* use netdev ifindex — n3 */ r = asprintf(&id, "n%u", ifindex); if (r < 0) return -ENOMEM; } else { - /* use $subsys:$sysname -- pci:0000:00:1f.2 + /* use $subsys:$sysname — pci:0000:00:1f.2 * sysname() has '!' translated, get it from devpath */ const char *sysname; @@ -1397,7 +1397,7 @@ _public_ const char *sd_device_get_tag_first(sd_device *device) { device->tags_iterator_generation = device->tags_generation; device->tags_iterator = ITERATOR_FIRST; - set_iterate(device->tags, &device->tags_iterator, &v); + (void) set_iterate(device->tags, &device->tags_iterator, &v); return v; } @@ -1411,7 +1411,7 @@ _public_ const char *sd_device_get_tag_next(sd_device *device) { if (device->tags_iterator_generation != device->tags_generation) return NULL; - set_iterate(device->tags, &device->tags_iterator, &v); + (void) set_iterate(device->tags, &device->tags_iterator, &v); return v; } @@ -1425,7 +1425,7 @@ _public_ const char *sd_device_get_devlink_first(sd_device *device) { device->devlinks_iterator_generation = device->devlinks_generation; device->devlinks_iterator = ITERATOR_FIRST; - set_iterate(device->devlinks, &device->devlinks_iterator, &v); + (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v); return v; } @@ -1439,7 +1439,7 @@ _public_ const char *sd_device_get_devlink_next(sd_device *device) { if (device->devlinks_iterator_generation != device->devlinks_generation) return NULL; - set_iterate(device->devlinks, &device->devlinks_iterator, &v); + (void) set_iterate(device->devlinks, &device->devlinks_iterator, &v); return v; } @@ -1457,15 +1457,20 @@ static int device_properties_prepare(sd_device *device) { return r; if (device->property_devlinks_outdated) { - char *devlinks = NULL; + _cleanup_free_ char *devlinks = NULL; + size_t devlinks_allocated = 0, devlinks_len = 0; const char *devlink; - devlink = sd_device_get_devlink_first(device); - if (devlink) - devlinks = strdupa(devlink); + for (devlink = sd_device_get_devlink_first(device); devlink; devlink = sd_device_get_devlink_next(device)) { + char *e; - while ((devlink = sd_device_get_devlink_next(device))) - devlinks = strjoina(devlinks, " ", devlink); + if (!GREEDY_REALLOC(devlinks, devlinks_allocated, devlinks_len + strlen(devlink) + 2)) + return -ENOMEM; + if (devlinks_len > 0) + stpcpy(devlinks + devlinks_len++, " "); + e = stpcpy(devlinks + devlinks_len, devlink); + devlinks_len = e - devlinks; + } r = device_add_property_internal(device, "DEVLINKS", devlinks); if (r < 0) @@ -1475,17 +1480,23 @@ static int device_properties_prepare(sd_device *device) { } if (device->property_tags_outdated) { - char *tags = NULL; + _cleanup_free_ char *tags = NULL; + size_t tags_allocated = 0, tags_len = 0; const char *tag; - tag = sd_device_get_tag_first(device); - if (tag) - tags = strjoina(":", tag); + if (!GREEDY_REALLOC(tags, tags_allocated, 2)) + return -ENOMEM; + stpcpy(tags, ":"); + tags_len++; - while ((tag = sd_device_get_tag_next(device))) - tags = strjoina(tags, ":", tag); + for (tag = sd_device_get_tag_first(device); tag; tag = sd_device_get_tag_next(device)) { + char *e; - tags = strjoina(tags, ":"); + if (!GREEDY_REALLOC(tags, tags_allocated, tags_len + strlen(tag) + 2)) + return -ENOMEM; + e = stpcpy(stpcpy(tags + tags_len, tag), ":"); + tags_len = e - tags; + } r = device_add_property_internal(device, "TAGS", tags); if (r < 0) @@ -1606,7 +1617,7 @@ _public_ const char *sd_device_get_sysattr_first(sd_device *device) { device->sysattrs_iterator = ITERATOR_FIRST; - set_iterate(device->sysattrs, &device->sysattrs_iterator, &v); + (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v); return v; } @@ -1618,7 +1629,7 @@ _public_ const char *sd_device_get_sysattr_next(sd_device *device) { if (!device->sysattrs_read) return NULL; - set_iterate(device->sysattrs, &device->sysattrs_iterator, &v); + (void) set_iterate(device->sysattrs, &device->sysattrs_iterator, &v); return v; } diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index deef6ba9d3..7ba6527f63 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -951,7 +951,7 @@ static sd_event_source *source_new(sd_event *e, bool floating, EventSourceType t sd_event_ref(e); LIST_PREPEND(sources, e->sources, s); - e->n_sources ++; + e->n_sources++; return s; } @@ -1072,6 +1072,10 @@ _public_ int sd_event_add_time( assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); + if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) && + !clock_boottime_supported()) + return -EOPNOTSUPP; + if (!callback) callback = time_exit_callback; @@ -1145,8 +1149,7 @@ _public_ int sd_event_add_signal( int r; assert_return(e, -EINVAL); - assert_return(sig > 0, -EINVAL); - assert_return(sig < _NSIG, -EINVAL); + assert_return(SIGNAL_VALID(sig), -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); @@ -1235,7 +1238,7 @@ _public_ int sd_event_add_child( return r; } - e->n_enabled_child_sources ++; + e->n_enabled_child_sources++; r = event_make_signal_data(e, SIGCHLD, NULL); if (r < 0) { @@ -2200,7 +2203,7 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) { if (_unlikely_(n != sizeof(si))) return -EIO; - assert(si.ssi_signo < _NSIG); + assert(SIGNAL_VALID(si.ssi_signo)); read_one = true; @@ -2528,7 +2531,8 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { } dual_timestamp_get(&e->timestamp); - e->timestamp_boottime = now(CLOCK_BOOTTIME); + if (clock_boottime_supported()) + e->timestamp_boottime = now(CLOCK_BOOTTIME); for (i = 0; i < m; i++) { @@ -2762,6 +2766,9 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) { CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM), -EOPNOTSUPP); + if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) && !clock_boottime_supported()) + return -EOPNOTSUPP; + if (!dual_timestamp_is_set(&e->timestamp)) { /* Implicitly fall back to now() if we never ran * before and thus have no cached time. */ @@ -2780,9 +2787,13 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) { *usec = e->timestamp.monotonic; break; - default: + case CLOCK_BOOTTIME: + case CLOCK_BOOTTIME_ALARM: *usec = e->timestamp_boottime; break; + + default: + assert_not_reached("Unknown clock?"); } return 0; diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c index daea4126f8..289114490c 100644 --- a/src/libsystemd/sd-event/test-event.c +++ b/src/libsystemd/sd-event/test-event.c @@ -270,8 +270,10 @@ static void test_sd_event_now(void) { assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) > 0); assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) > 0); assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) > 0); - assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) > 0); - assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) > 0); + if (clock_boottime_supported()) { + assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) > 0); + assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) > 0); + } assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP); assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP); @@ -280,8 +282,10 @@ static void test_sd_event_now(void) { assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) == 0); assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) == 0); assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) == 0); - assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) == 0); - assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) == 0); + if (clock_boottime_supported()) { + assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) == 0); + assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) == 0); + } assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP); assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP); } @@ -291,7 +295,7 @@ static int n_rtqueue = 0; static int rtqueue_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { last_rtqueue_sigval = si->ssi_int; - n_rtqueue ++; + n_rtqueue++; return 0; } diff --git a/src/libsystemd/sd-hwdb/hwdb-internal.h b/src/libsystemd/sd-hwdb/hwdb-internal.h index 13fddfc8ad..8ffb5e5c74 100644 --- a/src/libsystemd/sd-hwdb/hwdb-internal.h +++ b/src/libsystemd/sd-hwdb/hwdb-internal.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -16,7 +18,6 @@ You should have received a copy of the GNU Lesser General Public License along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once #include "sparse-endian.h" #include "util.h" diff --git a/src/libsystemd/sd-netlink/local-addresses.c b/src/libsystemd/sd-netlink/local-addresses.c index 6abd8fd0cc..ed9ee041ab 100644 --- a/src/libsystemd/sd-netlink/local-addresses.c +++ b/src/libsystemd/sd-netlink/local-addresses.c @@ -155,8 +155,7 @@ int local_addresses(sd_netlink *context, int ifindex, int af, struct local_addre n_list++; }; - if (n_list > 0) - qsort(list, n_list, sizeof(struct local_address), address_compare); + qsort_safe(list, n_list, sizeof(struct local_address), address_compare); *ret = list; list = NULL; diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c index 3a866fdafe..86d8dee867 100644 --- a/src/libsystemd/sd-netlink/netlink-message.c +++ b/src/libsystemd/sd-netlink/netlink-message.c @@ -34,7 +34,7 @@ #include "util.h" #define GET_CONTAINER(m, i) ((i) < (m)->n_containers ? (struct rtattr*)((uint8_t*)(m)->hdr + (m)->containers[i].offset) : NULL) -#define PUSH_CONTAINER(m, new) (m)->container_offsets[(m)->n_containers ++] = (uint8_t*)(new) - (uint8_t*)(m)->hdr; +#define PUSH_CONTAINER(m, new) (m)->container_offsets[(m)->n_containers++] = (uint8_t*)(new) - (uint8_t*)(m)->hdr; #define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK) #define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK) @@ -107,10 +107,7 @@ int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) { m->hdr->nlmsg_type == RTM_GETNEIGH, -EINVAL); - if (dump) - m->hdr->nlmsg_flags |= NLM_F_DUMP; - else - m->hdr->nlmsg_flags &= ~NLM_F_DUMP; + SET_FLAG(m->hdr->nlmsg_flags, NLM_F_DUMP, dump); return 0; } @@ -210,11 +207,11 @@ static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *da * and gives us too little data (so don't do that) */ padding = mempcpy(RTA_DATA(rta), data, data_length); - else { + + else /* if no data was passed, make sure we still initialize the padding note that we can have data_length > 0 (used by some containers) */ padding = RTA_DATA(rta); - } /* make sure also the padding at the end of the message is initialized */ padding_length = (uint8_t*)m->hdr + message_length - (uint8_t*)padding; @@ -346,7 +343,7 @@ int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, c assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); - r = add_rtattr(m, type, &data, len); + r = add_rtattr(m, type, data, len); if (r < 0) return r; @@ -467,7 +464,7 @@ int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type if (r < 0) return r; - m->containers[m->n_containers ++].offset = r; + m->containers[m->n_containers++].offset = r; return 0; } @@ -498,7 +495,7 @@ int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned shor if (r < 0) return r; - m->containers[m->n_containers ++].offset = r; + m->containers[m->n_containers++].offset = r; return 0; } @@ -510,7 +507,7 @@ int sd_netlink_message_close_container(sd_netlink_message *m) { assert_return(m->n_containers > 0, -EINVAL); m->containers[m->n_containers].type_system = NULL; - m->n_containers --; + m->n_containers--; return 0; } @@ -528,7 +525,7 @@ static int netlink_message_read_internal(sd_netlink_message *m, unsigned short t attribute = &m->containers[m->n_containers].attributes[type]; - if(!attribute->offset) + if (!attribute->offset) return -ENODATA; rta = (struct rtattr*)((uint8_t *) m->hdr + attribute->offset); @@ -735,7 +732,7 @@ static int netlink_container_parse(sd_netlink_message *m, _cleanup_free_ struct netlink_attribute *attributes = NULL; attributes = new0(struct netlink_attribute, count); - if(!attributes) + if (!attributes) return -ENOMEM; for (; RTA_OK(rta, rt_len); rta = RTA_NEXT(rta, rt_len)) { @@ -842,7 +839,7 @@ int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short typ else size = (size_t)r; - m->n_containers ++; + m->n_containers++; r = netlink_container_parse(m, &m->containers[m->n_containers], @@ -850,7 +847,7 @@ int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short typ container, size); if (r < 0) { - m->n_containers --; + m->n_containers--; return r; } @@ -867,7 +864,7 @@ int sd_netlink_message_exit_container(sd_netlink_message *m) { m->containers[m->n_containers].attributes = mfree(m->containers[m->n_containers].attributes); m->containers[m->n_containers].type_system = NULL; - m->n_containers --; + m->n_containers--; return 0; } diff --git a/src/libsystemd/sd-netlink/netlink-socket.c b/src/libsystemd/sd-netlink/netlink-socket.c index 590fc53fc7..c165fa3359 100644 --- a/src/libsystemd/sd-netlink/netlink-socket.c +++ b/src/libsystemd/sd-netlink/netlink-socket.c @@ -82,7 +82,7 @@ static int broadcast_groups_get(sd_netlink *nl) { return r; for (i = 0; i < len; i++) { - for (j = 0; j < sizeof(uint32_t) * 8; j ++) { + for (j = 0; j < sizeof(uint32_t) * 8; j++) { uint32_t offset; unsigned group; @@ -168,7 +168,7 @@ int socket_broadcast_group_ref(sd_netlink *nl, unsigned group) { n_ref = broadcast_group_get_ref(nl, group); - n_ref ++; + n_ref++; r = hashmap_ensure_allocated(&nl->broadcast_group_refs, NULL); if (r < 0) @@ -216,7 +216,7 @@ int socket_broadcast_group_unref(sd_netlink *nl, unsigned group) { assert(n_ref > 0); - n_ref --; + n_ref--; r = broadcast_group_set_ref(nl, group, n_ref); if (r < 0) @@ -444,14 +444,14 @@ int socket_read_message(sd_netlink *rtnl) { if (r < 0) return r; - rtnl->rqueue[rtnl->rqueue_size ++] = first; + rtnl->rqueue[rtnl->rqueue_size++] = first; first = NULL; if (multi_part && (i < rtnl->rqueue_partial_size)) { /* remove the message form the partial read queue */ memmove(rtnl->rqueue_partial + i,rtnl->rqueue_partial + i + 1, sizeof(sd_netlink_message*) * (rtnl->rqueue_partial_size - i - 1)); - rtnl->rqueue_partial_size --; + rtnl->rqueue_partial_size--; } return 1; @@ -465,7 +465,7 @@ int socket_read_message(sd_netlink *rtnl) { if (r < 0) return r; - rtnl->rqueue_partial[rtnl->rqueue_partial_size ++] = first; + rtnl->rqueue_partial[rtnl->rqueue_partial_size++] = first; } first = NULL; diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c index a5758bb516..3a4bac2ced 100644 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ b/src/libsystemd/sd-netlink/netlink-types.c @@ -95,12 +95,43 @@ static const NLType rtnl_link_info_data_macvlan_types[] = { }; static const NLType rtnl_link_info_data_bridge_types[] = { - [IFLA_BR_FORWARD_DELAY] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_HELLO_TIME] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_MAX_AGE] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_AGEING_TIME] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_STP_STATE] = { .type = NETLINK_TYPE_U32 }, - [IFLA_BR_PRIORITY] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_FORWARD_DELAY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_HELLO_TIME] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_MAX_AGE] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_AGEING_TIME] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_STP_STATE] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_PRIORITY] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_VLAN_FILTERING] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_VLAN_PROTOCOL] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_GROUP_FWD_MASK] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_ROOT_PORT] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_ROOT_PATH_COST] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_TOPOLOGY_CHANGE] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_TOPOLOGY_CHANGE_DETECTED] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_HELLO_TIMER] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_TCN_TIMER] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_TOPOLOGY_CHANGE_TIMER] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_GC_TIMER] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_GROUP_ADDR] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_FDB_FLUSH] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_MCAST_ROUTER] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_MCAST_SNOOPING] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_MCAST_QUERY_USE_IFADDR] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_MCAST_QUERIER] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_MCAST_HASH_ELASTICITY] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_MCAST_HASH_MAX] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_MCAST_LAST_MEMBER_CNT] = { .type = NETLINK_TYPE_U32 }, + [IFLA_BR_MCAST_STARTUP_QUERY_CNT] = { .type = NETLINK_TYPE_U16 }, + [IFLA_BR_MCAST_LAST_MEMBER_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_MCAST_MEMBERSHIP_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_MCAST_QUERIER_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_MCAST_QUERY_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_MCAST_QUERY_RESPONSE_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_MCAST_STARTUP_QUERY_INTVL] = { .type = NETLINK_TYPE_U64 }, + [IFLA_BR_NF_CALL_IPTABLES] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_NF_CALL_IP6TABLES] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_NF_CALL_ARPTABLES] = { .type = NETLINK_TYPE_U8 }, + [IFLA_BR_VLAN_DEFAULT_PVID] = { .type = NETLINK_TYPE_U16 }, }; static const NLType rtnl_link_info_data_vlan_types[] = { diff --git a/src/libsystemd/sd-netlink/rtnl-message.c b/src/libsystemd/sd-netlink/rtnl-message.c index 090552f576..09240c7b2a 100644 --- a/src/libsystemd/sd-netlink/rtnl-message.c +++ b/src/libsystemd/sd-netlink/rtnl-message.c @@ -111,6 +111,20 @@ int sd_rtnl_message_route_get_flags(sd_netlink_message *m, unsigned *flags) { return 0; } +int sd_rtnl_message_route_set_table(sd_netlink_message *m, unsigned char table) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + rtm->rtm_table = table; + + return 0; +} + int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family) { struct rtmsg *rtm; @@ -126,6 +140,20 @@ int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family) { return 0; } +int sd_rtnl_message_route_set_family(sd_netlink_message *m, int family) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + rtm->rtm_family = family; + + return 0; +} + int sd_rtnl_message_route_get_protocol(sd_netlink_message *m, unsigned char *protocol) { struct rtmsg *rtm; @@ -402,7 +430,6 @@ int sd_rtnl_message_new_link(sd_netlink *rtnl, sd_netlink_message **ret, int r; assert_return(rtnl_message_type_is_link(nlmsg_type), -EINVAL); - assert_return(nlmsg_type != RTM_DELLINK || index > 0, -EINVAL); assert_return(ret, -EINVAL); r = message_new(rtnl, ret, nlmsg_type); @@ -616,7 +643,7 @@ int sd_rtnl_message_link_get_flags(sd_netlink_message *m, unsigned *flags) { return 0; } -int sd_rtnl_message_link_get_type(sd_netlink_message *m, unsigned *type) { +int sd_rtnl_message_link_get_type(sd_netlink_message *m, unsigned short *type) { struct ifinfomsg *ifi; assert_return(m, -EINVAL); diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c index 4833815b43..91701405a5 100644 --- a/src/libsystemd/sd-netlink/sd-netlink.c +++ b/src/libsystemd/sd-netlink/sd-netlink.c @@ -279,7 +279,7 @@ static int dispatch_rqueue(sd_netlink *rtnl, sd_netlink_message **message) { /* Dispatch a queued message */ *message = rtnl->rqueue[0]; - rtnl->rqueue_size --; + rtnl->rqueue_size--; memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_netlink_message*) * rtnl->rqueue_size); return 1; @@ -774,7 +774,7 @@ static int prepare_callback(sd_event_source *s, void *userdata) { return 1; } -int sd_netlink_attach_event(sd_netlink *rtnl, sd_event *event, int priority) { +int sd_netlink_attach_event(sd_netlink *rtnl, sd_event *event, int64_t priority) { int r; assert_return(rtnl, -EINVAL); diff --git a/src/libsystemd/sd-netlink/test-netlink.c b/src/libsystemd/sd-netlink/test-netlink.c index de5e0ffc8f..58c2e892f5 100644 --- a/src/libsystemd/sd-netlink/test-netlink.c +++ b/src/libsystemd/sd-netlink/test-netlink.c @@ -217,7 +217,7 @@ static void test_event_loop(int ifindex) { assert_se(sd_netlink_open(&rtnl) >= 0); assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0); - assert_se(sd_netlink_call_async(rtnl, m, &link_handler, ifname, 0, NULL) >= 0); + assert_se(sd_netlink_call_async(rtnl, m, link_handler, ifname, 0, NULL) >= 0); assert_se(sd_event_default(&event) >= 0); @@ -234,7 +234,7 @@ static int pipe_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) int *counter = userdata; int r; - (*counter) --; + (*counter)--; r = sd_netlink_message_get_errno(m); @@ -258,7 +258,7 @@ static void test_async(int ifindex) { assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_GETLINK, ifindex) >= 0); - assert_se(sd_netlink_call_async(rtnl, m, &link_handler, ifname, 0, &serial) >= 0); + assert_se(sd_netlink_call_async(rtnl, m, link_handler, ifname, 0, &serial) >= 0); assert_se(sd_netlink_wait(rtnl, 0) >= 0); assert_se(sd_netlink_process(rtnl, &r) >= 0); @@ -276,11 +276,11 @@ static void test_pipe(int ifindex) { assert_se(sd_rtnl_message_new_link(rtnl, &m1, RTM_GETLINK, ifindex) >= 0); assert_se(sd_rtnl_message_new_link(rtnl, &m2, RTM_GETLINK, ifindex) >= 0); - counter ++; - assert_se(sd_netlink_call_async(rtnl, m1, &pipe_handler, &counter, 0, NULL) >= 0); + counter++; + assert_se(sd_netlink_call_async(rtnl, m1, pipe_handler, &counter, 0, NULL) >= 0); - counter ++; - assert_se(sd_netlink_call_async(rtnl, m2, &pipe_handler, &counter, 0, NULL) >= 0); + counter++; + assert_se(sd_netlink_call_async(rtnl, m2, pipe_handler, &counter, 0, NULL) >= 0); while (counter > 0) { assert_se(sd_netlink_wait(rtnl, 0) >= 0); @@ -330,12 +330,12 @@ static void test_match(void) { assert_se(sd_netlink_open(&rtnl) >= 0); - assert_se(sd_netlink_add_match(rtnl, RTM_NEWLINK, &link_handler, NULL) >= 0); - assert_se(sd_netlink_add_match(rtnl, RTM_NEWLINK, &link_handler, NULL) >= 0); + assert_se(sd_netlink_add_match(rtnl, RTM_NEWLINK, link_handler, NULL) >= 0); + assert_se(sd_netlink_add_match(rtnl, RTM_NEWLINK, link_handler, NULL) >= 0); - assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, &link_handler, NULL) == 1); - assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, &link_handler, NULL) == 1); - assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, &link_handler, NULL) == 0); + assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, link_handler, NULL) == 1); + assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, link_handler, NULL) == 1); + assert_se(sd_netlink_remove_match(rtnl, RTM_NEWLINK, link_handler, NULL) == 0); assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); } diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c index 62051992ef..f8e18f23fd 100644 --- a/src/libsystemd/sd-network/sd-network.c +++ b/src/libsystemd/sd-network/sd-network.c @@ -31,6 +31,7 @@ #include "fs-util.h" #include "macro.h" #include "parse-util.h" +#include "stdio-util.h" #include "string-util.h" #include "strv.h" #include "util.h" @@ -102,16 +103,16 @@ _public_ int sd_network_get_route_domains(char ***ret) { } static int network_link_get_string(int ifindex, const char *field, char **ret) { - _cleanup_free_ char *s = NULL, *p = NULL; + char path[strlen("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; + _cleanup_free_ char *s = NULL; int r; assert_return(ifindex > 0, -EINVAL); assert_return(ret, -EINVAL); - if (asprintf(&p, "/run/systemd/netif/links/%i", ifindex) < 0) - return -ENOMEM; + xsprintf(path, "/run/systemd/netif/links/%i", ifindex); - r = parse_env_file(p, NEWLINE, field, &s, NULL); + r = parse_env_file(path, NEWLINE, field, &s, NULL); if (r == -ENOENT) return -ENODATA; if (r < 0) @@ -126,17 +127,16 @@ static int network_link_get_string(int ifindex, const char *field, char **ret) { } static int network_link_get_strv(int ifindex, const char *key, char ***ret) { - _cleanup_free_ char *p = NULL, *s = NULL; + char path[strlen("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; _cleanup_strv_free_ char **a = NULL; + _cleanup_free_ char *s = NULL; int r; assert_return(ifindex > 0, -EINVAL); assert_return(ret, -EINVAL); - if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0) - return -ENOMEM; - - r = parse_env_file(p, NEWLINE, key, &s, NULL); + xsprintf(path, "/run/systemd/netif/links/%i", ifindex); + r = parse_env_file(path, NEWLINE, key, &s, NULL); if (r == -ENOENT) return -ENODATA; if (r < 0) @@ -187,32 +187,7 @@ _public_ int sd_network_link_get_dnssec_negative_trust_anchors(int ifindex, char return network_link_get_strv(ifindex, "DNSSEC_NTA", nta); } -_public_ int sd_network_link_get_lldp(int ifindex, char **lldp) { - _cleanup_free_ char *s = NULL, *p = NULL; - size_t size; - int r; - - assert_return(ifindex > 0, -EINVAL); - assert_return(lldp, -EINVAL); - - if (asprintf(&p, "/run/systemd/netif/lldp/%d", ifindex) < 0) - return -ENOMEM; - - r = read_full_file(p, &s, &size); - if (r == -ENOENT) - return -ENODATA; - if (r < 0) - return r; - if (size <= 0) - return -ENODATA; - - *lldp = s; - s = NULL; - - return 0; -} - -int sd_network_link_get_timezone(int ifindex, char **ret) { +_public_ int sd_network_link_get_timezone(int ifindex, char **ret) { return network_link_get_string(ifindex, "TIMEZONE", ret); } @@ -232,12 +207,64 @@ _public_ int sd_network_link_get_route_domains(int ifindex, char ***ret) { return network_link_get_strv(ifindex, "ROUTE_DOMAINS", ret); } -_public_ int sd_network_link_get_carrier_bound_to(int ifindex, char ***ret) { - return network_link_get_strv(ifindex, "CARRIER_BOUND_TO", ret); +static int network_link_get_ifindexes(int ifindex, const char *key, int **ret) { + char path[strlen("/run/systemd/netif/links/") + DECIMAL_STR_MAX(ifindex) + 1]; + _cleanup_free_ int *ifis = NULL; + _cleanup_free_ char *s = NULL; + size_t allocated = 0, c = 0; + const char *x; + int r; + + assert_return(ifindex > 0, -EINVAL); + assert_return(ret, -EINVAL); + + xsprintf(path, "/run/systemd/netif/links/%i", ifindex); + r = parse_env_file(path, NEWLINE, key, &s, NULL); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + if (isempty(s)) { + *ret = NULL; + return 0; + } + + x = s; + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&x, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; + + r = parse_ifindex(word, &ifindex); + if (r < 0) + return r; + + if (!GREEDY_REALLOC(ifis, allocated, c + 1)) + return -ENOMEM; + + ifis[c++] = ifindex; + } + + if (!GREEDY_REALLOC(ifis, allocated, c + 1)) + return -ENOMEM; + ifis[c] = 0; /* Let's add a 0 ifindex to the end, to be nice*/ + + *ret = ifis; + ifis = NULL; + + return c; +} + +_public_ int sd_network_link_get_carrier_bound_to(int ifindex, int **ret) { + return network_link_get_ifindexes(ifindex, "CARRIER_BOUND_TO", ret); } -_public_ int sd_network_link_get_carrier_bound_by(int ifindex, char ***ret) { - return network_link_get_strv(ifindex, "CARRIER_BOUND_BY", ret); +_public_ int sd_network_link_get_carrier_bound_by(int ifindex, int **ret) { + return network_link_get_ifindexes(ifindex, "CARRIER_BOUND_BY", ret); } static inline int MONITOR_TO_FD(sd_network_monitor *m) { diff --git a/src/libsystemd/sd-path/sd-path.c b/src/libsystemd/sd-path/sd-path.c index 480f1ad065..b7aec1f20a 100644 --- a/src/libsystemd/sd-path/sd-path.c +++ b/src/libsystemd/sd-path/sd-path.c @@ -89,7 +89,8 @@ static int from_home_dir(const char *envname, const char *suffix, char **buffer, static int from_user_dir(const char *field, char **buffer, const char **ret) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *b = NULL; - const char *fn = NULL; + _cleanup_free_ const char *fn = NULL; + const char *c = NULL; char line[LINE_MAX]; size_t n; int r; @@ -98,10 +99,14 @@ static int from_user_dir(const char *field, char **buffer, const char **ret) { assert(buffer); assert(ret); - r = from_home_dir(NULL, ".config/user-dirs.dirs", &b, &fn); + r = from_home_dir("XDG_CONFIG_HOME", ".config", &b, &c); if (r < 0) return r; + fn = strappend(c, "/user-dirs.dirs"); + if (!fn) + return -ENOMEM; + f = fopen(fn, "re"); if (!f) { if (errno == ENOENT) diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c index 653dbfbe57..d8303e2e69 100644 --- a/src/libsystemd/sd-resolve/sd-resolve.c +++ b/src/libsystemd/sd-resolve/sd-resolve.c @@ -217,9 +217,8 @@ static void *serialize_addrinfo(void *p, const struct addrinfo *ai, size_t *leng memcpy((uint8_t*) p, &s, sizeof(AddrInfoSerialization)); memcpy((uint8_t*) p + sizeof(AddrInfoSerialization), ai->ai_addr, ai->ai_addrlen); - - if (ai->ai_canonname) - memcpy((char*) p + sizeof(AddrInfoSerialization) + ai->ai_addrlen, ai->ai_canonname, cnl); + memcpy_safe((char*) p + sizeof(AddrInfoSerialization) + ai->ai_addrlen, + ai->ai_canonname, cnl); *length += l; return (uint8_t*) p + l; @@ -404,7 +403,7 @@ static void* thread_worker(void *p) { assert_se(pthread_sigmask(SIG_BLOCK, &fullset, NULL) == 0); /* Assign a pretty name to this thread */ - prctl(PR_SET_NAME, (unsigned long) "sd-resolve"); + (void) prctl(PR_SET_NAME, (unsigned long) "sd-resolve"); while (!resolve->dead) { union { @@ -448,7 +447,7 @@ static int start_threads(sd_resolve *resolve, unsigned extra) { if (r != 0) return -r; - resolve->n_valid_workers ++; + resolve->n_valid_workers++; } return 0; @@ -580,9 +579,10 @@ static void resolve_free(sd_resolve *resolve) { (void) send(resolve->fds[REQUEST_SEND_FD], &req, req.length, MSG_NOSIGNAL); } - /* Now terminate them and wait until they are gone. */ + /* Now terminate them and wait until they are gone. + If we get an error than most likely the thread already exited. */ for (i = 0; i < resolve->n_valid_workers; i++) - pthread_join(resolve->workers[i], NULL); + (void) pthread_join(resolve->workers[i], NULL); /* Close all communication channels */ for (i = 0; i < _FD_MAX; i++) @@ -658,7 +658,7 @@ static int complete_query(sd_resolve *resolve, sd_resolve_query *q) { assert(q->resolve == resolve); q->done = true; - resolve->n_done ++; + resolve->n_done++; resolve->current = sd_resolve_query_ref(q); @@ -1192,7 +1192,7 @@ static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userd return 1; } -_public_ int sd_resolve_attach_event(sd_resolve *resolve, sd_event *event, int priority) { +_public_ int sd_resolve_attach_event(sd_resolve *resolve, sd_event *event, int64_t priority) { int r; assert_return(resolve, -EINVAL); diff --git a/src/libsystemd/sd-resolve/test-resolve.c b/src/libsystemd/sd-resolve/test-resolve.c index 33ef6fc0f7..1be1a7f8a7 100644 --- a/src/libsystemd/sd-resolve/test-resolve.c +++ b/src/libsystemd/sd-resolve/test-resolve.c @@ -63,7 +63,7 @@ static int getnameinfo_handler(sd_resolve_query *q, int ret, const char *host, c return 0; } - printf("Host: %s -- Serv: %s\n", strna(host), strna(serv)); + printf("Host: %s — Serv: %s\n", strna(host), strna(serv)); return 0; } diff --git a/src/libudev/libudev-device-internal.h b/src/libudev/libudev-device-internal.h index 40d59201cf..0e9af8ec09 100644 --- a/src/libudev/libudev-device-internal.h +++ b/src/libudev/libudev-device-internal.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -18,8 +20,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - #include "libudev.h" #include "sd-device.h" diff --git a/src/libudev/libudev-enumerate.c b/src/libudev/libudev-enumerate.c index e416e178b4..3b8abfb260 100644 --- a/src/libudev/libudev-enumerate.c +++ b/src/libudev/libudev-enumerate.c @@ -112,7 +112,7 @@ _public_ struct udev_enumerate *udev_enumerate_new(struct udev *udev) { **/ _public_ struct udev_enumerate *udev_enumerate_ref(struct udev_enumerate *udev_enumerate) { if (udev_enumerate) - udev_enumerate->refcount ++; + udev_enumerate->refcount++; return udev_enumerate; } diff --git a/src/libudev/libudev.h b/src/libudev/libudev.h index eb58740d26..3f6d0ed16c 100644 --- a/src/libudev/libudev.h +++ b/src/libudev/libudev.h @@ -21,6 +21,7 @@ #define _LIBUDEV_H_ #include <stdarg.h> +#include <sys/sysmacros.h> #include <sys/types.h> #ifdef __cplusplus diff --git a/src/locale/language-fallback-map b/src/locale/language-fallback-map index 6aadda091a..d0b02a6b98 100644 --- a/src/locale/language-fallback-map +++ b/src/locale/language-fallback-map @@ -3,6 +3,10 @@ en_AU en_AU:en_GB en_IE en_IE:en_GB en_NZ en_NZ:en_GB en_ZA en_ZA:en_GB +fr_BE fr_BE:fr_FR +fr_CA fr_CA:fr_FR +fr_CH fr_CH:fr_FR +fr_LU fr_LU:fr_FR it_CH it_CH:it_IT mai_IN mai:hi nds_DE nds:de diff --git a/src/locale/localectl.c b/src/locale/localectl.c index 365c79aa51..4865335349 100644 --- a/src/locale/localectl.c +++ b/src/locale/localectl.c @@ -46,14 +46,6 @@ static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; static char *arg_host = NULL; static bool arg_convert = true; -static void pager_open_if_enabled(void) { - - if (arg_no_pager) - return; - - pager_open(false); -} - static void polkit_agent_open_if_enabled(void) { /* Open the polkit agent as a child process if necessary */ @@ -124,11 +116,11 @@ static void print_overridden_variables(void) { if (variables[j]) { if (print_warning) { log_warning("Warning: Settings on kernel command line override system locale settings in /etc/locale.conf.\n" - " Command Line: %s=%s", locale_variable_to_string(j), variables[j]); + " Command Line: %s=%s", locale_variable_to_string(j), variables[j]); print_warning = false; } else - log_warning(" %s=%s", locale_variable_to_string(j), variables[j]); + log_warning(" %s=%s", locale_variable_to_string(j), variables[j]); } finish: for (j = 0; j < _VARIABLE_LC_MAX; j++) @@ -139,7 +131,7 @@ static void print_status_info(StatusInfo *i) { assert(i); if (strv_isempty(i->locale)) - puts(" System Locale: n/a\n"); + puts(" System Locale: n/a"); else { char **j; @@ -239,7 +231,7 @@ static int list_locales(sd_bus *bus, char **args, unsigned n) { if (r < 0) return log_error_errno(r, "Failed to read list of locales: %m"); - pager_open_if_enabled(); + pager_open(arg_no_pager, false); strv_print(l); return 0; @@ -341,7 +333,7 @@ static int list_vconsole_keymaps(sd_bus *bus, char **args, unsigned n) { strv_sort(l); - pager_open_if_enabled(); + pager_open(arg_no_pager, false); strv_print(l); @@ -479,7 +471,7 @@ static int list_x11_keymaps(sd_bus *bus, char **args, unsigned n) { strv_sort(list); strv_uniq(list); - pager_open_if_enabled(); + pager_open(arg_no_pager, false); strv_print(list); return 0; diff --git a/src/locale/localed.c b/src/locale/localed.c index f0fe59cc67..3b22a582ac 100644 --- a/src/locale/localed.c +++ b/src/locale/localed.c @@ -24,6 +24,7 @@ #ifdef HAVE_XKBCOMMON #include <xkbcommon/xkbcommon.h> +#include <dlfcn.h> #endif #include "sd-bus.h" @@ -542,7 +543,7 @@ static int read_next_mapping(const char* filename, return 0; } - (*n) ++; + (*n)++; l = strstrip(line); if (l[0] == 0 || l[0] == '#') @@ -1101,6 +1102,7 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro } #ifdef HAVE_XKBCOMMON + _printf_(3, 0) static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) { const char *fmt; @@ -1109,7 +1111,24 @@ static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char log_internalv(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, fmt, args); } +#define LOAD_SYMBOL(symbol, dl, name) \ + ({ \ + (symbol) = (typeof(symbol)) dlvsym((dl), (name), "V_0.5.0"); \ + (symbol) ? 0 : -EOPNOTSUPP; \ + }) + static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) { + + /* We dlopen() the library in order to make the dependency soft. The library (and what it pulls in) is huge + * after all, hence let's support XKB maps when the library is around, and refuse otherwise. The function + * pointers to the shared library are below: */ + + struct xkb_context* (*symbol_xkb_context_new)(enum xkb_context_flags flags) = NULL; + void (*symbol_xkb_context_unref)(struct xkb_context *context) = NULL; + void (*symbol_xkb_context_set_log_fn)(struct xkb_context *context, void (*log_fn)(struct xkb_context *context, enum xkb_log_level level, const char *format, va_list args)) = NULL; + struct xkb_keymap* (*symbol_xkb_keymap_new_from_names)(struct xkb_context *context, const struct xkb_rule_names *names, enum xkb_keymap_compile_flags flags) = NULL; + void (*symbol_xkb_keymap_unref)(struct xkb_keymap *keymap) = NULL; + const struct xkb_rule_names rmlvo = { .model = model, .layout = layout, @@ -1118,35 +1137,68 @@ static int verify_xkb_rmlvo(const char *model, const char *layout, const char *v }; struct xkb_context *ctx = NULL; struct xkb_keymap *km = NULL; + void *dl; int r; - /* compile keymap from RMLVO information to check out its validity */ + /* Compile keymap from RMLVO information to check out its validity */ + + dl = dlopen("libxkbcommon.so.0", RTLD_LAZY); + if (!dl) + return -EOPNOTSUPP; + + r = LOAD_SYMBOL(symbol_xkb_context_new, dl, "xkb_context_new"); + if (r < 0) + goto finish; + + r = LOAD_SYMBOL(symbol_xkb_context_unref, dl, "xkb_context_unref"); + if (r < 0) + goto finish; + + r = LOAD_SYMBOL(symbol_xkb_context_set_log_fn, dl, "xkb_context_set_log_fn"); + if (r < 0) + goto finish; - ctx = xkb_context_new(XKB_CONTEXT_NO_ENVIRONMENT_NAMES); + r = LOAD_SYMBOL(symbol_xkb_keymap_new_from_names, dl, "xkb_keymap_new_from_names"); + if (r < 0) + goto finish; + + r = LOAD_SYMBOL(symbol_xkb_keymap_unref, dl, "xkb_keymap_unref"); + if (r < 0) + goto finish; + + ctx = symbol_xkb_context_new(XKB_CONTEXT_NO_ENVIRONMENT_NAMES); if (!ctx) { r = -ENOMEM; - goto exit; + goto finish; } - xkb_context_set_log_fn(ctx, log_xkb); + symbol_xkb_context_set_log_fn(ctx, log_xkb); - km = xkb_keymap_new_from_names(ctx, &rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS); + km = symbol_xkb_keymap_new_from_names(ctx, &rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS); if (!km) { r = -EINVAL; - goto exit; + goto finish; } r = 0; -exit: - xkb_keymap_unref(km); - xkb_context_unref(ctx); +finish: + if (symbol_xkb_keymap_unref && km) + symbol_xkb_keymap_unref(km); + + if (symbol_xkb_context_unref && ctx) + symbol_xkb_context_unref(ctx); + + (void) dlclose(dl); return r; } + #else + static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) { return 0; } + #endif static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) { @@ -1203,7 +1255,11 @@ static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_err if (r < 0) { log_error_errno(r, "Cannot compile XKB keymap for new x11 keyboard layout ('%s' / '%s' / '%s' / '%s'): %m", strempty(model), strempty(layout), strempty(variant), strempty(options)); - return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot compile XKB keymap, refusing"); + + if (r == -EOPNOTSUPP) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Local keyboard configuration not supported on this system."); + + return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Specified keymap cannot be compiled, refusing as invalid."); } if (free_and_strdup(&c->x11_layout, layout) < 0 || @@ -1296,7 +1352,7 @@ int main(int argc, char *argv[]) { log_open(); umask(0022); - mac_selinux_init("/etc"); + mac_selinux_init(); if (argc != 1) { log_error("This program takes no arguments."); diff --git a/src/login/.gitignore b/src/login/.gitignore index 39088ec252..3a8ba497c1 100644 --- a/src/login/.gitignore +++ b/src/login/.gitignore @@ -1,4 +1,5 @@ /logind-gperf.c +/logind.conf /org.freedesktop.login1.policy /71-seat.rules /73-seat-late.rules diff --git a/src/login/70-uaccess.rules b/src/login/70-uaccess.rules index 694df2cfc8..886c5bfcdf 100644 --- a/src/login/70-uaccess.rules +++ b/src/login/70-uaccess.rules @@ -42,8 +42,9 @@ SUBSYSTEM=="firewire", ATTR{units}=="*0x00b09d:0x00010*", TAG+="uaccess" SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x010001*", TAG+="uaccess" SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x014001*", TAG+="uaccess" -# DRI video devices +# DRI and frame buffer video devices SUBSYSTEM=="drm", KERNEL=="card*|renderD*", TAG+="uaccess" +SUBSYSTEM=="graphics", KERNEL=="fb*", TAG+="uaccess" # KVM SUBSYSTEM=="misc", KERNEL=="kvm", TAG+="uaccess" @@ -75,4 +76,7 @@ SUBSYSTEM=="usb", ENV{ID_MEDIA_PLAYER}=="?*", TAG+="uaccess" # software-defined radio communication devices ENV{ID_SOFTWARE_RADIO}=="?*", TAG+="uaccess" +# 3D printers, CNC machines, laser cutters, 3D scanners, etc. +ENV{ID_MAKER_TOOL}=="?*", TAG+="uaccess" + LABEL="uaccess_end" diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 6ad3d089bd..1c75565636 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -27,6 +27,7 @@ #include "alloc-util.h" #include "bus-error.h" +#include "bus-unit-util.h" #include "bus-util.h" #include "cgroup-show.h" #include "cgroup-util.h" @@ -48,6 +49,7 @@ static char **arg_property = NULL; static bool arg_all = false; +static bool arg_value = false; static bool arg_full = false; static bool arg_no_pager = false; static bool arg_legend = true; @@ -59,14 +61,6 @@ static bool arg_ask_password = true; static unsigned arg_lines = 10; static OutputMode arg_output = OUTPUT_SHORT; -static void pager_open_if_enabled(void) { - - if (arg_no_pager) - return; - - pager_open(false); -} - static void polkit_agent_open_if_enabled(void) { /* Open the polkit agent as a child process if necessary */ @@ -101,7 +95,7 @@ static int list_sessions(int argc, char *argv[], void *userdata) { assert(bus); assert(argv); - pager_open_if_enabled(); + pager_open(arg_no_pager, false); r = sd_bus_call_method( bus, @@ -148,7 +142,7 @@ static int list_users(int argc, char *argv[], void *userdata) { assert(bus); assert(argv); - pager_open_if_enabled(); + pager_open(arg_no_pager, false); r = sd_bus_call_method( bus, @@ -194,7 +188,7 @@ static int list_seats(int argc, char *argv[], void *userdata) { assert(bus); assert(argv); - pager_open_if_enabled(); + pager_open(arg_no_pager, false); r = sd_bus_call_method( bus, @@ -234,18 +228,15 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_free_ char *path = NULL; const char *cgroup; - int r; unsigned c; + int r; assert(bus); assert(unit); - if (arg_transport != BUS_TRANSPORT_LOCAL) - return 0; - path = unit_dbus_path_from_name(unit); if (!path) - return -ENOMEM; + return log_oom(); r = sd_bus_get_property( bus, @@ -253,27 +244,40 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit path, interface, "ControlGroup", - &error, &reply, "s"); + &error, + &reply, + "s"); if (r < 0) - return r; + return log_error_errno(r, "Failed to query ControlGroup: %s", bus_error_message(&error, r)); r = sd_bus_message_read(reply, "s", &cgroup); if (r < 0) - return r; + return bus_log_parse_error(r); if (isempty(cgroup)) return 0; - if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0) - return 0; - c = columns(); if (c > 18) c -= 18; else c = 0; - show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags()); + r = unit_show_processes(bus, unit, cgroup, "\t\t ", c, get_output_flags(), &error); + if (r == -EBADR) { + + if (arg_transport == BUS_TRANSPORT_REMOTE) + return 0; + + /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */ + + if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0) + return 0; + + show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, &leader, leader > 0, get_output_flags()); + } else if (r < 0) + return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r)); + return 0; } @@ -300,6 +304,7 @@ typedef struct SessionStatusInfo { typedef struct UserStatusInfo { uid_t uid; + bool linger; char *name; struct dual_timestamp timestamp; char *state; @@ -558,6 +563,7 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) static const struct bus_properties_map map[] = { { "Name", "s", NULL, offsetof(UserStatusInfo, name) }, + { "Linger", "b", NULL, offsetof(UserStatusInfo, linger) }, { "Slice", "s", NULL, offsetof(UserStatusInfo, slice) }, { "State", "s", NULL, offsetof(UserStatusInfo, state) }, { "UID", "u", NULL, offsetof(UserStatusInfo, uid) }, @@ -602,16 +608,16 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) char **l; printf("\tSessions:"); - STRV_FOREACH(l, i.sessions) { - if (streq_ptr(*l, i.display)) - printf(" *%s", *l); - else - printf(" %s", *l); - } + STRV_FOREACH(l, i.sessions) + printf(" %s%s", + streq_ptr(*l, i.display) ? "*" : "", + *l); printf("\n"); } + printf("\t Linger: %s\n", yes_no(i.linger)); + if (i.slice) { printf("\t Unit: %s\n", i.slice); show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i.slice, 0); @@ -687,6 +693,14 @@ static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) return 0; } +#define property(name, fmt, ...) \ + do { \ + if (arg_value) \ + printf(fmt "\n", __VA_ARGS__); \ + else \ + printf("%s=" fmt "\n", name, __VA_ARGS__); \ + } while(0) + static int print_property(const char *name, sd_bus_message *m, const char *contents) { int r; @@ -710,7 +724,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte return bus_log_parse_error(r); if (arg_all || !isempty(s)) - printf("%s=%s\n", name, s); + property(name, "%s", s); return 0; @@ -726,8 +740,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte return -EINVAL; } - printf("%s=" UID_FMT "\n", name, uid); - + property(name, UID_FMT, uid); return 0; } @@ -743,14 +756,16 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte if (r < 0) return bus_log_parse_error(r); - printf("%s=", name); + if (!arg_value) + printf("%s=", name); while ((r = sd_bus_message_read(m, "(so)", &s, NULL)) > 0) { printf("%s%s", space ? " " : "", s); space = true; } - printf("\n"); + if (space || !arg_value) + printf("\n"); if (r < 0) return bus_log_parse_error(r); @@ -765,7 +780,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte break; } - r = bus_print_property(name, m, arg_all); + r = bus_print_property(name, m, arg_value, arg_all); if (r < 0) return bus_log_parse_error(r); @@ -858,7 +873,7 @@ static int show_session(int argc, char *argv[], void *userdata) { properties = !strstr(argv[0], "status"); - pager_open_if_enabled(); + pager_open(arg_no_pager, false); if (argc <= 1) { /* If not argument is specified inspect the manager @@ -914,7 +929,7 @@ static int show_user(int argc, char *argv[], void *userdata) { properties = !strstr(argv[0], "status"); - pager_open_if_enabled(); + pager_open(arg_no_pager, false); if (argc <= 1) { /* If not argument is specified inspect the manager @@ -974,7 +989,7 @@ static int show_seat(int argc, char *argv[], void *userdata) { properties = !strstr(argv[0], "status"); - pager_open_if_enabled(); + pager_open(arg_no_pager, false); if (argc <= 1) { /* If not argument is specified inspect the manager @@ -1338,6 +1353,7 @@ static int help(int argc, char *argv[], void *userdata) { " -M --machine=CONTAINER Operate on local container\n" " -p --property=NAME Show only properties by this name\n" " -a --all Show all properties, including empty ones\n" + " --value When showing properties, only print the value\n" " -l --full Do not ellipsize output\n" " --kill-who=WHO Who to send signal to\n" " -s --signal=SIGNAL Which signal to send\n" @@ -1379,6 +1395,7 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, + ARG_VALUE, ARG_NO_PAGER, ARG_NO_LEGEND, ARG_KILL_WHO, @@ -1390,6 +1407,7 @@ static int parse_argv(int argc, char *argv[]) { { "version", no_argument, NULL, ARG_VERSION }, { "property", required_argument, NULL, 'p' }, { "all", no_argument, NULL, 'a' }, + { "value", no_argument, NULL, ARG_VALUE }, { "full", no_argument, NULL, 'l' }, { "no-pager", no_argument, NULL, ARG_NO_PAGER }, { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, @@ -1435,6 +1453,10 @@ static int parse_argv(int argc, char *argv[]) { arg_all = true; break; + case ARG_VALUE: + arg_value = true; + break; + case 'l': arg_full = true; break; diff --git a/src/login/logind-core.c b/src/login/logind-core.c index 8bdb3a9a38..cbf8d757fe 100644 --- a/src/login/logind-core.c +++ b/src/login/logind-core.c @@ -364,16 +364,16 @@ bool manager_shall_kill(Manager *m, const char *user) { assert(m); assert(user); - if (!m->kill_user_processes) + if (!m->kill_exclude_users && streq(user, "root")) return false; if (strv_contains(m->kill_exclude_users, user)) return false; - if (strv_isempty(m->kill_only_users)) - return true; + if (!strv_isempty(m->kill_only_users)) + return strv_contains(m->kill_only_users, user); - return strv_contains(m->kill_only_users, user); + return m->kill_user_processes; } static int vt_is_busy(unsigned int vtnr) { diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 1d3133ee25..0a84d75e24 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -265,6 +265,42 @@ static int property_get_docked( return sd_bus_message_append(reply, "b", manager_is_docked_or_external_displays(m)); } +static int property_get_current_sessions( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(bus); + assert(reply); + assert(m); + + return sd_bus_message_append(reply, "t", (uint64_t) hashmap_size(m->sessions)); +} + +static int property_get_current_inhibitors( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(bus); + assert(reply); + assert(m); + + return sd_bus_message_append(reply, "t", (uint64_t) hashmap_size(m->inhibitors)); +} + static int method_get_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_free_ char *p = NULL; Manager *m = userdata; @@ -725,6 +761,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus m->seat0->positions[vtnr]->class != SESSION_GREETER) return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already occupied by a session"); + if (hashmap_size(m->sessions) >= m->sessions_max) + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.", m->sessions_max); + audit_session_from_pid(leader, &audit_id); if (audit_id > 0) { /* Keep our session IDs and the audit session IDs in sync */ @@ -1077,11 +1116,11 @@ static int method_terminate_seat(sd_bus_message *message, void *userdata, sd_bus static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_free_ char *cc = NULL; Manager *m = userdata; - int b, r; + int r, b, interactive; struct passwd *pw; const char *path; uint32_t uid; - int interactive; + bool self = false; assert(message); assert(m); @@ -1102,6 +1141,8 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu if (r < 0) return r; + self = true; + } else if (!uid_is_valid(uid)) return -EINVAL; @@ -1113,7 +1154,7 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, - "org.freedesktop.login1.set-user-linger", + self ? "org.freedesktop.login1.set-self-linger" : "org.freedesktop.login1.set-user-linger", NULL, interactive, UID_INVALID, @@ -2440,6 +2481,9 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error if (r < 0) return r; + if (hashmap_size(m->inhibitors) >= m->inhibitors_max) + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of inhibitors (%" PRIu64 ") reached, refusing further inhibitors.", m->inhibitors_max); + do { id = mfree(id); @@ -2510,6 +2554,13 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_PROPERTY("PreparingForSleep", "b", property_get_preparing, 0, 0), SD_BUS_PROPERTY("ScheduledShutdown", "(st)", property_get_scheduled_shutdown, 0, 0), SD_BUS_PROPERTY("Docked", "b", property_get_docked, 0, 0), + SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(Manager, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RuntimeDirectorySize", "t", bus_property_get_size, offsetof(Manager, runtime_dir_size), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("InhibitorsMax", "t", NULL, offsetof(Manager, inhibitors_max), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NCurrentInhibitors", "t", property_get_current_inhibitors, 0, 0), + SD_BUS_PROPERTY("SessionsMax", "t", NULL, offsetof(Manager, sessions_max), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NCurrentSessions", "t", property_get_current_sessions, 0, 0), + SD_BUS_PROPERTY("UserTasksMax", "t", NULL, offsetof(Manager, user_tasks_max), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_METHOD("GetSession", "s", "o", method_get_session, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetSessionByPID", "u", "o", method_get_session_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf index 8552c464cc..6bd08adc05 100644 --- a/src/login/logind-gperf.gperf +++ b/src/login/logind-gperf.gperf @@ -34,4 +34,6 @@ Login.IdleAction, config_parse_handle_action, 0, offsetof(Manag Login.IdleActionSec, config_parse_sec, 0, offsetof(Manager, idle_action_usec) Login.RuntimeDirectorySize, config_parse_tmpfs_size, 0, offsetof(Manager, runtime_dir_size) Login.RemoveIPC, config_parse_bool, 0, offsetof(Manager, remove_ipc) +Login.InhibitorsMax, config_parse_uint64, 0, offsetof(Manager, inhibitors_max) +Login.SessionsMax, config_parse_uint64, 0, offsetof(Manager, sessions_max) Login.UserTasksMax, config_parse_uint64, 0, offsetof(Manager, user_tasks_max) diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c index a0e3ba2b7c..6c78e0dddc 100644 --- a/src/login/logind-inhibit.c +++ b/src/login/logind-inhibit.c @@ -317,7 +317,7 @@ int inhibitor_create_fifo(Inhibitor *i) { if (r < 0) return r; - r = sd_event_source_set_priority(i->event_source, SD_EVENT_PRIORITY_IDLE); + r = sd_event_source_set_priority(i->event_source, SD_EVENT_PRIORITY_IDLE-10); if (r < 0) return r; } diff --git a/src/login/logind-seat-dbus.c b/src/login/logind-seat-dbus.c index 3cee10d009..f934a5326a 100644 --- a/src/login/logind-seat-dbus.c +++ b/src/login/logind-seat-dbus.c @@ -306,7 +306,7 @@ const sd_bus_vtable seat_vtable[] = { SD_BUS_PROPERTY("CanMultiSession", "b", property_get_can_multi_session, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("CanTTY", "b", property_get_can_tty, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("CanGraphical", "b", property_get_can_graphical, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0), SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c index ff9170683b..22dea5db1f 100644 --- a/src/login/logind-session-dbus.c +++ b/src/login/logind-session-dbus.c @@ -28,6 +28,7 @@ #include "logind-session-device.h" #include "logind-session.h" #include "logind.h" +#include "signal-util.h" #include "strv.h" #include "util.h" @@ -179,6 +180,24 @@ static int property_get_idle_since_hint( return sd_bus_message_append(reply, "t", u); } +static int property_get_locked_hint( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Session *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "b", session_get_locked_hint(s) > 0); +} + int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) { Session *s = userdata; int r; @@ -278,6 +297,35 @@ static int method_set_idle_hint(sd_bus_message *message, void *userdata, sd_bus_ return sd_bus_reply_method_return(message, NULL); } +static int method_set_locked_hint(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + Session *s = userdata; + uid_t uid; + int r, b; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_euid(creds, &uid); + if (r < 0) + return r; + + if (uid != 0 && uid != s->user->uid) + return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set locked hint"); + + session_set_locked_hint(s, b); + + return sd_bus_reply_method_return(message, NULL); +} + int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) { Session *s = userdata; const char *swho; @@ -300,7 +348,7 @@ int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho); } - if (signo <= 0 || signo >= _NSIG) + if (!SIGNAL_VALID(signo)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); r = bus_verify_polkit_async( @@ -486,12 +534,14 @@ const sd_bus_vtable session_vtable[] = { SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("LockedHint", "b", property_get_locked_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_METHOD("Terminate", NULL, NULL, bus_session_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Activate", NULL, NULL, bus_session_method_activate, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Lock", NULL, NULL, bus_session_method_lock, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Unlock", NULL, NULL, bus_session_method_lock, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetIdleHint", "b", NULL, method_set_idle_hint, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetLockedHint", "b", NULL, method_set_locked_hint, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Kill", "si", NULL, bus_session_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("TakeControl", "b", NULL, method_take_control, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ReleaseControl", NULL, NULL, method_release_control, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/login/logind-session.c b/src/login/logind-session.c index 417b7f5d98..1e0666884a 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -446,17 +446,10 @@ int session_load(Session *s) { safe_close(fd); } - if (realtime) { - unsigned long long l; - if (sscanf(realtime, "%llu", &l) > 0) - s->timestamp.realtime = l; - } - - if (monotonic) { - unsigned long long l; - if (sscanf(monotonic, "%llu", &l) > 0) - s->timestamp.monotonic = l; - } + if (realtime) + timestamp_deserialize(realtime, &s->timestamp.realtime); + if (monotonic) + timestamp_deserialize(monotonic, &s->timestamp.monotonic); if (controller) { if (bus_name_has_owner(s->manager->bus, controller, NULL) > 0) @@ -520,7 +513,7 @@ static int session_start_scope(Session *s) { if (!scope) return log_oom(); - description = strjoina("Session ", s->id, " of user ", s->user->name, NULL); + description = strjoina("Session ", s->id, " of user ", s->user->name); r = manager_start_scope( s->manager, @@ -804,7 +797,7 @@ int session_get_idle_hint(Session *s, dual_timestamp *t) { /* Graphical sessions should really implement a real * idle hint logic */ - if (s->display) + if (SESSION_TYPE_IS_GRAPHICAL(s->type)) goto dont_know; /* For sessions with an explicitly configured tty, let's check @@ -859,6 +852,23 @@ void session_set_idle_hint(Session *s, bool b) { manager_send_changed(s->manager, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL); } +int session_get_locked_hint(Session *s) { + assert(s); + + return s->locked_hint; +} + +void session_set_locked_hint(Session *s, bool b) { + assert(s); + + if (s->locked_hint == b) + return; + + s->locked_hint = b; + + session_send_changed(s, "LockedHint", NULL); +} + static int session_dispatch_fifo(sd_event_source *es, int fd, uint32_t revents, void *userdata) { Session *s = userdata; @@ -904,7 +914,9 @@ int session_create_fifo(Session *s) { if (r < 0) return r; - r = sd_event_source_set_priority(s->fifo_event_source, SD_EVENT_PRIORITY_IDLE); + /* Let's make sure we noticed dead sessions before we process new bus requests (which might create new + * sessions). */ + r = sd_event_source_set_priority(s->fifo_event_source, SD_EVENT_PRIORITY_NORMAL-10); if (r < 0) return r; } diff --git a/src/login/logind-session.h b/src/login/logind-session.h index e24b808474..ffb7cd2d41 100644 --- a/src/login/logind-session.h +++ b/src/login/logind-session.h @@ -105,6 +105,8 @@ struct Session { bool idle_hint; dual_timestamp idle_hint_timestamp; + bool locked_hint; + bool in_gc_queue:1; bool started:1; bool stopping:1; @@ -132,6 +134,8 @@ int session_activate(Session *s); bool session_is_active(Session *s); int session_get_idle_hint(Session *s, dual_timestamp *t); void session_set_idle_hint(Session *s, bool b); +int session_get_locked_hint(Session *s); +void session_set_locked_hint(Session *s, bool b); int session_create_fifo(Session *s); int session_start(Session *s); int session_stop(Session *s, bool force); diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c index fd98c7beca..af6392e025 100644 --- a/src/login/logind-user-dbus.c +++ b/src/login/logind-user-dbus.c @@ -25,6 +25,7 @@ #include "formats-util.h" #include "logind-user.h" #include "logind.h" +#include "signal-util.h" #include "strv.h" #include "user-util.h" @@ -222,7 +223,7 @@ int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error * if (r < 0) return r; - if (signo <= 0 || signo >= _NSIG) + if (!SIGNAL_VALID(signo)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); r = user_kill(u, signo); @@ -244,7 +245,7 @@ const sd_bus_vtable user_vtable[] = { SD_BUS_PROPERTY("Slice", "s", NULL, offsetof(User, slice), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Display", "(so)", property_get_display, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), - SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0), SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), diff --git a/src/login/logind-user.c b/src/login/logind-user.c index 6b9c69cc45..a826321bf0 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -321,17 +321,10 @@ int user_load(User *u) { if (s && s->display && display_is_local(s->display)) u->display = s; - if (realtime) { - unsigned long long l; - if (sscanf(realtime, "%llu", &l) > 0) - u->timestamp.realtime = l; - } - - if (monotonic) { - unsigned long long l; - if (sscanf(monotonic, "%llu", &l) > 0) - u->timestamp.monotonic = l; - } + if (realtime) + timestamp_deserialize(realtime, &u->timestamp.realtime); + if (monotonic) + timestamp_deserialize(monotonic, &u->timestamp.monotonic); return r; } diff --git a/src/login/logind-utmp.c b/src/login/logind-utmp.c index 11a91c3947..29ab00eb1f 100644 --- a/src/login/logind-utmp.c +++ b/src/login/logind-utmp.c @@ -65,7 +65,7 @@ bool logind_wall_tty_filter(const char *tty, void *userdata) { assert(m); - if (!startswith(tty, "/dev/")) + if (!startswith(tty, "/dev/") || !m->scheduled_shutdown_tty) return true; return !streq(tty + 5, m->scheduled_shutdown_tty); diff --git a/src/login/logind.c b/src/login/logind.c index 933602eb08..caf149cfb7 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -41,17 +41,7 @@ static void manager_free(Manager *m); -static Manager *manager_new(void) { - Manager *m; - int r; - - m = new0(Manager, 1); - if (!m) - return NULL; - - m->console_active_fd = -1; - m->reserve_vt_fd = -1; - +static void manager_reset_config(Manager *m) { m->n_autovts = 6; m->reserve_vt = 6; m->remove_ipc = true; @@ -61,15 +51,39 @@ static Manager *manager_new(void) { m->handle_hibernate_key = HANDLE_HIBERNATE; m->handle_lid_switch = HANDLE_SUSPEND; m->handle_lid_switch_docked = HANDLE_IGNORE; + m->power_key_ignore_inhibited = false; + m->suspend_key_ignore_inhibited = false; + m->hibernate_key_ignore_inhibited = false; m->lid_switch_ignore_inhibited = true; + m->holdoff_timeout_usec = 30 * USEC_PER_SEC; m->idle_action_usec = 30 * USEC_PER_MINUTE; m->idle_action = HANDLE_IGNORE; - m->idle_action_not_before_usec = now(CLOCK_MONOTONIC); m->runtime_dir_size = PAGE_ALIGN((size_t) (physical_memory() / 10)); /* 10% */ - m->user_tasks_max = UINT64_C(12288); + m->user_tasks_max = 12288; + m->sessions_max = 8192; + m->inhibitors_max = 8192; + + m->kill_user_processes = KILL_USER_PROCESSES; + + m->kill_only_users = strv_free(m->kill_only_users); + m->kill_exclude_users = strv_free(m->kill_exclude_users); +} + +static Manager *manager_new(void) { + Manager *m; + int r; + + m = new0(Manager, 1); + if (!m) + return NULL; + + m->console_active_fd = -1; + m->reserve_vt_fd = -1; + + m->idle_action_not_before_usec = now(CLOCK_MONOTONIC); m->devices = hashmap_new(&string_hash_ops); m->seats = hashmap_new(&string_hash_ops); @@ -84,10 +98,6 @@ static Manager *manager_new(void) { if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || !m->user_units || !m->session_units) goto fail; - m->kill_exclude_users = strv_new("root", NULL); - if (!m->kill_exclude_users) - goto fail; - m->udev = udev_new(); if (!m->udev) goto fail; @@ -98,6 +108,8 @@ static Manager *manager_new(void) { sd_event_set_watchdog(m->event, true); + manager_reset_config(m); + return m; fail: @@ -676,7 +688,7 @@ static int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to register name: %m"); - r = sd_bus_attach_event(m->bus, m->event, 0); + r = sd_bus_attach_event(m->bus, m->event, SD_EVENT_PRIORITY_NORMAL); if (r < 0) return log_error_errno(r, "Failed to attach bus to event loop: %m"); @@ -986,6 +998,30 @@ static int manager_dispatch_idle_action(sd_event_source *s, uint64_t t, void *us return 0; } +static int manager_parse_config_file(Manager *m) { + assert(m); + + return config_parse_many(PKGSYSCONFDIR "/logind.conf", + 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) { + Manager *m = userdata; + int r; + + manager_reset_config(m); + r = manager_parse_config_file(m); + if (r < 0) + log_warning_errno(r, "Failed to parse config file, using defaults: %m"); + else + log_info("Config file reloaded."); + + return 0; +} + static int manager_startup(Manager *m) { int r; Seat *seat; @@ -997,6 +1033,12 @@ static int manager_startup(Manager *m) { assert(m); + assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGHUP, -1) >= 0); + + r = sd_event_add_signal(m->event, NULL, SIGHUP, manager_dispatch_reload_signal, m); + if (r < 0) + return log_error_errno(r, "Failed to register SIGHUP handler: %m"); + /* Connect to console */ r = manager_connect_console(m); if (r < 0) @@ -1099,16 +1141,6 @@ static int manager_run(Manager *m) { } } -static int manager_parse_config_file(Manager *m) { - assert(m); - - return config_parse_many(PKGSYSCONFDIR "/logind.conf", - CONF_PATHS_NULSTR("systemd/logind.conf.d"), - "Login\0", - config_item_perf_lookup, logind_gperf_lookup, - false, m); -} - int main(int argc, char *argv[]) { Manager *m = NULL; int r; @@ -1126,7 +1158,7 @@ int main(int argc, char *argv[]) { goto finish; } - r = mac_selinux_init("/run"); + r = mac_selinux_init(); if (r < 0) { log_error_errno(r, "Could not initialize labelling: %m"); goto finish; diff --git a/src/login/logind.conf b/src/login/logind.conf.in index 6095e482ac..32c0844cb6 100644 --- a/src/login/logind.conf +++ b/src/login/logind.conf.in @@ -14,7 +14,7 @@ [Login] #NAutoVTs=6 #ReserveVT=6 -#KillUserProcesses=no +#KillUserProcesses=@KILL_USER_PROCESSES@ #KillOnlyUsers= #KillExcludeUsers=root #InhibitDelayMaxSec=5 @@ -32,4 +32,6 @@ #IdleActionSec=30min #RuntimeDirectorySize=10% #RemoveIPC=yes +#InhibitorsMax=8192 +#SessionsMax=8192 #UserTasksMax=12288 diff --git a/src/login/logind.h b/src/login/logind.h index 6748af3c07..90431eb4b0 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -133,6 +133,8 @@ struct Manager { size_t runtime_dir_size; uint64_t user_tasks_max; + uint64_t sessions_max; + uint64_t inhibitors_max; }; int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device); diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf index 1662d4c428..c89e40457e 100644 --- a/src/login/org.freedesktop.login1.conf +++ b/src/login/org.freedesktop.login1.conf @@ -234,6 +234,10 @@ <allow send_destination="org.freedesktop.login1" send_interface="org.freedesktop.login1.Session" + send_member="SetLockedHint"/> + + <allow send_destination="org.freedesktop.login1" + send_interface="org.freedesktop.login1.Session" send_member="Kill"/> <allow send_destination="org.freedesktop.login1" diff --git a/src/login/org.freedesktop.login1.policy.in b/src/login/org.freedesktop.login1.policy.in index 23326bb79f..1fa6441629 100644 --- a/src/login/org.freedesktop.login1.policy.in +++ b/src/login/org.freedesktop.login1.policy.in @@ -111,6 +111,14 @@ </defaults> </action> + <action id="org.freedesktop.login1.set-self-linger"> + <_description>Allow non-logged-in user to run programs</_description> + <_message>Explicit request is required to run programs as a non-logged-in user.</_message> + <defaults> + <allow_any>yes</allow_any> + </defaults> + </action> + <action id="org.freedesktop.login1.set-user-linger"> <_description>Allow non-logged-in users to run programs</_description> <_message>Authentication is required to run programs as a non-logged-in user.</_message> diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index 40e246bb06..98dc201340 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -150,7 +150,7 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_ if (fd < 0) return -errno; - if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) + if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) return -errno; r = getpeercred(fd, &ucred); diff --git a/src/login/sysfs-show.c b/src/login/sysfs-show.c index bd603e297d..29785e2f11 100644 --- a/src/login/sysfs-show.c +++ b/src/login/sysfs-show.c @@ -110,7 +110,7 @@ static int show_sysfs_one( if (!k) return -ENOMEM; - printf("%s%s%s\n", prefix, draw_special_char(lookahead ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT), k); + printf("%s%s%s\n", prefix, special_glyph(lookahead ? TREE_BRANCH : TREE_RIGHT), k); if (asprintf(&l, "%s%s:%s%s%s%s", @@ -124,13 +124,13 @@ static int show_sysfs_one( if (!k) return -ENOMEM; - printf("%s%s%s\n", prefix, lookahead ? draw_special_char(DRAW_TREE_VERTICAL) : " ", k); + printf("%s%s%s\n", prefix, lookahead ? special_glyph(TREE_VERTICAL) : " ", k); *item = next; if (*item) { _cleanup_free_ char *p = NULL; - p = strappend(prefix, lookahead ? draw_special_char(DRAW_TREE_VERTICAL) : " "); + p = strappend(prefix, lookahead ? special_glyph(TREE_VERTICAL) : " "); if (!p) return -ENOMEM; @@ -183,7 +183,7 @@ int show_sysfs(const char *seat, const char *prefix, unsigned n_columns) { if (first) show_sysfs_one(udev, seat, &first, "/", prefix, n_columns); else - printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_RIGHT), "(none)"); + printf("%s%s%s\n", prefix, special_glyph(TREE_RIGHT), "(none)"); return r; } diff --git a/src/login/systemd-user.m4 b/src/login/systemd-user.m4 index 7933508f2b..f188a8e548 100644 --- a/src/login/systemd-user.m4 +++ b/src/login/systemd-user.m4 @@ -8,4 +8,5 @@ m4_ifdef(`HAVE_SELINUX', session required pam_selinux.so close session required pam_selinux.so nottys open )m4_dnl +session required pam_loginuid.so session include system-auth diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c index 73f5112c4d..0eed9b81bb 100644 --- a/src/machine/image-dbus.c +++ b/src/machine/image-dbus.c @@ -20,9 +20,11 @@ #include "alloc-util.h" #include "bus-label.h" #include "bus-util.h" +#include "fd-util.h" #include "image-dbus.h" #include "io-util.h" #include "machine-image.h" +#include "process-util.h" #include "strv.h" #include "user-util.h" @@ -33,13 +35,18 @@ int bus_image_method_remove( void *userdata, sd_bus_error *error) { + _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; Image *image = userdata; Manager *m = image->userdata; + pid_t child; int r; assert(message); assert(image); + if (m->n_operations >= OPERATIONS_MAX) + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations."); + r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, @@ -54,11 +61,35 @@ int bus_image_method_remove( if (r == 0) return 1; /* Will call us back */ - r = image_remove(image); - if (r < 0) + if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); + + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + if (child == 0) { + errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); + + r = image_remove(image); + if (r < 0) { + (void) write(errno_pipe_fd[1], &r, sizeof(r)); + _exit(EXIT_FAILURE); + } + + _exit(EXIT_SUCCESS); + } + + errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); + + r = operation_new(m, NULL, child, message, errno_pipe_fd[0]); + if (r < 0) { + (void) sigkill_wait(child); return r; + } - return sd_bus_reply_method_return(message, NULL); + errno_pipe_fd[0] = -1; + + return 1; } int bus_image_method_rename( @@ -107,13 +138,19 @@ int bus_image_method_clone( void *userdata, sd_bus_error *error) { + _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; Image *image = userdata; Manager *m = image->userdata; const char *new_name; int r, read_only; + pid_t child; assert(message); assert(image); + assert(m); + + if (m->n_operations >= OPERATIONS_MAX) + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations."); r = sd_bus_message_read(message, "sb", &new_name, &read_only); if (r < 0) @@ -136,11 +173,35 @@ int bus_image_method_clone( if (r == 0) return 1; /* Will call us back */ - r = image_clone(image, new_name, read_only); - if (r < 0) + if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); + + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + if (child == 0) { + errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); + + r = image_clone(image, new_name, read_only); + if (r < 0) { + (void) write(errno_pipe_fd[1], &r, sizeof(r)); + _exit(EXIT_FAILURE); + } + + _exit(EXIT_SUCCESS); + } + + errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); + + r = operation_new(m, NULL, child, message, errno_pipe_fd[0]); + if (r < 0) { + (void) sigkill_wait(child); return r; + } - return sd_bus_reply_method_return(message, NULL); + errno_pipe_fd[0] = -1; + + return 1; } int bus_image_method_mark_read_only( diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index 71f20b3f07..7b9aa66d63 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -20,6 +20,7 @@ #include <errno.h> #include <string.h> #include <sys/mount.h> +#include <sys/wait.h> /* When we include libgen.h because we need dirname() we immediately * undefine basename() since libgen.h defines it as a macro to the POSIX @@ -45,6 +46,7 @@ #include "mkdir.h" #include "path-util.h" #include "process-util.h" +#include "signal-util.h" #include "strv.h" #include "terminal-util.h" #include "user-util.h" @@ -165,7 +167,7 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho); } - if (signo <= 0 || signo >= _NSIG) + if (!SIGNAL_VALID(signo)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); r = bus_verify_polkit_async( @@ -728,7 +730,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu return r; /* Name and mode */ - unit = strjoina("container-shell@", p, ".service", NULL); + unit = strjoina("container-shell@", p, ".service"); r = sd_bus_message_append(tm, "ss", unit, "fail"); if (r < 0) return r; @@ -1083,52 +1085,11 @@ finish: return r; } -static int machine_operation_done(sd_event_source *s, const siginfo_t *si, void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - MachineOperation *o = userdata; - int r; - - assert(o); - assert(si); - - o->pid = 0; - - if (si->si_code != CLD_EXITED) { - r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child died abnormally."); - goto fail; - } - - if (si->si_status != EXIT_SUCCESS) { - if (read(o->errno_fd, &r, sizeof(r)) == sizeof(r)) - r = sd_bus_error_set_errnof(&error, r, "%m"); - else - r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed."); - - goto fail; - } - - r = sd_bus_reply_method_return(o->message, NULL); - if (r < 0) - log_error_errno(r, "Failed to reply to message: %m"); - - machine_operation_unref(o); - return 0; - -fail: - r = sd_bus_reply_method_error(o->message, &error); - if (r < 0) - log_error_errno(r, "Failed to reply to message: %m"); - - machine_operation_unref(o); - return 0; -} - int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) { const char *src, *dest, *host_path, *container_path, *host_basename, *host_dirname, *container_basename, *container_dirname; _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; _cleanup_close_ int hostfd = -1; Machine *m = userdata; - MachineOperation *o; bool copy_from; pid_t child; char *t; @@ -1137,7 +1098,7 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro assert(message); assert(m); - if (m->n_operations >= MACHINE_OPERATIONS_MAX) + if (m->manager->n_operations >= OPERATIONS_MAX) return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing copies."); if (m->class != MACHINE_CONTAINER) @@ -1247,29 +1208,107 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); - /* Copying might take a while, hence install a watch the - * child, and return */ + /* Copying might take a while, hence install a watch on the child, and return */ - o = new0(MachineOperation, 1); - if (!o) - return log_oom(); - - o->pid = child; - o->message = sd_bus_message_ref(message); - o->errno_fd = errno_pipe_fd[0]; + r = operation_new(m->manager, m, child, message, errno_pipe_fd[0]); + if (r < 0) { + (void) sigkill_wait(child); + return r; + } errno_pipe_fd[0] = -1; - r = sd_event_add_child(m->manager->event, &o->event_source, child, WEXITED, machine_operation_done, o); - if (r < 0) { - machine_operation_unref(o); - return log_oom(); + return 1; +} + +int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_close_ int fd = -1; + Machine *m = userdata; + int r; + + assert(message); + assert(m); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.machine1.manage-machines", + NULL, + false, + UID_INVALID, + &m->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + switch (m->class) { + + case MACHINE_HOST: + fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (fd < 0) + return -errno; + + break; + + case MACHINE_CONTAINER: { + _cleanup_close_ int mntns_fd = -1, root_fd = -1; + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + siginfo_t si; + pid_t child; + + r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd); + if (r < 0) + return r; + + if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) + return -errno; + + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + + if (child == 0) { + _cleanup_close_ int dfd = -1; + + pair[0] = safe_close(pair[0]); + + r = namespace_enter(-1, mntns_fd, -1, -1, root_fd); + if (r < 0) + _exit(EXIT_FAILURE); + + dfd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (dfd < 0) + _exit(EXIT_FAILURE); + + r = send_one_fd(pair[1], dfd, 0); + dfd = safe_close(dfd); + if (r < 0) + _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); + } + + pair[1] = safe_close(pair[1]); + + r = wait_for_terminate(child, &si); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); + + fd = receive_one_fd(pair[0], MSG_DONTWAIT); + if (fd < 0) + return fd; + + break; } - LIST_PREPEND(operations, m->operations, o); - m->n_operations++; - o->machine = m; + default: + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening the root directory is only supported on container machines."); + } - return 1; + return sd_bus_reply_method_return(message, "h", fd); } const sd_bus_vtable machine_vtable[] = { @@ -1295,6 +1334,7 @@ const sd_bus_vtable machine_vtable[] = { SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyFrom", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyTo", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenRootDirectory", NULL, "h", bus_machine_method_open_root_directory, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END }; diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h index 3a8162b171..241b23c7ec 100644 --- a/src/machine/machine-dbus.h +++ b/src/machine/machine-dbus.h @@ -38,6 +38,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_machine_method_open_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error); int machine_send_signal(Machine *m, bool new_machine); int machine_send_create_reply(Machine *m, sd_bus_error *error); diff --git a/src/machine/machine.c b/src/machine/machine.c index 406d5a4b85..c1fae57084 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -89,7 +89,7 @@ void machine_free(Machine *m) { assert(m); while (m->operations) - machine_operation_unref(m->operations); + operation_free(m->operations); if (m->in_gc_queue) LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m); @@ -299,17 +299,10 @@ int machine_load(Machine *m) { m->class = c; } - if (realtime) { - unsigned long long l; - if (sscanf(realtime, "%llu", &l) > 0) - m->timestamp.realtime = l; - } - - if (monotonic) { - unsigned long long l; - if (sscanf(monotonic, "%llu", &l) > 0) - m->timestamp.monotonic = l; - } + if (realtime) + timestamp_deserialize(realtime, &m->timestamp.realtime); + if (monotonic) + timestamp_deserialize(monotonic, &m->timestamp.monotonic); if (netif) { size_t allocated = 0, nr = 0; @@ -317,7 +310,7 @@ int machine_load(Machine *m) { int *ni = NULL; p = netif; - for(;;) { + for (;;) { _cleanup_free_ char *word = NULL; int ifi; @@ -603,28 +596,6 @@ int machine_open_terminal(Machine *m, const char *path, int mode) { } } -MachineOperation *machine_operation_unref(MachineOperation *o) { - if (!o) - return NULL; - - sd_event_source_unref(o->event_source); - - safe_close(o->errno_fd); - - if (o->pid > 1) - (void) kill(o->pid, SIGKILL); - - sd_bus_message_unref(o->message); - - if (o->machine) { - LIST_REMOVE(operations, o->machine->operations, o); - o->machine->n_operations--; - } - - free(o); - return NULL; -} - void machine_release_unit(Machine *m) { assert(m); diff --git a/src/machine/machine.h b/src/machine/machine.h index 1d8cc5911a..e5d75361a9 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -20,11 +20,11 @@ ***/ typedef struct Machine Machine; -typedef struct MachineOperation MachineOperation; typedef enum KillWho KillWho; #include "list.h" #include "machined.h" +#include "operation.h" typedef enum MachineState { MACHINE_OPENING, /* Machine is being registered */ @@ -49,17 +49,6 @@ enum KillWho { _KILL_WHO_INVALID = -1 }; -#define MACHINE_OPERATIONS_MAX 64 - -struct MachineOperation { - Machine *machine; - pid_t pid; - sd_bus_message *message; - int errno_fd; - sd_event_source *event_source; - LIST_FIELDS(MachineOperation, operations); -}; - struct Machine { Manager *manager; @@ -88,10 +77,9 @@ struct Machine { int *netif; unsigned n_netif; - LIST_FIELDS(Machine, gc_queue); + LIST_HEAD(Operation, operations); - MachineOperation *operations; - unsigned n_operations; + LIST_FIELDS(Machine, gc_queue); }; Machine* machine_new(Manager *manager, MachineClass class, const char *name); @@ -109,8 +97,6 @@ void machine_release_unit(Machine *m); MachineState machine_get_state(Machine *u); -MachineOperation *machine_operation_unref(MachineOperation *o); - const char* machine_class_to_string(MachineClass t) _const_; MachineClass machine_class_from_string(const char *s) _pure_; diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 4853139321..1165ab5afa 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -33,6 +33,7 @@ #include "alloc-util.h" #include "bus-error.h" +#include "bus-unit-util.h" #include "bus-util.h" #include "cgroup-show.h" #include "cgroup-util.h" @@ -61,6 +62,7 @@ static char **arg_property = NULL; static bool arg_all = false; +static bool arg_value = false; static bool arg_full = false; static bool arg_no_pager = false; static bool arg_legend = true; @@ -80,14 +82,6 @@ static const char* arg_format = NULL; static const char *arg_uid = NULL; static char **arg_setenv = NULL; -static void pager_open_if_enabled(void) { - - if (arg_no_pager) - return; - - pager_open(false); -} - static void polkit_agent_open_if_enabled(void) { /* Open the polkit agent as a child process if necessary */ @@ -135,17 +129,16 @@ static int list_machines(int argc, char *argv[], void *userdata) { assert(bus); - pager_open_if_enabled(); + pager_open(arg_no_pager, false); - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "ListMachines", - &error, - &reply, - NULL); + r = sd_bus_call_method(bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "ListMachines", + &error, + &reply, + NULL); if (r < 0) { log_error("Could not get machines: %s", bus_error_message(&error, -r)); return r; @@ -180,7 +173,7 @@ static int list_machines(int argc, char *argv[], void *userdata) { if (l > max_service) max_service = l; - n_machines ++; + n_machines++; } if (r < 0) return bus_log_parse_error(r); @@ -238,17 +231,16 @@ static int list_images(int argc, char *argv[], void *userdata) { assert(bus); - pager_open_if_enabled(); + pager_open(arg_no_pager, false); - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "ListImages", - &error, - &reply, - ""); + r = sd_bus_call_method(bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "ListImages", + &error, + &reply, + ""); if (r < 0) { log_error("Could not get images: %s", bus_error_message(&error, -r)); return r; @@ -340,8 +332,8 @@ static int list_images(int argc, char *argv[], void *userdata) { } static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_free_ char *path = NULL; const char *cgroup; int r; @@ -350,9 +342,6 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { assert(bus); assert(unit); - if (arg_transport == BUS_TRANSPORT_REMOTE) - return 0; - path = unit_dbus_path_from_name(unit); if (!path) return log_oom(); @@ -366,16 +355,14 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { &error, &reply, "s"); - if (r < 0) { - log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to query ControlGroup: %s", bus_error_message(&error, r)); r = sd_bus_message_read(reply, "s", &cgroup); if (r < 0) return bus_log_parse_error(r); - if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0) + if (isempty(cgroup)) return 0; c = columns(); @@ -384,7 +371,21 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { else c = 0; - show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, get_output_flags()); + r = unit_show_processes(bus, unit, cgroup, "\t\t ", c, get_output_flags(), &error); + if (r == -EBADR) { + + if (arg_transport == BUS_TRANSPORT_REMOTE) + return 0; + + /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */ + + if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0) + return 0; + + show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, &leader, leader > 0, get_output_flags()); + } else if (r < 0) + return log_error_errno(r, "Failed to dump process list: %s", bus_error_message(&error, r)); + return 0; } @@ -688,7 +689,7 @@ static int show_machine_properties(sd_bus *bus, const char *path, bool *new_line *new_line = true; - r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all); + r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_value, arg_all); if (r < 0) log_error_errno(r, "Could not get properties: %m"); @@ -707,7 +708,7 @@ static int show_machine(int argc, char *argv[], void *userdata) { properties = !strstr(argv[0], "status"); - pager_open_if_enabled(); + pager_open(arg_no_pager, false); if (properties && argc <= 1) { @@ -721,15 +722,14 @@ static int show_machine(int argc, char *argv[], void *userdata) { for (i = 1; i < argc; i++) { const char *path = NULL; - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetMachine", - &error, - &reply, - "s", argv[i]); + r = sd_bus_call_method(bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "GetMachine", + &error, + &reply, + "s", argv[i]); if (r < 0) { log_error("Could not get path to machine: %s", bus_error_message(&error, -r)); return r; @@ -937,7 +937,7 @@ static int show_image_properties(sd_bus *bus, const char *path, bool *new_line) *new_line = true; - r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all); + r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_value, arg_all); if (r < 0) log_error_errno(r, "Could not get properties: %m"); @@ -956,7 +956,7 @@ static int show_image(int argc, char *argv[], void *userdata) { properties = !strstr(argv[0], "status"); - pager_open_if_enabled(); + pager_open(arg_no_pager, false); if (argc <= 1) { @@ -1076,6 +1076,7 @@ static int terminate_machine(int argc, char *argv[], void *userdata) { static int copy_files(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_free_ char *abs_host_path = NULL; char *dest, *host_path, *container_path; sd_bus *bus = userdata; @@ -1099,19 +1100,28 @@ static int copy_files(int argc, char *argv[], void *userdata) { host_path = abs_host_path; } - r = sd_bus_call_method( + r = sd_bus_message_new_method_call( bus, + &m, "org.freedesktop.machine1", "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", - copy_from ? "CopyFromMachine" : "CopyToMachine", - &error, - NULL, + copy_from ? "CopyFromMachine" : "CopyToMachine"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append( + m, "sss", argv[1], copy_from ? container_path : host_path, copy_from ? host_path : container_path); if (r < 0) + return bus_log_create_error(r); + + /* This is a slow operation, hence turn off any method call timeouts */ + r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL); + if (r < 0) return log_error_errno(r, "Failed to copy: %s", bus_error_message(&error, r)); return 0; @@ -1393,7 +1403,6 @@ static int shell_machine(int argc, char *argv[], void *userdata) { } static int remove_image(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = userdata; int r, i; @@ -1402,19 +1411,27 @@ static int remove_image(int argc, char *argv[], void *userdata) { polkit_agent_open_if_enabled(); for (i = 1; i < argc; i++) { - r = sd_bus_call_method( + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + + r = sd_bus_message_new_method_call( bus, + &m, "org.freedesktop.machine1", "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", - "RemoveImage", - &error, - NULL, - "s", argv[i]); - if (r < 0) { - log_error("Could not remove image: %s", bus_error_message(&error, -r)); - return r; - } + "RemoveImage"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "s", argv[i]); + if (r < 0) + return bus_log_create_error(r); + + /* This is a slow operation, hence turn off any method call timeouts */ + r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL); + if (r < 0) + return log_error_errno(r, "Could not remove image: %s", bus_error_message(&error, r)); } return 0; @@ -1446,24 +1463,30 @@ static int rename_image(int argc, char *argv[], void *userdata) { static int clone_image(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; sd_bus *bus = userdata; int r; polkit_agent_open_if_enabled(); - r = sd_bus_call_method( + r = sd_bus_message_new_method_call( bus, + &m, "org.freedesktop.machine1", "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", - "CloneImage", - &error, - NULL, - "ssb", argv[1], argv[2], arg_read_only); - if (r < 0) { - log_error("Could not clone image: %s", bus_error_message(&error, -r)); - return r; - } + "CloneImage"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "ssb", argv[1], argv[2], arg_read_only); + if (r < 0) + return bus_log_create_error(r); + + /* This is a slow operation, hence turn off any method call timeouts */ + r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL); + if (r < 0) + return log_error_errno(r, "Could not clone image: %s", bus_error_message(&error, r)); return 0; } @@ -1763,7 +1786,7 @@ static int transfer_image_common(sd_bus *bus, sd_bus_message *m) { r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0) { - log_error("Failed transfer image: %s", bus_error_message(&error, -r)); + log_error("Failed to transfer image: %s", bus_error_message(&error, -r)); return r; } @@ -2189,17 +2212,16 @@ static int list_transfers(int argc, char *argv[], void *userdata) { double progress; int r; - pager_open_if_enabled(); + pager_open(arg_no_pager, false); - r = sd_bus_call_method( - bus, - "org.freedesktop.import1", - "/org/freedesktop/import1", - "org.freedesktop.import1.Manager", - "ListTransfers", - &error, - &reply, - NULL); + r = sd_bus_call_method(bus, + "org.freedesktop.import1", + "/org/freedesktop/import1", + "org.freedesktop.import1.Manager", + "ListTransfers", + &error, + &reply, + NULL); if (r < 0) { log_error("Could not get transfers: %s", bus_error_message(&error, -r)); return r; @@ -2236,7 +2258,7 @@ static int list_transfers(int argc, char *argv[], void *userdata) { if (id > max_id) max_id = id; - n_transfers ++; + n_transfers++; } if (r < 0) return bus_log_parse_error(r); @@ -2349,6 +2371,50 @@ static int set_limit(int argc, char *argv[], void *userdata) { return 0; } +static int clean_images(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + uint64_t usage, total = 0; + char fb[FORMAT_BYTES_MAX]; + sd_bus *bus = userdata; + const char *name; + unsigned c = 0; + int r; + + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "CleanPool", + &error, + &reply, + "s", arg_all ? "all" : "hidden"); + if (r < 0) + return log_error_errno(r, "Could not clean pool: %s", bus_error_message(&error, r)); + + r = sd_bus_message_enter_container(reply, 'a', "(st)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(reply, "(st)", &name, &usage)) > 0) { + log_info("Removed image '%s'. Freed exclusive disk space: %s", + name, format_bytes(fb, sizeof(fb), usage)); + + total += usage; + c++; + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + log_info("Removed %u images in total. Total freed exclusive disk space %s.", + c, format_bytes(fb, sizeof(fb), total)); + + return 0; +} + static int help(int argc, char *argv[], void *userdata) { printf("%s [OPTIONS...] {COMMAND} ...\n\n" @@ -2364,11 +2430,12 @@ static int help(int argc, char *argv[], void *userdata) { " -p --property=NAME Show only properties by this name\n" " -q --quiet Suppress output\n" " -a --all Show all properties, including empty ones\n" + " --value When showing properties, only print the value\n" " -l --full Do not ellipsize output\n" " --kill-who=WHO Who to send signal to\n" " -s --signal=SIGNAL Which signal to send\n" " --uid=USER Specify user ID to invoke shell as\n" - " --setenv=VAR=VALUE Add an environment variable for shell\n" + " -E --setenv=VAR=VALUE Add an environment variable for shell\n" " --read-only Create read-only bind mount\n" " --mkdir Create directory before bind mounting, if missing\n" " -n --lines=INTEGER Number of journal entries to show\n" @@ -2405,7 +2472,8 @@ static int help(int argc, char *argv[], void *userdata) { " rename NAME NAME Rename an image\n" " read-only NAME [BOOL] Mark or unmark image read-only\n" " remove NAME... Remove an image\n" - " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n\n" + " set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n" + " clean Remove hidden (or all) images\n\n" "Image Transfer Commands:\n" " pull-tar URL [NAME] Download a TAR container image\n" " pull-raw URL [NAME] Download a RAW container or VM image\n" @@ -2426,6 +2494,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_VERSION = 0x100, ARG_NO_PAGER, ARG_NO_LEGEND, + ARG_VALUE, ARG_KILL_WHO, ARG_READ_ONLY, ARG_MKDIR, @@ -2434,7 +2503,6 @@ static int parse_argv(int argc, char *argv[]) { ARG_FORCE, ARG_FORMAT, ARG_UID, - ARG_SETENV, }; static const struct option options[] = { @@ -2442,6 +2510,7 @@ static int parse_argv(int argc, char *argv[]) { { "version", no_argument, NULL, ARG_VERSION }, { "property", required_argument, NULL, 'p' }, { "all", no_argument, NULL, 'a' }, + { "value", no_argument, NULL, ARG_VALUE }, { "full", no_argument, NULL, 'l' }, { "no-pager", no_argument, NULL, ARG_NO_PAGER }, { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, @@ -2459,16 +2528,38 @@ static int parse_argv(int argc, char *argv[]) { { "force", no_argument, NULL, ARG_FORCE }, { "format", required_argument, NULL, ARG_FORMAT }, { "uid", required_argument, NULL, ARG_UID }, - { "setenv", required_argument, NULL, ARG_SETENV }, + { "setenv", required_argument, NULL, 'E' }, {} }; + bool reorder = false; int c, r; assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hp:als:H:M:qn:o:", options, NULL)) >= 0) + for (;;) { + const char * const option_string = "+hp:als:H:M:qn:o:"; + + c = getopt_long(argc, argv, option_string + reorder, options, NULL); + if (c < 0) { + /* We generally are fine with the fact that getopt_long() reorders the command line, and looks + * for switches after the main verb. However, for "shell" we really don't want that, since we + * want that switches passed after that are passed to the program to execute, and not processed + * by us. To make this possible, we'll first invoke getopt_long() with reordering disabled + * (i.e. with the "+" prefix in the option string), and as soon as we hit the end (i.e. the + * verb) we check if that's "shell". If it is, we exit the loop, since we don't want any + * further options processed. However, if it is anything else, we process the same argument + * again, but this time allow reordering. */ + + if (!reorder && optind < argc && !streq(argv[optind], "shell")) { + reorder = true; + optind--; + continue; + } + + break; + } switch (c) { @@ -2493,6 +2584,10 @@ static int parse_argv(int argc, char *argv[]) { arg_all = true; break; + case ARG_VALUE: + arg_value = true; + break; + case 'l': arg_full = true; break; @@ -2583,7 +2678,7 @@ static int parse_argv(int argc, char *argv[]) { arg_uid = optarg; break; - case ARG_SETENV: + case 'E': if (!env_assignment_is_valid(optarg)) { log_error("Environment assignment invalid: %s", optarg); return -EINVAL; @@ -2600,6 +2695,7 @@ static int parse_argv(int argc, char *argv[]) { default: assert_not_reached("Unhandled option"); } + } return 1; } @@ -2639,6 +2735,7 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) { { "list-transfers", VERB_ANY, 1, 0, list_transfers }, { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer }, { "set-limit", 2, 3, 0, set_limit }, + { "clean", VERB_ANY, 1, 0, clean_images }, {} }; diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index b933099330..31efa3695b 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -706,6 +706,26 @@ static int method_copy_machine(sd_bus_message *message, void *userdata, sd_bus_e return bus_machine_method_copy(message, machine, error); } +static int method_open_machine_root_directory(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + const char *name; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + return bus_machine_method_open_root_directory(message, machine, error); +} + static int method_remove_image(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_(image_unrefp) Image* i = NULL; const char *name; @@ -802,6 +822,93 @@ static int method_mark_image_read_only(sd_bus_message *message, void *userdata, return bus_image_method_mark_read_only(message, i, error); } +static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_error *error) { + enum { + REMOVE_ALL, + REMOVE_HIDDEN, + } mode; + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(image_hashmap_freep) Hashmap *images = NULL; + Manager *m = userdata; + Image *image; + const char *mm; + Iterator i; + int r; + + assert(message); + + r = sd_bus_message_read(message, "s", &mm); + if (r < 0) + return r; + + if (streq(mm, "all")) + mode = REMOVE_ALL; + else if (streq(mm, "hidden")) + mode = REMOVE_HIDDEN; + else + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown mode '%s'.", mm); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.machine1.manage-machines", + NULL, + false, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + images = hashmap_new(&string_hash_ops); + if (!images) + return -ENOMEM; + + r = image_discover(images); + if (r < 0) + return r; + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(st)"); + if (r < 0) + return r; + + HASHMAP_FOREACH(image, images, i) { + + /* We can't remove vendor images (i.e. those in /usr) */ + if (IMAGE_IS_VENDOR(image)) + continue; + + if (IMAGE_IS_HOST(image)) + continue; + + if (mode == REMOVE_HIDDEN && !IMAGE_IS_HIDDEN(image)) + continue; + + r = image_remove(image); + if (r == -EBUSY) /* keep images that are currently being used. */ + continue; + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to remove image %s: %m", image->name); + + r = sd_bus_message_append(reply, "(st)", image->name, image->usage_exclusive); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; uint64_t limit; @@ -1138,12 +1245,14 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("BindMountMachine", "sssbb", NULL, method_bind_mount_machine, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenMachineRootDirectory", "s", "h", method_open_machine_root_directory, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("RemoveImage", "s", NULL, method_remove_image, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("RenameImage", "ss", NULL, method_rename_image, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CloneImage", "ssb", NULL, method_clone_image, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("MarkImageReadOnly", "sb", NULL, method_mark_image_read_only, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetPoolLimit", "t", NULL, method_set_pool_limit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetImageLimit", "st", NULL, method_set_image_limit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CleanPool", "s", "a(st)", method_clean_pool, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("MapFromMachineUser", "su", "u", method_map_from_machine_user, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("MapToMachineUser", "u", "sou", method_map_to_machine_user, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("MapFromMachineGroup", "su", "u", method_map_from_machine_group, SD_BUS_VTABLE_UNPRIVILEGED), @@ -1212,7 +1321,7 @@ int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_err r = unit_name_from_dbus_path(path, &unit); if (r == -EINVAL) /* not for a unit */ return 0; - if (r < 0){ + if (r < 0) { log_oom(); return 0; } diff --git a/src/machine/machined.c b/src/machine/machined.c index f2c1966a6b..f7ceb5e603 100644 --- a/src/machine/machined.c +++ b/src/machine/machined.c @@ -70,6 +70,11 @@ void manager_free(Manager *m) { assert(m); + while (m->operations) + operation_free(m->operations); + + assert(m->n_operations == 0); + while ((machine = hashmap_first(m->machines))) machine_free(machine); @@ -336,6 +341,9 @@ int manager_startup(Manager *m) { static bool check_idle(void *userdata) { Manager *m = userdata; + if (m->operations) + return false; + manager_gc(m, true); return hashmap_isempty(m->machines); diff --git a/src/machine/machined.h b/src/machine/machined.h index e7d7dfdceb..7b9b148044 100644 --- a/src/machine/machined.h +++ b/src/machine/machined.h @@ -32,6 +32,7 @@ typedef struct Manager Manager; #include "image-dbus.h" #include "machine-dbus.h" #include "machine.h" +#include "operation.h" struct Manager { sd_event *event; @@ -49,6 +50,9 @@ struct Manager { LIST_HEAD(Machine, machine_gc_queue); Machine *host_machine; + + LIST_HEAD(Operation, operations); + unsigned n_operations; }; Manager *manager_new(void); diff --git a/src/machine/operation.c b/src/machine/operation.c new file mode 100644 index 0000000000..e6ddc41a55 --- /dev/null +++ b/src/machine/operation.c @@ -0,0 +1,131 @@ +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "alloc-util.h" +#include "fd-util.h" +#include "operation.h" +#include "process-util.h" + +static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + Operation *o = userdata; + int r; + + assert(o); + assert(si); + + log_debug("Operating " PID_FMT " is now complete with with code=%s status=%i", + o->pid, + sigchld_code_to_string(si->si_code), si->si_status); + + o->pid = 0; + + if (si->si_code != CLD_EXITED) { + r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child died abnormally."); + goto fail; + } + + if (si->si_status != EXIT_SUCCESS) { + if (read(o->errno_fd, &r, sizeof(r)) == sizeof(r)) + r = sd_bus_error_set_errnof(&error, r, "%m"); + else + r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed."); + + goto fail; + } + + r = sd_bus_reply_method_return(o->message, NULL); + if (r < 0) + log_error_errno(r, "Failed to reply to message: %m"); + + operation_free(o); + return 0; + +fail: + r = sd_bus_reply_method_error(o->message, &error); + if (r < 0) + log_error_errno(r, "Failed to reply to message: %m"); + + operation_free(o); + return 0; +} + +int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd) { + Operation *o; + int r; + + assert(manager); + assert(child > 1); + assert(message); + assert(errno_fd >= 0); + + o = new0(Operation, 1); + if (!o) + return -ENOMEM; + + r = sd_event_add_child(manager->event, &o->event_source, child, WEXITED, operation_done, o); + if (r < 0) { + free(o); + return r; + } + + o->pid = child; + o->message = sd_bus_message_ref(message); + o->errno_fd = errno_fd; + + LIST_PREPEND(operations, manager->operations, o); + manager->n_operations++; + o->manager = manager; + + if (machine) { + LIST_PREPEND(operations_by_machine, machine->operations, o); + o->machine = machine; + } + + log_debug("Started new operation " PID_FMT ".", child); + + /* At this point we took ownership of both the child and the errno file descriptor! */ + + return 0; +} + +Operation *operation_free(Operation *o) { + if (!o) + return NULL; + + sd_event_source_unref(o->event_source); + + safe_close(o->errno_fd); + + if (o->pid > 1) + (void) sigkill_wait(o->pid); + + sd_bus_message_unref(o->message); + + if (o->manager) { + LIST_REMOVE(operations, o->manager->operations, o); + o->manager->n_operations--; + } + + if (o->machine) + LIST_REMOVE(operations_by_machine, o->machine->operations, o); + + free(o); + return NULL; +} diff --git a/src/core/bus-endpoint.h b/src/machine/operation.h index f2fbc4701c..7ca47bc3af 100644 --- a/src/core/bus-endpoint.h +++ b/src/machine/operation.h @@ -3,7 +3,7 @@ /*** This file is part of systemd. - Copyright 2014 Daniel Mack + Copyright 2016 Lennart Poettering systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -19,24 +19,29 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -typedef struct BusEndpoint BusEndpoint; -typedef struct BusEndpointPolicy BusEndpointPolicy; +#include <sys/types.h> -#include "bus-policy.h" -#include "hashmap.h" +#include "sd-bus.h" +#include "sd-event.h" -struct BusEndpointPolicy { - char *name; - BusPolicyAccess access; -}; +#include "list.h" -struct BusEndpoint { - Hashmap *policy_hash; -}; +typedef struct Operation Operation; -int bus_endpoint_new(BusEndpoint **ep); -void bus_endpoint_free(BusEndpoint *endpoint); +#include "machined.h" -int bus_endpoint_add_policy(BusEndpoint *ep, const char *name, BusPolicyAccess access); +#define OPERATIONS_MAX 64 + +struct Operation { + Manager *manager; + Machine *machine; + pid_t pid; + sd_bus_message *message; + int errno_fd; + sd_event_source *event_source; + LIST_FIELDS(Operation, operations); + LIST_FIELDS(Operation, operations_by_machine); +}; -int bus_kernel_set_endpoint_policy(int fd, uid_t uid, BusEndpoint *ep); +int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd); +Operation *operation_free(Operation *o); diff --git a/src/network/.gitignore b/src/network/.gitignore index 8858596489..aca55206b7 100644 --- a/src/network/.gitignore +++ b/src/network/.gitignore @@ -1,2 +1,3 @@ /networkd-network-gperf.c /networkd-netdev-gperf.c +/networkd-gperf.c diff --git a/src/network/networkctl.c b/src/network/networkctl.c index 60724fce80..d2df9b7560 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -23,6 +23,7 @@ #include "sd-device.h" #include "sd-hwdb.h" +#include "sd-lldp.h" #include "sd-netlink.h" #include "sd-network.h" @@ -30,18 +31,20 @@ #include "arphrd-list.h" #include "device-util.h" #include "ether-addr-util.h" +#include "fd-util.h" #include "hwdb-util.h" -#include "lldp.h" #include "local-addresses.h" #include "locale-util.h" #include "netlink-util.h" #include "pager.h" #include "parse-util.h" #include "socket-util.h" +#include "sparse-endian.h" #include "stdio-util.h" #include "string-table.h" #include "string-util.h" #include "strv.h" +#include "strxcpyx.h" #include "terminal-util.h" #include "util.h" #include "verbs.h" @@ -50,15 +53,7 @@ static bool arg_no_pager = false; static bool arg_legend = true; static bool arg_all = false; -static void pager_open_if_enabled(void) { - - if (arg_no_pager) - return; - - pager_open(false); -} - -static int link_get_type_string(int iftype, sd_device *d, char **ret) { +static int link_get_type_string(unsigned short iftype, sd_device *d, char **ret) { const char *t; char *p; @@ -70,7 +65,7 @@ static int link_get_type_string(int iftype, sd_device *d, char **ret) { * to show a more useful type string for * them */ - (void)sd_device_get_devtype(d, &devtype); + (void) sd_device_get_devtype(d, &devtype); if (streq_ptr(devtype, "wlan")) id = "wlan"; @@ -103,10 +98,46 @@ static int link_get_type_string(int iftype, sd_device *d, char **ret) { return 0; } +static void operational_state_to_color(const char *state, const char **on, const char **off) { + assert(on); + assert(off); + + if (streq_ptr(state, "routable")) { + *on = ansi_highlight_green(); + *off = ansi_normal(); + } else if (streq_ptr(state, "degraded")) { + *on = ansi_highlight_yellow(); + *off = ansi_normal(); + } else + *on = *off = ""; +} + +static void setup_state_to_color(const char *state, const char **on, const char **off) { + assert(on); + assert(off); + + if (streq_ptr(state, "configured")) { + *on = ansi_highlight_green(); + *off = ansi_normal(); + } else if (streq_ptr(state, "configuring")) { + *on = ansi_highlight_yellow(); + *off = ansi_normal(); + } else if (streq_ptr(state, "failed") || streq_ptr(state, "linger")) { + *on = ansi_highlight_red(); + *off = ansi_normal(); + } else + *on = *off = ""; +} + typedef struct LinkInfo { - const char *name; + char name[IFNAMSIZ+1]; int ifindex; - unsigned iftype; + unsigned short iftype; + struct ether_addr mac_address; + uint32_t mtu; + + bool has_mac_address:1; + bool has_mtu:1; } LinkInfo; static int link_info_compare(const void *a, const void *b) { @@ -115,44 +146,84 @@ static int link_info_compare(const void *a, const void *b) { return x->ifindex - y->ifindex; } -static int decode_and_sort_links(sd_netlink_message *m, LinkInfo **ret) { +static int decode_link(sd_netlink_message *m, LinkInfo *info) { + const char *name; + uint16_t type; + int r; + + assert(m); + assert(info); + + r = sd_netlink_message_get_type(m, &type); + if (r < 0) + return r; + + if (type != RTM_NEWLINK) + return 0; + + r = sd_rtnl_message_link_get_ifindex(m, &info->ifindex); + if (r < 0) + return r; + + r = sd_netlink_message_read_string(m, IFLA_IFNAME, &name); + if (r < 0) + return r; + + r = sd_rtnl_message_link_get_type(m, &info->iftype); + if (r < 0) + return r; + + strscpy(info->name, sizeof info->name, name); + + info->has_mac_address = + sd_netlink_message_read_ether_addr(m, IFLA_ADDRESS, &info->mac_address) >= 0 && + memcmp(&info->mac_address, ÐER_ADDR_NULL, sizeof(struct ether_addr)) != 0; + + info->has_mtu = + sd_netlink_message_read_u32(m, IFLA_MTU, &info->mtu) && + info->mtu > 0; + + return 1; +} + +static int acquire_link_info_strv(sd_netlink *rtnl, char **l, LinkInfo **ret) { _cleanup_free_ LinkInfo *links = NULL; - size_t size = 0, c = 0; - sd_netlink_message *i; + char **i; + size_t c = 0; int r; - for (i = m; i; i = sd_netlink_message_next(i)) { - const char *name; - unsigned iftype; - uint16_t type; - int ifindex; + assert(rtnl); + assert(ret); - r = sd_netlink_message_get_type(i, &type); - if (r < 0) - return r; + links = new(LinkInfo, strv_length(l)); + if (!links) + return log_oom(); - if (type != RTM_NEWLINK) - continue; + STRV_FOREACH(i, l) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; + int ifindex; + + if (parse_ifindex(*i, &ifindex) >= 0) + r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex); + else { + r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0); + if (r < 0) + return rtnl_log_create_error(r); - r = sd_rtnl_message_link_get_ifindex(i, &ifindex); + r = sd_netlink_message_append_string(req, IFLA_IFNAME, *i); + } if (r < 0) - return r; + return rtnl_log_create_error(r); - r = sd_netlink_message_read_string(i, IFLA_IFNAME, &name); + r = sd_netlink_call(rtnl, req, 0, &reply); if (r < 0) - return r; + return log_error_errno(r, "Failed to request link: %m"); - r = sd_rtnl_message_link_get_type(i, &iftype); + r = decode_link(reply, links + c); if (r < 0) return r; - - if (!GREEDY_REALLOC(links, size, c+1)) - return -ENOMEM; - - links[c].name = name; - links[c].ifindex = ifindex; - links[c].iftype = iftype; - c++; + if (r > 0) + c++; } qsort_safe(links, c, sizeof(LinkInfo), link_info_compare); @@ -163,48 +234,15 @@ static int decode_and_sort_links(sd_netlink_message *m, LinkInfo **ret) { return (int) c; } -static void operational_state_to_color(const char *state, const char **on, const char **off) { - assert(on); - assert(off); - - if (streq_ptr(state, "routable")) { - *on = ansi_highlight_green(); - *off = ansi_normal(); - } else if (streq_ptr(state, "degraded")) { - *on = ansi_highlight_yellow(); - *off = ansi_normal(); - } else - *on = *off = ""; -} - -static void setup_state_to_color(const char *state, const char **on, const char **off) { - assert(on); - assert(off); - - if (streq_ptr(state, "configured")) { - *on = ansi_highlight_green(); - *off = ansi_normal(); - } else if (streq_ptr(state, "configuring")) { - *on = ansi_highlight_yellow(); - *off = ansi_normal(); - } else if (streq_ptr(state, "failed") || streq_ptr(state, "linger")) { - *on = ansi_highlight_red(); - *off = ansi_normal(); - } else - *on = *off = ""; -} - -static int list_links(int argc, char *argv[], void *userdata) { +static int acquire_link_info_all(sd_netlink *rtnl, LinkInfo **ret) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; _cleanup_free_ LinkInfo *links = NULL; - int r, c, i; - - pager_open_if_enabled(); + size_t allocated = 0, c = 0; + sd_netlink_message *i; + int r; - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); + assert(rtnl); + assert(ret); r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0); if (r < 0) @@ -218,12 +256,50 @@ static int list_links(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to enumerate links: %m"); - if (arg_legend) - printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP"); + for (i = reply; i; i = sd_netlink_message_next(i)) { + if (!GREEDY_REALLOC(links, allocated, c+1)) + return -ENOMEM; + + r = decode_link(i, links + c); + if (r < 0) + return r; + if (r > 0) + c++; + } - c = decode_and_sort_links(reply, &links); + qsort_safe(links, c, sizeof(LinkInfo), link_info_compare); + + *ret = links; + links = NULL; + + return (int) c; +} + +static int list_links(int argc, char *argv[], void *userdata) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + _cleanup_free_ LinkInfo *links = NULL; + int c, i, r; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + if (argc > 1) + c = acquire_link_info_strv(rtnl, argv + 1, &links); + else + c = acquire_link_info_all(rtnl, &links); if (c < 0) - return rtnl_log_parse_error(c); + return c; + + pager_open(arg_no_pager, false); + + if (arg_legend) + printf("%3s %-16s %-18s %-11s %-10s\n", + "IDX", + "LINK", + "TYPE", + "OPERATIONAL", + "SETUP"); for (i = 0; i < c; i++) { _cleanup_free_ char *setup_state = NULL, *operational_state = NULL; @@ -233,16 +309,18 @@ static int list_links(int argc, char *argv[], void *userdata) { char devid[2 + DECIMAL_STR_MAX(int)]; _cleanup_free_ char *t = NULL; - sd_network_link_get_operational_state(links[i].ifindex, &operational_state); + (void) sd_network_link_get_operational_state(links[i].ifindex, &operational_state); operational_state_to_color(operational_state, &on_color_operational, &off_color_operational); - sd_network_link_get_setup_state(links[i].ifindex, &setup_state); + r = sd_network_link_get_setup_state(links[i].ifindex, &setup_state); + if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */ + setup_state = strdup("unmanaged"); setup_state_to_color(setup_state, &on_color_setup, &off_color_setup); - sprintf(devid, "n%i", links[i].ifindex); - (void)sd_device_new_from_device_id(&d, devid); + xsprintf(devid, "n%i", links[i].ifindex); + (void) sd_device_new_from_device_id(&d, devid); - link_get_type_string(links[i].iftype, d, &t); + (void) link_get_type_string(links[i].iftype, d, &t); printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n", links[i].ifindex, links[i].name, strna(t), @@ -257,7 +335,7 @@ static int list_links(int argc, char *argv[], void *userdata) { } /* IEEE Organizationally Unique Identifier vendor string */ -static int ieee_oui(sd_hwdb *hwdb, struct ether_addr *mac, char **ret) { +static int ieee_oui(sd_hwdb *hwdb, const struct ether_addr *mac, char **ret) { const char *description; char modalias[strlen("OUI:XXYYXXYYXXYY") + 1], *desc; int r; @@ -404,6 +482,9 @@ static int dump_gateways( _cleanup_free_ struct local_address *local = NULL; int r, n, i; + assert(rtnl); + assert(prefix); + n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local); if (n < 0) return n; @@ -453,6 +534,9 @@ static int dump_addresses( _cleanup_free_ struct local_address *local = NULL; int r, n, i; + assert(rtnl); + assert(prefix); + n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local); if (n < 0) return n; @@ -485,6 +569,116 @@ static int dump_addresses( return 0; } +static int open_lldp_neighbors(int ifindex, FILE **ret) { + _cleanup_free_ char *p = NULL; + FILE *f; + + if (asprintf(&p, "/run/systemd/netif/lldp/%i", ifindex) < 0) + return -ENOMEM; + + f = fopen(p, "re"); + if (!f) + return -errno; + + *ret = f; + return 0; +} + +static int next_lldp_neighbor(FILE *f, sd_lldp_neighbor **ret) { + _cleanup_free_ void *raw = NULL; + size_t l; + le64_t u; + int r; + + assert(f); + assert(ret); + + l = fread(&u, 1, sizeof(u), f); + if (l == 0 && feof(f)) + return 0; + if (l != sizeof(u)) + return -EBADMSG; + + raw = new(uint8_t, le64toh(u)); + if (!raw) + return -ENOMEM; + + if (fread(raw, 1, le64toh(u), f) != le64toh(u)) + return -EBADMSG; + + r = sd_lldp_neighbor_from_raw(ret, raw, le64toh(u)); + if (r < 0) + return r; + + return 1; +} + +static int dump_lldp_neighbors(const char *prefix, int ifindex) { + _cleanup_fclose_ FILE *f = NULL; + int r, c = 0; + + assert(prefix); + assert(ifindex > 0); + + r = open_lldp_neighbors(ifindex, &f); + if (r < 0) + return r; + + for (;;) { + const char *system_name = NULL, *port_id = NULL, *port_description = NULL; + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; + + r = next_lldp_neighbor(f, &n); + if (r < 0) + return r; + if (r == 0) + break; + + printf("%*s", + (int) strlen(prefix), + c == 0 ? prefix : ""); + + (void) sd_lldp_neighbor_get_system_name(n, &system_name); + (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id); + (void) sd_lldp_neighbor_get_port_description(n, &port_description); + + printf("%s on port %s", strna(system_name), strna(port_id)); + + if (!isempty(port_description)) + printf(" (%s)", port_description); + + putchar('\n'); + + c++; + } + + return c; +} + +static void dump_ifindexes(const char *prefix, const int *ifindexes) { + unsigned c; + + assert(prefix); + + if (!ifindexes || ifindexes[0] <= 0) + return; + + for (c = 0; ifindexes[c] > 0; c++) { + char name[IF_NAMESIZE+1]; + + printf("%*s", + (int) strlen(prefix), + c == 0 ? prefix : ""); + + if (if_indextoname(ifindexes[c], name)) + fputs(name, stdout); + else + printf("%i", ifindexes[c]); + + fputc('\n', stdout); + } +} + static void dump_list(const char *prefix, char **l) { char **i; @@ -502,85 +696,36 @@ static void dump_list(const char *prefix, char **l) { static int link_status_one( sd_netlink *rtnl, sd_hwdb *hwdb, - const char *name) { + const LinkInfo *info) { + _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL; _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL; - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; _cleanup_(sd_device_unrefp) sd_device *d = NULL; char devid[2 + DECIMAL_STR_MAX(int)]; _cleanup_free_ char *t = NULL, *network = NULL; const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL; const char *on_color_operational, *off_color_operational, *on_color_setup, *off_color_setup; - _cleanup_strv_free_ char **carrier_bound_to = NULL; - _cleanup_strv_free_ char **carrier_bound_by = NULL; - struct ether_addr e; - unsigned iftype; - int r, ifindex; - bool have_mac; - uint32_t mtu; + _cleanup_free_ int *carrier_bound_to = NULL, *carrier_bound_by = NULL; + int r; assert(rtnl); - assert(name); - - if (parse_ifindex(name, &ifindex) >= 0) - r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex); - else { - r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0); - if (r < 0) - return rtnl_log_create_error(r); - - r = sd_netlink_message_append_string(req, IFLA_IFNAME, name); - } + assert(info); - if (r < 0) - return rtnl_log_create_error(r); - - r = sd_netlink_call(rtnl, req, 0, &reply); - if (r < 0) - return log_error_errno(r, "Failed to query link: %m"); - - r = sd_rtnl_message_link_get_ifindex(reply, &ifindex); - if (r < 0) - return rtnl_log_parse_error(r); - - r = sd_netlink_message_read_string(reply, IFLA_IFNAME, &name); - if (r < 0) - return rtnl_log_parse_error(r); - - r = sd_rtnl_message_link_get_type(reply, &iftype); - if (r < 0) - return rtnl_log_parse_error(r); - - have_mac = sd_netlink_message_read_ether_addr(reply, IFLA_ADDRESS, &e) >= 0; - if (have_mac) { - const uint8_t *p; - bool all_zeroes = true; - - for (p = (uint8_t*) &e; p < (uint8_t*) &e + sizeof(e); p++) - if (*p != 0) { - all_zeroes = false; - break; - } - - if (all_zeroes) - have_mac = false; - } - - (void) sd_netlink_message_read_u32(reply, IFLA_MTU, &mtu); - - (void) sd_network_link_get_operational_state(ifindex, &operational_state); + (void) sd_network_link_get_operational_state(info->ifindex, &operational_state); operational_state_to_color(operational_state, &on_color_operational, &off_color_operational); - (void) sd_network_link_get_setup_state(ifindex, &setup_state); + r = sd_network_link_get_setup_state(info->ifindex, &setup_state); + if (r == -ENODATA) /* If there's no info available about this iface, it's unmanaged by networkd */ + setup_state = strdup("unmanaged"); setup_state_to_color(setup_state, &on_color_setup, &off_color_setup); - (void) sd_network_link_get_dns(ifindex, &dns); - (void) sd_network_link_get_search_domains(ifindex, &search_domains); - (void) sd_network_link_get_route_domains(ifindex, &route_domains); - (void) sd_network_link_get_ntp(ifindex, &ntp); + (void) sd_network_link_get_dns(info->ifindex, &dns); + (void) sd_network_link_get_search_domains(info->ifindex, &search_domains); + (void) sd_network_link_get_route_domains(info->ifindex, &route_domains); + (void) sd_network_link_get_ntp(info->ifindex, &ntp); - sprintf(devid, "n%i", ifindex); + xsprintf(devid, "n%i", info->ifindex); (void) sd_device_new_from_device_id(&d, devid); @@ -598,14 +743,14 @@ static int link_status_one( (void) sd_device_get_property_value(d, "ID_MODEL", &model); } - link_get_type_string(iftype, d, &t); + (void) link_get_type_string(info->iftype, d, &t); - sd_network_link_get_network_file(ifindex, &network); + (void) sd_network_link_get_network_file(info->ifindex, &network); - sd_network_link_get_carrier_bound_to(ifindex, &carrier_bound_to); - sd_network_link_get_carrier_bound_by(ifindex, &carrier_bound_by); + (void) sd_network_link_get_carrier_bound_to(info->ifindex, &carrier_bound_to); + (void) sd_network_link_get_carrier_bound_by(info->ifindex, &carrier_bound_by); - printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, ifindex, name); + printf("%s%s%s %i: %s\n", on_color_operational, special_glyph(BLACK_CIRCLE), off_color_operational, info->ifindex, info->name); printf(" Link File: %s\n" " Network File: %s\n" @@ -626,23 +771,23 @@ static int link_status_one( if (model) printf(" Model: %s\n", model); - if (have_mac) { + if (info->has_mac_address) { _cleanup_free_ char *description = NULL; char ea[ETHER_ADDR_TO_STRING_MAX]; - ieee_oui(hwdb, &e, &description); + (void) ieee_oui(hwdb, &info->mac_address, &description); if (description) - printf(" HW Address: %s (%s)\n", ether_addr_to_string(&e, ea), description); + printf(" HW Address: %s (%s)\n", ether_addr_to_string(&info->mac_address, ea), description); else - printf(" HW Address: %s\n", ether_addr_to_string(&e, ea)); + printf(" HW Address: %s\n", ether_addr_to_string(&info->mac_address, ea)); } - if (mtu > 0) - printf(" MTU: %u\n", mtu); + if (info->has_mtu) + printf(" MTU: %u\n", info->mtu); - dump_addresses(rtnl, " Address: ", ifindex); - dump_gateways(rtnl, hwdb, " Gateway: ", ifindex); + (void) dump_addresses(rtnl, " Address: ", info->ifindex); + (void) dump_gateways(rtnl, hwdb, " Gateway: ", info->ifindex); dump_list(" DNS: ", dns); dump_list(" Search Domains: ", search_domains); @@ -650,362 +795,237 @@ static int link_status_one( dump_list(" NTP: ", ntp); - dump_list("Carrier Bound To: ", carrier_bound_to); - dump_list("Carrier Bound By: ", carrier_bound_by); + dump_ifindexes("Carrier Bound To: ", carrier_bound_to); + dump_ifindexes("Carrier Bound By: ", carrier_bound_by); - (void) sd_network_link_get_timezone(ifindex, &tz); + (void) sd_network_link_get_timezone(info->ifindex, &tz); if (tz) - printf(" Time Zone: %s", tz); + printf(" Time Zone: %s\n", tz); + + (void) dump_lldp_neighbors(" Connected To: ", info->ifindex); return 0; } -static int link_status(int argc, char *argv[], void *userdata) { - _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL; - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - char **name; - int r; - - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); - - r = sd_hwdb_new(&hwdb); - if (r < 0) - log_debug_errno(r, "Failed to open hardware database: %m"); - - if (argc <= 1 && !arg_all) { - _cleanup_free_ char *operational_state = NULL; - _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains; - const char *on_color_operational, *off_color_operational; - - sd_network_get_operational_state(&operational_state); - operational_state_to_color(operational_state, &on_color_operational, &off_color_operational); - - printf("%s%s%s State: %s%s%s\n", - on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, - on_color_operational, strna(operational_state), off_color_operational); - - dump_addresses(rtnl, " Address: ", 0); - dump_gateways(rtnl, hwdb, " Gateway: ", 0); - - sd_network_get_dns(&dns); - dump_list(" DNS: ", dns); - - sd_network_get_search_domains(&search_domains); - dump_list("Search Domains: ", search_domains); - - sd_network_get_route_domains(&route_domains); - dump_list(" Route Domains: ", route_domains); - - sd_network_get_ntp(&ntp); - dump_list(" NTP: ", ntp); - - return 0; - } +static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) { + _cleanup_free_ char *operational_state = NULL; + _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **search_domains = NULL, **route_domains = NULL; + const char *on_color_operational, *off_color_operational; - pager_open_if_enabled(); + assert(rtnl); - if (arg_all) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - _cleanup_free_ LinkInfo *links = NULL; - int c, i; + (void) sd_network_get_operational_state(&operational_state); + operational_state_to_color(operational_state, &on_color_operational, &off_color_operational); - r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0); - if (r < 0) - return rtnl_log_create_error(r); + printf("%s%s%s State: %s%s%s\n", + on_color_operational, special_glyph(BLACK_CIRCLE), off_color_operational, + on_color_operational, strna(operational_state), off_color_operational); - r = sd_netlink_message_request_dump(req, true); - if (r < 0) - return rtnl_log_create_error(r); + (void) dump_addresses(rtnl, " Address: ", 0); + (void) dump_gateways(rtnl, hwdb, " Gateway: ", 0); - r = sd_netlink_call(rtnl, req, 0, &reply); - if (r < 0) - return log_error_errno(r, "Failed to enumerate links: %m"); + (void) sd_network_get_dns(&dns); + dump_list(" DNS: ", dns); - c = decode_and_sort_links(reply, &links); - if (c < 0) - return rtnl_log_parse_error(c); + (void) sd_network_get_search_domains(&search_domains); + dump_list("Search Domains: ", search_domains); - for (i = 0; i < c; i++) { - if (i > 0) - fputc('\n', stdout); + (void) sd_network_get_route_domains(&route_domains); + dump_list(" Route Domains: ", route_domains); - link_status_one(rtnl, hwdb, links[i].name); - } - } else { - STRV_FOREACH(name, argv + 1) { - if (name != argv + 1) - fputc('\n', stdout); - - link_status_one(rtnl, hwdb, *name); - } - } + (void) sd_network_get_ntp(&ntp); + dump_list(" NTP: ", ntp); return 0; } -const char *lldp_system_capability_to_string(LLDPSystemCapabilities d) _const_; -LLDPSystemCapabilities lldp_system_capability_from_string(const char *d) _pure_; - -static const char* const lldp_system_capability_table[_LLDP_SYSTEM_CAPABILITIES_MAX + 1] = { - [LLDP_SYSTEM_CAPABILITIES_OTHER] = "O", - [LLDP_SYSTEM_CAPABILITIES_REPEATER] = "P", - [LLDP_SYSTEM_CAPABILITIES_BRIDGE] = "B", - [LLDP_SYSTEM_CAPABILITIES_WLAN_AP] = "W", - [LLDP_SYSTEM_CAPABILITIES_ROUTER] = "R", - [LLDP_SYSTEM_CAPABILITIES_PHONE] = "T", - [LLDP_SYSTEM_CAPABILITIES_DOCSIS] = "D", - [LLDP_SYSTEM_CAPABILITIES_STATION] = "A", - [LLDP_SYSTEM_CAPABILITIES_CVLAN] = "C", - [LLDP_SYSTEM_CAPABILITIES_SVLAN] = "S", - [LLDP_SYSTEM_CAPABILITIES_TPMR] = "M", - [_LLDP_SYSTEM_CAPABILITIES_MAX] = "N/A", -}; - -DEFINE_STRING_TABLE_LOOKUP(lldp_system_capability, LLDPSystemCapabilities); - -static char *lldp_system_caps(uint16_t cap) { - _cleanup_free_ char *s = NULL, *t = NULL; - char *capability; - - t = strdup("[ "); - if (!t) - return NULL; - - if (cap & LLDP_SYSTEM_CAPABILITIES_OTHER) { - s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_OTHER), " ", NULL); - if (!s) - return NULL; - - free(t); - t = s; - } - - if (cap & LLDP_SYSTEM_CAPABILITIES_REPEATER) { - s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_REPEATER), " ", NULL); - if (!s) - return NULL; - - free(t); - t = s; - } - - if (cap & LLDP_SYSTEM_CAPABILITIES_BRIDGE) { - s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_BRIDGE), " ", NULL); - if (!s) - return NULL; - - free(t); - t = s; - } - - if (cap & LLDP_SYSTEM_CAPABILITIES_WLAN_AP) { - s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_WLAN_AP), " ", NULL); - if (!s) - return NULL; - - free(t); - t = s; - } - - if (cap & LLDP_SYSTEM_CAPABILITIES_ROUTER) { - s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_ROUTER), " ", NULL); - if (!s) - return NULL; - - free(t); - t = s; - } - - if (cap & LLDP_SYSTEM_CAPABILITIES_PHONE) { - s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_PHONE), " ", NULL); - if (!s) - return NULL; - - free(t); - t = s; - } +static int link_status(int argc, char *argv[], void *userdata) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL; + _cleanup_free_ LinkInfo *links = NULL; + int r, c, i; - if (cap & LLDP_SYSTEM_CAPABILITIES_DOCSIS) { - s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_DOCSIS), " ", NULL); - if (!s) - return NULL; + pager_open(arg_no_pager, false); - free(t); - t = s; - } + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); - if (cap & LLDP_SYSTEM_CAPABILITIES_STATION) { - s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_STATION), " ", NULL); - if (!s) - return NULL; + r = sd_hwdb_new(&hwdb); + if (r < 0) + log_debug_errno(r, "Failed to open hardware database: %m"); - free(t); - t = s; - } + if (arg_all) + c = acquire_link_info_all(rtnl, &links); + else if (argc <= 1) + return system_status(rtnl, hwdb); + else + c = acquire_link_info_strv(rtnl, argv + 1, &links); + if (c < 0) + return c; - if (cap & LLDP_SYSTEM_CAPABILITIES_CVLAN) { - s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_CVLAN), " ", NULL); - if (!s) - return NULL; + for (i = 0; i < c; i++) { + if (i > 0) + fputc('\n', stdout); - free(t); - t = s; + link_status_one(rtnl, hwdb, links + i); } - if (cap & LLDP_SYSTEM_CAPABILITIES_SVLAN) { - s = strjoin(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_SVLAN), " ", NULL); - if (!s) - return NULL; - - free(t); - t = s; - } + return 0; +} - if (cap & LLDP_SYSTEM_CAPABILITIES_TPMR) { - s = strappend(t, lldp_system_capability_to_string(LLDP_SYSTEM_CAPABILITIES_TPMR)); - if (!s) - return NULL; +static char *lldp_capabilities_to_string(uint16_t x) { + static const char characters[] = { + 'o', 'p', 'b', 'w', 'r', 't', 'd', 'a', 'c', 's', 'm', + }; + char *ret; + unsigned i; - free(t); - } + ret = new(char, ELEMENTSOF(characters) + 1); + if (!ret) + return NULL; - if (!s) { - s = strappend(t, lldp_system_capability_to_string(_LLDP_SYSTEM_CAPABILITIES_MAX)); - if (!s) - return NULL; + for (i = 0; i < ELEMENTSOF(characters); i++) + ret[i] = (x & (1U << i)) ? characters[i] : '.'; - free(t); - } + ret[i] = 0; + return ret; +} - t = strappend(s, "]"); - if (!t) - return NULL; +static void lldp_capabilities_legend(uint16_t x) { + unsigned w, i, cols = columns(); + static const char* const table[] = { + "o - Other", + "p - Repeater", + "b - Bridge", + "w - WLAN Access Point", + "r - Router", + "t - Telephone", + "d - DOCSIS cable device", + "a - Station", + "c - Customer VLAN", + "s - Service VLAN", + "m - Two-port MAC Relay (TPMR)", + }; - free(s); - capability = t; + if (x == 0) + return; - s = NULL; - t = NULL; + printf("\nCapability Flags:\n"); + for (w = 0, i = 0; i < ELEMENTSOF(table); i++) + if (x & (1U << i) || arg_all) { + bool newline; - return capability; + newline = w + strlen(table[i]) + (w == 0 ? 0 : 2) > cols; + if (newline) + w = 0; + w += printf("%s%s%s", newline ? "\n" : "", w == 0 ? "" : "; ", table[i]); + } + puts(""); } static int link_lldp_status(int argc, char *argv[], void *userdata) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; _cleanup_free_ LinkInfo *links = NULL; - double ttl = -1; - uint32_t capability; - int i, r, c, j; - const char *p; - char **s; - - pager_open_if_enabled(); + int i, r, c, m = 0; + uint16_t all = 0; r = sd_netlink_open(&rtnl); if (r < 0) return log_error_errno(r, "Failed to connect to netlink: %m"); - r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0); - if (r < 0) - return rtnl_log_create_error(r); - - r = sd_netlink_message_request_dump(req, true); - if (r < 0) - return rtnl_log_create_error(r); - - r = sd_netlink_call(rtnl, req, 0, &reply); - if (r < 0) - return log_error_errno(r, "Failed to enumerate links: %m"); - - c = decode_and_sort_links(reply, &links); + if (argc > 1) + c = acquire_link_info_strv(rtnl, argv + 1, &links); + else + c = acquire_link_info_all(rtnl, &links); if (c < 0) - return rtnl_log_parse_error(c); + return c; + + pager_open(arg_no_pager, false); if (arg_legend) - printf("%s %16s %24s %16s %16s\n", "Local Intf", "Device ID", "Port ID", "TTL", "Capability"); + printf("%-16s %-17s %-16s %-11s %-17s %-16s\n", + "LINK", + "CHASSIS ID", + "SYSTEM NAME", + "CAPS", + "PORT ID", + "PORT DESCRIPTION"); - for (i = j = 0; i < c; i++) { - _cleanup_free_ char *chassis = NULL, *port = NULL, *cap = NULL, *lldp = NULL; - _cleanup_strv_free_ char **l = NULL; + for (i = 0; i < c; i++) { + _cleanup_fclose_ FILE *f = NULL; - r = sd_network_link_get_lldp(links[i].ifindex, &lldp); - if (r < 0) + r = open_lldp_neighbors(links[i].ifindex, &f); + if (r == -ENOENT) continue; + if (r < 0) { + log_warning_errno(r, "Failed to open LLDP data for %i, ignoring: %m", links[i].ifindex); + continue; + } - l = strv_split_newlines(lldp); - if (!l) - return -ENOMEM; - - STRV_FOREACH(s, l) { - - p = *s; - for (;;) { - _cleanup_free_ char *a = NULL, *b = NULL, *word = NULL; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); - if (r < 0) - return log_error_errno(r, "Failed to parse LLDP syntax \"%s\": %m", *s); - - if (r == 0) - break; - - r = split_pair(word, "=", &a, &b); - if (r < 0) - continue; - - if (streq(a, "_Chassis")) { - r = free_and_strdup(&chassis, b); - if (r < 0) - return r; - - } else if (streq(a, "_Port")) { - r = free_and_strdup(&port, b); - if (r < 0) - return r; - - } else if (streq(a, "_TTL")) { - long long unsigned x = 0; - usec_t time; + for (;;) { + _cleanup_free_ char *cid = NULL, *pid = NULL, *sname = NULL, *pdesc = NULL; + const char *chassis_id = NULL, *port_id = NULL, *system_name = NULL, *port_description = NULL, *capabilities = NULL; + _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL; + uint16_t cc; - r = safe_atollu(b, &x); - if (r < 0 || (usec_t) x != x) - return log_warning_errno(r < 0 ? r : ERANGE, - "Failed to parse TTL \"%s\": %m", b); + r = next_lldp_neighbor(f, &n); + if (r < 0) { + log_warning_errno(r, "Failed to read neighbor data: %m"); + break; + } + if (r == 0) + break; - time = now(clock_boottime_or_monotonic()); - if (x < time) - continue; + (void) sd_lldp_neighbor_get_chassis_id_as_string(n, &chassis_id); + (void) sd_lldp_neighbor_get_port_id_as_string(n, &port_id); + (void) sd_lldp_neighbor_get_system_name(n, &system_name); + (void) sd_lldp_neighbor_get_port_description(n, &port_description); - ttl = (double) (x - time) / USEC_PER_SEC; + if (chassis_id) { + cid = ellipsize(chassis_id, 17, 100); + if (cid) + chassis_id = cid; + } - } else if (streq(a, "_CAP")) { - sscanf(b, "%x", &capability); + if (port_id) { + pid = ellipsize(port_id, 17, 100); + if (pid) + port_id = pid; + } - cap = lldp_system_caps(capability); - } + if (system_name) { + sname = ellipsize(system_name, 16, 100); + if (sname) + system_name = sname; + } + if (port_description) { + pdesc = ellipsize(port_description, 16, 100); + if (pdesc) + port_description = pdesc; } - if (ttl >= 0) { - printf("%10s %24s %16s %16f %16s\n", - links[i].name, - strna(chassis), strna(port), - ttl, cap); - j++; + if (sd_lldp_neighbor_get_enabled_capabilities(n, &cc) >= 0) { + capabilities = lldp_capabilities_to_string(cc); + all |= cc; } + + printf("%-16s %-17s %-16s %-11s %-17s %-16s\n", + links[i].name, + strna(chassis_id), + strna(system_name), + strna(capabilities), + strna(port_id), + strna(port_description)); + + m++; } } if (arg_legend) { - printf("\nCapability Codes:\n" - "(O) - Other, (P) - Repeater, (B) - Bridge , (W) - WLAN Access Point, (R) = Router,\n" - "(T) - Telephone, (D) - Data Over Cable Service Interface Specifications, (A) - Station,\n" - "(C) - Customer VLAN, (S) - Service VLAN, (M) - Two-port MAC Relay (TPMR)\n\n"); - - printf("Total entries displayed: %d\n", j); + lldp_capabilities_legend(all); + printf("\n%i neighbors listed.\n", m); } return 0; @@ -1020,9 +1040,9 @@ static void help(void) { " --no-legend Do not show the headers and footers\n" " -a --all Show status for all links\n\n" "Commands:\n" - " list List links\n" + " list [LINK...] List links\n" " status [LINK...] Show link status\n" - " lldp Show lldp information\n" + " lldp [LINK...] Show LLDP neighbors\n" , program_invocation_short_name); } @@ -1084,15 +1104,23 @@ static int parse_argv(int argc, char *argv[]) { static int networkctl_main(int argc, char *argv[]) { const Verb verbs[] = { - { "list", VERB_ANY, 1, VERB_DEFAULT, list_links }, - { "status", 1, VERB_ANY, 0, link_status }, - { "lldp", VERB_ANY, 1, VERB_DEFAULT, link_lldp_status }, + { "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 }, {} }; return dispatch_verb(argc, argv, verbs, NULL); } +static void warn_networkd_missing(void) { + + if (access("/run/systemd/netif/state", F_OK) >= 0) + return; + + fprintf(stderr, "WARNING: systemd-networkd is not running, output will be incomplete.\n\n"); +} + int main(int argc, char* argv[]) { int r; @@ -1103,6 +1131,8 @@ int main(int argc, char* argv[]) { if (r <= 0) goto finish; + warn_networkd_missing(); + r = networkctl_main(argc, argv); finish: diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c index d9d487d805..ebc6c9eb9e 100644 --- a/src/network/networkd-address-pool.c +++ b/src/network/networkd-address-pool.c @@ -148,8 +148,12 @@ int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union for (;;) { if (!address_pool_prefix_is_taken(p, &u, prefixlen)) { _cleanup_free_ char *s = NULL; + int r; + + r = in_addr_to_string(p->family, &u, &s); + if (r < 0) + return r; - in_addr_to_string(p->family, &u, &s); log_debug("Found range %s/%u", strna(s), prefixlen); *found = u; diff --git a/src/network/networkd-address-pool.h b/src/network/networkd-address-pool.h index 8e1378ff40..af30decfe0 100644 --- a/src/network/networkd-address-pool.h +++ b/src/network/networkd-address-pool.h @@ -22,7 +22,9 @@ typedef struct AddressPool AddressPool; #include "in-addr-util.h" -#include "networkd.h" +#include "list.h" + +typedef struct Manager Manager; struct AddressPool { Manager *manager; diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 7f9a7268cc..367c340e08 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -27,6 +27,7 @@ #include "networkd.h" #include "parse-util.h" #include "set.h" +#include "socket-util.h" #include "string-util.h" #include "utf8.h" #include "util.h" @@ -67,16 +68,15 @@ int address_new_static(Network *network, unsigned section, Address **ret) { if (r < 0) return r; - address->network = network; - - LIST_APPEND(addresses, network->static_addresses, address); - if (section) { address->section = section; hashmap_put(network->addresses_by_section, UINT_TO_PTR(address->section), address); } + address->network = network; + LIST_APPEND(addresses, network->static_addresses, address); + *ret = address; address = NULL; @@ -98,6 +98,9 @@ void address_free(Address *address) { if (address->link) { set_remove(address->link->addresses, address); set_remove(address->link->addresses_foreign, address); + + if (in_addr_equal(AF_INET6, &address->in_addr, (const union in_addr_union *) &address->link->ipv6ll_address)) + memzero(&address->link->ipv6ll_address, sizeof(struct in6_addr)); } free(address); @@ -331,6 +334,10 @@ int address_update(Address *address, unsigned char flags, unsigned char scope, s assert(address); assert(cinfo); + assert_return(address->link, 1); + + if (IN_SET(address->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; ready = address_is_ready(address); @@ -338,19 +345,18 @@ int address_update(Address *address, unsigned char flags, unsigned char scope, s address->scope = scope; address->cinfo = *cinfo; - if (address->link) { - link_update_operstate(address->link); - - if (!ready && address_is_ready(address)) { - link_check_ready(address->link); - - if (address->family == AF_INET6 && - in_addr_is_link_local(AF_INET6, &address->in_addr) > 0 && - in_addr_is_null(AF_INET6, (const union in_addr_union*) &address->link->ipv6ll_address) > 0) { - r = link_ipv6ll_gained(address->link, &address->in_addr.in6); - if (r < 0) - return r; - } + link_update_operstate(address->link); + + if (!ready && address_is_ready(address)) { + link_check_ready(address->link); + + if (address->family == AF_INET6 && + in_addr_is_link_local(AF_INET6, &address->in_addr) > 0 && + in_addr_is_null(AF_INET6, (const union in_addr_union*) &address->link->ipv6ll_address) > 0) { + + r = link_ipv6ll_gained(address->link, &address->in_addr.in6); + if (r < 0) + return r; } } @@ -404,8 +410,11 @@ int address_get(Link *link, int family, const union in_addr_union *in_addr, unsi return 0; } -int address_remove(Address *address, Link *link, - sd_netlink_message_handler_t callback) { +int address_remove( + Address *address, + Link *link, + sd_netlink_message_handler_t callback) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -727,7 +736,8 @@ int config_parse_address(const char *unit, return 0; } -int config_parse_label(const char *unit, +int config_parse_label( + const char *unit, const char *filename, unsigned line, const char *section, @@ -737,9 +747,9 @@ int config_parse_label(const char *unit, const char *rvalue, void *data, void *userdata) { - Network *network = userdata; + _cleanup_address_free_ Address *n = NULL; - char *label; + Network *network = userdata; int r; assert(filename); @@ -752,25 +762,64 @@ int config_parse_label(const char *unit, if (r < 0) return r; - label = strdup(rvalue); - if (!label) + if (!ifname_valid(rvalue)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is not valid or too long, ignoring assignment: %s", rvalue); + return 0; + } + + r = free_and_strdup(&n->label, rvalue); + if (r < 0) return log_oom(); - if (!ascii_is_valid(label) || strlen(label) >= IFNAMSIZ) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is not ASCII clean or is too long, ignoring assignment: %s", rvalue); - free(label); + n = NULL; + + return 0; +} + +int config_parse_lifetime(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Network *network = userdata; + _cleanup_address_free_ Address *n = NULL; + unsigned k; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = address_new_static(network, section_line, &n); + if (r < 0) + return r; + + if (STR_IN_SET(rvalue, "forever", "infinity")) { + n->cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME; + n = NULL; + return 0; } - free(n->label); - if (*label) - n->label = label; - else { - free(label); - n->label = NULL; + r = safe_atou(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse PreferredLifetime, ignoring: %s", rvalue); + return 0; } - n = NULL; + if (k != 0) + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid PreferredLifetime value, ignoring: %d", k); + else { + n->cinfo.ifa_prefered = k; + n = NULL; + } return 0; } diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 338f6eb9a2..784ab18b27 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -28,10 +28,12 @@ typedef struct Address Address; #include "networkd-link.h" #include "networkd-network.h" -#include "networkd.h" #define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU +typedef struct Network Network; +typedef struct Link Link; + struct Address { Network *network; unsigned section; @@ -74,3 +76,4 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free); int config_parse_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_broadcast(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_label(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_lifetime(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/network/networkd-conf.c b/src/network/networkd-conf.c new file mode 100644 index 0000000000..b67a1f6d09 --- /dev/null +++ b/src/network/networkd-conf.c @@ -0,0 +1,111 @@ +/*** + This file is part of systemd. + + Copyright 2014 Vinay Kulkarni <kulkarniv@vmware.com> + + 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 <ctype.h> + +#include "conf-parser.h" +#include "def.h" +#include "dhcp-identifier.h" +#include "hexdecoct.h" +#include "networkd-conf.h" +#include "string-table.h" + +int manager_parse_config_file(Manager *m) { + assert(m); + + return config_parse_many(PKGSYSCONFDIR "/networkd.conf", + 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] = { + [DUID_TYPE_LLT] = "link-layer-time", + [DUID_TYPE_EN] = "vendor", + [DUID_TYPE_LL] = "link-layer", + [DUID_TYPE_UUID] = "uuid", +}; +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(duid_type, DUIDType); +DEFINE_CONFIG_PARSE_ENUM(config_parse_duid_type, duid_type, DUIDType, "Failed to parse DUID type"); + +int config_parse_duid_rawdata( + 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) { + + DUID *ret = data; + uint8_t raw_data[MAX_DUID_LEN]; + unsigned count = 0; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(ret); + + /* RawData contains DUID in format "NN:NN:NN..." */ + for (;;) { + int n1, n2, len, r; + uint32_t byte; + _cleanup_free_ char *cbyte = NULL; + + r = extract_first_word(&rvalue, &cbyte, ":", 0); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to read DUID, ignoring assignment: %s.", rvalue); + return 0; + } + if (r == 0) + break; + if (count >= MAX_DUID_LEN) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue); + return 0; + } + + len = strlen(cbyte); + if (len != 1 && len != 2) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte, rvalue); + return 0; + } + n1 = unhexchar(cbyte[0]); + if (len == 2) + n2 = unhexchar(cbyte[1]); + else + n2 = 0; + + if (n1 < 0 || n2 < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte, rvalue); + return 0; + } + + byte = ((uint8_t) n1 << (4 * (len-1))) | (uint8_t) n2; + raw_data[count++] = byte; + } + + assert_cc(sizeof(raw_data) == sizeof(ret->raw_data)); + memcpy(ret->raw_data, raw_data, count); + ret->raw_data_len = count; + return 0; +} diff --git a/src/network/networkd-conf.h b/src/network/networkd-conf.h new file mode 100644 index 0000000000..c7bfb42a72 --- /dev/null +++ b/src/network/networkd-conf.h @@ -0,0 +1,49 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Vinay Kulkarni <kulkarniv@vmware.com> + + 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.h" + +int manager_parse_config_file(Manager *m); + +const struct ConfigPerfItem* networkd_gperf_lookup(const char *key, unsigned length); + +int config_parse_duid_type( + 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_duid_rawdata( + 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-dhcp4.c b/src/network/networkd-dhcp4.c index 03c28bbcb6..2ddcee9db8 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -24,7 +24,7 @@ #include "dhcp-lease-internal.h" #include "hostname-util.h" #include "network-internal.h" -#include "networkd-link.h" +#include "networkd.h" static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { @@ -34,7 +34,7 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, assert(link); assert(link->dhcp4_messages > 0); - link->dhcp4_messages --; + link->dhcp4_messages--; r = sd_netlink_message_get_errno(m); if (r < 0 && r != -EEXIST) { @@ -57,6 +57,10 @@ static int link_set_dhcp_routes(Link *link) { assert(link); assert(link->dhcp_lease); + assert(link->network); + + if (!link->network->dhcp_use_routes) + return 0; r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway); if (r < 0 && r != -ENODATA) @@ -92,25 +96,25 @@ static int link_set_dhcp_routes(Link *link) { route_gw->protocol = RTPROT_DHCP; route_gw->priority = link->network->dhcp_route_metric; - r = route_configure(route_gw, link, &dhcp4_route_handler); + r = route_configure(route_gw, link, dhcp4_route_handler); if (r < 0) return log_link_warning_errno(link, r, "Could not set host route: %m"); - link->dhcp4_messages ++; + link->dhcp4_messages++; route->family = AF_INET; route->gw.in = gateway; route->prefsrc.in = address; route->priority = link->network->dhcp_route_metric; - r = route_configure(route, link, &dhcp4_route_handler); + r = route_configure(route, link, dhcp4_route_handler); if (r < 0) { log_link_warning_errno(link, r, "Could not set routes: %m"); link_enter_failed(link); return r; } - link->dhcp4_messages ++; + link->dhcp4_messages++; } n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes); @@ -133,11 +137,11 @@ 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; - r = route_configure(route, link, &dhcp4_route_handler); + r = route_configure(route, link, dhcp4_route_handler); if (r < 0) return log_link_warning_errno(link, r, "Could not set host route: %m"); - link->dhcp4_messages ++; + link->dhcp4_messages++; } return 0; @@ -173,7 +177,7 @@ static int dhcp_lease_lost(Link *link) { assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &route->dst_prefixlen) >= 0); route_remove(route, link, - &link_route_remove_handler); + link_route_remove_handler); } } } @@ -194,7 +198,7 @@ static int dhcp_lease_lost(Link *link) { route_gw->scope = RT_SCOPE_LINK; route_remove(route_gw, link, - &link_route_remove_handler); + link_route_remove_handler); } r = route_new(&route); @@ -203,7 +207,7 @@ static int dhcp_lease_lost(Link *link) { route->gw.in = gateway; route_remove(route, link, - &link_route_remove_handler); + link_route_remove_handler); } } @@ -217,7 +221,7 @@ static int dhcp_lease_lost(Link *link) { address->in_addr.in = addr; address->prefixlen = prefixlen; - address_remove(address, link, &link_address_remove_handler); + address_remove(address, link, link_address_remove_handler); } } @@ -305,7 +309,7 @@ static int dhcp4_update_address(Link *link, /* allow reusing an existing address and simply update its lifetime * in case it already exists */ - r = address_configure(addr, link, &dhcp4_address_handler, true); + r = address_configure(addr, link, dhcp4_address_handler, true); if (r < 0) return r; @@ -624,14 +628,24 @@ int dhcp4_configure(Link *link) { } switch (link->network->dhcp_client_identifier) { - case DHCP_CLIENT_ID_DUID: - /* Library defaults to this. */ + case DHCP_CLIENT_ID_DUID: { + /* If configured, apply user specified DUID and/or IAID */ + const DUID *duid = link_duid(link); + + r = sd_dhcp_client_set_iaid_duid(link->dhcp_client, + link->network->iaid, + duid->type, + duid->raw_data_len > 0 ? duid->raw_data : NULL, + duid->raw_data_len); + if (r < 0) + return r; break; + } case DHCP_CLIENT_ID_MAC: r = sd_dhcp_client_set_client_id(link->dhcp_client, ARPHRD_ETHER, (const uint8_t *) &link->mac, - sizeof (link->mac)); + sizeof(link->mac)); if (r < 0) return r; break; diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index 5f7a005c36..37e13e639e 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -23,7 +23,7 @@ #include "sd-dhcp6-client.h" #include "network-internal.h" -#include "networkd-link.h" +#include "networkd.h" static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link); @@ -103,8 +103,8 @@ static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link) { sd_dhcp6_lease_reset_address_iter(lease); while (sd_dhcp6_lease_get_address(lease, &ip6_addr, - &lifetime_preferred, - &lifetime_valid) >= 0) { + &lifetime_preferred, + &lifetime_valid) >= 0) { r = dhcp6_address_change(link, &ip6_addr, lifetime_preferred, lifetime_valid); if (r < 0) @@ -194,18 +194,13 @@ int dhcp6_request_address(Link *link) { if (r < 0) return r; - if (running) { - r = sd_dhcp6_client_start(link->dhcp6_client); - if (r < 0) - return r; - } - return 0; } int dhcp6_configure(Link *link) { sd_dhcp6_client *client = NULL; int r; + const DUID *duid; assert(link); @@ -230,6 +225,18 @@ int dhcp6_configure(Link *link) { if (r < 0) goto error; + r = sd_dhcp6_client_set_iaid(client, link->network->iaid); + if (r < 0) + goto error; + + duid = link_duid(link); + r = sd_dhcp6_client_set_duid(client, + duid->type, + duid->raw_data_len > 0 ? duid->raw_data : NULL, + duid->raw_data_len); + if (r < 0) + goto error; + r = sd_dhcp6_client_set_index(client, link->ifindex); if (r < 0) goto error; diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c index 1538caa204..241f486211 100644 --- a/src/network/networkd-fdb.c +++ b/src/network/networkd-fdb.c @@ -37,7 +37,7 @@ int fdb_entry_new_static(Network *const network, assert(network); /* search entry in hashmap first. */ - if(section) { + if (section) { fdb_entry = hashmap_get(network->fdb_entries_by_section, UINT_TO_PTR(section)); if (fdb_entry) { *ret = fdb_entry; @@ -141,10 +141,10 @@ int fdb_entry_configure(Link *const link, FdbEntry *const fdb_entry) { /* remove and FDB entry. */ void fdb_entry_free(FdbEntry *fdb_entry) { - if(!fdb_entry) + if (!fdb_entry) return; - if(fdb_entry->network) { + if (fdb_entry->network) { LIST_REMOVE(static_fdb_entries, fdb_entry->network->static_fdb_entries, fdb_entry); diff --git a/src/network/networkd-fdb.h b/src/network/networkd-fdb.h index 89b3e29405..84410714f5 100644 --- a/src/network/networkd-fdb.h +++ b/src/network/networkd-fdb.h @@ -19,10 +19,12 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -typedef struct FdbEntry FdbEntry; +#include "list.h" +#include "macro.h" -#include "networkd-network.h" -#include "networkd.h" +typedef struct Network Network; +typedef struct FdbEntry FdbEntry; +typedef struct Link Link; struct FdbEntry { Network *network; diff --git a/src/network/networkd-gperf.gperf b/src/network/networkd-gperf.gperf new file mode 100644 index 0000000000..3fdfe74955 --- /dev/null +++ b/src/network/networkd-gperf.gperf @@ -0,0 +1,18 @@ +%{ +#include <stddef.h> +#include "conf-parser.h" +#include "networkd-conf.h" +%} +struct ConfigPerfItem; +%null_strings +%language=ANSI-C +%define slot-name section_and_lvalue +%define hash-function-name networkd_gperf_hash +%define lookup-function-name networkd_gperf_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Manager, duid.type) +DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Manager, duid) diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 949c75337c..ae323d595b 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -21,7 +21,7 @@ #include <linux/if.h> #include "network-internal.h" -#include "networkd-link.h" +#include "networkd.h" static int ipv4ll_address_lost(Link *link) { _cleanup_address_free_ Address *address = NULL; @@ -51,7 +51,7 @@ static int ipv4ll_address_lost(Link *link) { address->prefixlen = 16; address->scope = RT_SCOPE_LINK; - address_remove(address, link, &link_address_remove_handler); + address_remove(address, link, link_address_remove_handler); r = route_new(&route); if (r < 0) { @@ -63,7 +63,7 @@ static int ipv4ll_address_lost(Link *link) { route->scope = RT_SCOPE_LINK; route->priority = IPV4LL_ROUTE_METRIC; - route_remove(route, link, &link_route_remove_handler); + route_remove(route, link, link_route_remove_handler); link_check_ready(link); @@ -165,7 +165,7 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) { return 0; } -static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata){ +static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) { Link *link = userdata; int r; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 692c0bf63d..a021fc886f 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -28,8 +28,8 @@ #include "fileio.h" #include "netlink-util.h" #include "network-internal.h" -#include "networkd-link.h" -#include "networkd-netdev.h" +#include "networkd.h" +#include "networkd-lldp-tx.h" #include "set.h" #include "socket-util.h" #include "stdio-util.h" @@ -38,7 +38,12 @@ #include "util.h" #include "virt.h" -bool link_dhcp6_enabled(Link *link) { +static bool link_dhcp6_enabled(Link *link) { + assert(link); + + if (!socket_ipv6_is_supported()) + return false; + if (link->flags & IFF_LOOPBACK) return false; @@ -48,7 +53,9 @@ bool link_dhcp6_enabled(Link *link) { return link->network->dhcp & ADDRESS_FAMILY_IPV6; } -bool link_dhcp4_enabled(Link *link) { +static bool link_dhcp4_enabled(Link *link) { + assert(link); + if (link->flags & IFF_LOOPBACK) return false; @@ -58,7 +65,9 @@ bool link_dhcp4_enabled(Link *link) { return link->network->dhcp & ADDRESS_FAMILY_IPV4; } -bool link_dhcp4_server_enabled(Link *link) { +static bool link_dhcp4_server_enabled(Link *link) { + assert(link); + if (link->flags & IFF_LOOPBACK) return false; @@ -68,7 +77,9 @@ bool link_dhcp4_server_enabled(Link *link) { return link->network->dhcp_server; } -bool link_ipv4ll_enabled(Link *link) { +static bool link_ipv4ll_enabled(Link *link) { + assert(link); + if (link->flags & IFF_LOOPBACK) return false; @@ -78,7 +89,12 @@ bool link_ipv4ll_enabled(Link *link) { return link->network->link_local & ADDRESS_FAMILY_IPV4; } -bool link_ipv6ll_enabled(Link *link) { +static bool link_ipv6ll_enabled(Link *link) { + assert(link); + + if (!socket_ipv6_is_supported()) + return false; + if (link->flags & IFF_LOOPBACK) return false; @@ -88,20 +104,51 @@ bool link_ipv6ll_enabled(Link *link) { return link->network->link_local & ADDRESS_FAMILY_IPV6; } -bool link_lldp_enabled(Link *link) { +static bool link_ipv6_enabled(Link *link) { + assert(link); + + if (!socket_ipv6_is_supported()) + return false; + + return link_dhcp6_enabled(link) || link_ipv6ll_enabled(link) || network_has_static_ipv6_addresses(link->network); +} + +static bool link_lldp_rx_enabled(Link *link) { + assert(link); + if (link->flags & IFF_LOOPBACK) return false; + if (link->iftype != ARPHRD_ETHER) + return false; + if (!link->network) return false; if (link->network->bridge) return false; - return link->network->lldp; + return link->network->lldp_mode != LLDP_MODE_NO; +} + +static bool link_lldp_emit_enabled(Link *link) { + assert(link); + + if (link->flags & IFF_LOOPBACK) + return false; + + if (link->iftype != ARPHRD_ETHER) + return false; + + if (!link->network) + return false; + + return link->network->lldp_emit != LLDP_EMIT_NO; } static bool link_ipv4_forward_enabled(Link *link) { + assert(link); + if (link->flags & IFF_LOOPBACK) return false; @@ -115,6 +162,7 @@ static bool link_ipv4_forward_enabled(Link *link) { } static bool link_ipv6_forward_enabled(Link *link) { + assert(link); if (!socket_ipv6_is_supported()) return false; @@ -131,7 +179,27 @@ static bool link_ipv6_forward_enabled(Link *link) { return link->network->ip_forward & ADDRESS_FAMILY_IPV6; } -bool link_ipv6_accept_ra_enabled(Link *link) { +static bool link_proxy_arp_enabled(Link *link) { + assert(link); + + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + return false; + + if (link->network->proxy_arp < 0) + return false; + + return true; +} + +static bool link_ipv6_accept_ra_enabled(Link *link) { + assert(link); + + if (!socket_ipv6_is_supported()) + return false; + if (link->flags & IFF_LOOPBACK) return false; @@ -154,6 +222,7 @@ bool link_ipv6_accept_ra_enabled(Link *link) { } static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) { + assert(link); if (!socket_ipv6_is_supported()) return _IPV6_PRIVACY_EXTENSIONS_INVALID; @@ -167,6 +236,31 @@ static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) { return link->network->ipv6_privacy_extensions; } +static int link_enable_ipv6(Link *link) { + const char *p = NULL; + bool disabled; + int r; + + if (link->flags & IFF_LOOPBACK) + return 0; + + disabled = !link_ipv6_enabled(link); + + p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/disable_ipv6"); + + r = write_string_file(p, one_zero(disabled), WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) + log_link_warning_errno(link, r, "Cannot %s IPv6 for interface %s: %m", disabled ? "disable" : "enable", link->ifname); + else { + if (disabled) + log_link_info(link, "IPv6 disabled for interface: %m"); + else + log_link_info(link, "IPv6 enabled for interface: %m"); + } + + return 0; +} + void link_update_operstate(Link *link) { LinkOperationalState operstate; assert(link); @@ -214,7 +308,6 @@ void link_update_operstate(Link *link) { link->operstate = operstate; link_send_changed(link, "OperationalState", NULL); link_dirty(link); - manager_dirty(link->manager); } } @@ -300,6 +393,7 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { uint16_t type; const char *ifname; int r, ifindex; + unsigned short iftype; assert(manager); assert(message); @@ -317,6 +411,10 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { else if (ifindex <= 0) return -EINVAL; + r = sd_rtnl_message_link_get_type(message, &iftype); + if (r < 0) + return r; + r = sd_netlink_message_read_string(message, IFLA_IFNAME, &ifname); if (r < 0) return r; @@ -330,30 +428,24 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { link->state = LINK_STATE_PENDING; link->rtnl_extended_attrs = true; link->ifindex = ifindex; + link->iftype = iftype; link->ifname = strdup(ifname); if (!link->ifname) return -ENOMEM; r = sd_netlink_message_read_ether_addr(message, IFLA_ADDRESS, &link->mac); if (r < 0) - log_link_debug(link, "MAC address not found for new device, continuing without"); + log_link_debug_errno(link, r, "MAC address not found for new device, continuing without"); - r = asprintf(&link->state_file, "/run/systemd/netif/links/%d", - link->ifindex); - if (r < 0) + if (asprintf(&link->state_file, "/run/systemd/netif/links/%d", link->ifindex) < 0) return -ENOMEM; - r = asprintf(&link->lease_file, "/run/systemd/netif/leases/%d", - link->ifindex); - if (r < 0) + if (asprintf(&link->lease_file, "/run/systemd/netif/leases/%d", link->ifindex) < 0) return -ENOMEM; - r = asprintf(&link->lldp_file, "/run/systemd/netif/lldp/%d", - link->ifindex); - if (r < 0) + if (asprintf(&link->lldp_file, "/run/systemd/netif/lldp/%d", link->ifindex) < 0) return -ENOMEM; - r = hashmap_ensure_allocated(&manager->links, NULL); if (r < 0) return r; @@ -399,10 +491,11 @@ static void link_free(Link *link) { sd_dhcp_client_unref(link->dhcp_client); sd_dhcp_lease_unref(link->dhcp_lease); + link_lldp_emit_stop(link); + free(link->lease_file); sd_lldp_unref(link->lldp); - free(link->lldp_file); sd_ipv4ll_unref(link->ipv4ll); @@ -436,7 +529,7 @@ Link *link_unref(Link *link) { assert(link->n_ref > 0); - link->n_ref --; + link->n_ref--; if (link->n_ref > 0) return NULL; @@ -452,7 +545,7 @@ Link *link_ref(Link *link) { assert(link->n_ref > 0); - link->n_ref ++; + link->n_ref++; return link; } @@ -482,8 +575,6 @@ static void link_set_state(Link *link, LinkState state) { link->state = state; link_send_changed(link, "AdministrativeState", NULL); - - return; } static void link_enter_unmanaged(Link *link) { @@ -506,33 +597,28 @@ static int link_stop_clients(Link *link) { if (link->dhcp_client) { k = sd_dhcp_client_stop(link->dhcp_client); if (k < 0) - r = log_link_warning_errno(link, r, "Could not stop DHCPv4 client: %m"); + r = log_link_warning_errno(link, k, "Could not stop DHCPv4 client: %m"); } if (link->ipv4ll) { k = sd_ipv4ll_stop(link->ipv4ll); if (k < 0) - r = log_link_warning_errno(link, r, "Could not stop IPv4 link-local: %m"); + r = log_link_warning_errno(link, k, "Could not stop IPv4 link-local: %m"); } if (link->dhcp6_client) { k = sd_dhcp6_client_stop(link->dhcp6_client); if (k < 0) - r = log_link_warning_errno(link, r, "Could not stop DHCPv6 client: %m"); + r = log_link_warning_errno(link, k, "Could not stop DHCPv6 client: %m"); } if (link->ndisc_router_discovery) { k = sd_ndisc_stop(link->ndisc_router_discovery); if (k < 0) - r = log_link_warning_errno(link, r, "Could not stop IPv6 Router Discovery: %m"); - } - - if (link->lldp) { - k = sd_lldp_stop(link->lldp); - if (k < 0) - r = log_link_warning_errno(link, r, "Could not stop LLDP: %m"); + r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Discovery: %m"); } + link_lldp_emit_stop(link); return r; } @@ -600,6 +686,9 @@ void link_check_ready(Link *link) { assert(link); + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + if (!link->network) return; @@ -645,7 +734,7 @@ static int route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata LINK_STATE_SETTING_ROUTES, LINK_STATE_FAILED, LINK_STATE_LINGER)); - link->link_messages --; + link->link_messages--; if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; @@ -674,14 +763,14 @@ static int link_enter_set_routes(Link *link) { link_set_state(link, LINK_STATE_SETTING_ROUTES); LIST_FOREACH(routes, rt, link->network->static_routes) { - r = route_configure(rt, link, &route_handler); + r = route_configure(rt, link, route_handler); if (r < 0) { log_link_warning_errno(link, r, "Could not set routes: %m"); link_enter_failed(link); return r; } - link->link_messages ++; + link->link_messages++; } if (link->link_messages == 0) { @@ -723,7 +812,7 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda assert(IN_SET(link->state, LINK_STATE_SETTING_ADDRESSES, LINK_STATE_FAILED, LINK_STATE_LINGER)); - link->link_messages --; + link->link_messages--; if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; @@ -843,14 +932,14 @@ static int link_enter_set_addresses(Link *link) { link_set_state(link, LINK_STATE_SETTING_ADDRESSES); LIST_FOREACH(addresses, ad, link->network->static_addresses) { - r = address_configure(ad, link, &address_handler, false); + r = address_configure(ad, link, address_handler, false); if (r < 0) { log_link_warning_errno(link, r, "Could not set addresses: %m"); link_enter_failed(link); return r; } - link->link_messages ++; + link->link_messages++; } /* now that we can figure out a default address for the dhcp server, @@ -934,6 +1023,12 @@ static int link_enter_set_addresses(Link *link) { log_link_warning_errno(link, r, "Failed to set NTP server for DHCP server, ignoring: %m"); } + r = sd_dhcp_server_set_emit_router(link->dhcp_server, link->network->dhcp_server_emit_router); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to set router emission for DHCP server: %m"); + return r; + } + if (link->network->dhcp_server_emit_timezone) { _cleanup_free_ char *buffer = NULL; const char *tz = NULL; @@ -999,7 +1094,7 @@ static int link_set_bridge_fdb(Link *const link) { LIST_FOREACH(static_fdb_entries, fdb_entry, link->network->static_fdb_entries) { r = fdb_entry_configure(link, fdb_entry); - if(r < 0) { + if (r < 0) { log_link_error_errno(link, r, "Failed to add MAC entry to static MAC table: %m"); break; } @@ -1008,6 +1103,22 @@ static int link_set_bridge_fdb(Link *const link) { return r; } +static int link_set_proxy_arp(Link *const link) { + const char *p = NULL; + int r; + + if (!link_proxy_arp_enabled(link)) + return 0; + + p = strjoina("/proc/sys/net/ipv4/conf/", link->ifname, "/proxy_arp"); + + r = write_string_file(p, one_zero(link->network->proxy_arp), WRITE_STRING_FILE_VERIFY_ON_FAILURE); + if (r < 0) + log_link_warning_errno(link, r, "Cannot configure proxy ARP for interface: %m"); + + return 0; +} + static int link_set_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { _cleanup_link_unref_ Link *link = userdata; int r; @@ -1211,7 +1322,7 @@ static int link_set_bridge(Link *link) { if (r < 0) return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_UNICAST_FLOOD attribute: %m"); - if(link->network->cost != 0) { + if (link->network->cost != 0) { r = sd_netlink_message_append_u32(req, IFLA_BRPORT_COST, link->network->cost); if (r < 0) return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_COST attribute: %m"); @@ -1230,23 +1341,93 @@ static int link_set_bridge(Link *link) { return r; } -static void lldp_handler(sd_lldp *lldp, int event, void *userdata) { +static int link_lldp_save(Link *link) { + _cleanup_free_ char *temp_path = NULL; + _cleanup_fclose_ FILE *f = NULL; + sd_lldp_neighbor **l = NULL; + int n = 0, r, i; + + assert(link); + assert(link->lldp_file); + + if (!link->lldp) { + (void) unlink(link->lldp_file); + return 0; + } + + r = sd_lldp_get_neighbors(link->lldp, &l); + if (r < 0) + goto finish; + if (r == 0) { + (void) unlink(link->lldp_file); + goto finish; + } + + n = r; + + r = fopen_temporary(link->lldp_file, &f, &temp_path); + if (r < 0) + goto finish; + + fchmod(fileno(f), 0644); + + for (i = 0; i < n; i++) { + const void *p; + le64_t u; + size_t sz; + + r = sd_lldp_neighbor_get_raw(l[i], &p, &sz); + if (r < 0) + goto finish; + + u = htole64(sz); + (void) fwrite(&u, 1, sizeof(u), f); + (void) fwrite(p, 1, sz, f); + } + + r = fflush_and_check(f); + if (r < 0) + goto finish; + + if (rename(temp_path, link->lldp_file) < 0) { + r = -errno; + goto finish; + } + +finish: + if (r < 0) { + (void) unlink(link->lldp_file); + if (temp_path) + (void) unlink(temp_path); + + log_link_error_errno(link, r, "Failed to save LLDP data to %s: %m", link->lldp_file); + } + + if (l) { + for (i = 0; i < n; i++) + sd_lldp_neighbor_unref(l[i]); + free(l); + } + + return r; +} + +static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata) { Link *link = userdata; int r; assert(link); - assert(link->network); - assert(link->manager); - switch (event) { - case SD_LLDP_EVENT_UPDATE_INFO: - r = sd_lldp_save(link->lldp, link->lldp_file); - if (r < 0) - log_link_warning_errno(link, r, "Could not save LLDP: %m"); + (void) link_lldp_save(link); + + if (link_lldp_emit_enabled(link) && event == SD_LLDP_EVENT_ADDED) { + /* If we received information about a new neighbor, restart the LLDP "fast" logic */ - break; - default: - break; + log_link_debug(link, "Received LLDP datagram from previously unknown neighbor, restarting 'fast' LLDP transmission."); + + r = link_lldp_emit_start(link); + if (r < 0) + log_link_warning_errno(link, r, "Failed to restart LLDP transmission: %m"); } } @@ -1283,7 +1464,7 @@ static int link_acquire_ipv6_conf(Link *link) { return 0; } -static int link_acquire_conf(Link *link) { +static int link_acquire_ipv4_conf(Link *link) { int r; assert(link); @@ -1311,14 +1492,28 @@ static int link_acquire_conf(Link *link) { return log_link_warning_errno(link, r, "Could not acquire DHCPv4 lease: %m"); } - if (link_lldp_enabled(link)) { - assert(link->lldp); + return 0; +} + +static int link_acquire_conf(Link *link) { + int r; - log_link_debug(link, "Starting LLDP"); + assert(link); - r = sd_lldp_start(link->lldp); + r = link_acquire_ipv4_conf(link); + if (r < 0) + return r; + + if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) == 0) { + r = link_acquire_ipv6_conf(link); + if (r < 0) + return r; + } + + if (link_lldp_emit_enabled(link)) { + r = link_lldp_emit_start(link); if (r < 0) - return log_link_warning_errno(link, r, "Could not start LLDP: %m"); + return log_link_warning_errno(link, r, "Failed to start LLDP transmission: %m"); } return 0; @@ -1382,7 +1577,21 @@ static int link_up(Link *link) { return log_link_error_errno(link, r, "Could not set MAC address: %m"); } + /* If IPv6 not configured (no static IPv6 address and neither DHCPv6 nor IPv6LL is enabled) + for this interface then disable IPv6 else enable it. */ + (void) link_enable_ipv6(link); + if (link->network->mtu) { + /* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes + on the interface. Bump up MTU bytes to IPV6_MTU_MIN. */ + if (link_ipv6_enabled(link) && link->network->mtu < IPV6_MIN_MTU) { + + log_link_warning(link, "Bumping MTU to " STRINGIFY(IPV6_MIN_MTU) ", as " + "IPv6 is requested and requires a minimum MTU of " STRINGIFY(IPV6_MIN_MTU) " bytes: %m"); + + link->network->mtu = IPV6_MIN_MTU; + } + r = sd_netlink_message_append_u32(req, IFLA_MTU, link->network->mtu); if (r < 0) return log_link_error_errno(link, r, "Could not set MTU: %m"); @@ -1392,7 +1601,7 @@ static int link_up(Link *link) { if (r < 0) return log_link_error_errno(link, r, "Could not open IFLA_AF_SPEC container: %m"); - if (socket_ipv6_is_supported()) { + if (link_ipv6_enabled(link)) { /* if the kernel lacks ipv6 support setting IFF_UP fails if any ipv6 options are passed */ r = sd_netlink_message_open_container(req, AF_INET6); if (r < 0) @@ -1559,7 +1768,7 @@ static int link_new_bound_by_list(Link *link) { m = link->manager; - HASHMAP_FOREACH (carrier, m->links, i) { + HASHMAP_FOREACH(carrier, m->links, i) { if (!carrier->network) continue; @@ -1578,7 +1787,7 @@ static int link_new_bound_by_list(Link *link) { if (list_updated) link_dirty(link); - HASHMAP_FOREACH (carrier, link->bound_by_links, i) { + HASHMAP_FOREACH(carrier, link->bound_by_links, i) { r = link_put_carrier(carrier, link, &carrier->bound_to_links); if (r < 0) return r; @@ -1738,7 +1947,7 @@ static int link_joined(Link *link) { } } - if(link->network->bridge) { + if (link->network->bridge) { r = link_set_bridge(link); if (r < 0) log_link_error_errno(link, r, "Could not set bridge message: %m"); @@ -1754,7 +1963,7 @@ static int netdev_join_handler(sd_netlink *rtnl, sd_netlink_message *m, void *us assert(link); assert(link->network); - link->enslaving --; + link->enslaving--; if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; @@ -1810,7 +2019,7 @@ static int link_enter_join_netdev(Link *link) { return r; } - link->enslaving ++; + link->enslaving++; } if (link->network->bridge) { @@ -1831,7 +2040,7 @@ static int link_enter_join_netdev(Link *link) { return r; } - link->enslaving ++; + link->enslaving++; } HASHMAP_FOREACH(netdev, link->network->stacked_netdevs, i) { @@ -1853,7 +2062,7 @@ static int link_enter_join_netdev(Link *link) { return r; } - link->enslaving ++; + link->enslaving++; } return 0; @@ -2030,6 +2239,27 @@ static int link_drop_foreign_config(Link *link) { return 0; } +static int link_update_lldp(Link *link) { + int r; + + assert(link); + + if (!link->lldp) + return 0; + + if (link->flags & IFF_UP) { + r = sd_lldp_start(link->lldp); + if (r > 0) + log_link_debug(link, "Started LLDP."); + } else { + r = sd_lldp_stop(link->lldp); + if (r > 0) + log_link_debug(link, "Stopped LLDP."); + } + + return r; +} + static int link_configure(Link *link) { int r; @@ -2049,6 +2279,10 @@ static int link_configure(Link *link) { if (r < 0) return r; + r = link_set_proxy_arp(link); + if (r < 0) + return r; + r = link_set_ipv4_forward(link); if (r < 0) return r; @@ -2108,8 +2342,19 @@ static int link_configure(Link *link) { return r; } - if (link_lldp_enabled(link)) { - r = sd_lldp_new(link->ifindex, link->ifname, &link->mac, &link->lldp); + if (link_lldp_rx_enabled(link)) { + r = sd_lldp_new(&link->lldp, link->ifindex); + if (r < 0) + return r; + + r = sd_lldp_match_capabilities(link->lldp, + link->network->lldp_mode == LLDP_MODE_ROUTERS_ONLY ? + SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS : + SD_LLDP_SYSTEM_CAPABILITIES_ALL); + if (r < 0) + return r; + + r = sd_lldp_set_filter_address(link->lldp, &link->mac); if (r < 0) return r; @@ -2117,8 +2362,11 @@ static int link_configure(Link *link) { if (r < 0) return r; - r = sd_lldp_set_callback(link->lldp, - lldp_handler, link); + r = sd_lldp_set_callback(link->lldp, lldp_handler, link); + if (r < 0) + return r; + + r = link_update_lldp(link); if (r < 0) return r; } @@ -2127,12 +2375,6 @@ static int link_configure(Link *link) { r = link_acquire_conf(link); if (r < 0) return r; - - if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) == 0) { - r = link_acquire_ipv6_conf(link); - if (r < 0) - return r; - } } return link_enter_join_netdev(link); @@ -2307,7 +2549,7 @@ network_file_fail: continue; } - *prefixlen_str ++ = '\0'; + *prefixlen_str++ = '\0'; r = sscanf(prefixlen_str, "%hhu", &prefixlen); if (r != 1) { @@ -2354,7 +2596,7 @@ network_file_fail: continue; } - *prefixlen_str ++ = '\0'; + *prefixlen_str++ = '\0'; r = sscanf(prefixlen_str, "%hhu/%hhu/%"SCNu32"/%hhu/"USEC_FMT, &prefixlen, &tos, &priority, &table, &lifetime); if (r != 5) { @@ -2515,6 +2757,10 @@ static int link_carrier_gained(Link *link) { link_enter_failed(link); return r; } + + r = link_enter_set_addresses(link); + if (r < 0) + return r; } r = link_handle_bound_by_list(link); @@ -2562,7 +2808,6 @@ int link_carrier_reset(Link *link) { return 0; } - int link_update(Link *link, sd_netlink_message *m) { struct ether_addr mac; const char *ifname; @@ -2643,21 +2888,45 @@ int link_update(Link *link, sd_netlink_message *m) { } if (link->dhcp_client) { + const DUID *duid = link_duid(link); + r = sd_dhcp_client_set_mac(link->dhcp_client, (const uint8_t *) &link->mac, sizeof (link->mac), ARPHRD_ETHER); if (r < 0) return log_link_warning_errno(link, r, "Could not update MAC address in DHCP client: %m"); + + r = sd_dhcp_client_set_iaid_duid(link->dhcp_client, + link->network->iaid, + duid->type, + duid->raw_data_len > 0 ? duid->raw_data : NULL, + duid->raw_data_len); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update DUID/IAID in DHCP client: %m"); } if (link->dhcp6_client) { + const DUID* duid = link_duid(link); + r = sd_dhcp6_client_set_mac(link->dhcp6_client, (const uint8_t *) &link->mac, sizeof (link->mac), ARPHRD_ETHER); if (r < 0) return log_link_warning_errno(link, r, "Could not update MAC address in DHCPv6 client: %m"); + + r = sd_dhcp6_client_set_iaid(link->dhcp6_client, + link->network->iaid); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update DHCPv6 IAID: %m"); + + r = sd_dhcp6_client_set_duid(link->dhcp6_client, + duid->type, + duid->raw_data_len > 0 ? duid->raw_data : NULL, + duid->raw_data_len); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update DHCPv6 DUID: %m"); } } } @@ -2668,6 +2937,10 @@ int link_update(Link *link, sd_netlink_message *m) { if (r < 0) return r; + r = link_update_lldp(link); + if (r < 0) + return r; + carrier_gained = !had_carrier && link_has_carrier(link); carrier_lost = had_carrier && !link_has_carrier(link); @@ -2683,12 +2956,34 @@ int link_update(Link *link, sd_netlink_message *m) { r = link_carrier_lost(link); if (r < 0) return r; - } return 0; } +static void print_link_hashmap(FILE *f, const char *prefix, Hashmap* h) { + bool space = false; + Iterator i; + Link *link; + + assert(f); + assert(prefix); + + if (hashmap_isempty(h)) + return; + + fputs(prefix, f); + HASHMAP_FOREACH(link, h, i) { + if (space) + fputc(' ', f); + + fprintf(f, "%i", link->ifindex); + space = true; + } + + fputc('\n', f); +} + int link_save(Link *link) { _cleanup_free_ char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; @@ -2708,6 +3003,8 @@ int link_save(Link *link) { return 0; } + link_lldp_save(link); + admin_state = link_state_to_string(link->state); assert(admin_state); @@ -2887,27 +3184,8 @@ int link_save(Link *link) { fputc('\n', f); } - if (!hashmap_isempty(link->bound_to_links)) { - Link *carrier; - bool space = false; - - fputs("CARRIER_BOUND_TO=", f); - HASHMAP_FOREACH(carrier, link->bound_to_links, i) - fputs_with_space(f, carrier->ifname, NULL, &space); - - fputc('\n', f); - } - - if (!hashmap_isempty(link->bound_by_links)) { - Link *carrier; - bool space = false; - - fputs("CARRIER_BOUND_BY=", f); - HASHMAP_FOREACH(carrier, link->bound_by_links, i) - fputs_with_space(f, carrier->ifname, NULL, &space); - - fputc('\n', f); - } + print_link_hashmap(f, "CARRIER_BOUND_TO=", link->bound_to_links); + print_link_hashmap(f, "CARRIER_BOUND_BY=", link->bound_by_links); if (link->dhcp_lease) { struct in_addr address; @@ -2947,19 +3225,6 @@ int link_save(Link *link) { } } - if (link->lldp) { - assert(link->network); - - r = sd_lldp_save(link->lldp, link->lldp_file); - if (r < 0) - goto fail; - - fprintf(f, - "LLDP_FILE=%s\n", - link->lldp_file); - } else - unlink(link->lldp_file); - r = fflush_and_check(f); if (r < 0) goto fail; @@ -2985,14 +3250,17 @@ void link_dirty(Link *link) { assert(link); + /* mark manager dirty as link is dirty */ + manager_dirty(link->manager); + r = set_ensure_allocated(&link->manager->dirty_links, NULL); if (r < 0) /* allocation errors are ignored */ return; r = set_put(link->manager->dirty_links, link); - if (r < 0) - /* allocation errors are ignored */ + if (r <= 0) + /* don't take another ref if the link was already dirty */ return; link_ref(link); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 0e6a7b6f21..14c4a02c7e 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -21,14 +21,17 @@ #include <endian.h> +#include "sd-bus.h" #include "sd-dhcp-client.h" #include "sd-dhcp-server.h" #include "sd-dhcp6-client.h" #include "sd-ipv4ll.h" #include "sd-lldp.h" #include "sd-ndisc.h" +#include "sd-netlink.h" -typedef struct Link Link; +#include "list.h" +#include "set.h" typedef enum LinkState { LINK_STATE_PENDING, @@ -54,17 +57,18 @@ typedef enum LinkOperationalState { _LINK_OPERSTATE_INVALID = -1 } LinkOperationalState; -#include "networkd-address.h" -#include "networkd-network.h" -#include "networkd.h" +typedef struct Manager Manager; +typedef struct Network Network; +typedef struct Address Address; -struct Link { +typedef struct Link { Manager *manager; int n_ref; int ifindex; char *ifname; + unsigned short iftype; char *state_file; struct ether_addr mac; struct in6_addr ipv6ll_address; @@ -111,12 +115,17 @@ struct Link { sd_dhcp6_client *dhcp6_client; bool rtnl_extended_attrs; + /* This is about LLDP reception */ sd_lldp *lldp; char *lldp_file; + /* This is about LLDP transmission */ + unsigned lldp_tx_fast; /* The LLDP txFast counter (See 802.1ab-2009, section 9.2.5.18) */ + sd_event_source *lldp_emit_event_source; + Hashmap *bound_by_links; Hashmap *bound_to_links; -}; +} Link; Link *link_unref(Link *link); Link *link_ref(Link *link); @@ -154,14 +163,6 @@ int dhcp6_configure(Link *link); int dhcp6_request_address(Link *link); int ndisc_configure(Link *link); -bool link_lldp_enabled(Link *link); -bool link_ipv4ll_enabled(Link *link); -bool link_ipv6ll_enabled(Link *link); -bool link_dhcp4_server_enabled(Link *link); -bool link_dhcp4_enabled(Link *link); -bool link_dhcp6_enabled(Link *link); -bool link_ipv6_accept_ra_enabled(Link *link); - const char* link_state_to_string(LinkState s) _const_; LinkState link_state_from_string(const char *s) _pure_; diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c new file mode 100644 index 0000000000..3aa768388b --- /dev/null +++ b/src/network/networkd-lldp-tx.c @@ -0,0 +1,416 @@ +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <endian.h> +#include <inttypes.h> +#include <string.h> + +#include "alloc-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "hostname-util.h" +#include "networkd-lldp-tx.h" +#include "networkd.h" +#include "parse-util.h" +#include "random-util.h" +#include "socket-util.h" +#include "string-util.h" +#include "unaligned.h" + +/* The LLDP spec calls this "txFastInit", see 9.2.5.19 */ +#define LLDP_TX_FAST_INIT 4U + +/* The LLDP spec calls this "msgTxHold", see 9.2.5.6 */ +#define LLDP_TX_HOLD 4U + +/* The jitter range to add, see 9.2.2. */ +#define LLDP_JITTER_USEC (400U * USEC_PER_MSEC) + +/* The LLDP spec calls this msgTxInterval, but we subtract half the jitter off it. */ +#define LLDP_TX_INTERVAL_USEC (30U * USEC_PER_SEC - LLDP_JITTER_USEC / 2) + +/* The LLDP spec calls this msgFastTx, but we subtract half the jitter off it. */ +#define LLDP_FAST_TX_USEC (1U * USEC_PER_SEC - LLDP_JITTER_USEC / 2) + +static const struct ether_addr lldp_multicast_addr[_LLDP_EMIT_MAX] = { + [LLDP_EMIT_NEAREST_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }}, + [LLDP_EMIT_NON_TPMR_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }}, + [LLDP_EMIT_CUSTOMER_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }}, +}; + +static int lldp_write_tlv_header(uint8_t **p, uint8_t id, size_t sz) { + assert(p); + + if (id > 127) + return -EBADMSG; + if (sz > 511) + return -ENOBUFS; + + (*p)[0] = (id << 1) | !!(sz & 256); + (*p)[1] = sz & 255; + + *p = *p + 2; + return 0; +} + +static int lldp_make_packet( + LLDPEmit mode, + const struct ether_addr *hwaddr, + const char *machine_id, + const char *ifname, + uint16_t ttl, + const char *port_description, + const char *hostname, + const char *pretty_hostname, + uint16_t system_capabilities, + uint16_t enabled_capabilities, + void **ret, size_t *sz) { + + size_t machine_id_length, ifname_length, port_description_length = 0, hostname_length = 0, pretty_hostname_length = 0; + _cleanup_free_ void *packet = NULL; + struct ether_header *h; + uint8_t *p; + size_t l; + int r; + + assert(mode > LLDP_EMIT_NO); + assert(mode < _LLDP_EMIT_MAX); + assert(hwaddr); + assert(machine_id); + assert(ifname); + assert(ret); + assert(sz); + + machine_id_length = strlen(machine_id); + ifname_length = strlen(ifname); + + if (port_description) + port_description_length = strlen(port_description); + + if (hostname) + hostname_length = strlen(hostname); + + if (pretty_hostname) + pretty_hostname_length = strlen(pretty_hostname); + + l = sizeof(struct ether_header) + + /* Chassis ID */ + 2 + 1 + machine_id_length + + /* Port ID */ + 2 + 1 + ifname_length + + /* TTL */ + 2 + 2 + + /* System Capabilities */ + 2 + 4 + + /* End */ + 2; + + /* Port Description */ + if (port_description) + l += 2 + port_description_length; + + /* System Name */ + if (hostname) + l += 2 + hostname_length; + + /* System Description */ + if (pretty_hostname) + l += 2 + pretty_hostname_length; + + packet = malloc(l); + if (!packet) + return -ENOMEM; + + h = (struct ether_header*) packet; + h->ether_type = htobe16(ETHERTYPE_LLDP); + memcpy(h->ether_dhost, lldp_multicast_addr + mode, ETH_ALEN); + memcpy(h->ether_shost, hwaddr, ETH_ALEN); + + p = (uint8_t*) packet + sizeof(struct ether_header); + + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_CHASSIS_ID, 1 + machine_id_length); + if (r < 0) + return r; + *(p++) = SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED; + p = mempcpy(p, machine_id, machine_id_length); + + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_PORT_ID, 1 + ifname_length); + if (r < 0) + return r; + *(p++) = SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME; + p = mempcpy(p, ifname, ifname_length); + + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_TTL, 2); + if (r < 0) + return r; + unaligned_write_be16(p, ttl); + p += 2; + + if (port_description) { + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_PORT_DESCRIPTION, port_description_length); + if (r < 0) + return r; + p = mempcpy(p, port_description, port_description_length); + } + + if (hostname) { + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_NAME, hostname_length); + if (r < 0) + return r; + p = mempcpy(p, hostname, hostname_length); + } + + if (pretty_hostname) { + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_DESCRIPTION, pretty_hostname_length); + if (r < 0) + return r; + p = mempcpy(p, pretty_hostname, pretty_hostname_length); + } + + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_CAPABILITIES, 4); + if (r < 0) + return r; + unaligned_write_be16(p, system_capabilities); + p += 2; + unaligned_write_be16(p, enabled_capabilities); + p += 2; + + r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_END, 0); + if (r < 0) + return r; + + assert(p == (uint8_t*) packet + l); + + *ret = packet; + *sz = l; + + packet = NULL; + return 0; +} + +static int lldp_send_packet( + int ifindex, + const struct ether_addr *address, + const void *packet, + size_t packet_size) { + + union sockaddr_union sa = { + .ll.sll_family = AF_PACKET, + .ll.sll_protocol = htobe16(ETHERTYPE_LLDP), + .ll.sll_ifindex = ifindex, + .ll.sll_halen = ETH_ALEN, + }; + + _cleanup_close_ int fd = -1; + ssize_t l; + + assert(ifindex > 0); + assert(address); + assert(packet || packet_size <= 0); + + memcpy(sa.ll.sll_addr, address, ETH_ALEN); + + fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC, IPPROTO_RAW); + if (fd < 0) + return -errno; + + l = sendto(fd, packet, packet_size, MSG_NOSIGNAL, &sa.sa, sizeof(sa.ll)); + if (l < 0) + return -errno; + + if ((size_t) l != packet_size) + return -EIO; + + return 0; +} + +static int link_send_lldp(Link *link) { + char machine_id_string[SD_ID128_STRING_MAX]; + _cleanup_free_ char *hostname = NULL, *pretty_hostname = NULL; + _cleanup_free_ void *packet = NULL; + size_t packet_size = 0; + sd_id128_t machine_id; + uint16_t caps; + usec_t ttl; + int r; + + assert(link); + + if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO) + return 0; + + assert(link->network->lldp_emit < _LLDP_EMIT_MAX); + + r = sd_id128_get_machine(&machine_id); + if (r < 0) + return r; + + (void) gethostname_strict(&hostname); + (void) parse_env_file("/etc/machine-info", NEWLINE, "PRETTY_HOSTNAME", &pretty_hostname, NULL); + + assert_cc(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1 <= (UINT16_MAX - 1) * USEC_PER_SEC); + ttl = DIV_ROUND_UP(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1, USEC_PER_SEC); + + caps = (link->network && link->network->ip_forward != ADDRESS_FAMILY_NO) ? + SD_LLDP_SYSTEM_CAPABILITIES_ROUTER : + SD_LLDP_SYSTEM_CAPABILITIES_STATION; + + r = lldp_make_packet(link->network->lldp_emit, + &link->mac, + sd_id128_to_string(machine_id, machine_id_string), + link->ifname, + (uint16_t) ttl, + link->network ? link->network->description : NULL, + hostname, + pretty_hostname, + SD_LLDP_SYSTEM_CAPABILITIES_STATION|SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE|SD_LLDP_SYSTEM_CAPABILITIES_ROUTER, + caps, + &packet, &packet_size); + if (r < 0) + return r; + + return lldp_send_packet(link->ifindex, lldp_multicast_addr + link->network->lldp_emit, packet, packet_size); +} + +static int on_lldp_timer(sd_event_source *s, usec_t t, void *userdata) { + Link *link = userdata; + usec_t current, delay, next; + int r; + + assert(s); + assert(userdata); + + log_link_debug(link, "Sending LLDP packet..."); + + r = link_send_lldp(link); + if (r < 0) + log_link_debug_errno(link, r, "Failed to send LLDP packet, ignoring: %m"); + + if (link->lldp_tx_fast > 0) + link->lldp_tx_fast--; + + assert_se(sd_event_now(sd_event_source_get_event(s), clock_boottime_or_monotonic(), ¤t) >= 0); + + delay = link->lldp_tx_fast > 0 ? LLDP_FAST_TX_USEC : LLDP_TX_INTERVAL_USEC; + next = usec_add(usec_add(current, delay), (usec_t) random_u64() % LLDP_JITTER_USEC); + + r = sd_event_source_set_time(s, next); + if (r < 0) + return log_link_error_errno(link, r, "Failed to restart LLDP timer: %m"); + + r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT); + if (r < 0) + return log_link_error_errno(link, r, "Failed to enable LLDP timer: %m"); + + return 0; +} + +int link_lldp_emit_start(Link *link) { + usec_t next; + int r; + + assert(link); + + if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO) { + link_lldp_emit_stop(link); + return 0; + } + + /* Starts the LLDP transmission in "fast" mode. If it is already started, turns "fast" mode back on again. */ + + link->lldp_tx_fast = LLDP_TX_FAST_INIT; + + next = usec_add(usec_add(now(clock_boottime_or_monotonic()), LLDP_FAST_TX_USEC), + (usec_t) random_u64() % LLDP_JITTER_USEC); + + if (link->lldp_emit_event_source) { + usec_t old; + + /* Lower the timeout, maybe */ + r = sd_event_source_get_time(link->lldp_emit_event_source, &old); + if (r < 0) + return r; + + if (old <= next) + return 0; + + return sd_event_source_set_time(link->lldp_emit_event_source, next); + } else { + r = sd_event_add_time( + link->manager->event, + &link->lldp_emit_event_source, + clock_boottime_or_monotonic(), + next, + 0, + on_lldp_timer, + link); + if (r < 0) + return r; + + (void) sd_event_source_set_description(link->lldp_emit_event_source, "lldp-tx"); + } + + return 0; +} + +void link_lldp_emit_stop(Link *link) { + assert(link); + + link->lldp_emit_event_source = sd_event_source_unref(link->lldp_emit_event_source); +} + +int config_parse_lldp_emit( + 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) { + + LLDPEmit *emit = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) + *emit = LLDP_EMIT_NO; + else if (streq(rvalue, "nearest-bridge")) + *emit = LLDP_EMIT_NEAREST_BRIDGE; + else if (streq(rvalue, "non-tpmr-bridge")) + *emit = LLDP_EMIT_NON_TPMR_BRIDGE; + else if (streq(rvalue, "customer-bridge")) + *emit = LLDP_EMIT_CUSTOMER_BRIDGE; + else { + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse LLDP emission setting, ignoring: %s", rvalue); + return 0; + } + + *emit = r ? LLDP_EMIT_NEAREST_BRIDGE : LLDP_EMIT_NO; + } + + return 0; +} diff --git a/src/bootchart/svg.h b/src/network/networkd-lldp-tx.h index 6e06b5ad97..4680c9d950 100644 --- a/src/bootchart/svg.h +++ b/src/network/networkd-lldp-tx.h @@ -3,10 +3,7 @@ /*** This file is part of systemd. - Copyright (C) 2009-2013 Intel Corporation - - Authors: - Auke Kok <auke-jan.h.kok@intel.com> + Copyright 2016 Lennart Poettering systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -22,14 +19,17 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -int svg_do(FILE *of, - const char *build, - struct list_sample_data *head, - struct ps_struct *ps_first, - int n_samples, - int pscount, - int n_cpus, - double graph_start, - double log_start, - double interval, - int overrun); +#include "networkd-link.h" + +typedef enum LLDPEmit { + LLDP_EMIT_NO, + LLDP_EMIT_NEAREST_BRIDGE, + LLDP_EMIT_NON_TPMR_BRIDGE, + LLDP_EMIT_CUSTOMER_BRIDGE, + _LLDP_EMIT_MAX, +} LLDPEmit; + +int link_lldp_emit_start(Link *link); +void link_lldp_emit_stop(Link *link); + +int config_parse_lldp_emit(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-manager.c b/src/network/networkd-manager.c index b527191a5a..9174dcc7f4 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -457,11 +457,9 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo break; case RTM_DELROUTE: - - if (route) - route_drop(route); - + route_free(route); break; + default: assert_not_reached("Received invalid RTNL message type"); } @@ -1037,6 +1035,8 @@ int manager_new(Manager **ret) { if (r < 0) return r; + m->duid.type = DUID_TYPE_EN; + *ret = m; m = NULL; @@ -1091,22 +1091,19 @@ static bool manager_check_idle(void *userdata) { assert(m); + /* Check whether we are idle now. The only case when we decide to be idle is when there's only a loopback + * device around, for which we have no configuration, and which already left the PENDING state. In all other + * cases we are not idle. */ + HASHMAP_FOREACH(link, m->links, i) { - /* we are not woken on udev activity, so let's just wait for the - * pending udev event */ + /* We are not woken on udev activity, so let's just wait for the pending udev event */ if (link->state == LINK_STATE_PENDING) return false; - if (!link->network) - continue; + if ((link->flags & IFF_LOOPBACK) == 0) + return false; - /* we are not woken on netork activity, so let's stay around */ - if (link_lldp_enabled(link) || - link_ipv4ll_enabled(link) || - link_dhcp4_server_enabled(link) || - link_dhcp4_enabled(link) || - link_dhcp6_enabled(link) || - link_ipv6_accept_ra_enabled(link)) + if (link->network) return false; } diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index f2287be20a..1a380bd214 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -19,11 +19,12 @@ #include <netinet/ether.h> #include <netinet/icmp6.h> +#include <netinet/in.h> #include <linux/if.h> #include "sd-ndisc.h" -#include "networkd-link.h" +#include "networkd.h" static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { _cleanup_link_unref_ Link *link = userdata; @@ -32,7 +33,7 @@ static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void * assert(link); assert(link->ndisc_messages > 0); - link->ndisc_messages --; + link->ndisc_messages--; r = sd_netlink_message_get_errno(m); if (r < 0 && r != -EEXIST) { @@ -76,15 +77,15 @@ static void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr memcpy(((char *)&address->in_addr.in6) + 8, ((char *)&link->network->ipv6_token) + 8, 8); else { /* see RFC4291 section 2.5.1 */ - address->in_addr.in6.__in6_u.__u6_addr8[8] = link->mac.ether_addr_octet[0]; - address->in_addr.in6.__in6_u.__u6_addr8[8] ^= 1 << 1; - address->in_addr.in6.__in6_u.__u6_addr8[9] = link->mac.ether_addr_octet[1]; - address->in_addr.in6.__in6_u.__u6_addr8[10] = link->mac.ether_addr_octet[2]; - address->in_addr.in6.__in6_u.__u6_addr8[11] = 0xff; - address->in_addr.in6.__in6_u.__u6_addr8[12] = 0xfe; - address->in_addr.in6.__in6_u.__u6_addr8[13] = link->mac.ether_addr_octet[3]; - address->in_addr.in6.__in6_u.__u6_addr8[14] = link->mac.ether_addr_octet[4]; - address->in_addr.in6.__in6_u.__u6_addr8[15] = link->mac.ether_addr_octet[5]; + address->in_addr.in6.s6_addr[8] = link->mac.ether_addr_octet[0]; + address->in_addr.in6.s6_addr[8] ^= 1 << 1; + address->in_addr.in6.s6_addr[9] = link->mac.ether_addr_octet[1]; + address->in_addr.in6.s6_addr[10] = link->mac.ether_addr_octet[2]; + address->in_addr.in6.s6_addr[11] = 0xff; + address->in_addr.in6.s6_addr[12] = 0xfe; + address->in_addr.in6.s6_addr[13] = link->mac.ether_addr_octet[3]; + address->in_addr.in6.s6_addr[14] = link->mac.ether_addr_octet[4]; + address->in_addr.in6.s6_addr[15] = link->mac.ether_addr_octet[5]; } address->prefixlen = prefixlen; address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR; @@ -98,7 +99,7 @@ static void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr return; } - link->ndisc_messages ++; + link->ndisc_messages++; } static void ndisc_prefix_onlink_handler(sd_ndisc *nd, const struct in6_addr *prefix, unsigned prefixlen, unsigned lifetime, void *userdata) { @@ -136,7 +137,7 @@ static void ndisc_prefix_onlink_handler(sd_ndisc *nd, const struct in6_addr *pre return; } - link->ndisc_messages ++; + link->ndisc_messages++; } static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata) { @@ -156,6 +157,10 @@ static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_a if (flags & ND_RA_FLAG_MANAGED) dhcp6_request_address(link); + r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address); + if (r < 0 && r != -EBUSY) + log_link_warning_errno(link, r, "Could not set IPv6LL address in DHCP client: %m"); + r = sd_dhcp6_client_start(link->dhcp6_client); if (r < 0 && r != -EBUSY) log_link_warning_errno(link, r, "Starting DHCPv6 client on NDisc request failed: %m"); @@ -186,7 +191,7 @@ static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_a return; } - link->ndisc_messages ++; + link->ndisc_messages++; } static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) { @@ -202,6 +207,10 @@ static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) { case SD_NDISC_EVENT_TIMEOUT: dhcp6_request_address(link); + r = sd_dhcp6_client_set_local_address(link->dhcp6_client, &link->ipv6ll_address); + if (r < 0 && r != -EBUSY) + log_link_warning_errno(link, r, "Could not set IPv6LL address in DHCP client: %m"); + r = sd_dhcp6_client_start(link->dhcp6_client); if (r < 0 && r != -EBUSY) log_link_warning_errno(link, r, "Starting DHCPv6 client after NDisc timeout failed: %m"); diff --git a/src/network/networkd-netdev-bond.c b/src/network/networkd-netdev-bond.c index 106f15fabc..7913b0088e 100644 --- a/src/network/networkd-netdev-bond.c +++ b/src/network/networkd-netdev-bond.c @@ -25,6 +25,7 @@ #include "alloc-util.h" #include "conf-parser.h" +#include "extract-word.h" #include "missing.h" #include "networkd-netdev-bond.h" #include "string-table.h" @@ -375,7 +376,7 @@ int config_parse_arp_ip_target_address(const char *unit, } LIST_PREPEND(arp_ip_target, b->arp_ip_targets, buffer); - b->n_arp_ip_targets ++; + b->n_arp_ip_targets++; buffer = NULL; } diff --git a/src/network/networkd-netdev-bond.h b/src/network/networkd-netdev-bond.h index cb6baea24f..b941edb344 100644 --- a/src/network/networkd-netdev-bond.h +++ b/src/network/networkd-netdev-bond.h @@ -20,8 +20,7 @@ ***/ #include "in-addr-util.h" - -typedef struct Bond Bond; +#include "list.h" #include "networkd-netdev.h" @@ -106,7 +105,7 @@ typedef struct ArpIpTarget { LIST_FIELDS(struct ArpIpTarget, arp_ip_target); } ArpIpTarget; -struct Bond { +typedef struct Bond { NetDev meta; BondMode mode; @@ -133,8 +132,9 @@ struct Bond { int n_arp_ip_targets; ArpIpTarget *arp_ip_targets; -}; +} Bond; +DEFINE_NETDEV_CAST(BOND, Bond); extern const NetDevVTable bond_vtable; const char *bond_mode_to_string(BondMode d) _const_; diff --git a/src/network/networkd-netdev-bridge.c b/src/network/networkd-netdev-bridge.c index cdcd08f057..4cfd00413f 100644 --- a/src/network/networkd-netdev-bridge.c +++ b/src/network/networkd-netdev-bridge.c @@ -22,6 +22,7 @@ #include "missing.h" #include "netlink-util.h" +#include "networkd.h" #include "networkd-netdev-bridge.h" /* callback for brige netdev's parameter set */ @@ -89,6 +90,18 @@ 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_MAX_AGE attribute: %m"); } + if (b->mcast_querier >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BR_MCAST_QUERIER, b->mcast_querier); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MCAST_QUERIER attribute: %m"); + } + + if (b->mcast_snooping >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BR_MCAST_SNOOPING, b->mcast_snooping); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MCAST_SNOOPING attribute: %m"); + } + r = sd_netlink_message_close_container(req); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); @@ -106,8 +119,20 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_mess return r; } +static void bridge_init(NetDev *n) { + Bridge *b; + + b = BRIDGE(n); + + assert(b); + + b->mcast_querier = -1; + b->mcast_snooping = -1; +} + const NetDevVTable bridge_vtable = { .object_size = sizeof(Bridge), + .init = bridge_init, .sections = "Match\0NetDev\0Bridge\0", .post_create = netdev_bridge_post_create, .create_type = NETDEV_CREATE_MASTER, diff --git a/src/network/networkd-netdev-bridge.h b/src/network/networkd-netdev-bridge.h index b2bf7e15f1..f2ae21fc50 100644 --- a/src/network/networkd-netdev-bridge.h +++ b/src/network/networkd-netdev-bridge.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,18 +19,18 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - -typedef struct Bridge Bridge; - #include "networkd-netdev.h" -struct Bridge { +typedef struct Bridge { NetDev meta; + int mcast_querier; + int mcast_snooping; + usec_t forward_delay; usec_t hello_time; usec_t max_age; -}; +} Bridge; +DEFINE_NETDEV_CAST(BRIDGE, Bridge); extern const NetDevVTable bridge_vtable; diff --git a/src/network/networkd-netdev-dummy.h b/src/network/networkd-netdev-dummy.h index 29f75a149b..efe302267e 100644 --- a/src/network/networkd-netdev-dummy.h +++ b/src/network/networkd-netdev-dummy.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,14 +19,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - -typedef struct Dummy Dummy; - #include "networkd-netdev.h" -struct Dummy { +typedef struct Dummy { NetDev meta; -}; +} Dummy; +DEFINE_NETDEV_CAST(DUMMY, Dummy); extern const NetDevVTable dummy_vtable; diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf index 8f506af092..ba04bb0165 100644 --- a/src/network/networkd-netdev-gperf.gperf +++ b/src/network/networkd-netdev-gperf.gperf @@ -1,11 +1,17 @@ %{ #include <stddef.h> #include "conf-parser.h" -#include "networkd-netdev.h" -#include "networkd-netdev-tunnel.h" +#include "network-internal.h" #include "networkd-netdev-bond.h" +#include "networkd-netdev-ipvlan.h" #include "networkd-netdev-macvlan.h" -#include "network-internal.h" +#include "networkd-netdev-tunnel.h" +#include "networkd-netdev-tuntap.h" +#include "networkd-netdev-veth.h" +#include "networkd-netdev-vlan.h" +#include "networkd-netdev-vxlan.h" +#include "networkd-netdev-bridge.h" +#include "networkd-netdev.h" %} struct ConfigPerfItem; %null_strings @@ -92,3 +98,5 @@ Bond.LearnPacketIntervalSec, config_parse_sec, 0, Bridge.HelloTimeSec, config_parse_sec, 0, offsetof(Bridge, hello_time) Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age) Bridge.ForwardDelaySec, config_parse_sec, 0, offsetof(Bridge, forward_delay) +Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier) +Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping) diff --git a/src/network/networkd-netdev-ipvlan.h b/src/network/networkd-netdev-ipvlan.h index 5b85ef2150..10d4079844 100644 --- a/src/network/networkd-netdev-ipvlan.h +++ b/src/network/networkd-netdev-ipvlan.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,10 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - -typedef struct IPVlan IPVlan; - #include "missing.h" #include "networkd-netdev.h" @@ -31,12 +29,13 @@ typedef enum IPVlanMode { _NETDEV_IPVLAN_MODE_INVALID = -1 } IPVlanMode; -struct IPVlan { +typedef struct IPVlan { NetDev meta; IPVlanMode mode; -}; +} IPVlan; +DEFINE_NETDEV_CAST(IPVLAN, IPVlan); extern const NetDevVTable ipvlan_vtable; const char *ipvlan_mode_to_string(IPVlanMode d) _const_; diff --git a/src/network/networkd-netdev-macvlan.h b/src/network/networkd-netdev-macvlan.h index 8b42684de6..3663f4f051 100644 --- a/src/network/networkd-netdev-macvlan.h +++ b/src/network/networkd-netdev-macvlan.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - typedef struct MacVlan MacVlan; #include "networkd-netdev.h" @@ -38,6 +38,8 @@ struct MacVlan { MacVlanMode mode; }; +DEFINE_NETDEV_CAST(MACVLAN, MacVlan); +DEFINE_NETDEV_CAST(MACVTAP, MacVlan); extern const NetDevVTable macvlan_vtable; extern const NetDevVTable macvtap_vtable; diff --git a/src/network/networkd-netdev-tunnel.c b/src/network/networkd-netdev-tunnel.c index 46ff2974f4..7aaa041ba3 100644 --- a/src/network/networkd-netdev-tunnel.c +++ b/src/network/networkd-netdev-tunnel.c @@ -54,7 +54,7 @@ static int netdev_ipip_fill_message_create(NetDev *netdev, Link *link, sd_netlin assert(link); assert(m); assert(t); - assert(t->family == AF_INET || t->family != -1); + assert(IN_SET(t->family, AF_INET, AF_UNSPEC)); r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex); if (r < 0) @@ -87,7 +87,7 @@ static int netdev_sit_fill_message_create(NetDev *netdev, Link *link, sd_netlink assert(link); assert(m); assert(t); - assert(t->family == AF_INET || t->family != -1); + assert(IN_SET(t->family, AF_INET, AF_UNSPEC)); r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex); if (r < 0) @@ -124,7 +124,7 @@ static int netdev_gre_fill_message_create(NetDev *netdev, Link *link, sd_netlink t = GRETAP(netdev); assert(t); - assert(t->family == AF_INET || t->family != -1); + assert(IN_SET(t->family, AF_INET, AF_UNSPEC)); assert(link); assert(m); @@ -497,7 +497,7 @@ static void ipip_init(NetDev *n) { assert(t); t->pmtudisc = true; - t->family = -1; + t->family = AF_UNSPEC; } static void sit_init(NetDev *n) { @@ -507,7 +507,7 @@ static void sit_init(NetDev *n) { assert(t); t->pmtudisc = true; - t->family = -1; + t->family = AF_UNSPEC; } static void vti_init(NetDev *n) { @@ -538,7 +538,7 @@ static void gre_init(NetDev *n) { assert(t); t->pmtudisc = true; - t->family = -1; + t->family = AF_UNSPEC; } static void ip6gre_init(NetDev *n) { diff --git a/src/network/networkd-netdev-tunnel.h b/src/network/networkd-netdev-tunnel.h index ea1d9a79e7..7d31e7b687 100644 --- a/src/network/networkd-netdev-tunnel.h +++ b/src/network/networkd-netdev-tunnel.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,9 +19,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - -typedef struct Tunnel Tunnel; +#include "in-addr-util.h" #include "networkd-netdev.h" @@ -37,7 +37,7 @@ typedef enum IPv6FlowLabel { _NETDEV_IPV6_FLOWLABEL_INVALID = -1, } IPv6FlowLabel; -struct Tunnel { +typedef struct Tunnel { NetDev meta; uint8_t encap_limit; @@ -56,8 +56,17 @@ struct Tunnel { bool pmtudisc; bool copy_dscp; -}; - +} Tunnel; + +DEFINE_NETDEV_CAST(IPIP, Tunnel); +DEFINE_NETDEV_CAST(GRE, Tunnel); +DEFINE_NETDEV_CAST(GRETAP, Tunnel); +DEFINE_NETDEV_CAST(IP6GRE, Tunnel); +DEFINE_NETDEV_CAST(IP6GRETAP, Tunnel); +DEFINE_NETDEV_CAST(SIT, Tunnel); +DEFINE_NETDEV_CAST(VTI, Tunnel); +DEFINE_NETDEV_CAST(VTI6, Tunnel); +DEFINE_NETDEV_CAST(IP6TNL, Tunnel); extern const NetDevVTable ipip_vtable; extern const NetDevVTable sit_vtable; extern const NetDevVTable vti_vtable; diff --git a/src/network/networkd-netdev-tuntap.c b/src/network/networkd-netdev-tuntap.c index ab9a1b0426..088a4d8d32 100644 --- a/src/network/networkd-netdev-tuntap.c +++ b/src/network/networkd-netdev-tuntap.c @@ -17,9 +17,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <fcntl.h> +#include <linux/if_tun.h> #include <net/if.h> +#include <netinet/if_ether.h> #include <sys/ioctl.h> -#include <linux/if_tun.h> +#include <sys/stat.h> +#include <sys/types.h> #include "alloc-util.h" #include "fd-util.h" @@ -87,7 +91,7 @@ static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) { assert(t); - if(t->user_name) { + if (t->user_name) { user = t->user_name; @@ -126,7 +130,7 @@ static int netdev_create_tuntap(NetDev *netdev) { int r; r = netdev_fill_tuntap_message(netdev, &ifr); - if(r < 0) + if (r < 0) return r; return netdev_tuntap_add(netdev, &ifr); diff --git a/src/network/networkd-netdev-tuntap.h b/src/network/networkd-netdev-tuntap.h index b970b0ce3b..120f00a353 100644 --- a/src/network/networkd-netdev-tuntap.h +++ b/src/network/networkd-netdev-tuntap.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - typedef struct TunTap TunTap; #include "networkd-netdev.h" @@ -34,5 +34,7 @@ struct TunTap { bool vnet_hdr; }; +DEFINE_NETDEV_CAST(TUN, TunTap); +DEFINE_NETDEV_CAST(TAP, TunTap); extern const NetDevVTable tun_vtable; extern const NetDevVTable tap_vtable; diff --git a/src/network/networkd-netdev-veth.h b/src/network/networkd-netdev-veth.h index f7fdf906ab..e69bfbc8f0 100644 --- a/src/network/networkd-netdev-veth.h +++ b/src/network/networkd-netdev-veth.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - typedef struct Veth Veth; #include "networkd-netdev.h" @@ -30,4 +30,5 @@ struct Veth { struct ether_addr *mac_peer; }; +DEFINE_NETDEV_CAST(VETH, Veth); extern const NetDevVTable veth_vtable; diff --git a/src/network/networkd-netdev-vlan.h b/src/network/networkd-netdev-vlan.h index 8701c4b785..73aacf4a0f 100644 --- a/src/network/networkd-netdev-vlan.h +++ b/src/network/networkd-netdev-vlan.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - typedef struct VLan VLan; #include "networkd-netdev.h" @@ -31,4 +31,5 @@ struct VLan { uint64_t id; }; +DEFINE_NETDEV_CAST(VLAN, VLan); extern const NetDevVTable vlan_vtable; diff --git a/src/network/networkd-netdev-vxlan.c b/src/network/networkd-netdev-vxlan.c index eb9a2c06b3..724f9861be 100644 --- a/src/network/networkd-netdev-vxlan.c +++ b/src/network/networkd-netdev-vxlan.c @@ -23,8 +23,10 @@ #include "conf-parser.h" #include "alloc-util.h" +#include "extract-word.h" #include "parse-util.h" #include "missing.h" + #include "networkd-link.h" #include "networkd-netdev-vxlan.h" @@ -54,13 +56,13 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LINK attribute: %m"); - if(v->ttl) { + if (v->ttl) { r = sd_netlink_message_append_u8(m, IFLA_VXLAN_TTL, v->ttl); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_TTL attribute: %m"); } - if(v->tos) { + if (v->tos) { r = sd_netlink_message_append_u8(m, IFLA_VXLAN_TOS, v->tos); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_TOS attribute: %m"); @@ -86,7 +88,7 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_L3MISS attribute: %m"); - if(v->fdb_ageing) { + if (v->fdb_ageing) { r = sd_netlink_message_append_u32(m, IFLA_VXLAN_AGEING, v->fdb_ageing / USEC_PER_SEC); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_AGEING attribute: %m"); diff --git a/src/network/networkd-netdev-vxlan.h b/src/network/networkd-netdev-vxlan.h index 459ce53f5e..4614c66fd1 100644 --- a/src/network/networkd-netdev-vxlan.h +++ b/src/network/networkd-netdev-vxlan.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - typedef struct VxLan VxLan; #include "in-addr-util.h" @@ -55,6 +55,7 @@ struct VxLan { struct ifla_vxlan_port_range port_range; }; +DEFINE_NETDEV_CAST(VXLAN, VxLan); extern const NetDevVTable vxlan_vtable; int config_parse_vxlan_group_address(const char *unit, diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c index d7d014f05d..851a36290c 100644 --- a/src/network/networkd-netdev.c +++ b/src/network/networkd-netdev.c @@ -656,7 +656,7 @@ static int netdev_load_one(Manager *manager, const char *filename) { if (!netdev->filename) return log_oom(); - if (!netdev->mac) { + if (!netdev->mac && netdev->kind != NETDEV_KIND_VLAN) { r = netdev_get_mac(netdev->ifname, &netdev->mac); if (r < 0) return log_error_errno(r, "Failed to generate predictable MAC address for %s: %m", netdev->ifname); diff --git a/src/network/networkd-netdev.h b/src/network/networkd-netdev.h index 3eacee824b..20244c0309 100644 --- a/src/network/networkd-netdev.h +++ b/src/network/networkd-netdev.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,17 +19,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once +#include "sd-netlink.h" #include "list.h" - -typedef struct NetDev NetDev; -typedef struct NetDevVTable NetDevVTable; - -#include "networkd-link.h" -#include "networkd.h" +#include "time-util.h" typedef struct netdev_join_callback netdev_join_callback; +typedef struct Link Link; struct netdev_join_callback { sd_netlink_message_handler_t callback; @@ -78,7 +76,10 @@ typedef enum NetDevCreateType { _NETDEV_CREATE_INVALID = -1, } NetDevCreateType; -struct NetDev { +typedef struct Manager Manager; +typedef struct Condition Condition; + +typedef struct NetDev { Manager *manager; int n_ref; @@ -99,20 +100,9 @@ struct NetDev { int ifindex; LIST_HEAD(netdev_join_callback, callbacks); -}; +} NetDev; -#include "networkd-netdev-bond.h" -#include "networkd-netdev-bridge.h" -#include "networkd-netdev-dummy.h" -#include "networkd-netdev-ipvlan.h" -#include "networkd-netdev-macvlan.h" -#include "networkd-netdev-tunnel.h" -#include "networkd-netdev-tuntap.h" -#include "networkd-netdev-veth.h" -#include "networkd-netdev-vlan.h" -#include "networkd-netdev-vxlan.h" - -struct NetDevVTable { +typedef struct NetDevVTable { /* How much memory does an object of this unit type need */ size_t object_size; @@ -144,14 +134,14 @@ struct NetDevVTable { /* verify that compulsory configuration options were specified */ int (*config_verify)(NetDev *netdev, const char *filename); -}; +} NetDevVTable; extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX]; #define NETDEV_VTABLE(n) netdev_vtable[(n)->kind] /* For casting a netdev into the various netdev kinds */ -#define DEFINE_CAST(UPPERCASE, MixedCase) \ +#define DEFINE_NETDEV_CAST(UPPERCASE, MixedCase) \ static inline MixedCase* UPPERCASE(NetDev *n) { \ if (_unlikely_(!n || n->kind != NETDEV_KIND_##UPPERCASE)) \ return NULL; \ @@ -162,27 +152,6 @@ extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX]; /* For casting the various netdev kinds into a netdev */ #define NETDEV(n) (&(n)->meta) -DEFINE_CAST(BRIDGE, Bridge); -DEFINE_CAST(BOND, Bond); -DEFINE_CAST(VLAN, VLan); -DEFINE_CAST(MACVLAN, MacVlan); -DEFINE_CAST(MACVTAP, MacVlan); -DEFINE_CAST(IPVLAN, IPVlan); -DEFINE_CAST(VXLAN, VxLan); -DEFINE_CAST(IPIP, Tunnel); -DEFINE_CAST(GRE, Tunnel); -DEFINE_CAST(GRETAP, Tunnel); -DEFINE_CAST(IP6GRE, Tunnel); -DEFINE_CAST(IP6GRETAP, Tunnel); -DEFINE_CAST(SIT, Tunnel); -DEFINE_CAST(VTI, Tunnel); -DEFINE_CAST(VTI6, Tunnel); -DEFINE_CAST(IP6TNL, Tunnel); -DEFINE_CAST(VETH, Veth); -DEFINE_CAST(DUMMY, Dummy); -DEFINE_CAST(TUN, TunTap); -DEFINE_CAST(TAP, TunTap); - int netdev_load(Manager *manager); void netdev_drop(NetDev *netdev); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 409df1709f..03e4e3b39f 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -2,6 +2,7 @@ #include <stddef.h> #include "conf-parser.h" #include "networkd.h" +#include "networkd-conf.h" #include "network-internal.h" %} struct ConfigPerfItem; @@ -40,7 +41,8 @@ Network.DHCPServer, config_parse_bool, Network.LinkLocalAddressing, config_parse_address_family_boolean, 0, offsetof(Network, link_local) Network.IPv4LLRoute, config_parse_bool, 0, offsetof(Network, ipv4ll_route) Network.IPv6Token, config_parse_ipv6token, 0, offsetof(Network, ipv6_token) -Network.LLDP, config_parse_bool, 0, offsetof(Network, lldp) +Network.LLDP, config_parse_lldp_mode, 0, offsetof(Network, lldp_mode) +Network.EmitLLDP, config_parse_lldp_emit, 0, offsetof(Network, lldp_emit) Network.Address, config_parse_address, 0, 0 Network.Gateway, config_parse_gateway, 0, 0 Network.Domains, config_parse_domains, 0, 0 @@ -56,17 +58,20 @@ Network.IPv6PrivacyExtensions, config_parse_ipv6_privacy_extensions, 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.ProxyARP, config_parse_tristate, 0, offsetof(Network, proxy_arp) Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier) Address.Address, config_parse_address, 0, 0 Address.Peer, config_parse_address, 0, 0 Address.Broadcast, config_parse_broadcast, 0, 0 Address.Label, config_parse_label, 0, 0 +Address.PreferredLifetime, config_parse_lifetime, 0, 0 Route.Gateway, config_parse_gateway, 0, 0 Route.Destination, config_parse_destination, 0, 0 Route.Source, config_parse_destination, 0, 0 Route.Metric, config_parse_route_priority, 0, 0 Route.Scope, config_parse_route_scope, 0, 0 Route.PreferredSource, config_parse_preferred_src, 0, 0 +Route.Table, config_parse_route_table, 0, 0 DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier) DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns) DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp) @@ -79,14 +84,18 @@ DHCP.Hostname, config_parse_hostname, DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast) DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical) DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier) +DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid.type) +DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid) DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric) DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone) +DHCP.IAID, config_parse_iaid, 0, offsetof(Network, iaid) DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec) DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec) DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns) DHCPServer.DNS, config_parse_dhcp_server_dns, 0, 0 DHCPServer.EmitNTP, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_ntp) DHCPServer.NTP, config_parse_dhcp_server_ntp, 0, 0 +DHCPServer.EmitRouter, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_router) DHCPServer.EmitTimezone, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_timezone) DHCPServer.Timezone, config_parse_timezone, 0, offsetof(Network, dhcp_server_timezone) DHCPServer.PoolOffset, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_offset) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 4315790093..dd89b3770c 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -113,12 +113,15 @@ static int network_load_one(Manager *manager, const char *filename) { network->dhcp_server_emit_dns = true; network->dhcp_server_emit_ntp = true; + network->dhcp_server_emit_router = true; network->dhcp_server_emit_timezone = true; network->use_bpdu = true; network->allow_port_to_be_root = true; network->unicast_flood = true; + network->lldp_mode = LLDP_MODE_ROUTERS_ONLY; + network->llmnr = RESOLVE_SUPPORT_YES; network->mdns = RESOLVE_SUPPORT_NO; network->dnssec_mode = _DNSSEC_MODE_INVALID; @@ -129,6 +132,8 @@ 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->duid.type = _DUID_TYPE_INVALID; + network->proxy_arp = -1; r = config_parse(NULL, filename, file, "Match\0" @@ -394,6 +399,19 @@ int network_apply(Manager *manager, Network *network, Link *link) { return 0; } +bool network_has_static_ipv6_addresses(Network *network) { + Address *address; + + assert(network); + + LIST_FOREACH(addresses, address, network->static_addresses) { + if (address->family == AF_INET6) + return true; + } + + return false; +} + int config_parse_netdev(const char *unit, const char *filename, unsigned line, @@ -627,10 +645,7 @@ int config_parse_ipv4ll( * config_parse_address_family_boolean(), except that it * applies only to IPv4 */ - if (parse_boolean(rvalue)) - *link_local |= ADDRESS_FAMILY_IPV4; - else - *link_local &= ~ADDRESS_FAMILY_IPV4; + SET_FLAG(*link_local, ADDRESS_FAMILY_IPV4, parse_boolean(rvalue)); return 0; } @@ -994,6 +1009,10 @@ int config_parse_dnssec_negative_trust_anchors( continue; } + r = set_ensure_allocated(&n->dnssec_negative_trust_anchors, &dns_name_hash_ops); + if (r < 0) + return log_oom(); + r = set_put(n->dnssec_negative_trust_anchors, w); if (r < 0) return log_oom(); @@ -1013,3 +1032,13 @@ static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES); + +DEFINE_CONFIG_PARSE_ENUM(config_parse_lldp_mode, lldp_mode, LLDPMode, "Failed to parse LLDP= setting."); + +static const char* const lldp_mode_table[_LLDP_MODE_MAX] = { + [LLDP_MODE_NO] = "no", + [LLDP_MODE_YES] = "yes", + [LLDP_MODE_ROUTERS_ONLY] = "routers-only", +}; + +DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(lldp_mode, LLDPMode, LLDP_MODE_YES); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 03c3f206c3..91099161ce 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -19,17 +19,20 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "sd-bus.h" +#include "udev.h" + #include "condition.h" +#include "dhcp-identifier.h" +#include "hashmap.h" #include "resolve-util.h" -typedef struct Network Network; - #include "networkd-address.h" #include "networkd-fdb.h" +#include "networkd-lldp-tx.h" #include "networkd-netdev.h" #include "networkd-route.h" #include "networkd-util.h" -#include "networkd.h" #define DHCP_ROUTE_METRIC 1024 #define IPV4LL_ROUTE_METRIC 2048 @@ -58,6 +61,24 @@ typedef enum DHCPUseDomains { _DHCP_USE_DOMAINS_INVALID = -1, } DHCPUseDomains; +typedef enum LLDPMode { + LLDP_MODE_NO = 0, + LLDP_MODE_YES = 1, + LLDP_MODE_ROUTERS_ONLY = 2, + _LLDP_MODE_MAX, + _LLDP_MODE_INVALID = -1, +} LLDPMode; + +typedef struct DUID { + /* Value of Type in [DHCP] section */ + DUIDType type; + + uint8_t raw_data_len; + uint8_t raw_data[MAX_DUID_LEN]; +} DUID; + +typedef struct Manager Manager; + struct Network { Manager *manager; @@ -106,6 +127,7 @@ struct Network { bool dhcp_server_emit_ntp; struct in_addr *dhcp_server_ntp; unsigned n_dhcp_server_ntp; + bool dhcp_server_emit_router; bool dhcp_server_emit_timezone; char *dhcp_server_timezone; usec_t dhcp_server_default_lease_time_usec, dhcp_server_max_lease_time_usec; @@ -130,14 +152,18 @@ struct Network { int ipv6_accept_ra; int ipv6_dad_transmits; int ipv6_hop_limit; + int proxy_arp; union in_addr_union ipv6_token; IPv6PrivacyExtensions ipv6_privacy_extensions; struct ether_addr *mac; unsigned mtu; + uint32_t iaid; + DUID duid; - bool lldp; + LLDPMode lldp_mode; /* LLDP reception */ + LLDPEmit lldp_emit; /* LLDP transmission */ LIST_HEAD(Address, static_addresses); LIST_HEAD(Route, static_routes); @@ -168,6 +194,8 @@ int network_get_by_name(Manager *manager, const char *name, Network **ret); int network_get(Manager *manager, struct udev_device *device, const char *ifname, const struct ether_addr *mac, Network **ret); int network_apply(Manager *manager, Network *network, Link *link); +bool network_has_static_ipv6_addresses(Network *network); + int config_parse_netdev(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_domains(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_tunnel(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); @@ -181,6 +209,7 @@ int config_parse_dhcp_server_dns(const char *unit, const char *filename, unsigne int config_parse_dhcp_server_ntp(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_dnssec_negative_trust_anchors(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_dhcp_use_domains(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_lldp_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); /* Legacy IPv4LL support */ int config_parse_ipv4ll(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); @@ -197,3 +226,6 @@ IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_; const char* dhcp_use_domains_to_string(DHCPUseDomains p) _const_; DHCPUseDomains dhcp_use_domains_from_string(const char *s) _pure_; + +const char* lldp_mode_to_string(LLDPMode m) _const_; +LLDPMode lldp_mode_from_string(const char *s) _pure_; diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index e065a5a5a9..f001de772a 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -52,8 +52,7 @@ int route_new_static(Network *network, unsigned section, Route **ret) { int r; if (section) { - route = hashmap_get(network->routes_by_section, - UINT_TO_PTR(section)); + route = hashmap_get(network->routes_by_section, UINT_TO_PTR(section)); if (route) { *ret = route; route = NULL; @@ -67,16 +66,18 @@ int route_new_static(Network *network, unsigned section, Route **ret) { return r; route->protocol = RTPROT_STATIC; - route->network = network; - - LIST_PREPEND(routes, network->static_routes, route); if (section) { route->section = section; - hashmap_put(network->routes_by_section, - UINT_TO_PTR(route->section), route); + + r = hashmap_put(network->routes_by_section, UINT_TO_PTR(route->section), route); + if (r < 0) + return r; } + route->network = network; + LIST_PREPEND(routes, network->static_routes, route); + *ret = route; route = NULL; @@ -323,12 +324,6 @@ int route_update(Route *route, return 0; } -void route_drop(Route *route) { - assert(route); - - route_free(route); -} - int route_remove(Route *route, Link *link, sd_netlink_message_handler_t callback) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; @@ -374,7 +369,7 @@ int route_remove(Route *route, Link *link, else if (route->family == AF_INET6) r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6); if (r < 0) - return log_error_errno(r, "Could not append RTA_DST attribute: %m"); + return log_error_errno(r, "Could not append RTA_SRC attribute: %m"); r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen); if (r < 0) @@ -411,15 +406,45 @@ int route_remove(Route *route, Link *link, return 0; } +static int route_expire_callback(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + Link *link = userdata; + int r; + + assert(rtnl); + assert(m); + assert(link); + assert(link->ifname); + assert(link->link_messages > 0); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + link->link_messages--; + + r = sd_netlink_message_get_errno(m); + if (r < 0 && r != -EEXIST) + log_link_warning_errno(link, r, "could not remove route: %m"); + + if (link->link_messages == 0) + log_link_debug(link, "route removed"); + + return 1; +} + int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata) { Route *route = userdata; int r; assert(route); - r = route_remove(route, route->link, NULL); + r = route_remove(route, route->link, route_expire_callback); if (r < 0) log_warning_errno(r, "Could not remove route: %m"); + else { + /* route may not be exist in kernel. If we fail still remove it */ + route->link->link_messages++; + route_free(route); + } return 1; } @@ -450,6 +475,10 @@ int route_configure(Route *route, Link *link, r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6); if (r < 0) return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m"); + + r = sd_rtnl_message_route_set_family(req, route->family); + if (r < 0) + return log_error_errno(r, "Could not set route family: %m"); } if (route->dst_prefixlen) { @@ -493,7 +522,26 @@ int route_configure(Route *route, Link *link, r = sd_rtnl_message_route_set_flags(req, route->flags); if (r < 0) - return log_error_errno(r, "Colud not set flags: %m"); + return log_error_errno(r, "Could not set flags: %m"); + + if (route->table != RT_TABLE_DEFAULT) { + + if (route->table < 256) { + r = sd_rtnl_message_route_set_table(req, route->table); + if (r < 0) + return log_error_errno(r, "Could not set route table: %m"); + } else { + + r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC); + if (r < 0) + return log_error_errno(r, "Could not set route table: %m"); + + /* Table attribute to allow more than 256. */ + r = sd_netlink_message_append_data(req, RTA_TABLE, &route->table, sizeof(route->table)); + if (r < 0) + return log_error_errno(r, "Could not append RTA_TABLE attribute: %m"); + } + } r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority); if (r < 0) @@ -776,3 +824,42 @@ int config_parse_route_scope(const char *unit, return 0; } + +int config_parse_route_table(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + _cleanup_route_free_ Route *n = NULL; + Network *network = userdata; + uint32_t k; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = route_new_static(network, section_line, &n); + if (r < 0) + return r; + + r = safe_atou32(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue); + return 0; + } + + n->table = k; + + n = NULL; + + return 0; +} diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index a4a4bf2653..39de8363ed 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -22,7 +22,6 @@ typedef struct Route Route; #include "networkd-network.h" -#include "networkd.h" struct Route { Network *network; @@ -37,7 +36,7 @@ struct Route { unsigned char protocol; /* RTPROT_* */ unsigned char tos; uint32_t priority; /* note that ip(8) calls this 'metric' */ - unsigned char table; + uint32_t table; unsigned char pref; unsigned flags; @@ -62,7 +61,6 @@ int route_get(Link *link, int family, union in_addr_union *dst, unsigned char ds int route_add(Link *link, int family, 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, union in_addr_union *dst, unsigned char dst_prefixlen, unsigned char tos, uint32_t priority, unsigned char table, Route **ret); int route_update(Route *route, union in_addr_union *src, unsigned char src_prefixlen, union in_addr_union *gw, union in_addr_union *prefsrc, unsigned char scope, unsigned char protocol); -void route_drop(Route *route); int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata); @@ -74,3 +72,4 @@ int config_parse_preferred_src(const char *unit, const char *filename, unsigned int config_parse_destination(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_route_priority(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_route_scope(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_route_table(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/network/networkd-wait-online.h b/src/network/networkd-wait-online.h index 421c2bdf44..f91995c306 100644 --- a/src/network/networkd-wait-online.h +++ b/src/network/networkd-wait-online.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - #include "sd-event.h" #include "sd-netlink.h" #include "sd-network.h" diff --git a/src/network/networkd.c b/src/network/networkd.c index 3a2615e6fd..c8f81a2ca6 100644 --- a/src/network/networkd.c +++ b/src/network/networkd.c @@ -21,6 +21,7 @@ #include "capability-util.h" #include "networkd.h" +#include "networkd-conf.h" #include "signal-util.h" #include "user-util.h" @@ -89,6 +90,10 @@ int main(int argc, char *argv[]) { goto out; } + r = manager_parse_config_file(m); + if (r < 0) + log_warning_errno(r, "Failed to parse configuration file: %m"); + r = manager_load_config(m); if (r < 0) { log_error_errno(r, "Could not load configuration files: %m"); diff --git a/src/network/networkd.h b/src/network/networkd.h index 7ee922621a..ab512f0d08 100644 --- a/src/network/networkd.h +++ b/src/network/networkd.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,25 +19,34 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - #include <arpa/inet.h> #include "sd-bus.h" #include "sd-event.h" #include "sd-netlink.h" +#include "udev.h" +#include "dhcp-identifier.h" #include "hashmap.h" #include "list.h" -#include "udev.h" - -typedef struct Manager Manager; #include "networkd-address-pool.h" #include "networkd-link.h" +#include "networkd-netdev-bond.h" +#include "networkd-netdev-bridge.h" +#include "networkd-netdev-dummy.h" +#include "networkd-netdev-ipvlan.h" +#include "networkd-netdev-macvlan.h" +#include "networkd-netdev-tunnel.h" +#include "networkd-netdev-tuntap.h" +#include "networkd-netdev-veth.h" +#include "networkd-netdev-vlan.h" +#include "networkd-netdev-vxlan.h" #include "networkd-network.h" #include "networkd-util.h" +extern const char* const network_dirs[]; + struct Manager { sd_netlink *rtnl; sd_event *event; @@ -61,11 +72,16 @@ struct Manager { LIST_HEAD(AddressPool, address_pools); usec_t network_dirs_ts_usec; -}; -extern const char* const network_dirs[]; + DUID duid; +}; -/* Manager */ +static inline const DUID* link_duid(const Link *link) { + if (link->network->duid.type != _DUID_TYPE_INVALID) + return &link->network->duid; + else + return &link->manager->duid; +} extern const sd_bus_vtable manager_vtable[]; diff --git a/src/network/test-network-tables.c b/src/network/test-network-tables.c index ecbbe6c3c9..adbe09a5e1 100644 --- a/src/network/test-network-tables.c +++ b/src/network/test-network-tables.c @@ -9,7 +9,7 @@ int main(int argc, char **argv) { test_table(bond_mode, NETDEV_BOND_MODE); - /* test_table(link_state, LINK_STATE); -- not a reversible mapping */ + /* test_table(link_state, LINK_STATE); — not a reversible mapping */ test_table(link_operstate, LINK_OPERSTATE); test_table(address_family_boolean, ADDRESS_FAMILY_BOOLEAN); test_table(netdev_kind, NETDEV_KIND); diff --git a/src/network/test-networkd-conf.c b/src/network/test-networkd-conf.c new file mode 100644 index 0000000000..0e1a18457d --- /dev/null +++ b/src/network/test-networkd-conf.c @@ -0,0 +1,142 @@ +/*** + 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 "hexdecoct.h" +#include "log.h" +#include "macro.h" +#include "string-util.h" +#include "ether-addr-util.h" + +#include "networkd-conf.h" +#include "networkd-network.h" +#include "network-internal.h" + +static void test_config_parse_duid_type_one(const char *rvalue, int ret, DUIDType expected) { + DUIDType actual = 0; + int r; + + r = config_parse_duid_type("network", "filename", 1, "section", 1, "lvalue", 0, rvalue, &actual, NULL); + log_info_errno(r, "\"%s\" → %d (%m)", rvalue, actual); + assert_se(r == ret); + assert_se(expected == actual); +} + +static void test_config_parse_duid_type(void) { + test_config_parse_duid_type_one("", 0, 0); + test_config_parse_duid_type_one("link-layer-time", 0, DUID_TYPE_LLT); + test_config_parse_duid_type_one("vendor", 0, DUID_TYPE_EN); + test_config_parse_duid_type_one("link-layer", 0, DUID_TYPE_LL); + test_config_parse_duid_type_one("uuid", 0, DUID_TYPE_UUID); + test_config_parse_duid_type_one("foo", 0, 0); +} + +static void test_config_parse_duid_rawdata_one(const char *rvalue, int ret, const DUID* expected) { + DUID actual = {}; + int r; + _cleanup_free_ char *d = NULL; + + r = config_parse_duid_rawdata("network", "filename", 1, "section", 1, "lvalue", 0, rvalue, &actual, NULL); + d = hexmem(actual.raw_data, actual.raw_data_len); + log_info_errno(r, "\"%s\" → \"%s\" (%m)", + rvalue, strnull(d)); + assert_se(r == ret); + if (expected) { + assert_se(actual.raw_data_len == expected->raw_data_len); + assert_se(memcmp(actual.raw_data, expected->raw_data, expected->raw_data_len) == 0); + } +} + +static void test_config_parse_hwaddr_one(const char *rvalue, int ret, const struct ether_addr* expected) { + struct ether_addr *actual = NULL; + int r; + + r = config_parse_hwaddr("network", "filename", 1, "section", 1, "lvalue", 0, rvalue, &actual, NULL); + assert_se(ret == r); + if (expected) { + assert_se(actual); + assert(ether_addr_equal(expected, actual)); + } else { + assert_se(actual == NULL); + } + free(actual); +} + +#define BYTES_0_128 "0:1:2:3:4:5:6:7:8:9:a:b:c:d:e:f:10:11:12:13:14:15:16:17:18:19:1a:1b:1c:1d:1e:1f:20:21:22:23:24:25:26:27:28:29:2a:2b:2c:2d:2e:2f:30:31:32:33:34:35:36:37:38:39:3a:3b:3c:3d:3e:3f:40:41:42:43:44:45:46:47:48:49:4a:4b:4c:4d:4e:4f:50:51:52:53:54:55:56:57:58:59:5a:5b:5c:5d:5e:5f:60:61:62:63:64:65:66:67:68:69:6a:6b:6c:6d:6e:6f:70:71:72:73:74:75:76:77:78:79:7a:7b:7c:7d:7e:7f:80" + +#define BYTES_1_128 {0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x80} + +static void test_config_parse_duid_rawdata(void) { + test_config_parse_duid_rawdata_one("", 0, &(DUID){}); + test_config_parse_duid_rawdata_one("00:11:22:33:44:55:66:77", 0, + &(DUID){0, 8, {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77}}); + test_config_parse_duid_rawdata_one("00:11:22:", 0, + &(DUID){0, 3, {0x00,0x11,0x22}}); + test_config_parse_duid_rawdata_one("000:11:22", 0, &(DUID){}); /* error, output is all zeros */ + test_config_parse_duid_rawdata_one("00:111:22", 0, &(DUID){}); + test_config_parse_duid_rawdata_one("0:1:2:3:4:5:6:7", 0, + &(DUID){0, 8, {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7}}); + test_config_parse_duid_rawdata_one("11::", 0, &(DUID){0, 1, {0x11}}); /* FIXME: should this be an error? */ + test_config_parse_duid_rawdata_one("abcdef", 0, &(DUID){}); + test_config_parse_duid_rawdata_one(BYTES_0_128, 0, &(DUID){}); + test_config_parse_duid_rawdata_one(BYTES_0_128 + 2, 0, &(DUID){0, 128, BYTES_1_128}); +} + +static void test_config_parse_hwaddr(void) { + const struct ether_addr t[] = { + { .ether_addr_octet = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff } }, + { .ether_addr_octet = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab } }, + }; + test_config_parse_hwaddr_one("", 0, NULL); + test_config_parse_hwaddr_one("no:ta:ma:ca:dd:re", 0, NULL); + test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:fx", 0, NULL); + test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:ff", 0, &t[0]); + test_config_parse_hwaddr_one(" aa:bb:cc:dd:ee:ff", 0, &t[0]); + test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:ff \t\n", 0, &t[0]); + test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:ff \t\nxxx", 0, NULL); + test_config_parse_hwaddr_one("aa:bb:cc: dd:ee:ff", 0, NULL); + test_config_parse_hwaddr_one("aa:bb:cc:d d:ee:ff", 0, NULL); + test_config_parse_hwaddr_one("aa:bb:cc:dd:ee", 0, NULL); + test_config_parse_hwaddr_one("9:aa:bb:cc:dd:ee:ff", 0, NULL); + test_config_parse_hwaddr_one("aa:bb:cc:dd:ee:ff:gg", 0, NULL); + test_config_parse_hwaddr_one("aa:Bb:CC:dd:ee:ff", 0, &t[0]); + test_config_parse_hwaddr_one("01:23:45:67:89:aB", 0, &t[1]); + test_config_parse_hwaddr_one("1:23:45:67:89:aB", 0, &t[1]); + test_config_parse_hwaddr_one("aa-bb-cc-dd-ee-ff", 0, &t[0]); + test_config_parse_hwaddr_one("AA-BB-CC-DD-EE-FF", 0, &t[0]); + test_config_parse_hwaddr_one("01-23-45-67-89-ab", 0, &t[1]); + test_config_parse_hwaddr_one("aabb.ccdd.eeff", 0, &t[0]); + test_config_parse_hwaddr_one("0123.4567.89ab", 0, &t[1]); + test_config_parse_hwaddr_one("123.4567.89ab.", 0, NULL); + test_config_parse_hwaddr_one("aabbcc.ddeeff", 0, NULL); + test_config_parse_hwaddr_one("aabbccddeeff", 0, NULL); + test_config_parse_hwaddr_one("aabbccddee:ff", 0, NULL); + test_config_parse_hwaddr_one("012345.6789ab", 0, NULL); + test_config_parse_hwaddr_one("123.4567.89ab", 0, &t[1]); +} + +int main(int argc, char **argv) { + log_parse_environment(); + log_open(); + + test_config_parse_duid_type(); + test_config_parse_duid_rawdata(); + test_config_parse_hwaddr(); + + return 0; +} diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c index 1db5ba7116..f50f1ad6c2 100644 --- a/src/nspawn/nspawn-cgroup.c +++ b/src/nspawn/nspawn-cgroup.c @@ -55,8 +55,7 @@ int chown_cgroup(pid_t pid, uid_t uid_shift) { "cgroup.events", "cgroup.clone_children", "cgroup.controllers", - "cgroup.subtree_control", - "cgroup.populated") + "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); @@ -73,7 +72,7 @@ int sync_cgroup(pid_t pid, bool unified_requested) { unified = cg_unified(); if (unified < 0) - return log_error_errno(unified, "Failed to determine whether the unified hierachy is used: %m"); + return log_error_errno(unified, "Failed to determine whether the unified hierarchy is used: %m"); if ((unified > 0) == unified_requested) return 0; @@ -94,7 +93,7 @@ int sync_cgroup(pid_t pid, bool unified_requested) { if (unified) r = mount("cgroup", tree, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, "none,name=systemd,xattr"); else - r = mount("cgroup", tree, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, "__DEVEL__sane_behavior"); + r = mount("cgroup", tree, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL); if (r < 0) { r = log_error_errno(errno, "Failed to mount unified hierarchy: %m"); goto finish; @@ -135,7 +134,7 @@ int create_subcgroup(pid_t pid, bool unified_requested) { unified = cg_unified(); if (unified < 0) - return log_error_errno(unified, "Failed to determine whether the unified hierachy is used: %m"); + return log_error_errno(unified, "Failed to determine whether the unified hierarchy is used: %m"); if (unified == 0) return 0; diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf index 116655cdd2..2b5d452662 100644 --- a/src/nspawn/nspawn-gperf.gperf +++ b/src/nspawn/nspawn-gperf.gperf @@ -16,7 +16,7 @@ struct ConfigPerfItem; %includes %% Exec.Boot, config_parse_boot, 0, 0 -Exec.ProcessTwo, config_parse_pid2, 0, 0, +Exec.ProcessTwo, config_parse_pid2, 0, 0 Exec.Parameters, config_parse_strv, 0, offsetof(Settings, parameters) Exec.Environment, config_parse_strv, 0, offsetof(Settings, environment) Exec.User, config_parse_string, 0, offsetof(Settings, user) @@ -26,16 +26,19 @@ Exec.KillSignal, config_parse_signal, 0, offsetof(Settings, Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality) Exec.MachineID, config_parse_id128, 0, offsetof(Settings, machine_id) Exec.WorkingDirectory, config_parse_path, 0, offsetof(Settings, working_directory) +Exec.PrivateUsers, config_parse_private_users, 0, 0 Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only) Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode) Files.Bind, config_parse_bind, 0, 0 Files.BindReadOnly, config_parse_bind, 1, 0 Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0 +Files.PrivateUsersChown, config_parse_tristate, 0, offsetof(Settings, userns_chown) Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network) Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces) Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, network_macvlan) Network.IPVLAN, config_parse_strv, 0, offsetof(Settings, network_ipvlan) Network.VirtualEthernet, config_parse_tristate, 0, offsetof(Settings, network_veth) Network.VirtualEthernetExtra, config_parse_veth_extra, 0, 0 -Network.Bridge, config_parse_string, 0, offsetof(Settings, network_bridge) +Network.Bridge, config_parse_ifname, 0, offsetof(Settings, network_bridge) +Network.Zone, config_parse_network_zone, 0, 0 Network.Port, config_parse_expose_port, 0, 0 diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 70cca15278..8e2d2d543c 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -438,21 +438,22 @@ static int mount_bind(const char *dest, CustomMount *m) { r = mkdir_parents_label(where, 0755); if (r < 0) return log_error_errno(r, "Failed to make parents of %s: %m", where); + + /* Create the mount point. Any non-directory file can be + * mounted on any non-directory file (regular, fifo, socket, + * char, block). + */ + if (S_ISDIR(source_st.st_mode)) + r = mkdir_label(where, 0755); + else + r = touch(where); + if (r < 0) + return log_error_errno(r, "Failed to create mount point %s: %m", where); + } else { return log_error_errno(errno, "Failed to stat %s: %m", where); } - /* Create the mount point. Any non-directory file can be - * mounted on any non-directory file (regular, fifo, socket, - * char, block). - */ - if (S_ISDIR(source_st.st_mode)) - r = mkdir_label(where, 0755); - else - r = touch(where); - if (r < 0 && r != -EEXIST) - return log_error_errno(r, "Failed to create mount point %s: %m", where); - if (mount(m->source, where, NULL, mount_flags, mount_opts) < 0) return log_error_errno(errno, "mount(%s) failed: %m", where); @@ -750,7 +751,7 @@ static int mount_unified_cgroups(const char *dest) { return -EINVAL; } - if (mount("cgroup", p, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, "__DEVEL__sane_behavior") < 0) + if (mount("cgroup", p, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL) < 0) return log_error_errno(errno, "Failed to mount unified cgroup hierarchy to %s: %m", p); return 0; diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c index fcb1efaa74..8da47a2ca6 100644 --- a/src/nspawn/nspawn-network.c +++ b/src/nspawn/nspawn-network.c @@ -26,9 +26,12 @@ #include "alloc-util.h" #include "ether-addr-util.h" +#include "lockfile-util.h" #include "netlink-util.h" #include "nspawn-network.h" #include "siphash24.h" +#include "socket-util.h" +#include "stat-util.h" #include "string-util.h" #include "udev-util.h" #include "util.h" @@ -39,6 +42,30 @@ #define VETH_EXTRA_CONTAINER_HASH_KEY SD_ID128_MAKE(af,50,17,61,ce,f9,4d,35,84,0d,2b,20,54,be,ce,59) #define MACVLAN_HASH_KEY SD_ID128_MAKE(00,13,6d,bc,66,83,44,81,bb,0c,f9,51,1f,24,a6,6f) +static int remove_one_link(sd_netlink *rtnl, const char *name) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r; + + if (isempty(name)) + return 0; + + r = sd_rtnl_message_new_link(rtnl, &m, RTM_DELLINK, 0); + if (r < 0) + return log_error_errno(r, "Failed to allocate netlink message: %m"); + + r = sd_netlink_message_append_string(m, IFLA_IFNAME, name); + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface name: %m"); + + r = sd_netlink_call(rtnl, m, 0, NULL); + if (r == -ENODEV) /* Already gone */ + return 0; + if (r < 0) + return log_error_errno(r, "Failed to remove interface %s: %m", name); + + return 1; +} + static int generate_mac( const char *machine_name, struct ether_addr *mac, @@ -232,51 +259,155 @@ int setup_veth_extra( if (r < 0) return r; - idx ++; + idx++; } return 0; } -int setup_bridge(const char *veth_name, const char *bridge_name) { +static int join_bridge(sd_netlink *rtnl, const char *veth_name, const char *bridge_name) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; int r, bridge_ifi; + assert(rtnl); assert(veth_name); assert(bridge_name); bridge_ifi = (int) if_nametoindex(bridge_name); if (bridge_ifi <= 0) - return log_error_errno(errno, "Failed to resolve interface %s: %m", bridge_name); - - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); + return -errno; r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, 0); if (r < 0) - return log_error_errno(r, "Failed to allocate netlink message: %m"); + return r; r = sd_rtnl_message_link_set_flags(m, IFF_UP, IFF_UP); if (r < 0) - return log_error_errno(r, "Failed to set IFF_UP flag: %m"); + return r; r = sd_netlink_message_append_string(m, IFLA_IFNAME, veth_name); if (r < 0) - return log_error_errno(r, "Failed to add netlink interface name field: %m"); + return r; r = sd_netlink_message_append_u32(m, IFLA_MASTER, bridge_ifi); if (r < 0) - return log_error_errno(r, "Failed to add netlink master field: %m"); + return r; r = sd_netlink_call(rtnl, m, 0, NULL); if (r < 0) - return log_error_errno(r, "Failed to add veth interface to bridge: %m"); + return r; return bridge_ifi; } +static int create_bridge(sd_netlink *rtnl, const char *bridge_name) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r; + + r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0); + if (r < 0) + return r; + + r = sd_netlink_message_append_string(m, IFLA_IFNAME, bridge_name); + if (r < 0) + return r; + + r = sd_netlink_message_open_container(m, IFLA_LINKINFO); + if (r < 0) + return r; + + r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "bridge"); + if (r < 0) + return r; + + r = sd_netlink_message_close_container(m); + if (r < 0) + return r; + + r = sd_netlink_message_close_container(m); + if (r < 0) + return r; + + r = sd_netlink_call(rtnl, m, 0, NULL); + if (r < 0) + return r; + + return 0; +} + +int setup_bridge(const char *veth_name, const char *bridge_name, bool create) { + _cleanup_release_lock_file_ LockFile bridge_lock = LOCK_FILE_INIT; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + int r, bridge_ifi; + unsigned n = 0; + + assert(veth_name); + assert(bridge_name); + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + if (create) { + /* We take a system-wide lock here, so that we can safely check whether there's still a member in the + * bridge before removing it, without risking interferance from other nspawn instances. */ + + r = make_lock_file("/run/systemd/nspawn-network-zone", LOCK_EX, &bridge_lock); + if (r < 0) + return log_error_errno(r, "Failed to take network zone lock: %m"); + } + + for (;;) { + bridge_ifi = join_bridge(rtnl, veth_name, bridge_name); + if (bridge_ifi >= 0) + return bridge_ifi; + if (bridge_ifi != -ENODEV || !create || n > 10) + return log_error_errno(bridge_ifi, "Failed to add interface %s to bridge %s: %m", veth_name, bridge_name); + + /* Count attempts, so that we don't enter an endless loop here. */ + n++; + + /* The bridge doesn't exist yet. Let's create it */ + r = create_bridge(rtnl, bridge_name); + if (r < 0) + return log_error_errno(r, "Failed to create bridge interface %s: %m", bridge_name); + + /* Try again, now that the bridge exists */ + } +} + +int remove_bridge(const char *bridge_name) { + _cleanup_release_lock_file_ LockFile bridge_lock = LOCK_FILE_INIT; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + const char *path; + int r; + + /* Removes the specified bridge, but only if it is currently empty */ + + if (isempty(bridge_name)) + return 0; + + r = make_lock_file("/run/systemd/nspawn-network-zone", LOCK_EX, &bridge_lock); + if (r < 0) + return log_error_errno(r, "Failed to take network zone lock: %m"); + + path = strjoina("/sys/class/net/", bridge_name, "/brif"); + + r = dir_is_empty(path); + if (r == -ENOENT) /* Already gone? */ + return 0; + if (r < 0) + return log_error_errno(r, "Can't detect if bridge %s is empty: %m", bridge_name); + if (r == 0) /* Still populated, leave it around */ + return 0; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + return remove_one_link(rtnl, bridge_name); +} + static int parse_interface(struct udev *udev, const char *name) { _cleanup_udev_device_unref_ struct udev_device *d = NULL; char ifi_str[2 + DECIMAL_STR_MAX(int)]; @@ -515,13 +646,13 @@ int veth_extra_parse(char ***l, const char *p) { r = extract_first_word(&p, &a, ":", EXTRACT_DONT_COALESCE_SEPARATORS); if (r < 0) return r; - if (r == 0 || isempty(a)) + if (r == 0 || !ifname_valid(a)) return -EINVAL; r = extract_first_word(&p, &b, ":", EXTRACT_DONT_COALESCE_SEPARATORS); if (r < 0) return r; - if (r == 0 || isempty(b)) { + if (r == 0 || !ifname_valid(b)) { free(b); b = strdup(a); if (!b) @@ -538,3 +669,26 @@ int veth_extra_parse(char ***l, const char *p) { a = b = NULL; return 0; } + +int remove_veth_links(const char *primary, char **pairs) { + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + char **a, **b; + int r; + + /* In some cases the kernel might pin the veth links between host and container even after the namespace + * died. Hence, let's better remove them explicitly too. */ + + if (isempty(primary) && strv_isempty(pairs)) + return 0; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + remove_one_link(rtnl, primary); + + STRV_FOREACH_PAIR(a, b, pairs) + remove_one_link(rtnl, *a); + + return 0; +} diff --git a/src/nspawn/nspawn-network.h b/src/nspawn/nspawn-network.h index 9ab1606d1c..3d8861e1e5 100644 --- a/src/nspawn/nspawn-network.h +++ b/src/nspawn/nspawn-network.h @@ -26,7 +26,8 @@ int setup_veth(const char *machine_name, pid_t pid, char iface_name[IFNAMSIZ], bool bridge); int setup_veth_extra(const char *machine_name, pid_t pid, char **pairs); -int setup_bridge(const char *veth_name, const char *bridge_name); +int setup_bridge(const char *veth_name, const char *bridge_name, bool create); +int remove_bridge(const char *bridge_name); int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces); int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces); @@ -34,3 +35,5 @@ int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces); int move_network_interfaces(pid_t pid, char **ifaces); int veth_extra_parse(char ***l, const char *p); + +int remove_veth_links(const char *primary, char **pairs); diff --git a/src/nspawn/nspawn-patch-uid.c b/src/nspawn/nspawn-patch-uid.c new file mode 100644 index 0000000000..c7382d412d --- /dev/null +++ b/src/nspawn/nspawn-patch-uid.c @@ -0,0 +1,469 @@ +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <fcntl.h> +#include <linux/magic.h> +#ifdef HAVE_ACL +#include <sys/acl.h> +#endif +#include <sys/stat.h> +#include <sys/vfs.h> +#include <unistd.h> + +#include "acl-util.h" +#include "dirent-util.h" +#include "fd-util.h" +#include "missing.h" +#include "nspawn-patch-uid.h" +#include "stat-util.h" +#include "stdio-util.h" +#include "string-util.h" +#include "strv.h" +#include "user-util.h" + +#ifdef HAVE_ACL + +static int get_acl(int fd, const char *name, acl_type_t type, acl_t *ret) { + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; + acl_t acl; + + assert(fd >= 0); + assert(ret); + + if (name) { + _cleanup_close_ int child_fd = -1; + + child_fd = openat(fd, name, O_PATH|O_CLOEXEC|O_NOFOLLOW); + if (child_fd < 0) + return -errno; + + xsprintf(procfs_path, "/proc/self/fd/%i", child_fd); + acl = acl_get_file(procfs_path, type); + } else if (type == ACL_TYPE_ACCESS) + acl = acl_get_fd(fd); + else { + xsprintf(procfs_path, "/proc/self/fd/%i", fd); + acl = acl_get_file(procfs_path, type); + } + if (!acl) + return -errno; + + *ret = acl; + return 0; +} + +static int set_acl(int fd, const char *name, acl_type_t type, acl_t acl) { + char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; + int r; + + assert(fd >= 0); + assert(acl); + + if (name) { + _cleanup_close_ int child_fd = -1; + + child_fd = openat(fd, name, O_PATH|O_CLOEXEC|O_NOFOLLOW); + if (child_fd < 0) + return -errno; + + xsprintf(procfs_path, "/proc/self/fd/%i", child_fd); + r = acl_set_file(procfs_path, type, acl); + } else if (type == ACL_TYPE_ACCESS) + r = acl_set_fd(fd, acl); + else { + xsprintf(procfs_path, "/proc/self/fd/%i", fd); + r = acl_set_file(procfs_path, type, acl); + } + if (r < 0) + return -errno; + + return 0; +} + +static int shift_acl(acl_t acl, uid_t shift, acl_t *ret) { + _cleanup_(acl_freep) acl_t copy = NULL; + acl_entry_t i; + int r; + + assert(acl); + assert(ret); + + r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); + if (r < 0) + return -errno; + while (r > 0) { + uid_t *old_uid, new_uid; + bool modify = false; + acl_tag_t tag; + + if (acl_get_tag_type(i, &tag) < 0) + return -errno; + + if (IN_SET(tag, ACL_USER, ACL_GROUP)) { + + /* We don't distuingish here between uid_t and gid_t, let's make sure the compiler checks that + * this is actually OK */ + assert_cc(sizeof(uid_t) == sizeof(gid_t)); + + old_uid = acl_get_qualifier(i); + if (!old_uid) + return -errno; + + new_uid = shift | (*old_uid & UINT32_C(0xFFFF)); + if (!uid_is_valid(new_uid)) + return -EINVAL; + + modify = new_uid != *old_uid; + if (modify && !copy) { + int n; + + /* There's no copy of the ACL yet? if so, let's create one, and start the loop from the + * beginning, so that we copy all entries, starting from the first, this time. */ + + n = acl_entries(acl); + if (n < 0) + return -errno; + + copy = acl_init(n); + if (!copy) + return -errno; + + /* Seek back to the beginning */ + r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); + if (r < 0) + return -errno; + continue; + } + } + + if (copy) { + acl_entry_t new_entry; + + if (acl_create_entry(©, &new_entry) < 0) + return -errno; + + if (acl_copy_entry(new_entry, i) < 0) + return -errno; + + if (modify) + if (acl_set_qualifier(new_entry, &new_uid) < 0) + return -errno; + } + + r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i); + if (r < 0) + return -errno; + } + + *ret = copy; + copy = NULL; + + return !!*ret; +} + +static int patch_acls(int fd, const char *name, const struct stat *st, uid_t shift) { + _cleanup_(acl_freep) acl_t acl = NULL, shifted = NULL; + bool changed = false; + int r; + + assert(fd >= 0); + assert(st); + + /* ACLs are not supported on symlinks, there's no point in trying */ + if (S_ISLNK(st->st_mode)) + return 0; + + r = get_acl(fd, name, ACL_TYPE_ACCESS, &acl); + if (r == -EOPNOTSUPP) + return 0; + if (r < 0) + return r; + + r = shift_acl(acl, shift, &shifted); + if (r < 0) + return r; + if (r > 0) { + r = set_acl(fd, name, ACL_TYPE_ACCESS, shifted); + if (r < 0) + return r; + + changed = true; + } + + if (S_ISDIR(st->st_mode)) { + acl_free(acl); + acl_free(shifted); + + acl = shifted = NULL; + + r = get_acl(fd, name, ACL_TYPE_DEFAULT, &acl); + if (r < 0) + return r; + + r = shift_acl(acl, shift, &shifted); + if (r < 0) + return r; + if (r > 0) { + r = set_acl(fd, name, ACL_TYPE_DEFAULT, shifted); + if (r < 0) + return r; + + changed = true; + } + } + + return changed; +} + +#else + +static int patch_acls(int fd, const char *name, const struct stat *st, uid_t shift) { + return 0; +} + +#endif + +static int patch_fd(int fd, const char *name, const struct stat *st, uid_t shift) { + uid_t new_uid; + gid_t new_gid; + bool changed = false; + int r; + + assert(fd >= 0); + assert(st); + + new_uid = shift | (st->st_uid & UINT32_C(0xFFFF)); + new_gid = (gid_t) shift | (st->st_gid & UINT32_C(0xFFFF)); + + if (!uid_is_valid(new_uid) || !gid_is_valid(new_gid)) + return -EINVAL; + + if (st->st_uid != new_uid || st->st_gid != new_gid) { + if (name) + r = fchownat(fd, name, new_uid, new_gid, AT_SYMLINK_NOFOLLOW); + else + r = fchown(fd, new_uid, new_gid); + if (r < 0) + return -errno; + + /* The Linux kernel alters the mode in some cases of chown(). Let's undo this. */ + if (name && !S_ISLNK(st->st_mode)) + r = fchmodat(fd, name, st->st_mode, 0); + else + r = fchmod(fd, st->st_mode); + if (r < 0) + return -errno; + + changed = true; + } + + r = patch_acls(fd, name, st, shift); + if (r < 0) + return r; + + return r > 0 || changed; +} + +static int is_procfs_sysfs_or_suchlike(int fd) { + struct statfs sfs; + + assert(fd >= 0); + + if (fstatfs(fd, &sfs) < 0) + return -errno; + + return F_TYPE_EQUAL(sfs.f_type, BINFMTFS_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, CGROUP_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, CGROUP2_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, DEBUGFS_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, DEVPTS_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, EFIVARFS_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, HUGETLBFS_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, MQUEUE_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, PROC_SUPER_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, PSTOREFS_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, SELINUX_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, SMACK_MAGIC) || + F_TYPE_EQUAL(sfs.f_type, SYSFS_MAGIC); +} + +static int recurse_fd(int fd, bool donate_fd, const struct stat *st, uid_t shift, bool is_toplevel) { + bool changed = false; + int r; + + assert(fd >= 0); + + /* We generally want to permit crossing of mount boundaries when patching the UIDs/GIDs. However, we + * probably shouldn't do this for /proc and /sys if that is already mounted into place. Hence, let's + * stop the recursion when we hit a procfs or sysfs file system. */ + r = is_procfs_sysfs_or_suchlike(fd); + if (r < 0) + goto finish; + if (r > 0) { + r = 0; /* don't recurse */ + goto finish; + } + + r = patch_fd(fd, NULL, st, shift); + if (r == -EROFS) { + _cleanup_free_ char *name = NULL; + + if (!is_toplevel) { + /* When we hit a ready-only subtree we simply skip it, but log about it. */ + (void) fd_get_path(fd, &name); + log_debug("Skippping read-only file or directory %s.", strna(name)); + r = 0; + } + + goto finish; + } + if (r < 0) + goto finish; + + if (S_ISDIR(st->st_mode)) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + + if (!donate_fd) { + int copy; + + copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (copy < 0) { + r = -errno; + goto finish; + } + + fd = copy; + donate_fd = true; + } + + d = fdopendir(fd); + if (!d) { + r = -errno; + goto finish; + } + fd = -1; + + FOREACH_DIRENT_ALL(de, d, r = -errno; goto finish) { + struct stat fst; + + if (STR_IN_SET(de->d_name, ".", "..")) + continue; + + if (fstatat(dirfd(d), de->d_name, &fst, AT_SYMLINK_NOFOLLOW) < 0) { + r = -errno; + goto finish; + } + + if (S_ISDIR(fst.st_mode)) { + int subdir_fd; + + subdir_fd = openat(dirfd(d), de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); + if (subdir_fd < 0) { + r = -errno; + goto finish; + + } + + r = recurse_fd(subdir_fd, true, &fst, shift, false); + if (r < 0) + goto finish; + if (r > 0) + changed = true; + + } else { + r = patch_fd(dirfd(d), de->d_name, &fst, shift); + if (r < 0) + goto finish; + if (r > 0) + changed = true; + } + } + } + + r = changed; + +finish: + if (donate_fd) + safe_close(fd); + + return r; +} + +static int fd_patch_uid_internal(int fd, bool donate_fd, uid_t shift, uid_t range) { + struct stat st; + int r; + + assert(fd >= 0); + + /* Recursively adjusts the UID/GIDs of all files of a directory tree. This is used to automatically fix up an + * OS tree to the used user namespace UID range. Note that this automatic adjustment only works for UID ranges + * following the concept that the upper 16bit of a UID identify the container, and the lower 16bit are the actual + * UID within the container. */ + + if ((shift & 0xFFFF) != 0) { + /* We only support containers where the shift starts at a 2^16 boundary */ + r = -EOPNOTSUPP; + goto finish; + } + + if (range != 0x10000) { + /* We only support containers with 16bit UID ranges for the patching logic */ + r = -EOPNOTSUPP; + goto finish; + } + + if (fstat(fd, &st) < 0) { + r = -errno; + goto finish; + } + + if ((uint32_t) st.st_uid >> 16 != (uint32_t) st.st_gid >> 16) { + /* We only support containers where the uid/gid container ID match */ + r = -EBADE; + goto finish; + } + + /* Try to detect if the range is already right. Of course, this a pretty drastic optimization, as we assume + * that if the top-level dir has the right upper 16bit assigned, then everything below will have too... */ + if (((uint32_t) (st.st_uid ^ shift) >> 16) == 0) + return 0; + + return recurse_fd(fd, donate_fd, &st, shift, true); + +finish: + if (donate_fd) + safe_close(fd); + + return r; +} + +int fd_patch_uid(int fd, uid_t shift, uid_t range) { + return fd_patch_uid_internal(fd, false, shift, range); +} + +int path_patch_uid(const char *path, uid_t shift, uid_t range) { + int fd; + + fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME); + if (fd < 0) + return -errno; + + return fd_patch_uid_internal(fd, true, shift, range); +} diff --git a/src/bus-proxyd/driver.h b/src/nspawn/nspawn-patch-uid.h index 1630cdc7b9..55d0990016 100644 --- a/src/bus-proxyd/driver.h +++ b/src/nspawn/nspawn-patch-uid.h @@ -1,9 +1,7 @@ -#pragma once - /*** This file is part of systemd. - Copyright 2014 Lennart Poettering + Copyright 2016 Lennart Poettering systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -19,9 +17,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "sd-bus.h" - -#include "bus-xml-policy.h" -#include "proxy.h" +#include <sys/types.h> -int bus_proxy_process_driver(Proxy *p, sd_bus *a, sd_bus *b, sd_bus_message *m, SharedPolicy *sp, const struct ucred *ucred, Set *owned_names); +int fd_patch_uid(int fd, uid_t shift, uid_t range); +int path_patch_uid(const char *path, uid_t shift, uid_t range); diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c index 760861089d..20103c5e88 100644 --- a/src/nspawn/nspawn-register.c +++ b/src/nspawn/nspawn-register.c @@ -20,6 +20,7 @@ #include "sd-bus.h" #include "bus-error.h" +#include "bus-unit-util.h" #include "bus-util.h" #include "nspawn-register.h" #include "stat-util.h" diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c index 4fb0054698..5f1522cfb6 100644 --- a/src/nspawn/nspawn-settings.c +++ b/src/nspawn/nspawn-settings.c @@ -24,7 +24,10 @@ #include "nspawn-settings.h" #include "parse-util.h" #include "process-util.h" +#include "socket-util.h" +#include "string-util.h" #include "strv.h" +#include "user-util.h" #include "util.h" int settings_load(FILE *f, const char *path, Settings **ret) { @@ -40,9 +43,13 @@ int settings_load(FILE *f, const char *path, Settings **ret) { s->start_mode = _START_MODE_INVALID; s->personality = PERSONALITY_INVALID; + s->userns_mode = _USER_NAMESPACE_MODE_INVALID; + s->uid_shift = UID_INVALID; + s->uid_range = UID_INVALID; s->read_only = -1; s->volatile_mode = _VOLATILE_MODE_INVALID; + s->userns_chown = -1; s->private_network = -1; s->network_veth = -1; @@ -59,6 +66,16 @@ int settings_load(FILE *f, const char *path, Settings **ret) { if (r < 0) return r; + /* Make sure that if userns_mode is set, userns_chown is set to something appropriate, and vice versa. Either + * both fields shall be initialized or neither. */ + if (s->userns_mode == USER_NAMESPACE_PICK) + s->userns_chown = true; + else if (s->userns_mode != _USER_NAMESPACE_MODE_INVALID && s->userns_chown < 0) + s->userns_chown = false; + + if (s->userns_chown >= 0 && s->userns_mode == _USER_NAMESPACE_MODE_INVALID) + s->userns_mode = USER_NAMESPACE_NO; + *ret = s; s = NULL; @@ -80,6 +97,7 @@ Settings* settings_free(Settings *s) { strv_free(s->network_ipvlan); strv_free(s->network_veth_extra); free(s->network_bridge); + free(s->network_zone); expose_port_free_all(s->expose_ports); custom_mount_free_all(s->custom_mounts, s->n_custom_mounts); @@ -95,6 +113,7 @@ bool settings_private_network(Settings *s) { s->private_network > 0 || s->network_veth > 0 || s->network_bridge || + s->network_zone || s->network_interfaces || s->network_macvlan || s->network_ipvlan || @@ -106,7 +125,8 @@ bool settings_network_veth(Settings *s) { return s->network_veth > 0 || - s->network_bridge; + s->network_bridge || + s->network_zone; } DEFINE_CONFIG_PARSE_ENUM(config_parse_volatile_mode, volatile_mode, VolatileMode, "Failed to parse volatile mode"); @@ -303,6 +323,38 @@ int config_parse_veth_extra( return 0; } +int config_parse_network_zone( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Settings *settings = data; + _cleanup_free_ char *j = NULL; + + assert(filename); + assert(lvalue); + assert(rvalue); + + j = strappend("vz-", rvalue); + if (!ifname_valid(j)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid network zone name %s, ignoring: %m", rvalue); + return 0; + } + + free(settings->network_zone); + settings->network_zone = j; + j = NULL; + + return 0; +} + int config_parse_boot( const char *unit, const char *filename, @@ -392,3 +444,73 @@ conflict: log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring."); return 0; } + +int config_parse_private_users( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Settings *settings = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = parse_boolean(rvalue); + if (r == 0) { + /* no: User namespacing off */ + settings->userns_mode = USER_NAMESPACE_NO; + settings->uid_shift = UID_INVALID; + settings->uid_range = UINT32_C(0x10000); + } else if (r > 0) { + /* yes: User namespacing on, UID range is read from root dir */ + settings->userns_mode = USER_NAMESPACE_FIXED; + settings->uid_shift = UID_INVALID; + settings->uid_range = UINT32_C(0x10000); + } else if (streq(rvalue, "pick")) { + /* pick: User namespacing on, UID range is picked randomly */ + settings->userns_mode = USER_NAMESPACE_PICK; + settings->uid_shift = UID_INVALID; + settings->uid_range = UINT32_C(0x10000); + } else { + const char *range, *shift; + uid_t sh, rn; + + /* anything else: User namespacing on, UID range is explicitly configured */ + + range = strchr(rvalue, ':'); + if (range) { + shift = strndupa(rvalue, range - rvalue); + range++; + + r = safe_atou32(range, &rn); + if (r < 0 || rn <= 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID range invalid, ignoring: %s", range); + return 0; + } + } else { + shift = rvalue; + rn = UINT32_C(0x10000); + } + + r = parse_uid(shift, &sh); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "UID/GID shift invalid, ignoring: %s", range); + return 0; + } + + settings->userns_mode = USER_NAMESPACE_FIXED; + settings->uid_shift = sh; + settings->uid_range = rn; + } + + return 0; +} diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index a017405cd9..1c47e37912 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -33,6 +33,14 @@ typedef enum StartMode { _START_MODE_INVALID = -1 } StartMode; +typedef enum UserNamespaceMode { + USER_NAMESPACE_NO, + USER_NAMESPACE_FIXED, + USER_NAMESPACE_PICK, + _USER_NAMESPACE_MODE_MAX, + _USER_NAMESPACE_MODE_INVALID = -1, +} UserNamespaceMode; + typedef enum SettingsMask { SETTING_START_MODE = 1 << 0, SETTING_ENVIRONMENT = 1 << 1, @@ -47,7 +55,8 @@ typedef enum SettingsMask { SETTING_VOLATILE_MODE = 1 << 10, SETTING_CUSTOM_MOUNTS = 1 << 11, SETTING_WORKING_DIRECTORY = 1 << 12, - _SETTINGS_MASK_ALL = (1 << 13) -1 + SETTING_USERNS = 1 << 13, + _SETTINGS_MASK_ALL = (1 << 14) -1 } SettingsMask; typedef struct Settings { @@ -62,17 +71,21 @@ typedef struct Settings { unsigned long personality; sd_id128_t machine_id; char *working_directory; + UserNamespaceMode userns_mode; + uid_t uid_shift, uid_range; /* [Image] */ int read_only; VolatileMode volatile_mode; CustomMount *custom_mounts; unsigned n_custom_mounts; + int userns_chown; /* [Network] */ int private_network; int network_veth; char *network_bridge; + char *network_zone; char **network_interfaces; char **network_macvlan; char **network_ipvlan; @@ -97,5 +110,7 @@ int config_parse_volatile_mode(const char *unit, const char *filename, unsigned int config_parse_bind(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_tmpfs(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_veth_extra(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_network_zone(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_boot(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_pid2(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_private_users(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index ef348c335b..8ec058431b 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -22,7 +22,9 @@ #endif #include <errno.h> #include <getopt.h> +#include <grp.h> #include <linux/loop.h> +#include <pwd.h> #include <sched.h> #ifdef HAVE_SECCOMP #include <seccomp.h> @@ -64,6 +66,7 @@ #include "hostname-util.h" #include "log.h" #include "loopback-setup.h" +#include "machine-id-setup.h" #include "machine-image.h" #include "macro.h" #include "missing.h" @@ -74,6 +77,7 @@ #include "nspawn-expose-ports.h" #include "nspawn-mount.h" #include "nspawn-network.h" +#include "nspawn-patch-uid.h" #include "nspawn-register.h" #include "nspawn-settings.h" #include "nspawn-setuid.h" @@ -87,6 +91,7 @@ #ifdef HAVE_SECCOMP #include "seccomp-util.h" #endif +#include "selinux-util.h" #include "signal-util.h" #include "socket-util.h" #include "stat-util.h" @@ -99,6 +104,11 @@ #include "user-util.h" #include "util.h" +/* Note that devpts's gid= parameter parses GIDs as signed values, hence we stay away from the upper half of the 32bit + * UID range here */ +#define UID_SHIFT_PICK_MIN ((uid_t) UINT32_C(0x00080000)) +#define UID_SHIFT_PICK_MAX ((uid_t) UINT32_C(0x6FFF0000)) + typedef enum ContainerStatus { CONTAINER_TERMINATED, CONTAINER_REBOOTED @@ -166,13 +176,15 @@ static char **arg_network_ipvlan = NULL; static bool arg_network_veth = false; static char **arg_network_veth_extra = NULL; static char *arg_network_bridge = NULL; +static char *arg_network_zone = NULL; static unsigned long arg_personality = PERSONALITY_INVALID; static char *arg_image = NULL; static VolatileMode arg_volatile_mode = VOLATILE_NO; static ExposePort *arg_expose_ports = NULL; static char **arg_property = NULL; +static UserNamespaceMode arg_userns_mode = USER_NAMESPACE_NO; static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U; -static bool arg_userns = false; +static bool arg_userns_chown = false; static int arg_kill_signal = 0; static bool arg_unified_cgroup_hierarchy = false; static SettingsMask arg_settings_mask = 0; @@ -200,8 +212,10 @@ static void help(void) { " --uuid=UUID Set a specific machine UUID for the container\n" " -S --slice=SLICE Place the container in the specified slice\n" " --property=NAME=VALUE Set scope unit property\n" + " -U --private-users=pick Run within user namespace, pick UID/GID range automatically\n" " --private-users[=UIDBASE[:NUIDS]]\n" - " Run within user namespace\n" + " Run within user namespace, user configured UID/GID range\n" + " --private-user-chown Adjust OS tree file ownership for private UID/GID range\n" " --private-network Disable network in container\n" " --network-interface=INTERFACE\n" " Assign an existing network interface to the\n" @@ -221,6 +235,8 @@ static void help(void) { " Add a virtual Ethernet connection between host\n" " and container and add it to an existing bridge on\n" " the host\n" + " --network-zone=NAME Add a virtual Ethernet connection to the container,\n" + " and add it to an automatically managed bridge interface\n" " -p --port=[PROTOCOL:]HOSTPORT[:CONTAINERPORT]\n" " Expose a container IP port on the host\n" " -Z --selinux-context=SECLABEL\n" @@ -248,7 +264,7 @@ static void help(void) { " the container\n" " --overlay-ro=PATH[:PATH...]:PATH\n" " Similar, but creates a read-only overlay mount\n" - " --setenv=NAME=VALUE Pass an environment variable to PID 1\n" + " -E --setenv=NAME=VALUE Pass an environment variable to PID 1\n" " --share-system Share system namespaces with host\n" " --register=BOOLEAN Register container as machine\n" " --keep-unit Do not register a scope for the machine, reuse\n" @@ -270,9 +286,15 @@ static int custom_mounts_prepare(void) { for (i = 0; i < arg_n_custom_mounts; i++) { CustomMount *m = &arg_custom_mounts[i]; - if (arg_userns && arg_uid_shift == UID_INVALID && path_equal(m->destination, "/")) { - log_error("--private-users with automatic UID shift may not be combined with custom root mounts."); - return -EINVAL; + if (path_equal(m->destination, "/") && arg_userns_mode != USER_NAMESPACE_NO) { + + if (arg_userns_chown) { + log_error("--private-users-chown may not be combined with custom root mounts."); + return -EINVAL; + } else if (arg_uid_shift == UID_INVALID) { + log_error("--private-users with automatic UID shift may not be combined with custom root mounts."); + return -EINVAL; + } } if (m->type != CUSTOM_MOUNT_OVERLAY) @@ -331,7 +353,6 @@ static int parse_argv(int argc, char *argv[]) { ARG_TMPFS, ARG_OVERLAY, ARG_OVERLAY_RO, - ARG_SETENV, ARG_SHARE_SYSTEM, ARG_REGISTER, ARG_KEEP_UNIT, @@ -339,6 +360,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_NETWORK_MACVLAN, ARG_NETWORK_IPVLAN, ARG_NETWORK_BRIDGE, + ARG_NETWORK_ZONE, ARG_NETWORK_VETH_EXTRA, ARG_PERSONALITY, ARG_VOLATILE, @@ -348,6 +370,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_KILL_SIGNAL, ARG_SETTINGS, ARG_CHDIR, + ARG_PRIVATE_USERS_CHOWN, }; static const struct option options[] = { @@ -372,7 +395,7 @@ static int parse_argv(int argc, char *argv[]) { { "overlay-ro", required_argument, NULL, ARG_OVERLAY_RO }, { "machine", required_argument, NULL, 'M' }, { "slice", required_argument, NULL, 'S' }, - { "setenv", required_argument, NULL, ARG_SETENV }, + { "setenv", required_argument, NULL, 'E' }, { "selinux-context", required_argument, NULL, 'Z' }, { "selinux-apifs-context", required_argument, NULL, 'L' }, { "quiet", no_argument, NULL, 'q' }, @@ -385,12 +408,14 @@ static int parse_argv(int argc, char *argv[]) { { "network-veth", no_argument, NULL, 'n' }, { "network-veth-extra", required_argument, NULL, ARG_NETWORK_VETH_EXTRA}, { "network-bridge", required_argument, NULL, ARG_NETWORK_BRIDGE }, + { "network-zone", required_argument, NULL, ARG_NETWORK_ZONE }, { "personality", required_argument, NULL, ARG_PERSONALITY }, { "image", required_argument, NULL, 'i' }, { "volatile", optional_argument, NULL, ARG_VOLATILE }, { "port", required_argument, NULL, 'p' }, { "property", required_argument, NULL, ARG_PROPERTY }, { "private-users", optional_argument, NULL, ARG_PRIVATE_USERS }, + { "private-users-chown", optional_argument, NULL, ARG_PRIVATE_USERS_CHOWN}, { "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL }, { "settings", required_argument, NULL, ARG_SETTINGS }, { "chdir", required_argument, NULL, ARG_CHDIR }, @@ -405,7 +430,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:n", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:nU", options, NULL)) >= 0) switch (c) { @@ -446,7 +471,35 @@ static int parse_argv(int argc, char *argv[]) { arg_settings_mask |= SETTING_USER; break; + case ARG_NETWORK_ZONE: { + char *j; + + j = strappend("vz-", optarg); + if (!j) + return log_oom(); + + if (!ifname_valid(j)) { + log_error("Network zone name not valid: %s", j); + free(j); + return -EINVAL; + } + + free(arg_network_zone); + arg_network_zone = j; + + arg_network_veth = true; + arg_private_network = true; + arg_settings_mask |= SETTING_NETWORK; + break; + } + case ARG_NETWORK_BRIDGE: + + if (!ifname_valid(optarg)) { + log_error("Bridge interface name not valid: %s", optarg); + return -EINVAL; + } + r = free_and_strdup(&arg_network_bridge, optarg); if (r < 0) return log_oom(); @@ -469,6 +522,12 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_NETWORK_INTERFACE: + + if (!ifname_valid(optarg)) { + log_error("Network interface name not valid: %s", optarg); + return -EINVAL; + } + if (strv_extend(&arg_network_interfaces, optarg) < 0) return log_oom(); @@ -477,6 +536,12 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_NETWORK_MACVLAN: + + if (!ifname_valid(optarg)) { + log_error("MACVLAN network interface name not valid: %s", optarg); + return -EINVAL; + } + if (strv_extend(&arg_network_macvlan, optarg) < 0) return log_oom(); @@ -485,6 +550,12 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_NETWORK_IPVLAN: + + if (!ifname_valid(optarg)) { + log_error("IPVLAN network interface name not valid: %s", optarg); + return -EINVAL; + } + if (strv_extend(&arg_network_ipvlan, optarg) < 0) return log_oom(); @@ -561,7 +632,7 @@ static int parse_argv(int argc, char *argv[]) { case ARG_CAPABILITY: case ARG_DROP_CAPABILITY: { p = optarg; - for(;;) { + for (;;) { _cleanup_free_ char *t = NULL; r = extract_first_word(&p, &t, ",", 0); @@ -709,7 +780,7 @@ static int parse_argv(int argc, char *argv[]) { break; } - case ARG_SETENV: { + case 'E': { char **n; if (!env_assignment_is_valid(optarg)) { @@ -796,10 +867,29 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_PRIVATE_USERS: - if (optarg) { + + r = optarg ? parse_boolean(optarg) : 1; + if (r == 0) { + /* no: User namespacing off */ + arg_userns_mode = USER_NAMESPACE_NO; + arg_uid_shift = UID_INVALID; + arg_uid_range = UINT32_C(0x10000); + } else if (r > 0) { + /* yes: User namespacing on, UID range is read from root dir */ + arg_userns_mode = USER_NAMESPACE_FIXED; + arg_uid_shift = UID_INVALID; + arg_uid_range = UINT32_C(0x10000); + } else if (streq(optarg, "pick")) { + /* pick: User namespacing on, UID range is picked randomly */ + arg_userns_mode = USER_NAMESPACE_PICK; + arg_uid_shift = UID_INVALID; + arg_uid_range = UINT32_C(0x10000); + } else { _cleanup_free_ char *buffer = NULL; const char *range, *shift; + /* anything else: User namespacing on, UID range is explicitly configured */ + range = strchr(optarg, ':'); if (range) { buffer = strndup(optarg, range - optarg); @@ -819,9 +909,28 @@ static int parse_argv(int argc, char *argv[]) { log_error("Failed to parse UID: %s", optarg); return -EINVAL; } + + arg_userns_mode = USER_NAMESPACE_FIXED; } - arg_userns = true; + arg_settings_mask |= SETTING_USERNS; + break; + + case 'U': + if (userns_supported()) { + arg_userns_mode = USER_NAMESPACE_PICK; + arg_uid_shift = UID_INVALID; + arg_uid_range = UINT32_C(0x10000); + + arg_settings_mask |= SETTING_USERNS; + } + + break; + + case ARG_PRIVATE_USERS_CHOWN: + arg_userns_chown = true; + + arg_settings_mask |= SETTING_USERNS; break; case ARG_KILL_SIGNAL: @@ -892,6 +1001,9 @@ static int parse_argv(int argc, char *argv[]) { if (arg_share_system) arg_register = false; + if (arg_userns_mode == USER_NAMESPACE_PICK) + arg_userns_chown = true; + if (arg_start_mode != START_PID1 && arg_share_system) { log_error("--boot and --share-system may not be combined."); return -EINVAL; @@ -932,8 +1044,20 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } - if (arg_userns && access("/proc/self/uid_map", F_OK) < 0) - return log_error_errno(EOPNOTSUPP, "--private-users= is not supported, kernel compiled without user namespace support."); + if (arg_userns_mode != USER_NAMESPACE_NO && !userns_supported()) { + log_error("--private-users= is not supported, kernel compiled without user namespace support."); + return -EOPNOTSUPP; + } + + if (arg_userns_chown && arg_read_only) { + log_error("--read-only and --private-users-chown may not be combined."); + return -EINVAL; + } + + if (arg_network_bridge && arg_network_zone) { + log_error("--network-bridge= and --network-zone= may not be combined."); + return -EINVAL; + } if (argc > optind) { arg_parameters = strv_copy(argv + optind); @@ -976,6 +1100,13 @@ static int verify_arguments(void) { return -EINVAL; } +#ifndef HAVE_LIBIPTC + if (arg_expose_ports) { + log_error("--port= is not supported, compiled without libiptc support."); + return -EOPNOTSUPP; + } +#endif + if (arg_start_mode == START_BOOT && arg_kill_signal <= 0) arg_kill_signal = SIGRTMIN+3; @@ -985,7 +1116,7 @@ static int verify_arguments(void) { static int userns_lchown(const char *p, uid_t uid, gid_t gid) { assert(p); - if (!arg_userns) + if (arg_userns_mode == USER_NAMESPACE_NO) return 0; if (uid == UID_INVALID && gid == GID_INVALID) @@ -1367,11 +1498,11 @@ static int setup_hostname(void) { } static int setup_journal(const char *directory) { - sd_id128_t machine_id, this_id; - _cleanup_free_ char *b = NULL, *d = NULL; - const char *etc_machine_id, *p, *q; + sd_id128_t this_id; + _cleanup_free_ char *d = NULL; + const char *p, *q; bool try; - char *id; + char id[33]; int r; /* Don't link journals in ephemeral mode */ @@ -1383,30 +1514,13 @@ static int setup_journal(const char *directory) { try = arg_link_journal_try || arg_link_journal == LINK_AUTO; - etc_machine_id = prefix_roota(directory, "/etc/machine-id"); - - r = read_one_line_file(etc_machine_id, &b); - if (r == -ENOENT && try) - return 0; - else if (r < 0) - return log_error_errno(r, "Failed to read machine ID from %s: %m", etc_machine_id); - - id = strstrip(b); - if (isempty(id) && try) - return 0; - - /* Verify validity */ - r = sd_id128_from_string(id, &machine_id); - if (r < 0) - return log_error_errno(r, "Failed to parse machine ID from %s: %m", etc_machine_id); - r = sd_id128_get_machine(&this_id); if (r < 0) return log_error_errno(r, "Failed to retrieve machine ID: %m"); - if (sd_id128_equal(machine_id, this_id)) { + if (sd_id128_equal(arg_uuid, this_id)) { log_full(try ? LOG_WARNING : LOG_ERR, - "Host and machine ids are equal (%s): refusing to link journals", id); + "Host and machine ids are equal (%s): refusing to link journals", sd_id128_to_string(arg_uuid, id)); if (try) return 0; return -EEXIST; @@ -1424,6 +1538,8 @@ static int setup_journal(const char *directory) { if (r < 0) return log_error_errno(r, "Failed to create /var/log/journal: %m"); + (void) sd_id128_to_string(arg_uuid, id); + p = strjoina("/var/log/journal/", id); q = prefix_roota(directory, p); @@ -1488,7 +1604,7 @@ static int setup_journal(const char *directory) { } if (arg_link_journal == LINK_HOST) { - /* don't create parents here -- if the host doesn't have + /* don't create parents here — if the host doesn't have * permanent journal set up, don't force it here */ if (mkdir(p, 0755) < 0 && errno != EEXIST) { @@ -1597,7 +1713,6 @@ static int setup_seccomp(void) { } } - /* Audit is broken in containers, much of the userspace audit hookup will fail if running inside a container. We don't @@ -2193,6 +2308,61 @@ static int mount_device(const char *what, const char *where, const char *directo #endif } +static int setup_machine_id(const char *directory) { + int r; + const char *etc_machine_id, *t; + _cleanup_free_ char *s = NULL; + + etc_machine_id = prefix_roota(directory, "/etc/machine-id"); + + r = read_one_line_file(etc_machine_id, &s); + if (r < 0) + return log_error_errno(r, "Failed to read machine ID from %s: %m", etc_machine_id); + + t = strstrip(s); + + if (!isempty(t)) { + r = sd_id128_from_string(t, &arg_uuid); + if (r < 0) + return log_error_errno(r, "Failed to parse machine ID from %s: %m", etc_machine_id); + } else { + if (sd_id128_is_null(arg_uuid)) { + r = sd_id128_randomize(&arg_uuid); + if (r < 0) + return log_error_errno(r, "Failed to generate random machine ID: %m"); + } + } + + r = machine_id_setup(directory, arg_uuid); + if (r < 0) + return log_error_errno(r, "Failed to setup machine ID: %m"); + + return 0; +} + +static int recursive_chown(const char *directory, uid_t shift, uid_t range) { + int r; + + assert(directory); + + if (arg_userns_mode == USER_NAMESPACE_NO || !arg_userns_chown) + return 0; + + r = path_patch_uid(directory, arg_uid_shift, arg_uid_range); + if (r == -EOPNOTSUPP) + return log_error_errno(r, "Automatic UID/GID adjusting is only supported for UID/GID ranges starting at multiples of 2^16 with a range of 2^16."); + if (r == -EBADE) + return log_error_errno(r, "Upper 16 bits of root directory UID and GID do not match."); + if (r < 0) + return log_error_errno(r, "Failed to adjust UID/GID shift of OS tree: %m"); + if (r == 0) + log_debug("Root directory of image is already owned by the right UID/GID range, skipping recursive chown operation."); + else + log_debug("Patched directory tree to match UID/GID range."); + + return r; +} + static int mount_devices( const char *where, const char *root_device, bool root_device_rw, @@ -2410,7 +2580,7 @@ static int determine_names(void) { static int determine_uid_shift(const char *directory) { int r; - if (!arg_userns) { + if (arg_userns_mode == USER_NAMESPACE_NO) { arg_uid_shift = 0; return 0; } @@ -2437,7 +2607,6 @@ static int determine_uid_shift(const char *directory) { return -EINVAL; } - log_info("Using user namespaces with base " UID_FMT " and range " UID_FMT ".", arg_uid_shift, arg_uid_range); return 0; } @@ -2450,6 +2619,7 @@ static int inner_child( FDSet *fds) { _cleanup_free_ char *home = NULL; + char as_uuid[37]; unsigned n_env = 1; const char *envp[] = { "PATH=" DEFAULT_PATH_SPLIT_USR, @@ -2473,7 +2643,7 @@ static int inner_child( cg_unified_flush(); - if (arg_userns) { + if (arg_userns_mode != USER_NAMESPACE_NO) { /* Tell the parent, that it now can write the UID map. */ (void) barrier_place(barrier); /* #1 */ @@ -2484,7 +2654,14 @@ static int inner_child( } } - r = mount_all(NULL, arg_userns, true, arg_uid_shift, arg_private_network, arg_uid_range, arg_selinux_apifs_context); + r = mount_all(NULL, + arg_userns_mode != USER_NAMESPACE_NO, + true, + arg_private_network, + arg_uid_shift, + arg_uid_range, + arg_selinux_apifs_context); + if (r < 0) return r; @@ -2560,19 +2737,17 @@ static int inner_child( envp[n_env] = strv_find_prefix(environ, "TERM="); if (envp[n_env]) - n_env ++; + n_env++; if ((asprintf((char**)(envp + n_env++), "HOME=%s", home ? home: "/root") < 0) || (asprintf((char**)(envp + n_env++), "USER=%s", arg_user ? arg_user : "root") < 0) || (asprintf((char**)(envp + n_env++), "LOGNAME=%s", arg_user ? arg_user : "root") < 0)) return log_oom(); - if (!sd_id128_equal(arg_uuid, SD_ID128_NULL)) { - char as_uuid[37]; + assert(!sd_id128_equal(arg_uuid, SD_ID128_NULL)); - if (asprintf((char**)(envp + n_env++), "container_uuid=%s", id128_format_as_uuid(arg_uuid, as_uuid)) < 0) - return log_oom(); - } + if (asprintf((char**)(envp + n_env++), "container_uuid=%s", id128_format_as_uuid(arg_uuid, as_uuid)) < 0) + return log_oom(); if (fdset_size(fds) > 0) { r = fdset_cloexec(fds, false); @@ -2623,12 +2798,10 @@ static int inner_child( /* Automatically search for the init system */ - m = 1 + strv_length(arg_parameters); - a = newa(char*, m + 1); - if (strv_isempty(arg_parameters)) - a[1] = NULL; - else - memcpy(a + 1, arg_parameters, m * sizeof(char*)); + m = strv_length(arg_parameters); + a = newa(char*, m + 2); + memcpy_safe(a + 1, arg_parameters, m * sizeof(char*)); + a[1 + m] = NULL; a[0] = (char*) "/usr/lib/systemd/systemd"; execve(a[0], a, env_use); @@ -2642,7 +2815,8 @@ static int inner_child( execvpe(arg_parameters[0], arg_parameters, env_use); else { if (!arg_chdir) - chdir(home ?: "/root"); + /* If we cannot change the directory, we'll end up in /, that is expected. */ + (void) chdir(home ?: "/root"); execle("/bin/bash", "-bash", NULL, env_use); execle("/bin/sh", "-sh", NULL, env_use); @@ -2663,6 +2837,7 @@ static int outer_child( bool interactive, bool secondary, int pid_socket, + int uuid_socket, int kmsg_socket, int rtnl_socket, int uid_shift_socket, @@ -2676,6 +2851,7 @@ static int outer_child( assert(directory); assert(console); assert(pid_socket >= 0); + assert(uuid_socket >= 0); assert(kmsg_socket >= 0); cg_unified_flush(); @@ -2724,7 +2900,8 @@ static int outer_child( if (r < 0) return r; - if (arg_userns) { + if (arg_userns_mode != USER_NAMESPACE_NO) { + /* Let the parent know which UID shift we read from the image */ l = send(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL); if (l < 0) return log_error_errno(errno, "Failed to send UID shift: %m"); @@ -2732,17 +2909,49 @@ static int outer_child( log_error("Short write while sending UID shift."); return -EIO; } + + if (arg_userns_mode == USER_NAMESPACE_PICK) { + /* When we are supposed to pick the UID shift, the parent will check now whether the UID shift + * we just read from the image is available. If yes, it will send the UID shift back to us, if + * not it will pick a different one, and send it back to us. */ + + l = recv(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), 0); + if (l < 0) + return log_error_errno(errno, "Failed to recv UID shift: %m"); + if (l != sizeof(arg_uid_shift)) { + log_error("Short read while recieving UID shift."); + return -EIO; + } + } + + log_info("Selected user namespace base " UID_FMT " and range " UID_FMT ".", arg_uid_shift, arg_uid_range); } /* Turn directory into bind mount */ if (mount(directory, directory, NULL, MS_BIND|MS_REC, NULL) < 0) return log_error_errno(errno, "Failed to make bind mount: %m"); - r = setup_volatile(directory, arg_volatile_mode, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_context); + r = recursive_chown(directory, arg_uid_shift, arg_uid_range); if (r < 0) return r; - r = setup_volatile_state(directory, arg_volatile_mode, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_context); + r = setup_volatile( + directory, + arg_volatile_mode, + arg_userns_mode != USER_NAMESPACE_NO, + arg_uid_shift, + arg_uid_range, + arg_selinux_context); + if (r < 0) + return r; + + r = setup_volatile_state( + directory, + arg_volatile_mode, + arg_userns_mode != USER_NAMESPACE_NO, + arg_uid_shift, + arg_uid_range, + arg_selinux_context); if (r < 0) return r; @@ -2756,7 +2965,13 @@ static int outer_child( return log_error_errno(r, "Failed to make tree read-only: %m"); } - r = mount_all(directory, arg_userns, false, arg_private_network, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context); + r = mount_all(directory, + arg_userns_mode != USER_NAMESPACE_NO, + false, + arg_private_network, + arg_uid_shift, + arg_uid_range, + arg_selinux_apifs_context); if (r < 0) return r; @@ -2790,15 +3005,32 @@ static int outer_child( if (r < 0) return r; + r = setup_machine_id(directory); + if (r < 0) + return r; + r = setup_journal(directory); if (r < 0) return r; - r = mount_custom(directory, arg_custom_mounts, arg_n_custom_mounts, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context); + r = mount_custom( + directory, + arg_custom_mounts, + arg_n_custom_mounts, + arg_userns_mode != USER_NAMESPACE_NO, + arg_uid_shift, + arg_uid_range, + arg_selinux_apifs_context); if (r < 0) return r; - r = mount_cgroups(directory, arg_unified_cgroup_hierarchy, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context); + r = mount_cgroups( + directory, + arg_unified_cgroup_hierarchy, + arg_userns_mode != USER_NAMESPACE_NO, + arg_uid_shift, + arg_uid_range, + arg_selinux_apifs_context); if (r < 0) return r; @@ -2809,12 +3041,13 @@ static int outer_child( pid = raw_clone(SIGCHLD|CLONE_NEWNS| (arg_share_system ? 0 : CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS) | (arg_private_network ? CLONE_NEWNET : 0) | - (arg_userns ? CLONE_NEWUSER : 0), + (arg_userns_mode != USER_NAMESPACE_NO ? CLONE_NEWUSER : 0), NULL); if (pid < 0) return log_error_errno(errno, "Failed to fork inner child: %m"); if (pid == 0) { pid_socket = safe_close(pid_socket); + uuid_socket = safe_close(uuid_socket); uid_shift_socket = safe_close(uid_shift_socket); /* The inner child has all namespaces that are @@ -2836,13 +3069,77 @@ static int outer_child( return -EIO; } + l = send(uuid_socket, &arg_uuid, sizeof(arg_uuid), MSG_NOSIGNAL); + if (l < 0) + return log_error_errno(errno, "Failed to send machine ID: %m"); + if (l != sizeof(arg_uuid)) { + log_error("Short write while sending machine ID."); + return -EIO; + } + pid_socket = safe_close(pid_socket); + uuid_socket = safe_close(uuid_socket); kmsg_socket = safe_close(kmsg_socket); rtnl_socket = safe_close(rtnl_socket); return 0; } +static int uid_shift_pick(uid_t *shift, LockFile *ret_lock_file) { + unsigned n_tries = 100; + uid_t candidate; + int r; + + assert(shift); + assert(ret_lock_file); + assert(arg_userns_mode == USER_NAMESPACE_PICK); + assert(arg_uid_range == 0x10000U); + + candidate = *shift; + + (void) mkdir("/run/systemd/nspawn-uid", 0755); + + for (;;) { + char lock_path[strlen("/run/systemd/nspawn-uid/") + DECIMAL_STR_MAX(uid_t) + 1]; + _cleanup_release_lock_file_ LockFile lf = LOCK_FILE_INIT; + + if (--n_tries <= 0) + return -EBUSY; + + if (candidate < UID_SHIFT_PICK_MIN || candidate > UID_SHIFT_PICK_MAX) + goto next; + if ((candidate & UINT32_C(0xFFFF)) != 0) + goto next; + + xsprintf(lock_path, "/run/systemd/nspawn-uid/" UID_FMT, candidate); + r = make_lock_file(lock_path, LOCK_EX|LOCK_NB, &lf); + if (r == -EBUSY) /* Range already taken by another nspawn instance */ + goto next; + if (r < 0) + return r; + + /* Make some superficial checks whether the range is currently known in the user database */ + if (getpwuid(candidate)) + goto next; + if (getpwuid(candidate + UINT32_C(0xFFFE))) + goto next; + if (getgrgid(candidate)) + goto next; + if (getgrgid(candidate + UINT32_C(0xFFFE))) + goto next; + + *ret_lock_file = lf; + lf = (struct LockFile) LOCK_FILE_INIT; + *shift = candidate; + return 0; + + next: + random_bytes(&candidate, sizeof(candidate)); + candidate = (candidate % (UID_SHIFT_PICK_MAX - UID_SHIFT_PICK_MIN)) + UID_SHIFT_PICK_MIN; + candidate &= (uid_t) UINT32_C(0xFFFF0000); + } +} + static int setup_uid_map(pid_t pid) { char uid_map[strlen("/proc//uid_map") + DECIMAL_STR_MAX(uid_t) + 1], line[DECIMAL_STR_MAX(uid_t)*3+3+1]; int r; @@ -3029,6 +3326,7 @@ static int load_settings(void) { (settings->private_network >= 0 || settings->network_veth >= 0 || settings->network_bridge || + settings->network_zone || settings->network_interfaces || settings->network_macvlan || settings->network_ipvlan || @@ -3059,6 +3357,10 @@ static int load_settings(void) { free(arg_network_bridge); arg_network_bridge = settings->network_bridge; settings->network_bridge = NULL; + + free(arg_network_zone); + arg_network_zone = settings->network_zone; + settings->network_zone = NULL; } } @@ -3074,6 +3376,19 @@ static int load_settings(void) { } } + if ((arg_settings_mask & SETTING_USERNS) == 0 && + settings->userns_mode != _USER_NAMESPACE_MODE_INVALID) { + + if (!arg_settings_trusted) + log_warning("Ignoring PrivateUsers= and PrivateUsersChown= settings, file %s is not trusted.", p); + else { + arg_userns_mode = settings->userns_mode; + arg_uid_shift = settings->uid_shift; + arg_uid_range = settings->uid_range; + arg_userns_chown = settings->userns_chown; + } + } + return 0; } @@ -3084,14 +3399,14 @@ int main(int argc, char *argv[]) { _cleanup_close_ int master = -1, image_fd = -1; _cleanup_fdset_free_ FDSet *fds = NULL; int r, n_fd_passed, loop_nr = -1; - char veth_name[IFNAMSIZ]; + char veth_name[IFNAMSIZ] = ""; bool secondary = false, remove_subvol = false; sigset_t mask_chld; pid_t pid = 0; int ret = EXIT_SUCCESS; union in_addr_union exposed = {}; _cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT; - bool interactive; + bool interactive, veth_created = false; log_parse_environment(); log_open(); @@ -3286,6 +3601,12 @@ int main(int argc, char *argv[]) { goto finish; } + if (arg_selinux_apifs_context) { + r = mac_selinux_apply(console, arg_selinux_apifs_context); + if (r < 0) + goto finish; + } + if (unlockpt(master) < 0) { r = log_error_errno(errno, "Failed to unlock tty: %m"); goto finish; @@ -3306,19 +3627,42 @@ int main(int argc, char *argv[]) { } for (;;) { - _cleanup_close_pair_ int kmsg_socket_pair[2] = { -1, -1 }, rtnl_socket_pair[2] = { -1, -1 }, pid_socket_pair[2] = { -1, -1 }, uid_shift_socket_pair[2] = { -1, -1 }; - ContainerStatus container_status; - _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL; static const struct sigaction sa = { .sa_handler = nop_signal_handler, .sa_flags = SA_NOCLDSTOP, }; - int ifi = 0; - ssize_t l; + + _cleanup_release_lock_file_ LockFile uid_shift_lock = LOCK_FILE_INIT; + _cleanup_close_ int etc_passwd_lock = -1; + _cleanup_close_pair_ int + kmsg_socket_pair[2] = { -1, -1 }, + rtnl_socket_pair[2] = { -1, -1 }, + pid_socket_pair[2] = { -1, -1 }, + uuid_socket_pair[2] = { -1, -1 }, + uid_shift_socket_pair[2] = { -1, -1 }; + _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL; _cleanup_(sd_event_unrefp) sd_event *event = NULL; _cleanup_(pty_forward_freep) PTYForward *forward = NULL; _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + ContainerStatus container_status; char last_char = 0; + int ifi = 0; + ssize_t l; + + if (arg_userns_mode == USER_NAMESPACE_PICK) { + /* When we shall pick the UID/GID range, let's first lock /etc/passwd, so that we can safely + * check with getpwuid() if the specific user already exists. Note that /etc might be + * read-only, in which case this will fail with EROFS. But that's really OK, as in that case we + * can be reasonably sure that no users are going to be added. Note that getpwuid() checks are + * really just an extra safety net. We kinda assume that the UID range we allocate from is + * really ours. */ + + etc_passwd_lock = take_etc_passwd_lock(NULL); + if (etc_passwd_lock < 0 && etc_passwd_lock != -EROFS) { + log_error_errno(r, "Failed to take /etc/passwd lock: %m"); + goto finish; + } + } r = barrier_create(&barrier); if (r < 0) { @@ -3341,7 +3685,12 @@ int main(int argc, char *argv[]) { goto finish; } - if (arg_userns) + if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uuid_socket_pair) < 0) { + r = log_error_errno(errno, "Failed to create id socket pair: %m"); + goto finish; + } + + if (arg_userns_mode != USER_NAMESPACE_NO) if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0) { r = log_error_errno(errno, "Failed to create uid shift socket pair: %m"); goto finish; @@ -3381,6 +3730,7 @@ int main(int argc, char *argv[]) { kmsg_socket_pair[0] = safe_close(kmsg_socket_pair[0]); rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]); pid_socket_pair[0] = safe_close(pid_socket_pair[0]); + uuid_socket_pair[0] = safe_close(uuid_socket_pair[0]); uid_shift_socket_pair[0] = safe_close(uid_shift_socket_pair[0]); (void) reset_all_signal_handlers(); @@ -3395,6 +3745,7 @@ int main(int argc, char *argv[]) { interactive, secondary, pid_socket_pair[1], + uuid_socket_pair[1], kmsg_socket_pair[1], rtnl_socket_pair[1], uid_shift_socket_pair[1], @@ -3412,8 +3763,46 @@ int main(int argc, char *argv[]) { kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]); rtnl_socket_pair[1] = safe_close(rtnl_socket_pair[1]); pid_socket_pair[1] = safe_close(pid_socket_pair[1]); + uuid_socket_pair[1] = safe_close(uuid_socket_pair[1]); uid_shift_socket_pair[1] = safe_close(uid_shift_socket_pair[1]); + if (arg_userns_mode != USER_NAMESPACE_NO) { + /* The child just let us know the UID shift it might have read from the image. */ + l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), 0); + if (l < 0) { + r = log_error_errno(errno, "Failed to read UID shift: %m"); + goto finish; + } + if (l != sizeof(arg_uid_shift)) { + log_error("Short read while reading UID shift."); + r = EIO; + goto finish; + } + + if (arg_userns_mode == USER_NAMESPACE_PICK) { + /* If we are supposed to pick the UID shift, let's try to use the shift read from the + * image, but if that's already in use, pick a new one, and report back to the child, + * which one we now picked. */ + + r = uid_shift_pick(&arg_uid_shift, &uid_shift_lock); + if (r < 0) { + log_error_errno(r, "Failed to pick suitable UID/GID range: %m"); + goto finish; + } + + l = send(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), MSG_NOSIGNAL); + if (l < 0) { + r = log_error_errno(errno, "Failed to send UID shift: %m"); + goto finish; + } + if (l != sizeof(arg_uid_shift)) { + log_error("Short write while writing UID shift."); + r = -EIO; + goto finish; + } + } + } + /* Wait for the outer child. */ r = wait_for_terminate_and_warn("namespace helper", pid, NULL); if (r < 0) @@ -3436,26 +3825,27 @@ int main(int argc, char *argv[]) { goto finish; } + /* We also retrieve container UUID in case it was generated by outer child */ + l = recv(uuid_socket_pair[0], &arg_uuid, sizeof(arg_uuid), 0); + if (l < 0) { + r = log_error_errno(errno, "Failed to read container machine ID: %m"); + goto finish; + } + if (l != sizeof(arg_uuid)) { + log_error("Short read while reading container machined ID."); + r = EIO; + goto finish; + } + log_debug("Init process invoked as PID " PID_FMT, pid); - if (arg_userns) { + if (arg_userns_mode != USER_NAMESPACE_NO) { if (!barrier_place_and_sync(&barrier)) { /* #1 */ log_error("Child died too early."); r = -ESRCH; goto finish; } - l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof(arg_uid_shift), 0); - if (l < 0) { - r = log_error_errno(errno, "Failed to read UID shift: %m"); - goto finish; - } - if (l != sizeof(arg_uid_shift)) { - log_error("Short read while reading UID shift."); - r = EIO; - goto finish; - } - r = setup_uid_map(pid); if (r < 0) goto finish; @@ -3470,14 +3860,23 @@ int main(int argc, char *argv[]) { goto finish; if (arg_network_veth) { - r = setup_veth(arg_machine, pid, veth_name, !!arg_network_bridge); + r = setup_veth(arg_machine, pid, veth_name, + arg_network_bridge || arg_network_zone); if (r < 0) goto finish; else if (r > 0) ifi = r; if (arg_network_bridge) { - r = setup_bridge(veth_name, arg_network_bridge); + /* Add the interface to a bridge */ + r = setup_bridge(veth_name, arg_network_bridge, false); + if (r < 0) + goto finish; + if (r > 0) + ifi = r; + } else if (arg_network_zone) { + /* Add the interface to a bridge, possibly creating it */ + r = setup_bridge(veth_name, arg_network_zone, true); if (r < 0) goto finish; if (r > 0) @@ -3489,6 +3888,12 @@ int main(int argc, char *argv[]) { if (r < 0) goto finish; + /* We created the primary and extra veth links now; let's remember this, so that we know to + remove them later on. Note that we don't bother with removing veth links that were created + here when their setup failed half-way, because in that case the kernel should be able to + remove them on its own, since they cannot be referenced by anything yet. */ + veth_created = true; + r = setup_macvlan(arg_machine, pid, arg_network_macvlan); if (r < 0) goto finish; @@ -3553,6 +3958,10 @@ int main(int argc, char *argv[]) { goto finish; } + /* At this point we have made use of the UID we picked, and thus nss-mymachines will make them appear + * in getpwuid(), thus we can release the /etc/passwd lock. */ + etc_passwd_lock = safe_close(etc_passwd_lock); + sd_notifyf(false, "READY=1\n" "STATUS=Container running.\n" @@ -3620,7 +4029,7 @@ int main(int argc, char *argv[]) { /* We failed to wait for the container, or the * container exited abnormally */ goto finish; - else if (r > 0 || container_status == CONTAINER_TERMINATED){ + else if (r > 0 || container_status == CONTAINER_TERMINATED) { /* The container exited with a non-zero * status, or with zero status and no reboot * was requested. */ @@ -3647,6 +4056,9 @@ int main(int argc, char *argv[]) { } expose_port_flush(arg_expose_ports, &exposed); + + (void) remove_veth_links(veth_name, arg_network_veth_extra); + veth_created = false; } finish: @@ -3680,6 +4092,10 @@ finish: expose_port_flush(arg_expose_ports, &exposed); + if (veth_created) + (void) remove_veth_links(veth_name, arg_network_veth_extra); + (void) remove_bridge(arg_network_zone); + free(arg_directory); free(arg_template); free(arg_image); diff --git a/src/nspawn/test-patch-uid.c b/src/nspawn/test-patch-uid.c new file mode 100644 index 0000000000..11c5321788 --- /dev/null +++ b/src/nspawn/test-patch-uid.c @@ -0,0 +1,61 @@ +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <stdlib.h> + +#include "log.h" +#include "nspawn-patch-uid.h" +#include "user-util.h" +#include "util.h" + +int main(int argc, char *argv[]) { + uid_t shift, range; + int r; + + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + if (argc != 4) { + log_error("Expected PATH SHIFT RANGE parameters."); + return EXIT_FAILURE; + } + + r = parse_uid(argv[2], &shift); + if (r < 0) { + log_error_errno(r, "Failed to parse UID shift %s.", argv[2]); + return EXIT_FAILURE; + } + + r = parse_gid(argv[3], &range); + if (r < 0) { + log_error_errno(r, "Failed to parse UID range %s.", argv[3]); + return EXIT_FAILURE; + } + + r = path_patch_uid(argv[1], shift, range); + if (r < 0) { + log_error_errno(r, "Failed to patch directory tree: %m"); + return EXIT_FAILURE; + } + + log_info("Changed: %s", yes_no(r)); + + return EXIT_SUCCESS; +} diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c index 2536ad2898..2b83d127b7 100644 --- a/src/nss-myhostname/nss-myhostname.c +++ b/src/nss-myhostname/nss-myhostname.c @@ -127,7 +127,8 @@ enum nss_status _nss_myhostname_gethostbyname4_r( memcpy(r_name, canonical, l+1); idx = ALIGN(l+1); - if (n_addresses <= 0) { + assert(n_addresses >= 0); + if (n_addresses == 0) { /* Second, fill in IPv6 tuple */ r_tuple = (struct gaih_addrtuple*) (buffer + idx); r_tuple->next = r_tuple_prev; @@ -453,38 +454,33 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r( } n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses); - if (n_addresses > 0) { - for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) { - if (af != a->family) - continue; + for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) { + if (af != a->family) + continue; - if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) - goto found; - } + if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) + goto found; } addresses = mfree(addresses); n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses); - if (n_addresses > 0) { - for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) { - if (af != a->family) - continue; + for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) { + if (af != a->family) + continue; - if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) { - canonical = "gateway"; - goto found; - } + if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) { + canonical = "gateway"; + goto found; } } *errnop = ENOENT; *h_errnop = HOST_NOT_FOUND; - return NSS_STATUS_NOTFOUND; found: - if (!canonical || (!additional && additional_from_hostname)) { + if (!canonical || additional_from_hostname) { hn = gethostname_malloc(); if (!hn) { *errnop = ENOMEM; @@ -494,8 +490,7 @@ found: if (!canonical) canonical = hn; - - if (!additional && additional_from_hostname) + else additional = hn; } diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c index 1582d702f8..8d57b26cbc 100644 --- a/src/nss-mymachines/nss-mymachines.c +++ b/src/nss-mymachines/nss-mymachines.c @@ -66,7 +66,7 @@ static int count_addresses(sd_bus_message *m, int af, unsigned *ret) { if (af != AF_UNSPEC && family != af) continue; - c ++; + c++; } if (r < 0) return r; diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c index 69c0d9bdc1..5ce10f1cbd 100644 --- a/src/nss-resolve/nss-resolve.c +++ b/src/nss-resolve/nss-resolve.c @@ -90,7 +90,7 @@ static int count_addresses(sd_bus_message *m, int af, const char **canonical) { if (af != AF_UNSPEC && family != af) continue; - c ++; + c++; } if (r < 0) return r; @@ -117,13 +117,6 @@ enum nss_status _nss_resolve_gethostbyname4_r( int *errnop, int *h_errnop, int32_t *ttlp) { - enum nss_status (*fallback)( - const char *name, - struct gaih_addrtuple **pat, - char *buffer, size_t buflen, - int *errnop, int *h_errnop, - int32_t *ttlp); - _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL; @@ -275,15 +268,15 @@ enum nss_status _nss_resolve_gethostbyname4_r( return NSS_STATUS_SUCCESS; fallback: - fallback = (enum nss_status (*)(const char *name, - struct gaih_addrtuple **pat, - char *buffer, size_t buflen, - int *errnop, int *h_errnop, - int32_t *ttlp)) - find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname4_r"); + { + _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); + if (fallback) + return fallback(name, pat, buffer, buflen, errnop, h_errnop, ttlp); + } fail: *errnop = -r; @@ -300,15 +293,6 @@ enum nss_status _nss_resolve_gethostbyname3_r( int32_t *ttlp, char **canonp) { - enum nss_status (*fallback)( - const char *name, - int af, - struct hostent *result, - char *buffer, size_t buflen, - int *errnop, int *h_errnop, - int32_t *ttlp, - char **canonp); - _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; char *r_name, *r_aliases, *r_addr, *r_addr_list; @@ -480,16 +464,14 @@ enum nss_status _nss_resolve_gethostbyname3_r( return NSS_STATUS_SUCCESS; fallback: - fallback = (enum nss_status (*)(const char *name, - int af, - struct hostent *result, - char *buffer, size_t buflen, - int *errnop, int *h_errnop, - int32_t *ttlp, - char **canonp)) - find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyname3_r"); - if (fallback) - return fallback(name, af, result, buffer, buflen, errnop, h_errnop, ttlp, canonp); + { + _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; @@ -505,15 +487,6 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( int *errnop, int *h_errnop, int32_t *ttlp) { - enum nss_status (*fallback)( - const void* addr, socklen_t len, - int af, - struct hostent *result, - char *buffer, size_t buflen, - int *errnop, int *h_errnop, - int32_t *ttlp); - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; char *r_name, *r_aliases, *r_addr, *r_addr_list; @@ -682,17 +655,15 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( return NSS_STATUS_SUCCESS; fallback: - fallback = (enum nss_status (*)( - const void* addr, socklen_t len, - int af, - struct hostent *result, - char *buffer, size_t buflen, - int *errnop, int *h_errnop, - int32_t *ttlp)) - find_fallback("libnss_dns.so.2", "_nss_dns_gethostbyaddr2_r"); - - if (fallback) - return fallback(addr, len, af, result, buffer, buflen, errnop, h_errnop, ttlp); + { + _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; diff --git a/src/rc-local-generator/rc-local-generator.c b/src/rc-local-generator/rc-local-generator.c index 9e9c161993..618bbe428d 100644 --- a/src/rc-local-generator/rc-local-generator.c +++ b/src/rc-local-generator/rc-local-generator.c @@ -36,7 +36,7 @@ #define RC_LOCAL_SCRIPT_PATH_STOP "/sbin/halt.local" #endif -const char *arg_dest = "/tmp"; +static const char *arg_dest = "/tmp"; static int add_symlink(const char *service, const char *where) { _cleanup_free_ char *from = NULL, *to = NULL; diff --git a/src/reply-password/reply-password.c b/src/reply-password/reply-password.c index e291758969..17eab9772e 100644 --- a/src/reply-password/reply-password.c +++ b/src/reply-password/reply-password.c @@ -26,14 +26,12 @@ #include "fd-util.h" #include "log.h" #include "macro.h" +#include "socket-util.h" #include "string-util.h" #include "util.h" static int send_on_socket(int fd, const char *socket_name, const void *packet, size_t size) { - union { - struct sockaddr sa; - struct sockaddr_un un; - } sa = { + union sockaddr_union sa = { .un.sun_family = AF_UNIX, }; @@ -43,7 +41,7 @@ static int send_on_socket(int fd, const char *socket_name, const void *packet, s strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path)); - if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) + if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) return log_error_errno(errno, "Failed to send: %m"); return 0; diff --git a/src/resolve/RFCs b/src/resolve/RFCs index 22004a00cd..09c85f9518 100644 --- a/src/resolve/RFCs +++ b/src/resolve/RFCs @@ -8,7 +8,7 @@ D = Comprehensively Implemented, by a dependency of resolved Y https://tools.ietf.org/html/rfc1034 → DOMAIN NAMES - CONCEPTS AND FACILITIES Y https://tools.ietf.org/html/rfc1035 → DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION ? https://tools.ietf.org/html/rfc1101 → DNS Encoding of Network Names and Other Types -Y https://tools.ietf.org/html/rfc1123 → Requirements for Internet Hosts -- Application and Support +Y https://tools.ietf.org/html/rfc1123 → Requirements for Internet Hosts — Application and Support ~ https://tools.ietf.org/html/rfc1464 → Using the Domain Name System To Store Arbitrary String Attributes Y https://tools.ietf.org/html/rfc1536 → Common DNS Implementation Errors and Suggested Fixes Y https://tools.ietf.org/html/rfc1876 → A Means for Expressing Location Information in the Domain Name System diff --git a/src/resolve/dns-type.c b/src/resolve/dns-type.c index b2f479cae5..78d9d5733f 100644 --- a/src/resolve/dns-type.c +++ b/src/resolve/dns-type.c @@ -193,6 +193,23 @@ bool dns_type_is_obsolete(uint16_t type) { DNS_TYPE_NULL); } +bool dns_type_needs_authentication(uint16_t type) { + + /* Returns true for all (non-obsolete) RR types where records are not useful if they aren't + * authenticated. I.e. everything that contains crypto keys. */ + + return IN_SET(type, + DNS_TYPE_CERT, + DNS_TYPE_SSHFP, + DNS_TYPE_IPSECKEY, + DNS_TYPE_DS, + DNS_TYPE_DNSKEY, + DNS_TYPE_TLSA, + DNS_TYPE_CDNSKEY, + DNS_TYPE_OPENPGPKEY, + DNS_TYPE_CAA); +} + int dns_type_to_af(uint16_t t) { switch (t) { diff --git a/src/resolve/dns-type.h b/src/resolve/dns-type.h index a6c1630021..7b79d29d7e 100644 --- a/src/resolve/dns-type.h +++ b/src/resolve/dns-type.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - #include "macro.h" /* DNS record types, taken from @@ -124,6 +124,9 @@ enum { _DNS_CLASS_INVALID = -1 }; +#define _DNS_CLASS_STRING_MAX (sizeof "CLASS" + DECIMAL_STR_MAX(uint16_t)) +#define _DNS_TYPE_STRING_MAX (sizeof "CLASS" + DECIMAL_STR_MAX(uint16_t)) + bool dns_type_is_pseudo(uint16_t type); bool dns_type_is_valid_query(uint16_t type); bool dns_type_is_valid_rr(uint16_t type); @@ -132,7 +135,8 @@ bool dns_type_is_dnssec(uint16_t type); bool dns_type_is_obsolete(uint16_t type); bool dns_type_may_wildcard(uint16_t type); bool dns_type_apex_only(uint16_t type); -int dns_type_to_af(uint16_t t); +bool dns_type_needs_authentication(uint16_t type); +int dns_type_to_af(uint16_t type); bool dns_class_is_pseudo(uint16_t class); bool dns_class_is_valid_rr(uint16_t class); @@ -141,7 +145,7 @@ bool dns_class_is_valid_rr(uint16_t class); const char *dns_type_to_string(int type); int dns_type_from_string(const char *s); -const char *dns_class_to_string(uint16_t type); +const char *dns_class_to_string(uint16_t class); int dns_class_from_string(const char *name); /* https://tools.ietf.org/html/draft-ietf-dane-protocol-23#section-7.2 */ @@ -152,3 +156,6 @@ const char *tlsa_selector_to_string(uint8_t selector); /* https://tools.ietf.org/html/draft-ietf-dane-protocol-23#section-7.4 */ const char *tlsa_matching_type_to_string(uint8_t selector); + +/* https://tools.ietf.org/html/rfc6844#section-5.1 */ +#define CAA_FLAG_CRITICAL (1u << 7) diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c index 824cb267b5..14ee01c49d 100644 --- a/src/resolve/resolve-tool.c +++ b/src/resolve/resolve-tool.c @@ -28,6 +28,7 @@ #include "bus-util.h" #include "escape.h" #include "in-addr-util.h" +#include "gcrypt-util.h" #include "parse-util.h" #include "resolved-def.h" #include "resolved-dns-packet.h" @@ -42,14 +43,54 @@ static uint16_t arg_class = 0; static bool arg_legend = true; static uint64_t arg_flags = 0; +typedef enum ServiceFamily { + SERVICE_FAMILY_TCP, + SERVICE_FAMILY_UDP, + SERVICE_FAMILY_SCTP, + _SERVICE_FAMILY_INVALID = -1, +} ServiceFamily; +static ServiceFamily arg_service_family = SERVICE_FAMILY_TCP; + +typedef enum RawType { + RAW_NONE, + RAW_PAYLOAD, + RAW_PACKET, +} RawType; +static RawType arg_raw = RAW_NONE; + static enum { MODE_RESOLVE_HOST, MODE_RESOLVE_RECORD, MODE_RESOLVE_SERVICE, + MODE_RESOLVE_OPENPGP, + MODE_RESOLVE_TLSA, MODE_STATISTICS, MODE_RESET_STATISTICS, } arg_mode = MODE_RESOLVE_HOST; +static ServiceFamily service_family_from_string(const char *s) { + if (s == NULL || streq(s, "tcp")) + return SERVICE_FAMILY_TCP; + if (streq(s, "udp")) + return SERVICE_FAMILY_UDP; + if (streq(s, "sctp")) + return SERVICE_FAMILY_SCTP; + return _SERVICE_FAMILY_INVALID; +} + +static const char* service_family_to_string(ServiceFamily service) { + switch(service) { + case SERVICE_FAMILY_TCP: + return "_tcp"; + case SERVICE_FAMILY_UDP: + return "_udp"; + case SERVICE_FAMILY_SCTP: + return "_sctp"; + default: + assert_not_reached("invalid service"); + } +} + static void print_source(uint64_t flags, usec_t rtt) { char rtt_str[FORMAT_TIMESTAMP_MAX]; @@ -328,6 +369,50 @@ static int parse_address(const char *s, int *family, union in_addr_union *addres return 0; } +static int output_rr_packet(const void *d, size_t l, int ifindex) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + int r; + char ifname[IF_NAMESIZE] = ""; + + r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0); + if (r < 0) + return log_oom(); + + p->refuse_compression = true; + + r = dns_packet_append_blob(p, d, l, NULL); + if (r < 0) + return log_oom(); + + r = dns_packet_read_rr(p, &rr, NULL, NULL); + if (r < 0) + return log_error_errno(r, "Failed to parse RR: %m"); + + if (arg_raw == RAW_PAYLOAD) { + void *data; + ssize_t k; + + k = dns_resource_record_payload(rr, &data); + if (k < 0) + return log_error_errno(k, "Cannot dump RR: %m"); + fwrite(data, 1, k, stdout); + } else { + const char *s; + + s = dns_resource_record_to_string(rr); + if (!s) + return log_oom(); + + if (ifindex > 0 && !if_indextoname(ifindex, ifname)) + log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex); + + printf("%s%s%s\n", s, isempty(ifname) ? "" : " # interface ", ifname); + } + + return 0; +} + static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_t type) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL, *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -336,6 +421,7 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_ uint64_t flags; int r; usec_t ts; + bool needs_authentication = false; assert(name); @@ -373,9 +459,6 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_ return bus_log_parse_error(r); while ((r = sd_bus_message_enter_container(reply, 'r', "iqqay")) > 0) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - const char *s; uint16_t c, t; int ifindex; const void *d; @@ -395,29 +478,20 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_ if (r < 0) return bus_log_parse_error(r); - r = dns_packet_new(&p, DNS_PROTOCOL_DNS, 0); - if (r < 0) - return log_oom(); - - p->refuse_compression = true; + if (arg_raw == RAW_PACKET) { + uint64_t u64 = htole64(l); - r = dns_packet_append_blob(p, d, l, NULL); - if (r < 0) - return log_oom(); - - r = dns_packet_read_rr(p, &rr, NULL, NULL); - if (r < 0) - return log_error_errno(r, "Failed to parse RR: %m"); + fwrite(&u64, sizeof(u64), 1, stdout); + fwrite(d, 1, l, stdout); + } else { + r = output_rr_packet(d, l, ifindex); + if (r < 0) + return r; + } - s = dns_resource_record_to_string(rr); - if (!s) - return log_oom(); + if (dns_type_needs_authentication(t)) + needs_authentication = true; - ifname[0] = 0; - if (ifindex > 0 && !if_indextoname(ifindex, ifname)) - log_warning_errno(errno, "Failed to resolve interface name for index %i: %m", ifindex); - - printf("%s%s%s\n", s, isempty(ifname) ? "" : " # interface ", ifname); n++; } if (r < 0) @@ -438,6 +512,18 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_ print_source(flags, ts); + if ((flags & SD_RESOLVED_AUTHENTICATED) == 0 && needs_authentication) { + fflush(stdout); + + fprintf(stderr, "\n%s" + "WARNING: The resources shown contain cryptographic key data which could not be\n" + " authenticated. It is not suitable to authenticate any communication.\n" + " This is usually indication that DNSSEC authentication was not enabled\n" + " or is not available for the selected protocol or DNS servers.%s\n", + ansi_highlight_red(), + ansi_normal()); + } + return 0; } @@ -545,15 +631,10 @@ static int resolve_rfc4501(sd_bus *bus, const char *name) { } else n = p; - if (type == 0) - type = arg_type; - if (type == 0) - type = DNS_TYPE_A; - - if (class == 0) - class = arg_class; if (class == 0) - class = DNS_CLASS_IN; + class = arg_class ?: DNS_CLASS_IN; + if (type == 0) + type = arg_type ?: DNS_TYPE_A; return resolve_record(bus, n, class, type); @@ -763,6 +844,68 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons return 0; } +static int resolve_openpgp(sd_bus *bus, const char *address) { + const char *domain, *full; + int r; + _cleanup_free_ char *hashed = NULL; + + assert(bus); + assert(address); + + domain = strrchr(address, '@'); + if (!domain) { + log_error("Address does not contain '@': \"%s\"", address); + return -EINVAL; + } else if (domain == address || domain[1] == '\0') { + log_error("Address starts or ends with '@': \"%s\"", address); + return -EINVAL; + } + domain++; + + r = string_hashsum_sha224(address, domain - 1 - address, &hashed); + if (r < 0) + return log_error_errno(r, "Hashing failed: %m"); + + full = strjoina(hashed, "._openpgpkey.", domain); + log_debug("Looking up \"%s\".", full); + + return resolve_record(bus, full, + arg_class ?: DNS_CLASS_IN, + arg_type ?: DNS_TYPE_OPENPGPKEY); +} + +static int resolve_tlsa(sd_bus *bus, const char *address) { + const char *port; + uint16_t port_num = 443; + _cleanup_free_ char *full = NULL; + int r; + + assert(bus); + assert(address); + + port = strrchr(address, ':'); + if (port) { + r = safe_atou16(port + 1, &port_num); + if (r < 0 || port_num == 0) + return log_error_errno(r, "Invalid port \"%s\".", port + 1); + + address = strndupa(address, port - address); + } + + r = asprintf(&full, "_%u.%s.%s", + port_num, + service_family_to_string(arg_service_family), + address); + if (r < 0) + return log_oom(); + + log_debug("Looking up \"%s\".", full); + + return resolve_record(bus, full, + arg_class ?: DNS_CLASS_IN, + arg_type ?: DNS_TYPE_TLSA); +} + static int show_statistics(sd_bus *bus) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; @@ -931,26 +1074,34 @@ static void help_dns_classes(void) { } static void help(void) { - printf("%s [OPTIONS...] NAME...\n" - "%s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n\n" + printf("%1$s [OPTIONS...] HOSTNAME|ADDRESS...\n" + "%1$s [OPTIONS...] --service [[NAME] TYPE] DOMAIN\n" + "%1$s [OPTIONS...] --openpgp EMAIL@DOMAIN...\n" + "%1$s [OPTIONS...] --statistics\n" + "%1$s [OPTIONS...] --reset-statistics\n" + "\n" "Resolve domain names, IPv4 and IPv6 addresses, DNS resource records, and services.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " -4 Resolve IPv4 addresses\n" - " -6 Resolve IPv6 addresses\n" - " -i --interface=INTERFACE Look on interface\n" - " -p --protocol=PROTOCOL|help Look via protocol\n" - " -t --type=TYPE|help Query RR with DNS type\n" - " -c --class=CLASS|help Query RR with DNS class\n" - " --service Resolve service (SRV)\n" - " --service-address=BOOL Do [not] resolve address for services\n" - " --service-txt=BOOL Do [not] resolve TXT records for services\n" - " --cname=BOOL Do [not] follow CNAME redirects\n" - " --search=BOOL Do [not] use search domains\n" - " --legend=BOOL Do [not] print column headers and meta information\n" - " --statistics Show resolver statistics\n" - " --reset-statistics Reset resolver statistics\n" - , program_invocation_short_name, program_invocation_short_name); + " -h --help Show this help\n" + " --version Show package version\n" + " -4 Resolve IPv4 addresses\n" + " -6 Resolve IPv6 addresses\n" + " -i --interface=INTERFACE Look on interface\n" + " -p --protocol=PROTO|help Look via protocol\n" + " -t --type=TYPE|help Query RR with DNS type\n" + " -c --class=CLASS|help Query RR with DNS class\n" + " --service Resolve service (SRV)\n" + " --service-address=BOOL Resolve address for services (default: yes)\n" + " --service-txt=BOOL Resolve TXT records for services (default: yes)\n" + " --openpgp Query OpenPGP public key\n" + " --tlsa Query TLS public key\n" + " --cname=BOOL Follow CNAME redirects (default: yes)\n" + " --search=BOOL Use search domains for single-label names\n" + " (default: yes)\n" + " --raw[=payload|packet] Dump the answer as binary data\n" + " --legend=BOOL Print headers and additional info (default: yes)\n" + " --statistics Show resolver statistics\n" + " --reset-statistics Reset resolver statistics\n" + , program_invocation_short_name); } static int parse_argv(int argc, char *argv[]) { @@ -961,6 +1112,9 @@ static int parse_argv(int argc, char *argv[]) { ARG_CNAME, ARG_SERVICE_ADDRESS, ARG_SERVICE_TXT, + ARG_OPENPGP, + ARG_TLSA, + ARG_RAW, ARG_SEARCH, ARG_STATISTICS, ARG_RESET_STATISTICS, @@ -978,6 +1132,9 @@ static int parse_argv(int argc, char *argv[]) { { "service", no_argument, NULL, ARG_SERVICE }, { "service-address", required_argument, NULL, ARG_SERVICE_ADDRESS }, { "service-txt", required_argument, NULL, ARG_SERVICE_TXT }, + { "openpgp", no_argument, NULL, ARG_OPENPGP }, + { "tlsa", optional_argument, NULL, ARG_TLSA }, + { "raw", optional_argument, NULL, ARG_RAW }, { "search", required_argument, NULL, ARG_SEARCH }, { "statistics", no_argument, NULL, ARG_STATISTICS, }, { "reset-statistics", no_argument, NULL, ARG_RESET_STATISTICS }, @@ -1087,44 +1244,63 @@ static int parse_argv(int argc, char *argv[]) { arg_mode = MODE_RESOLVE_SERVICE; break; + case ARG_OPENPGP: + arg_mode = MODE_RESOLVE_OPENPGP; + break; + + case ARG_TLSA: + arg_mode = MODE_RESOLVE_TLSA; + arg_service_family = service_family_from_string(optarg); + if (arg_service_family < 0) { + log_error("Unknown service family \"%s\".", optarg); + return -EINVAL; + } + break; + + case ARG_RAW: + if (on_tty()) { + log_error("Refusing to write binary data to tty."); + return -ENOTTY; + } + + if (optarg == NULL || streq(optarg, "payload")) + arg_raw = RAW_PAYLOAD; + else if (streq(optarg, "packet")) + arg_raw = RAW_PACKET; + else { + log_error("Unknown --raw specifier \"%s\".", optarg); + return -EINVAL; + } + + arg_legend = false; + break; + case ARG_CNAME: r = parse_boolean(optarg); if (r < 0) return log_error_errno(r, "Failed to parse --cname= argument."); - if (r == 0) - arg_flags |= SD_RESOLVED_NO_CNAME; - else - arg_flags &= ~SD_RESOLVED_NO_CNAME; + SET_FLAG(arg_flags, SD_RESOLVED_NO_CNAME, r == 0); break; case ARG_SERVICE_ADDRESS: r = parse_boolean(optarg); if (r < 0) return log_error_errno(r, "Failed to parse --service-address= argument."); - if (r == 0) - arg_flags |= SD_RESOLVED_NO_ADDRESS; - else - arg_flags &= ~SD_RESOLVED_NO_ADDRESS; + SET_FLAG(arg_flags, SD_RESOLVED_NO_ADDRESS, r == 0); break; case ARG_SERVICE_TXT: r = parse_boolean(optarg); if (r < 0) return log_error_errno(r, "Failed to parse --service-txt= argument."); - if (r == 0) - arg_flags |= SD_RESOLVED_NO_TXT; - else - arg_flags &= ~SD_RESOLVED_NO_TXT; + SET_FLAG(arg_flags, SD_RESOLVED_NO_TXT, r == 0); break; case ARG_SEARCH: r = parse_boolean(optarg); if (r < 0) return log_error_errno(r, "Failed to parse --search argument."); - if (r == 0) - arg_flags |= SD_RESOLVED_NO_SEARCH; - else - arg_flags &= ~SD_RESOLVED_NO_SEARCH; + SET_FLAG(arg_flags, SD_RESOLVED_NO_SEARCH, r == 0); break; case ARG_STATISTICS: @@ -1147,7 +1323,7 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } - if (arg_type != 0 && arg_mode != MODE_RESOLVE_RECORD) { + if (arg_type != 0 && arg_mode == MODE_RESOLVE_SERVICE) { log_error("--service and --type= may not be combined."); return -EINVAL; } @@ -1246,6 +1422,42 @@ int main(int argc, char **argv) { break; + case MODE_RESOLVE_OPENPGP: + if (argc < optind + 1) { + log_error("E-mail address required."); + r = -EINVAL; + goto finish; + + } + + r = 0; + while (optind < argc) { + int k; + + k = resolve_openpgp(bus, argv[optind++]); + if (k < 0) + r = k; + } + break; + + case MODE_RESOLVE_TLSA: + if (argc < optind + 1) { + log_error("Domain name required."); + r = -EINVAL; + goto finish; + + } + + r = 0; + while (optind < argc) { + int k; + + k = resolve_tlsa(bus, argv[optind++]); + if (k < 0) + r = k; + } + break; + case MODE_STATISTICS: if (argc > optind) { log_error("Too many arguments."); diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index fc5e6beca0..33f7c61557 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -23,6 +23,7 @@ #include "dns-domain.h" #include "resolved-bus.h" #include "resolved-def.h" +#include "resolved-dns-synthesize.h" #include "resolved-link-bus.h" static int reply_query_state(DnsQuery *q) { @@ -139,6 +140,7 @@ static int append_address(sd_bus_message *reply, DnsResourceRecord *rr, int ifin static void bus_method_resolve_hostname_complete(DnsQuery *q) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_free_ char *normalized = NULL; DnsResourceRecord *rr; unsigned added = 0; int ifindex, r; @@ -186,7 +188,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { if (!canonical) canonical = dns_resource_record_ref(rr); - added ++; + added++; } if (added <= 0) { @@ -198,11 +200,17 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { if (r < 0) goto finish; + /* The key names are not necessarily normalized, make sure that they are when we return them to our bus + * clients. */ + r = dns_name_normalize(dns_resource_key_name(canonical->key), &normalized); + if (r < 0) + goto finish; + /* Return the precise spelling and uppercasing and CNAME target reported by the server */ assert(canonical); r = sd_bus_message_append( reply, "st", - DNS_RESOURCE_KEY_NAME(canonical->key), + normalized, SD_RESOLVED_FLAGS_MAKE(q->answer_protocol, q->answer_family, q->answer_authenticated)); if (r < 0) goto finish; @@ -233,6 +241,65 @@ static int check_ifindex_flags(int ifindex, uint64_t *flags, uint64_t ok, sd_bus return 0; } +static int parse_as_address(sd_bus_message *m, int ifindex, const char *hostname, int family, uint64_t flags) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_free_ char *canonical = NULL; + union in_addr_union parsed; + int r, ff; + + /* Check if the hostname is actually already an IP address formatted as string. In that case just parse it, + * let's not attempt to look it up. */ + + r = in_addr_from_string_auto(hostname, &ff, &parsed); + if (r < 0) /* not an address */ + return 0; + + if (family != AF_UNSPEC && ff != family) + return sd_bus_reply_method_errorf(m, BUS_ERROR_NO_SUCH_RR, "The specified address is not of the requested family."); + + r = sd_bus_message_new_method_return(m, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(iiay)"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'r', "iiay"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "ii", ifindex, ff); + if (r < 0) + return r; + + r = sd_bus_message_append_array(reply, 'y', &parsed, FAMILY_ADDRESS_SIZE(ff)); + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + /* When an IP address is specified we just return it as canonical name, in order to avoid a DNS + * look-up. However, we reformat it to make sure it's in a truly canonical form (i.e. on IPv6 the inner + * omissions are always done the same way). */ + r = in_addr_to_string(ff, &parsed, &canonical); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "st", canonical, + SD_RESOLVED_FLAGS_MAKE(dns_synthesize_protocol(flags), ff, true)); + if (r < 0) + return r; + + return sd_bus_send(sd_bus_message_get_bus(m), reply, NULL); +} + static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_(dns_question_unrefp) DnsQuestion *question_idna = NULL, *question_utf8 = NULL; Manager *m = userdata; @@ -254,15 +321,19 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, if (!IN_SET(family, AF_INET, AF_INET6, AF_UNSPEC)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown address family %i", family); - r = dns_name_is_valid(hostname); + r = check_ifindex_flags(ifindex, &flags, SD_RESOLVED_NO_SEARCH, error); if (r < 0) return r; - if (r == 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname); - r = check_ifindex_flags(ifindex, &flags, SD_RESOLVED_NO_SEARCH, error); + r = parse_as_address(message, ifindex, hostname, family, flags); + if (r != 0) + return r; + + r = dns_name_is_valid(hostname); if (r < 0) return r; + if (r == 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid hostname '%s'", hostname); r = dns_question_new_address(&question_utf8, family, hostname, false); if (r < 0) @@ -331,24 +402,31 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { question = dns_query_question_for_protocol(q, q->answer_protocol); DNS_ANSWER_FOREACH_IFINDEX(rr, ifindex, q->answer) { + _cleanup_free_ char *normalized = NULL; + r = dns_question_matches_rr(question, rr, NULL); if (r < 0) goto finish; if (r == 0) continue; - r = sd_bus_message_append(reply, "(is)", ifindex, rr->ptr.name); + r = dns_name_normalize(rr->ptr.name, &normalized); + if (r < 0) + goto finish; + + r = sd_bus_message_append(reply, "(is)", ifindex, normalized); if (r < 0) goto finish; - added ++; + added++; } if (added <= 0) { _cleanup_free_ char *ip = NULL; - in_addr_to_string(q->request_family, &q->request_address, &ip); - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", strna(ip)); + (void) in_addr_to_string(q->request_family, &q->request_address, &ip); + r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_RR, + "Address '%s' does not have any RR of requested type", strnull(ip)); goto finish; } @@ -510,7 +588,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { if (r < 0) goto finish; - added ++; + added++; } if (added <= 0) { @@ -607,6 +685,7 @@ fail: static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL; + _cleanup_free_ char *normalized = NULL; DnsQuery *aux; int r; @@ -663,10 +742,14 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) if (r < 0) return r; + r = dns_name_normalize(rr->srv.name, &normalized); + if (r < 0) + return r; + r = sd_bus_message_append( reply, "qqqs", - rr->srv.priority, rr->srv.weight, rr->srv.port, rr->srv.name); + rr->srv.priority, rr->srv.weight, rr->srv.port, normalized); if (r < 0) return r; @@ -712,9 +795,17 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) if (r < 0) return r; + if (canonical) { + normalized = mfree(normalized); + + r = dns_name_normalize(dns_resource_key_name(canonical->key), &normalized); + if (r < 0) + return r; + } + /* Note that above we appended the hostname as encoded in the * SRV, and here the canonical hostname this maps to. */ - r = sd_bus_message_append(reply, "s", canonical ? DNS_RESOURCE_KEY_NAME(canonical->key) : rr->srv.name); + r = sd_bus_message_append(reply, "s", normalized); if (r < 0) return r; @@ -869,7 +960,7 @@ static void resolve_service_all_complete(DnsQuery *q) { goto finish; assert(canonical); - r = dns_service_split(DNS_RESOURCE_KEY_NAME(canonical->key), &name, &type, &domain); + r = dns_service_split(dns_resource_key_name(canonical->key), &name, &type, &domain); if (r < 0) goto finish; @@ -1004,9 +1095,9 @@ static void bus_method_resolve_service_complete(DnsQuery *q) { } if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) { - q->block_all_complete ++; + q->block_all_complete++; r = resolve_service_hostname(q, rr, ifindex); - q->block_all_complete --; + q->block_all_complete--; if (r < 0) goto finish; @@ -1047,7 +1138,6 @@ finish: static int bus_method_resolve_service(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_(dns_question_unrefp) DnsQuestion *question_idna = NULL, *question_utf8 = NULL; const char *name, *type, *domain; - _cleanup_free_ char *n = NULL; Manager *m = userdata; int family, ifindex; uint64_t flags; @@ -1198,7 +1288,7 @@ static int bus_property_get_dns_servers( return sd_bus_message_close_container(reply); } -static int bus_property_get_search_domains( +static int bus_property_get_domains( sd_bus *bus, const char *path, const char *interface, @@ -1396,8 +1486,8 @@ static int bus_method_set_link_dns_servers(sd_bus_message *message, void *userda return call_link_method(userdata, message, bus_link_method_set_dns_servers, error); } -static int bus_method_set_link_search_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) { - return call_link_method(userdata, message, bus_link_method_set_search_domains, error); +static int bus_method_set_link_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return call_link_method(userdata, message, bus_link_method_set_domains, error); } static int bus_method_set_link_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -1449,7 +1539,7 @@ static const sd_bus_vtable resolve_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("LLMNRHostname", "s", NULL, offsetof(Manager, llmnr_hostname), 0), SD_BUS_PROPERTY("DNS", "a(iiay)", bus_property_get_dns_servers, 0, 0), - SD_BUS_PROPERTY("SearchDomains", "a(isb)", bus_property_get_search_domains, 0, 0), + SD_BUS_PROPERTY("Domains", "a(isb)", bus_property_get_domains, 0, 0), SD_BUS_PROPERTY("TransactionStatistics", "(tt)", bus_property_get_transaction_statistics, 0, 0), SD_BUS_PROPERTY("CacheStatistics", "(ttt)", bus_property_get_cache_statistics, 0, 0), SD_BUS_PROPERTY("DNSSECStatistics", "(tttt)", bus_property_get_dnssec_statistics, 0, 0), @@ -1462,7 +1552,7 @@ static const sd_bus_vtable resolve_vtable[] = { SD_BUS_METHOD("ResetStatistics", NULL, NULL, bus_method_reset_statistics, 0), SD_BUS_METHOD("GetLink", "i", "o", bus_method_get_link, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetLinkDNS", "ia(iay)", NULL, bus_method_set_link_dns_servers, 0), - SD_BUS_METHOD("SetLinkDomains", "ia(sb)", NULL, bus_method_set_link_search_domains, 0), + SD_BUS_METHOD("SetLinkDomains", "ia(sb)", NULL, bus_method_set_link_domains, 0), SD_BUS_METHOD("SetLinkLLMNR", "is", NULL, bus_method_set_link_llmnr, 0), SD_BUS_METHOD("SetLinkMulticastDNS", "is", NULL, bus_method_set_link_mdns, 0), SD_BUS_METHOD("SetLinkDNSSEC", "is", NULL, bus_method_set_link_dnssec, 0), diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index bb93fbfda2..990dc03b60 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -59,7 +59,7 @@ int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, con assert(m); assert(string); - for(;;) { + for (;;) { _cleanup_free_ char *word = NULL; r = extract_first_word(&string, &word, NULL, 0); @@ -114,7 +114,7 @@ int manager_parse_search_domains_and_warn(Manager *m, const char *string) { assert(m); assert(string); - for(;;) { + for (;;) { _cleanup_free_ char *word = NULL; r = extract_first_word(&string, &word, NULL, EXTRACT_QUOTES); diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c index 7eb303ab95..0dadf8b1dd 100644 --- a/src/resolve/resolved-dns-answer.c +++ b/src/resolve/resolved-dns-answer.c @@ -330,7 +330,7 @@ int dns_answer_contains_zone_nsec3(DnsAnswer *answer, const char *zone) { if (rr->key->type != DNS_TYPE_NSEC3) continue; - p = DNS_RESOURCE_KEY_NAME(rr->key); + p = dns_resource_key_name(rr->key); r = dns_name_parent(&p); if (r < 0) return r; @@ -363,7 +363,7 @@ int dns_answer_find_soa(DnsAnswer *a, const DnsResourceKey *key, DnsResourceReco if (r > 0) { if (soa) { - r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(soa->key)); + r = dns_name_endswith(dns_resource_key_name(rr->key), dns_resource_key_name(soa->key)); if (r < 0) return r; if (r > 0) @@ -538,7 +538,7 @@ int dns_answer_remove_by_key(DnsAnswer **a, const DnsResourceKey *key) { dns_resource_record_unref((*a)->items[i].rr); memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1)); - (*a)->n_rrs --; + (*a)->n_rrs--; continue; } else @@ -624,7 +624,7 @@ int dns_answer_remove_by_rr(DnsAnswer **a, DnsResourceRecord *rm) { dns_resource_record_unref((*a)->items[i].rr); memmove((*a)->items + i, (*a)->items + i + 1, sizeof(DnsAnswerItem) * ((*a)->n_rrs - i - 1)); - (*a)->n_rrs --; + (*a)->n_rrs--; continue; } else @@ -757,7 +757,7 @@ int dns_answer_reserve_or_clone(DnsAnswer **a, unsigned n_free) { assert(a); /* Tries to extend the DnsAnswer object. And if that's not - * possibly, since we are not the sole owner, then allocate a + * possible, since we are not the sole owner, then allocate a * new, appropriately sized one. Either way, after this call * the object will only have a single reference, and has room * for at least the specified number of RRs. */ @@ -840,13 +840,13 @@ bool dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname) { if (rr->key->class != cname->key->class) continue; - r = dns_name_change_suffix(cname->cname.name, rr->dname.name, DNS_RESOURCE_KEY_NAME(rr->key), &n); + r = dns_name_change_suffix(cname->cname.name, rr->dname.name, dns_resource_key_name(rr->key), &n); if (r < 0) return r; if (r == 0) continue; - r = dns_name_equal(n, DNS_RESOURCE_KEY_NAME(cname->key)); + r = dns_name_equal(n, dns_resource_key_name(cname->key)); if (r < 0) return r; if (r > 0) diff --git a/src/resolve/resolved-dns-answer.h b/src/resolve/resolved-dns-answer.h index 8f9c15eab4..0679c610f5 100644 --- a/src/resolve/resolved-dns-answer.h +++ b/src/resolve/resolved-dns-answer.h @@ -30,7 +30,7 @@ typedef struct DnsAnswerItem DnsAnswerItem; * can qualify A and AAAA RRs referring to a local link with the * right ifindex. * - * Note that we usually encode the the empty DnsAnswer object as a simple NULL. */ + * Note that we usually encode the empty DnsAnswer object as a simple NULL. */ typedef enum DnsAnswerFlags { DNS_ANSWER_AUTHENTICATED = 1, /* Item has been authenticated */ diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 9bcc71724e..77c42d7aad 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -17,6 +17,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <net/if.h> + +#include "af-list.h" #include "alloc-util.h" #include "dns-domain.h" #include "resolved-dns-answer.h" @@ -180,6 +183,7 @@ void dns_cache_prune(DnsCache *c) { for (;;) { DnsCacheItem *i; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; i = prioq_peek(c->by_expiry); if (!i) @@ -192,8 +196,12 @@ void dns_cache_prune(DnsCache *c) { break; /* Depending whether this is an mDNS shared entry - * either remove only this one RR or the whole - * RRset */ + * either remove only this one RR or the whole RRset */ + log_debug("Removing %scache entry for %s (expired "USEC_FMT"s ago)", + i->shared_owner ? "shared " : "", + dns_resource_key_to_string(i->key, key_str, sizeof key_str), + (t - i->until) / USEC_PER_SEC); + if (i->shared_owner) dns_cache_item_unlink_and_free(c, i); else { @@ -375,8 +383,8 @@ static int dns_cache_put_positive( const union in_addr_union *owner_address) { _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL; - _cleanup_free_ char *key_str = NULL; DnsCacheItem *existing; + char key_str[DNS_RESOURCE_KEY_STRING_MAX], ifname[IF_NAMESIZE]; int r, k; assert(c); @@ -392,18 +400,9 @@ static int dns_cache_put_positive( /* New TTL is 0? Delete this specific entry... */ if (rr->ttl <= 0) { k = dns_cache_remove_by_rr(c, rr); - - if (log_get_max_level() >= LOG_DEBUG) { - r = dns_resource_key_to_string(rr->key, &key_str); - if (r < 0) - return r; - - if (k > 0) - log_debug("Removed zero TTL entry from cache: %s", key_str); - else - log_debug("Not caching zero TTL cache entry: %s", key_str); - } - + log_debug("%s: %s", + k > 0 ? "Removed zero TTL entry from cache" : "Not caching zero TTL cache entry", + dns_resource_key_to_string(rr->key, key_str, sizeof key_str)); return 0; } @@ -450,11 +449,18 @@ static int dns_cache_put_positive( return r; if (log_get_max_level() >= LOG_DEBUG) { - r = dns_resource_key_to_string(i->key, &key_str); - if (r < 0) - return r; - - log_debug("Added positive cache entry for %s", key_str); + _cleanup_free_ char *t = NULL; + + (void) in_addr_to_string(i->owner_family, &i->owner_address, &t); + + log_debug("Added positive %s%s cache entry for %s "USEC_FMT"s on %s/%s/%s", + i->authenticated ? "authenticated" : "unauthenticated", + i->shared_owner ? " shared" : "", + dns_resource_key_to_string(i->key, key_str, sizeof key_str), + (i->until - timestamp) / USEC_PER_SEC, + i->ifindex == 0 ? "*" : strna(if_indextoname(i->ifindex, ifname)), + af_to_name_short(i->owner_family), + strna(t)); } i = NULL; @@ -473,7 +479,7 @@ static int dns_cache_put_negative( const union in_addr_union *owner_address) { _cleanup_(dns_cache_item_freep) DnsCacheItem *i = NULL; - _cleanup_free_ char *key_str = NULL; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; int r; assert(c); @@ -490,14 +496,8 @@ static int dns_cache_put_negative( return 0; if (nsec_ttl <= 0 || soa->soa.minimum <= 0 || soa->ttl <= 0) { - if (log_get_max_level() >= LOG_DEBUG) { - r = dns_resource_key_to_string(key, &key_str); - if (r < 0) - return r; - - log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s", key_str); - } - + log_debug("Not caching negative entry with zero SOA/NSEC/NSEC3 TTL: %s", + dns_resource_key_to_string(key, key_str, sizeof key_str)); return 0; } @@ -524,7 +524,7 @@ static int dns_cache_put_negative( if (i->type == DNS_CACHE_NXDOMAIN) { /* NXDOMAIN entries should apply equally to all types, so we use ANY as * a pseudo type for this purpose here. */ - i->key = dns_resource_key_new(key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(key)); + i->key = dns_resource_key_new(key->class, DNS_TYPE_ANY, dns_resource_key_name(key)); if (!i->key) return -ENOMEM; @@ -542,13 +542,10 @@ static int dns_cache_put_negative( if (r < 0) return r; - if (log_get_max_level() >= LOG_DEBUG) { - r = dns_resource_key_to_string(i->key, &key_str); - if (r < 0) - return r; - - log_debug("Added %s cache entry for %s", i->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", key_str); - } + log_debug("Added %s cache entry for %s "USEC_FMT"s", + i->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", + dns_resource_key_to_string(i->key, key_str, sizeof key_str), + (i->until - timestamp) / USEC_PER_SEC); i = NULL; return 0; @@ -628,16 +625,10 @@ int dns_cache_put( dns_cache_remove_previous(c, key, answer); if (dns_answer_size(answer) <= 0) { - if (log_get_max_level() >= LOG_DEBUG) { - _cleanup_free_ char *key_str = NULL; - - r = dns_resource_key_to_string(key, &key_str); - if (r < 0) - return r; - - log_debug("Not caching negative entry without a SOA record: %s", key_str); - } + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; + log_debug("Not caching negative entry without a SOA record: %s", + dns_resource_key_to_string(key, key_str, sizeof key_str)); return 0; } @@ -649,7 +640,7 @@ int dns_cache_put( cache_keys = dns_answer_size(answer); if (key) - cache_keys ++; + cache_keys++; /* Make some space for our new entries */ dns_cache_make_space(c, cache_keys); @@ -759,7 +750,7 @@ static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, D if (i) return i; - n = DNS_RESOURCE_KEY_NAME(k); + n = dns_resource_key_name(k); /* Check if we have an NXDOMAIN cache item for the name, notice that we use * the pseudo-type ANY for NXDOMAIN cache items. */ @@ -801,10 +792,10 @@ static DnsCacheItem *dns_cache_get_by_key_follow_cname_dname_nsec(DnsCache *c, D int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **ret, bool *authenticated) { _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; unsigned n = 0; int r; bool nxdomain = false; - _cleanup_free_ char *key_str = NULL; DnsCacheItem *j, *first, *nsec = NULL; bool have_authenticated = false, have_non_authenticated = false; @@ -814,19 +805,12 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r assert(ret); assert(authenticated); - if (key->type == DNS_TYPE_ANY || - key->class == DNS_CLASS_ANY) { - + if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) { /* If we have ANY lookups we don't use the cache, so * that the caller refreshes via the network. */ - if (log_get_max_level() >= LOG_DEBUG) { - r = dns_resource_key_to_string(key, &key_str); - if (r < 0) - return r; - - log_debug("Ignoring cache for ANY lookup: %s", key_str); - } + log_debug("Ignoring cache for ANY lookup: %s", + dns_resource_key_to_string(key, key_str, sizeof key_str)); c->n_miss++; @@ -839,13 +823,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r if (!first) { /* If one question cannot be answered we need to refresh */ - if (log_get_max_level() >= LOG_DEBUG) { - r = dns_resource_key_to_string(key, &key_str); - if (r < 0) - return r; - - log_debug("Cache miss for %s", key_str); - } + log_debug("Cache miss for %s", + dns_resource_key_to_string(key, key_str, sizeof key_str)); c->n_miss++; @@ -873,13 +852,8 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r /* Note that we won't derive information for DS RRs from an NSEC, because we only cache NSEC RRs from * the lower-zone of a zone cut, but the DS RRs are on the upper zone. */ - if (log_get_max_level() >= LOG_DEBUG) { - r = dns_resource_key_to_string(key, &key_str); - if (r < 0) - return r; - - log_debug("NSEC NODATA cache hit for %s", key_str); - } + log_debug("NSEC NODATA cache hit for %s", + dns_resource_key_to_string(key, key_str, sizeof key_str)); /* We only found an NSEC record that matches our name. * If it says the type doesn't exist report @@ -900,16 +874,10 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r return 0; } - if (log_get_max_level() >= LOG_DEBUG) { - r = dns_resource_key_to_string(key, &key_str); - if (r < 0) - return r; - - log_debug("%s cache hit for %s", - n > 0 ? "Positive" : - nxdomain ? "NXDOMAIN" : "NODATA", - key_str); - } + log_debug("%s cache hit for %s", + n > 0 ? "Positive" : + nxdomain ? "NXDOMAIN" : "NODATA", + dns_resource_key_to_string(key, key_str, sizeof key_str)); if (n <= 0) { c->n_hit++; @@ -1019,7 +987,7 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) { if (r < 0) return r; - ancount ++; + ancount++; } } @@ -1031,7 +999,6 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) { void dns_cache_dump(DnsCache *cache, FILE *f) { Iterator iterator; DnsCacheItem *i; - int r; if (!cache) return; @@ -1057,14 +1024,9 @@ void dns_cache_dump(DnsCache *cache, FILE *f) { fputs(t, f); fputc('\n', f); } else { - _cleanup_free_ char *z = NULL; - r = dns_resource_key_to_string(j->key, &z); - if (r < 0) { - log_oom(); - continue; - } + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; - fputs(z, f); + fputs(dns_resource_key_to_string(j->key, key_str, sizeof key_str), f); fputs(" -- ", f); fputs(j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", f); fputc('\n', f); diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 7123d2d3a8..a54aed3a63 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -23,6 +23,7 @@ #include "alloc-util.h" #include "dns-domain.h" +#include "gcrypt-util.h" #include "hexdecoct.h" #include "resolved-dns-dnssec.h" #include "resolved-dns-packet.h" @@ -126,19 +127,6 @@ int dnssec_canonicalize(const char *n, char *buffer, size_t buffer_max) { #ifdef HAVE_GCRYPT -static void initialize_libgcrypt(void) { - const char *p; - - if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) - return; - - p = gcry_check_version("1.4.5"); - assert(p); - - gcry_control(GCRYCTL_DISABLE_SECMEM); - gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); -} - static int rr_compare(const void *a, const void *b) { DnsResourceRecord **x = (DnsResourceRecord**) a, **y = (DnsResourceRecord**) b; size_t m; @@ -479,7 +467,7 @@ static int dnssec_rrsig_prepare(DnsResourceRecord *rrsig) { if (rrsig->rrsig.inception > rrsig->rrsig.expiration) return -EINVAL; - name = DNS_RESOURCE_KEY_NAME(rrsig->key); + name = dns_resource_key_name(rrsig->key); n_key_labels = dns_name_count_labels(name); if (n_key_labels < 0) @@ -635,9 +623,9 @@ int dnssec_verify_rrset( assert(rrsig->key->type == DNS_TYPE_RRSIG); assert(dnskey->key->type == DNS_TYPE_DNSKEY); - /* Verifies the the RRSet matching the specified "key" in "a", + /* Verifies that the RRSet matches the specified "key" in "a", * using the signature "rrsig" and the key "dnskey". It's - * assumed the RRSIG and DNSKEY match. */ + * assumed that RRSIG and DNSKEY match. */ md_algorithm = algorithm_to_gcrypt_md(rrsig->rrsig.algorithm); if (md_algorithm == -EOPNOTSUPP) { @@ -663,7 +651,7 @@ int dnssec_verify_rrset( return 0; } - name = DNS_RESOURCE_KEY_NAME(key); + name = dns_resource_key_name(key); /* Some keys may only appear signed in the zone apex, and are invalid anywhere else. (SOA, NS...) */ if (dns_type_apex_only(rrsig->rrsig.type_covered)) { @@ -737,7 +725,7 @@ int dnssec_verify_rrset( qsort_safe(list, n, sizeof(DnsResourceRecord*), rr_compare); /* OK, the RRs are now in canonical order. Let's calculate the digest */ - initialize_libgcrypt(); + initialize_libgcrypt(false); hash_size = gcry_md_get_algo_dlen(md_algorithm); assert(hash_size > 0); @@ -863,7 +851,7 @@ int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnske if (dnssec_keytag(dnskey, false) != rrsig->rrsig.key_tag) return 0; - return dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), rrsig->rrsig.signer); + return dns_name_equal(dns_resource_key_name(dnskey->key), rrsig->rrsig.signer); } int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) { @@ -879,7 +867,7 @@ int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig) if (rrsig->rrsig.type_covered != key->type) return 0; - return dns_name_equal(DNS_RESOURCE_KEY_NAME(rrsig->key), DNS_RESOURCE_KEY_NAME(key)); + return dns_name_equal(dns_resource_key_name(rrsig->key), dns_resource_key_name(key)); } int dnssec_verify_rrset_search( @@ -1070,7 +1058,7 @@ int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, if (dnssec_keytag(dnskey, mask_revoke) != ds->ds.key_tag) return 0; - initialize_libgcrypt(); + initialize_libgcrypt(false); md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type); if (md_algorithm < 0) @@ -1082,7 +1070,7 @@ int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, if (ds->ds.digest_size != hash_size) return 0; - r = dnssec_canonicalize(DNS_RESOURCE_KEY_NAME(dnskey->key), owner_name, sizeof(owner_name)); + r = dnssec_canonicalize(dns_resource_key_name(dnskey->key), owner_name, sizeof(owner_name)); if (r < 0) return r; @@ -1132,7 +1120,7 @@ int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *vali if (ds->key->class != dnskey->key->class) continue; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dnskey->key), DNS_RESOURCE_KEY_NAME(ds->key)); + r = dns_name_equal(dns_resource_key_name(dnskey->key), dns_resource_key_name(ds->key)); if (r < 0) return r; if (r == 0) @@ -1189,7 +1177,7 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) { if (algorithm < 0) return algorithm; - initialize_libgcrypt(); + initialize_libgcrypt(false); hash_size = gcry_md_get_algo_dlen(algorithm); assert(hash_size > 0); @@ -1284,14 +1272,14 @@ static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) { if (memcmp(rr->nsec3.salt, nsec3->nsec3.salt, rr->nsec3.salt_size) != 0) return 0; - a = DNS_RESOURCE_KEY_NAME(rr->key); + a = dns_resource_key_name(rr->key); r = dns_name_parent(&a); /* strip off hash */ if (r < 0) return r; if (r == 0) return 0; - b = DNS_RESOURCE_KEY_NAME(nsec3->key); + b = dns_resource_key_name(nsec3->key); r = dns_name_parent(&b); /* strip off hash */ if (r < 0) return r; @@ -1365,7 +1353,7 @@ static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecR * any NSEC3 RR in the response. Any NSEC3 record will do as all NSEC3 * records from a given zone in a response must use the same * parameters. */ - zone = DNS_RESOURCE_KEY_NAME(key); + zone = dns_resource_key_name(key); for (;;) { DNS_ANSWER_FOREACH_FLAGS(zone_rr, flags, answer) { r = nsec3_is_good(zone_rr, NULL); @@ -1374,7 +1362,7 @@ static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecR if (r == 0) continue; - r = dns_name_equal_skip(DNS_RESOURCE_KEY_NAME(zone_rr->key), 1, zone); + r = dns_name_equal_skip(dns_resource_key_name(zone_rr->key), 1, zone); if (r < 0) return r; if (r > 0) @@ -1394,7 +1382,7 @@ static int dnssec_test_nsec3(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecR found_zone: /* Second step, find the closest encloser NSEC3 RR in 'answer' that matches 'key' */ - p = DNS_RESOURCE_KEY_NAME(key); + p = dns_resource_key_name(key); for (;;) { _cleanup_free_ char *hashed_domain = NULL; @@ -1417,7 +1405,7 @@ found_zone: if (enclosure_rr->nsec3.next_hashed_name_size != (size_t) hashed_size) continue; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(enclosure_rr->key), hashed_domain); + r = dns_name_equal(dns_resource_key_name(enclosure_rr->key), hashed_domain); if (r < 0) return r; if (r > 0) { @@ -1516,7 +1504,7 @@ found_closest_encloser: if (r < 0) return r; - r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), next_closer_domain, next_hashed_domain); + r = dns_name_between(dns_resource_key_name(rr->key), next_closer_domain, next_hashed_domain); if (r < 0) return r; if (r > 0) { @@ -1528,7 +1516,7 @@ found_closest_encloser: no_closer = true; } - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain); + r = dns_name_equal(dns_resource_key_name(rr->key), wildcard_domain); if (r < 0) return r; if (r > 0) { @@ -1537,7 +1525,7 @@ found_closest_encloser: wildcard_rr = rr; } - r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wildcard_domain, next_hashed_domain); + r = dns_name_between(dns_resource_key_name(rr->key), wildcard_domain, next_hashed_domain); if (r < 0) return r; if (r > 0) { @@ -1616,7 +1604,7 @@ static int dnssec_nsec_wildcard_equal(DnsResourceRecord *rr, const char *name) { if (rr->n_skip_labels_source != 1) return 0; - n = DNS_RESOURCE_KEY_NAME(rr->key); + n = dns_resource_key_name(rr->key); r = dns_label_unescape(&n, label, sizeof(label)); if (r <= 0) return r; @@ -1655,7 +1643,7 @@ static int dnssec_nsec_in_path(DnsResourceRecord *rr, const char *name) { return r; /* If the name we we are interested in is not a prefix of the common suffix of the NSEC RR's owner and next domain names, then we can't say anything either. */ - r = dns_name_common_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rr->nsec.next_domain_name, &common_suffix); + r = dns_name_common_suffix(dns_resource_key_name(rr->key), rr->nsec.next_domain_name, &common_suffix); if (r < 0) return r; @@ -1674,7 +1662,7 @@ static int dnssec_nsec_from_parent_zone(DnsResourceRecord *rr, const char *name) if (r <= 0) return r; - r = dns_name_equal(name, DNS_RESOURCE_KEY_NAME(rr->key)); + r = dns_name_equal(name, dns_resource_key_name(rr->key)); if (r <= 0) return r; @@ -1697,7 +1685,7 @@ static int dnssec_nsec_covers(DnsResourceRecord *rr, const char *name) { /* Checks whether the "Next Closer" is witin the space covered by the specified RR. */ - r = dns_name_common_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rr->nsec.next_domain_name, &common_suffix); + r = dns_name_common_suffix(dns_resource_key_name(rr->key), rr->nsec.next_domain_name, &common_suffix); if (r < 0) return r; @@ -1718,7 +1706,7 @@ static int dnssec_nsec_covers(DnsResourceRecord *rr, const char *name) { /* p is now the "Next Closer". */ - return dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), p, rr->nsec.next_domain_name); + return dns_name_between(dns_resource_key_name(rr->key), p, rr->nsec.next_domain_name); } static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) { @@ -1737,7 +1725,7 @@ static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) * NSEC yyy.zzz.xoo.bar → bar: indicates that a number of wildcards don#t exist either... */ - r = dns_name_common_suffix(DNS_RESOURCE_KEY_NAME(rr->key), rr->nsec.next_domain_name, &common_suffix); + r = dns_name_common_suffix(dns_resource_key_name(rr->key), rr->nsec.next_domain_name, &common_suffix); if (r < 0) return r; @@ -1746,8 +1734,8 @@ static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) if (r <= 0) return r; - wc = strjoina("*.", common_suffix, NULL); - return dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), wc, rr->nsec.next_domain_name); + wc = strjoina("*.", common_suffix); + return dns_name_between(dns_resource_key_name(rr->key), wc, rr->nsec.next_domain_name); } int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) { @@ -1762,7 +1750,7 @@ int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r /* Look for any NSEC/NSEC3 RRs that say something about the specified key. */ - name = DNS_RESOURCE_KEY_NAME(key); + name = dns_resource_key_name(key); DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) { @@ -1782,7 +1770,7 @@ int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r continue; /* Check if this is a direct match. If so, we have encountered a NODATA case */ - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), name); + r = dns_name_equal(dns_resource_key_name(rr->key), name); if (r < 0) return r; if (r == 0) { @@ -1912,7 +1900,7 @@ static int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const cha if (r == 0) continue; - r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), name, rr->nsec.next_domain_name); + r = dns_name_between(dns_resource_key_name(rr->key), name, rr->nsec.next_domain_name); if (r < 0) return r; @@ -1955,7 +1943,7 @@ static int dnssec_nsec_test_enclosed(DnsAnswer *answer, uint16_t type, const cha if (r < 0) return r; - r = dns_name_between(DNS_RESOURCE_KEY_NAME(rr->key), hashed_domain, next_hashed_domain); + r = dns_name_between(dns_resource_key_name(rr->key), hashed_domain, next_hashed_domain); if (r < 0) return r; @@ -1989,7 +1977,7 @@ static int dnssec_test_positive_wildcard_nsec3( /* Run a positive NSEC3 wildcard proof. Specifically: * - * A proof that the the "next closer" of the generating wildcard does not exist. + * A proof that the "next closer" of the generating wildcard does not exist. * * Note a key difference between the NSEC3 and NSEC versions of the proof. NSEC RRs don't have to exist for * empty non-transients. NSEC3 RRs however have to. This means it's sufficient to check if the next closer name diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index c940dd8929..b7907bb511 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -28,6 +28,19 @@ #define EDNS0_OPT_DO (1<<15) +typedef struct DnsPacketRewinder { + DnsPacket *packet; + size_t saved_rindex; +} DnsPacketRewinder; + +static void rewind_dns_packet(DnsPacketRewinder *rewinder) { + if (rewinder->packet) + dns_packet_rewind(rewinder->packet, rewinder->saved_rindex); +} + +#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) { DnsPacket *p; size_t a; @@ -431,8 +444,7 @@ int dns_packet_append_raw_string(DnsPacket *p, const void *s, size_t size, size_ ((uint8_t*) d)[0] = (uint8_t) size; - if (size > 0) - memcpy(((uint8_t*) d) + 1, s, size); + memcpy_safe(((uint8_t*) d) + 1, s, size); return 0; } @@ -565,7 +577,7 @@ int dns_packet_append_key(DnsPacket *p, const DnsResourceKey *k, size_t *start) saved_size = p->size; - r = dns_packet_append_name(p, DNS_RESOURCE_KEY_NAME(k), true, true, NULL); + r = dns_packet_append_name(p, dns_resource_key_name(k), true, true, NULL); if (r < 0) goto fail; @@ -1072,6 +1084,18 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star r = dns_packet_append_blob(p, rr->tlsa.data, rr->tlsa.data_size, NULL); break; + case DNS_TYPE_CAA: + r = dns_packet_append_uint8(p, rr->caa.flags, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_string(p, rr->caa.tag, NULL); + if (r < 0) + goto fail; + + r = dns_packet_append_blob(p, rr->caa.value, rr->caa.value_size, NULL); + break; + case DNS_TYPE_OPT: case DNS_TYPE_OPENPGPKEY: case _DNS_TYPE_INVALID: /* unparseable */ @@ -1230,80 +1254,67 @@ int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) { } int dns_packet_read_string(DnsPacket *p, char **ret, size_t *start) { - size_t saved_rindex; + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; const void *d; char *t; uint8_t c; int r; assert(p); - - saved_rindex = p->rindex; + INIT_REWINDER(rewinder, p); r = dns_packet_read_uint8(p, &c, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read(p, c, &d, NULL); if (r < 0) - goto fail; + return r; - if (memchr(d, 0, c)) { - r = -EBADMSG; - goto fail; - } + if (memchr(d, 0, c)) + return -EBADMSG; t = strndup(d, c); - if (!t) { - r = -ENOMEM; - goto fail; - } + if (!t) + return -ENOMEM; if (!utf8_is_valid(t)) { free(t); - r = -EBADMSG; - goto fail; + return -EBADMSG; } *ret = t; if (start) - *start = saved_rindex; + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); return 0; - -fail: - dns_packet_rewind(p, saved_rindex); - return r; } int dns_packet_read_raw_string(DnsPacket *p, const void **ret, size_t *size, size_t *start) { - size_t saved_rindex; + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; uint8_t c; int r; assert(p); - - saved_rindex = p->rindex; + INIT_REWINDER(rewinder, p); r = dns_packet_read_uint8(p, &c, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read(p, c, ret, NULL); if (r < 0) - goto fail; + return r; if (size) *size = c; if (start) - *start = saved_rindex; + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); return 0; - -fail: - dns_packet_rewind(p, saved_rindex); - return r; } int dns_packet_read_name( @@ -1312,7 +1323,8 @@ int dns_packet_read_name( bool allow_compression, size_t *start) { - size_t saved_rindex, after_rindex = 0, jump_barrier; + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; + size_t after_rindex = 0, jump_barrier; _cleanup_free_ char *ret = NULL; size_t n = 0, allocated = 0; bool first = true; @@ -1320,19 +1332,18 @@ int dns_packet_read_name( assert(p); assert(_ret); + INIT_REWINDER(rewinder, p); + jump_barrier = p->rindex; if (p->refuse_compression) allow_compression = false; - saved_rindex = p->rindex; - jump_barrier = p->rindex; - for (;;) { uint8_t c, d; r = dns_packet_read_uint8(p, &c, NULL); if (r < 0) - goto fail; + return r; if (c == 0) /* End of name */ @@ -1343,12 +1354,10 @@ int dns_packet_read_name( /* Literal label */ r = dns_packet_read(p, c, (const void**) &label, NULL); if (r < 0) - goto fail; + return r; - if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) { - r = -ENOMEM; - goto fail; - } + if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) + return -ENOMEM; if (first) first = false; @@ -1357,7 +1366,7 @@ int dns_packet_read_name( r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); if (r < 0) - goto fail; + return r; n += r; continue; @@ -1367,13 +1376,11 @@ int dns_packet_read_name( /* Pointer */ r = dns_packet_read_uint8(p, &d, NULL); if (r < 0) - goto fail; + return r; ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d; - if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= jump_barrier) { - r = -EBADMSG; - goto fail; - } + if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= jump_barrier) + return -EBADMSG; if (after_rindex == 0) after_rindex = p->rindex; @@ -1381,16 +1388,12 @@ int dns_packet_read_name( /* Jumps are limited to a "prior occurrence" (RFC-1035 4.1.4) */ jump_barrier = ptr; p->rindex = ptr; - } else { - r = -EBADMSG; - goto fail; - } + } else + return -EBADMSG; } - if (!GREEDY_REALLOC(ret, allocated, n + 1)) { - r = -ENOMEM; - goto fail; - } + if (!GREEDY_REALLOC(ret, allocated, n + 1)) + return -ENOMEM; ret[n] = 0; @@ -1401,13 +1404,10 @@ int dns_packet_read_name( ret = NULL; if (start) - *start = saved_rindex; + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); return 0; - -fail: - dns_packet_rewind(p, saved_rindex); - return r; } static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *start) { @@ -1417,32 +1417,31 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta uint8_t bit = 0; unsigned i; bool found = false; - size_t saved_rindex; + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; int r; assert(p); assert(types); - - saved_rindex = p->rindex; + INIT_REWINDER(rewinder, p); r = bitmap_ensure_allocated(types); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &window, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &length, NULL); if (r < 0) - goto fail; + return r; if (length == 0 || length > 32) return -EBADMSG; r = dns_packet_read(p, length, (const void **)&bitmap, NULL); if (r < 0) - goto fail; + return r; for (i = 0; i < length; i++) { uint8_t bitmask = 1 << 7; @@ -1467,10 +1466,10 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta r = bitmap_set(*types, n); if (r < 0) - goto fail; + return r; } - bit ++; + bit++; bitmask >>= 1; } } @@ -1479,70 +1478,61 @@ static int dns_packet_read_type_window(DnsPacket *p, Bitmap **types, size_t *sta return -EBADMSG; if (start) - *start = saved_rindex; + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); return 0; -fail: - dns_packet_rewind(p, saved_rindex); - return r; } static int dns_packet_read_type_windows(DnsPacket *p, Bitmap **types, size_t size, size_t *start) { - size_t saved_rindex; + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; int r; - saved_rindex = p->rindex; + INIT_REWINDER(rewinder, p); - while (p->rindex < saved_rindex + size) { + while (p->rindex < rewinder.saved_rindex + size) { r = dns_packet_read_type_window(p, types, NULL); if (r < 0) - goto fail; + return r; /* don't read past end of current RR */ - if (p->rindex > saved_rindex + size) { - r = -EBADMSG; - goto fail; - } + if (p->rindex > rewinder.saved_rindex + size) + return -EBADMSG; } - if (p->rindex != saved_rindex + size) { - r = -EBADMSG; - goto fail; - } + if (p->rindex != rewinder.saved_rindex + size) + return -EBADMSG; if (start) - *start = saved_rindex; + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); return 0; -fail: - dns_packet_rewind(p, saved_rindex); - return r; } int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flush, size_t *start) { + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; _cleanup_free_ char *name = NULL; bool cache_flush = false; uint16_t class, type; DnsResourceKey *key; - size_t saved_rindex; int r; assert(p); assert(ret); - - saved_rindex = p->rindex; + INIT_REWINDER(rewinder, p); r = dns_packet_read_name(p, &name, true, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint16(p, &type, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint16(p, &class, NULL); if (r < 0) - goto fail; + return r; if (p->protocol == DNS_PROTOCOL_MDNS) { /* See RFC6762, Section 10.2 */ @@ -1554,10 +1544,8 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flus } key = dns_resource_key_new_consume(class, type, name); - if (!key) { - r = -ENOMEM; - goto fail; - } + if (!key) + return -ENOMEM; name = NULL; *ret = key; @@ -1565,12 +1553,10 @@ int dns_packet_read_key(DnsPacket *p, DnsResourceKey **ret, bool *ret_cache_flus if (ret_cache_flush) *ret_cache_flush = cache_flush; if (start) - *start = saved_rindex; + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); return 0; -fail: - dns_packet_rewind(p, saved_rindex); - return r; } static bool loc_size_ok(uint8_t size) { @@ -1582,7 +1568,8 @@ static bool loc_size_ok(uint8_t size) { int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_flush, size_t *start) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - size_t saved_rindex, offset; + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder; + size_t offset; uint16_t rdlength; bool cache_flush; int r; @@ -1590,27 +1577,22 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl assert(p); assert(ret); - saved_rindex = p->rindex; + INIT_REWINDER(rewinder, p); r = dns_packet_read_key(p, &key, &cache_flush, NULL); if (r < 0) - goto fail; + return r; - if (!dns_class_is_valid_rr(key->class)|| - !dns_type_is_valid_rr(key->type)) { - r = -EBADMSG; - goto fail; - } + if (!dns_class_is_valid_rr(key->class) || !dns_type_is_valid_rr(key->type)) + return -EBADMSG; rr = dns_resource_record_new(key); - if (!rr) { - r = -ENOMEM; - goto fail; - } + if (!rr) + return -ENOMEM; r = dns_packet_read_uint32(p, &rr->ttl, NULL); if (r < 0) - goto fail; + return r; /* RFC 2181, Section 8, suggests to * treat a TTL with the MSB set as a zero TTL. */ @@ -1619,12 +1601,10 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl r = dns_packet_read_uint16(p, &rdlength, NULL); if (r < 0) - goto fail; + return r; - if (p->rindex + rdlength > p->size) { - r = -EBADMSG; - goto fail; - } + if (p->rindex + rdlength > p->size) + return -EBADMSG; offset = p->rindex; @@ -1633,13 +1613,13 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl case DNS_TYPE_SRV: r = dns_packet_read_uint16(p, &rr->srv.priority, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint16(p, &rr->srv.weight, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint16(p, &rr->srv.port, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_name(p, &rr->srv.name, true, NULL); break; @@ -1653,7 +1633,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl case DNS_TYPE_HINFO: r = dns_packet_read_string(p, &rr->hinfo.cpu, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_string(p, &rr->hinfo.os, NULL); break; @@ -1709,27 +1689,27 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl case DNS_TYPE_SOA: r = dns_packet_read_name(p, &rr->soa.mname, true, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_name(p, &rr->soa.rname, true, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint32(p, &rr->soa.serial, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint32(p, &rr->soa.refresh, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint32(p, &rr->soa.retry, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint32(p, &rr->soa.expire, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint32(p, &rr->soa.minimum, NULL); break; @@ -1737,7 +1717,7 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl case DNS_TYPE_MX: r = dns_packet_read_uint16(p, &rr->mx.priority, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_name(p, &rr->mx.exchange, true, NULL); break; @@ -1748,49 +1728,43 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl r = dns_packet_read_uint8(p, &t, &pos); if (r < 0) - goto fail; + return r; if (t == 0) { rr->loc.version = t; r = dns_packet_read_uint8(p, &rr->loc.size, NULL); if (r < 0) - goto fail; + return r; - if (!loc_size_ok(rr->loc.size)) { - r = -EBADMSG; - goto fail; - } + if (!loc_size_ok(rr->loc.size)) + return -EBADMSG; r = dns_packet_read_uint8(p, &rr->loc.horiz_pre, NULL); if (r < 0) - goto fail; + return r; - if (!loc_size_ok(rr->loc.horiz_pre)) { - r = -EBADMSG; - goto fail; - } + if (!loc_size_ok(rr->loc.horiz_pre)) + return -EBADMSG; r = dns_packet_read_uint8(p, &rr->loc.vert_pre, NULL); if (r < 0) - goto fail; + return r; - if (!loc_size_ok(rr->loc.vert_pre)) { - r = -EBADMSG; - goto fail; - } + if (!loc_size_ok(rr->loc.vert_pre)) + return -EBADMSG; r = dns_packet_read_uint32(p, &rr->loc.latitude, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint32(p, &rr->loc.longitude, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint32(p, &rr->loc.altitude, NULL); if (r < 0) - goto fail; + return r; break; } else { @@ -1803,122 +1777,114 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl case DNS_TYPE_DS: r = dns_packet_read_uint16(p, &rr->ds.key_tag, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &rr->ds.algorithm, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &rr->ds.digest_type, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_memdup(p, rdlength - 4, &rr->ds.digest, &rr->ds.digest_size, NULL); if (r < 0) - goto fail; + return r; - if (rr->ds.digest_size <= 0) { + if (rr->ds.digest_size <= 0) /* the accepted size depends on the algorithm, but for now just ensure that the value is greater than zero */ - r = -EBADMSG; - goto fail; - } + return -EBADMSG; break; case DNS_TYPE_SSHFP: r = dns_packet_read_uint8(p, &rr->sshfp.algorithm, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &rr->sshfp.fptype, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_memdup(p, rdlength - 2, &rr->sshfp.fingerprint, &rr->sshfp.fingerprint_size, NULL); - if (rr->sshfp.fingerprint_size <= 0) { + if (rr->sshfp.fingerprint_size <= 0) /* the accepted size depends on the algorithm, but for now just ensure that the value is greater than zero */ - r = -EBADMSG; - goto fail; - } + return -EBADMSG; break; case DNS_TYPE_DNSKEY: r = dns_packet_read_uint16(p, &rr->dnskey.flags, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &rr->dnskey.protocol, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &rr->dnskey.algorithm, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_memdup(p, rdlength - 4, &rr->dnskey.key, &rr->dnskey.key_size, NULL); - if (rr->dnskey.key_size <= 0) { + if (rr->dnskey.key_size <= 0) /* the accepted size depends on the algorithm, but for now just ensure that the value is greater than zero */ - r = -EBADMSG; - goto fail; - } + return -EBADMSG; break; case DNS_TYPE_RRSIG: r = dns_packet_read_uint16(p, &rr->rrsig.type_covered, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &rr->rrsig.algorithm, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &rr->rrsig.labels, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint32(p, &rr->rrsig.original_ttl, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint32(p, &rr->rrsig.expiration, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint32(p, &rr->rrsig.inception, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint16(p, &rr->rrsig.key_tag, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_name(p, &rr->rrsig.signer, false, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_memdup(p, offset + rdlength - p->rindex, &rr->rrsig.signature, &rr->rrsig.signature_size, NULL); - if (rr->rrsig.signature_size <= 0) { + if (rr->rrsig.signature_size <= 0) /* the accepted size depends on the algorithm, but for now just ensure that the value is greater than zero */ - r = -EBADMSG; - goto fail; - } + return -EBADMSG; break; @@ -1933,11 +1899,9 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl r = dns_packet_read_name(p, &rr->nsec.next_domain_name, allow_compressed, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_type_windows(p, &rr->nsec.types, offset + rdlength - p->rindex, NULL); - if (r < 0) - goto fail; /* We accept empty NSEC bitmaps. The bit indicating the presence of the NSEC record itself * is redundant and in e.g., RFC4956 this fact is used to define a use for NSEC records @@ -1950,41 +1914,39 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl r = dns_packet_read_uint8(p, &rr->nsec3.algorithm, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &rr->nsec3.flags, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint16(p, &rr->nsec3.iterations, NULL); if (r < 0) - goto fail; + return r; /* this may be zero */ r = dns_packet_read_uint8(p, &size, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_memdup(p, size, &rr->nsec3.salt, &rr->nsec3.salt_size, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &size, NULL); if (r < 0) - goto fail; + return r; - if (size <= 0) { - r = -EBADMSG; - goto fail; - } + if (size <= 0) + return -EBADMSG; - r = dns_packet_read_memdup(p, size, &rr->nsec3.next_hashed_name, &rr->nsec3.next_hashed_name_size, NULL); + r = dns_packet_read_memdup(p, size, + &rr->nsec3.next_hashed_name, &rr->nsec3.next_hashed_name_size, + NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_type_windows(p, &rr->nsec3.types, offset + rdlength - p->rindex, NULL); - if (r < 0) - goto fail; /* empty non-terminals can have NSEC3 records, so empty bitmaps are allowed */ @@ -1994,25 +1956,39 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl case DNS_TYPE_TLSA: r = dns_packet_read_uint8(p, &rr->tlsa.cert_usage, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &rr->tlsa.selector, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_uint8(p, &rr->tlsa.matching_type, NULL); if (r < 0) - goto fail; + return r; r = dns_packet_read_memdup(p, rdlength - 3, &rr->tlsa.data, &rr->tlsa.data_size, NULL); - if (rr->tlsa.data_size <= 0) { + + if (rr->tlsa.data_size <= 0) /* the accepted size depends on the algorithm, but for now just ensure that the value is greater than zero */ - r = -EBADMSG; - goto fail; - } + return -EBADMSG; + + break; + + case DNS_TYPE_CAA: + r = dns_packet_read_uint8(p, &rr->caa.flags, NULL); + if (r < 0) + return r; + + r = dns_packet_read_string(p, &rr->caa.tag, NULL); + if (r < 0) + return r; + + r = dns_packet_read_memdup(p, + rdlength + offset - p->rindex, + &rr->caa.value, &rr->caa.value_size, NULL); break; @@ -2021,16 +1997,13 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl default: unparseable: r = dns_packet_read_memdup(p, rdlength, &rr->generic.data, &rr->generic.data_size, NULL); - if (r < 0) - goto fail; + break; } if (r < 0) - goto fail; - if (p->rindex != offset + rdlength) { - r = -EBADMSG; - goto fail; - } + return r; + if (p->rindex != offset + rdlength) + return -EBADMSG; *ret = rr; rr = NULL; @@ -2038,12 +2011,10 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl if (ret_cache_flush) *ret_cache_flush = cache_flush; if (start) - *start = saved_rindex; + *start = rewinder.saved_rindex; + CANCEL_REWINDER(rewinder); return 0; -fail: - dns_packet_rewind(p, saved_rindex); - return r; } static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) { @@ -2091,23 +2062,21 @@ static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) { int dns_packet_extract(DnsPacket *p) { _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - size_t saved_rindex; + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = {}; unsigned n, i; int r; if (p->extracted) return 0; - saved_rindex = p->rindex; + INIT_REWINDER(rewinder, p); dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE); n = DNS_PACKET_QDCOUNT(p); if (n > 0) { question = dns_question_new(n); - if (!question) { - r = -ENOMEM; - goto finish; - } + if (!question) + return -ENOMEM; for (i = 0; i < n; i++) { _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; @@ -2115,21 +2084,17 @@ int dns_packet_extract(DnsPacket *p) { r = dns_packet_read_key(p, &key, &cache_flush, NULL); if (r < 0) - goto finish; + return r; - if (cache_flush) { - r = -EBADMSG; - goto finish; - } + if (cache_flush) + return -EBADMSG; - if (!dns_type_is_valid_query(key->type)) { - r = -EBADMSG; - goto finish; - } + if (!dns_type_is_valid_query(key->type)) + return -EBADMSG; r = dns_question_add(question, key); if (r < 0) - goto finish; + return r; } } @@ -2139,10 +2104,8 @@ int dns_packet_extract(DnsPacket *p) { bool bad_opt = false; answer = dns_answer_new(n); - if (!answer) { - r = -ENOMEM; - goto finish; - } + if (!answer) + return -ENOMEM; for (i = 0; i < n; i++) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; @@ -2150,7 +2113,7 @@ int dns_packet_extract(DnsPacket *p) { r = dns_packet_read_rr(p, &rr, &cache_flush, NULL); if (r < 0) - goto finish; + return r; /* Try to reduce memory usage a bit */ if (previous) @@ -2167,7 +2130,7 @@ int dns_packet_extract(DnsPacket *p) { continue; } - if (!dns_name_is_root(DNS_RESOURCE_KEY_NAME(rr->key))) { + if (!dns_name_is_root(dns_resource_key_name(rr->key))) { /* If the OPT RR is not owned by the root domain, then it is bad, let's ignore * it. */ log_debug("OPT RR is not owned by root domain, ignoring."); @@ -2213,7 +2176,7 @@ int dns_packet_extract(DnsPacket *p) { (i < DNS_PACKET_ANCOUNT(p) ? DNS_ANSWER_CACHEABLE : 0) | (p->protocol == DNS_PROTOCOL_MDNS && !cache_flush ? DNS_ANSWER_SHARED_OWNER : 0)); if (r < 0) - goto finish; + return r; } /* Remember this RR, so that we potentically can merge it's ->key object with the next RR. Note @@ -2234,11 +2197,8 @@ int dns_packet_extract(DnsPacket *p) { p->extracted = true; - r = 0; - -finish: - p->rindex = saved_rindex; - return r; + /* no CANCEL, always rewind */ + return 0; } int dns_packet_is_reply_for(DnsPacket *p, const DnsResourceKey *key) { diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 0bf34d270c..416335d0a2 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -262,11 +262,9 @@ static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family, return f|(family == AF_INET6 ? SD_RESOLVED_LLMNR_IPV6 : SD_RESOLVED_LLMNR_IPV4); case DNS_PROTOCOL_MDNS: - return family == AF_INET6 ? SD_RESOLVED_MDNS_IPV6 : SD_RESOLVED_MDNS_IPV4; + return f|(family == AF_INET6 ? SD_RESOLVED_MDNS_IPV6 : SD_RESOLVED_MDNS_IPV4); default: - break; + return f; } - - return 0; } diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index a378b2b7f7..ea04e58d61 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -62,6 +62,7 @@ static void dns_query_candidate_stop(DnsQueryCandidate *c) { while ((t = set_steal_first(c->transactions))) { set_remove(t->notify_query_candidates, c); + set_remove(t->notify_query_candidates_done, c); dns_transaction_gc(t); } } @@ -139,6 +140,10 @@ static int dns_query_candidate_add_transaction(DnsQueryCandidate *c, DnsResource if (r < 0) goto gc; + r = set_ensure_allocated(&t->notify_query_candidates_done, NULL); + if (r < 0) + goto gc; + r = set_put(t->notify_query_candidates, c); if (r < 0) goto gc; @@ -421,6 +426,7 @@ int dns_query_new( DnsResourceKey *key; bool good = false; int r; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; assert(m); @@ -471,31 +477,20 @@ int dns_query_new( q->answer_family = AF_UNSPEC; /* First dump UTF8 question */ - DNS_QUESTION_FOREACH(key, question_utf8) { - _cleanup_free_ char *p = NULL; - - r = dns_resource_key_to_string(key, &p); - if (r < 0) - return r; - - log_debug("Looking up RR for %s.", strstrip(p)); - } + DNS_QUESTION_FOREACH(key, question_utf8) + log_debug("Looking up RR for %s.", + dns_resource_key_to_string(key, key_str, sizeof key_str)); /* And then dump the IDNA question, but only what hasn't been dumped already through the UTF8 question. */ DNS_QUESTION_FOREACH(key, question_idna) { - _cleanup_free_ char *p = NULL; - r = dns_question_contains(question_utf8, key); if (r < 0) return r; if (r > 0) continue; - r = dns_resource_key_to_string(key, &p); - if (r < 0) - return r; - - log_debug("Looking up IDNA RR for %s.", strstrip(p)); + log_debug("Looking up IDNA RR for %s.", + dns_resource_key_to_string(key, key_str, sizeof key_str)); } LIST_PREPEND(queries, m->dns_queries, q); @@ -815,7 +810,7 @@ static void dns_query_accept(DnsQuery *q, DnsQueryCandidate *c) { switch (t->state) { case DNS_TRANSACTION_SUCCESS: { - /* We found a successfuly reply, merge it into the answer */ + /* We found a successfully reply, merge it into the answer */ r = dns_answer_extend(&q->answer, t->answer); if (r < 0) goto fail; @@ -937,7 +932,7 @@ static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) assert(q); - q->n_cname_redirects ++; + q->n_cname_redirects++; if (q->n_cname_redirects > CNAME_MAX) return -ELOOP; diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c index 8e452e79a4..c8b502d1cd 100644 --- a/src/resolve/resolved-dns-question.c +++ b/src/resolve/resolved-dns-question.c @@ -145,7 +145,7 @@ int dns_question_is_valid_for_query(DnsQuestion *q) { if (q->n_keys > 65535) return 0; - name = DNS_RESOURCE_KEY_NAME(q->keys[0]); + name = dns_resource_key_name(q->keys[0]); if (!name) return 0; @@ -154,7 +154,7 @@ int dns_question_is_valid_for_query(DnsQuestion *q) { assert(q->keys[i]); if (i > 0) { - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(q->keys[i]), name); + r = dns_name_equal(dns_resource_key_name(q->keys[i]), name); if (r <= 0) return r; } @@ -235,7 +235,7 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, if (cname->key->type == DNS_TYPE_CNAME) d = cname->cname.name; else { - r = dns_name_change_suffix(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(cname->key), cname->dname.name, &destination); + r = dns_name_change_suffix(dns_resource_key_name(key), dns_resource_key_name(cname->key), cname->dname.name, &destination); if (r < 0) return r; if (r == 0) @@ -244,7 +244,7 @@ int dns_question_cname_redirect(DnsQuestion *q, const DnsResourceRecord *cname, d = destination; } - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(key), d); + r = dns_name_equal(dns_resource_key_name(key), d); if (r < 0) return r; @@ -291,7 +291,7 @@ const char *dns_question_first_name(DnsQuestion *q) { if (q->n_keys < 1) return NULL; - return DNS_RESOURCE_KEY_NAME(q->keys[0]); + return dns_resource_key_name(q->keys[0]); } int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bool convert_idna) { diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index 40f8e28dfd..6a29a93a26 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -22,6 +22,7 @@ #include "alloc-util.h" #include "dns-domain.h" #include "dns-type.h" +#include "escape.h" #include "hexdecoct.h" #include "resolved-dns-dnssec.h" #include "resolved-dns-packet.h" @@ -65,7 +66,7 @@ DnsResourceKey* dns_resource_key_new_redirect(const DnsResourceKey *key, const D DnsResourceKey *k; char *destination = NULL; - r = dns_name_change_suffix(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(cname->key), cname->dname.name, &destination); + r = dns_name_change_suffix(dns_resource_key_name(key), dns_resource_key_name(cname->key), cname->dname.name, &destination); if (r < 0) return NULL; if (r == 0) @@ -95,7 +96,7 @@ int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key return 0; } - r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), name, &joined); + r = dns_name_concat(dns_resource_key_name(key), name, &joined); if (r < 0) return r; @@ -157,6 +158,23 @@ DnsResourceKey* dns_resource_key_unref(DnsResourceKey *k) { return NULL; } +const char* dns_resource_key_name(const DnsResourceKey *key) { + const char *name; + + if (!key) + return NULL; + + if (key->_name) + name = key->_name; + else + name = (char*) key + sizeof(DnsResourceKey); + + if (dns_name_is_root(name)) + return "."; + else + return name; +} + bool dns_resource_key_is_address(const DnsResourceKey *key) { assert(key); @@ -171,7 +189,7 @@ int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b) { if (a == b) return 1; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(a), DNS_RESOURCE_KEY_NAME(b)); + r = dns_name_equal(dns_resource_key_name(a), dns_resource_key_name(b)); if (r <= 0) return r; @@ -203,18 +221,18 @@ int dns_resource_key_match_rr(const DnsResourceKey *key, DnsResourceRecord *rr, if (rr->key->type != key->type && key->type != DNS_TYPE_ANY) return 0; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), DNS_RESOURCE_KEY_NAME(key)); + r = dns_name_equal(dns_resource_key_name(rr->key), dns_resource_key_name(key)); if (r != 0) return r; if (search_domain) { _cleanup_free_ char *joined = NULL; - r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), search_domain, &joined); + r = dns_name_concat(dns_resource_key_name(key), search_domain, &joined); if (r < 0) return r; - return dns_name_equal(DNS_RESOURCE_KEY_NAME(rr->key), joined); + return dns_name_equal(dns_resource_key_name(rr->key), joined); } return 0; @@ -230,9 +248,9 @@ int dns_resource_key_match_cname_or_dname(const DnsResourceKey *key, const DnsRe return 0; if (cname->type == DNS_TYPE_CNAME) - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(cname)); + r = dns_name_equal(dns_resource_key_name(key), dns_resource_key_name(cname)); else if (cname->type == DNS_TYPE_DNAME) - r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(cname)); + r = dns_name_endswith(dns_resource_key_name(key), dns_resource_key_name(cname)); else return 0; @@ -242,14 +260,14 @@ int dns_resource_key_match_cname_or_dname(const DnsResourceKey *key, const DnsRe if (search_domain) { _cleanup_free_ char *joined = NULL; - r = dns_name_concat(DNS_RESOURCE_KEY_NAME(key), search_domain, &joined); + r = dns_name_concat(dns_resource_key_name(key), search_domain, &joined); if (r < 0) return r; if (cname->type == DNS_TYPE_CNAME) - return dns_name_equal(joined, DNS_RESOURCE_KEY_NAME(cname)); + return dns_name_equal(joined, dns_resource_key_name(cname)); else if (cname->type == DNS_TYPE_DNAME) - return dns_name_endswith(joined, DNS_RESOURCE_KEY_NAME(cname)); + return dns_name_endswith(joined, dns_resource_key_name(cname)); } return 0; @@ -267,7 +285,7 @@ int dns_resource_key_match_soa(const DnsResourceKey *key, const DnsResourceKey * if (soa->type != DNS_TYPE_SOA) return 0; - return dns_name_endswith(DNS_RESOURCE_KEY_NAME(key), DNS_RESOURCE_KEY_NAME(soa)); + return dns_name_endswith(dns_resource_key_name(key), dns_resource_key_name(soa)); } static void dns_resource_key_hash_func(const void *i, struct siphash *state) { @@ -275,7 +293,7 @@ static void dns_resource_key_hash_func(const void *i, struct siphash *state) { assert(k); - dns_name_hash_func(DNS_RESOURCE_KEY_NAME(k), state); + dns_name_hash_func(dns_resource_key_name(k), state); siphash24_compress(&k->class, sizeof(k->class), state); siphash24_compress(&k->type, sizeof(k->type), state); } @@ -284,7 +302,7 @@ static int dns_resource_key_compare_func(const void *a, const void *b) { const DnsResourceKey *x = a, *y = b; int ret; - ret = dns_name_compare_func(DNS_RESOURCE_KEY_NAME(x), DNS_RESOURCE_KEY_NAME(y)); + ret = dns_name_compare_func(dns_resource_key_name(x), dns_resource_key_name(y)); if (ret != 0) return ret; @@ -306,32 +324,22 @@ const struct hash_ops dns_resource_key_hash_ops = { .compare = dns_resource_key_compare_func }; -int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) { - char cbuf[strlen("CLASS") + DECIMAL_STR_MAX(uint16_t)], tbuf[strlen("TYPE") + DECIMAL_STR_MAX(uint16_t)]; - const char *c, *t, *n; - char *s; +char* dns_resource_key_to_string(const DnsResourceKey *key, char *buf, size_t buf_size) { + const char *c, *t; + char *ans = buf; /* If we cannot convert the CLASS/TYPE into a known string, use the format recommended by RFC 3597, Section 5. */ c = dns_class_to_string(key->class); - if (!c) { - sprintf(cbuf, "CLASS%u", key->class); - c = cbuf; - } - t = dns_type_to_string(key->type); - if (!t){ - sprintf(tbuf, "TYPE%u", key->type); - t = tbuf; - } - n = DNS_RESOURCE_KEY_NAME(key); - if (asprintf(&s, "%s%s %s %-5s", n, endswith(n, ".") ? "" : ".", c, t) < 0) - return -ENOMEM; + snprintf(buf, buf_size, "%s %s%s%.0u %s%s%.0u", + dns_resource_key_name(key), + c ?: "", c ? "" : "CLASS", c ? 0 : key->class, + t ?: "", t ? "" : "TYPE", t ? 0 : key->class); - *ret = s; - return 0; + return ans; } bool dns_resource_key_reduce(DnsResourceKey **a, DnsResourceKey **b) { @@ -490,6 +498,11 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) { free(rr->tlsa.data); break; + case DNS_TYPE_CAA: + free(rr->caa.tag); + free(rr->caa.value); + break; + case DNS_TYPE_OPENPGPKEY: default: free(rr->generic.data); @@ -697,6 +710,12 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor a->tlsa.matching_type == b->tlsa.matching_type && FIELD_EQUAL(a->tlsa, b->tlsa, data); + case DNS_TYPE_CAA: + return a->caa.flags == b->caa.flags && + streq(a->caa.tag, b->caa.tag) && + FIELD_EQUAL(a->caa, b->caa, value); + + case DNS_TYPE_OPENPGPKEY: default: return FIELD_EQUAL(a->generic, b->generic, data); } @@ -818,8 +837,8 @@ static char *format_txt(DnsTxtItem *first) { } const char *dns_resource_record_to_string(DnsResourceRecord *rr) { - _cleanup_free_ char *k = NULL, *t = NULL; - char *s; + _cleanup_free_ char *t = NULL; + char *s, k[DNS_RESOURCE_KEY_STRING_MAX]; int r; assert(rr); @@ -827,9 +846,7 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { if (rr->to_string) return rr->to_string; - r = dns_resource_key_to_string(rr->key, &k); - if (r < 0) - return NULL; + dns_resource_key_to_string(rr->key, k, sizeof(k)); switch (rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) { @@ -966,7 +983,7 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { case DNS_TYPE_DNSKEY: { _cleanup_free_ char *alg = NULL; char *ss; - int n, n1; + int n; uint16_t key_tag; key_tag = dnssec_keytag(rr, true); @@ -975,9 +992,8 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { if (r < 0) return NULL; - r = asprintf(&s, "%s %n%u %u %s %n", + r = asprintf(&s, "%s %u %u %s %n", k, - &n1, rr->dnskey.flags, rr->dnskey.protocol, alg, @@ -992,14 +1008,12 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { return NULL; r = asprintf(&ss, "%s\n" - "%*s-- Flags:%s%s%s\n" - "%*s-- Key tag: %u", + " -- Flags:%s%s%s\n" + " -- Key tag: %u", s, - n1, "", rr->dnskey.flags & DNSKEY_FLAG_SEP ? " SEP" : "", rr->dnskey.flags & DNSKEY_FLAG_REVOKE ? " REVOKE" : "", rr->dnskey.flags & DNSKEY_FLAG_ZONE_KEY ? " ZONE_KEY" : "", - n1, "", key_tag); if (r < 0) return NULL; @@ -1102,40 +1116,52 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { case DNS_TYPE_TLSA: { const char *cert_usage, *selector, *matching_type; - char *ss; - int n; cert_usage = tlsa_cert_usage_to_string(rr->tlsa.cert_usage); selector = tlsa_selector_to_string(rr->tlsa.selector); matching_type = tlsa_matching_type_to_string(rr->tlsa.matching_type); - r = asprintf(&s, "%s %u %u %u %n", + t = hexmem(rr->sshfp.fingerprint, rr->sshfp.fingerprint_size); + if (!t) + return NULL; + + r = asprintf(&s, + "%s %u %u %u %s\n" + " -- Cert. usage: %s\n" + " -- Selector: %s\n" + " -- Matching type: %s", k, rr->tlsa.cert_usage, rr->tlsa.selector, rr->tlsa.matching_type, - &n); + t, + cert_usage, + selector, + matching_type); if (r < 0) return NULL; - r = base64_append(&s, n, - rr->tlsa.data, rr->tlsa.data_size, - 8, columns()); - if (r < 0) + break; + } + + case DNS_TYPE_CAA: { + _cleanup_free_ char *value; + + value = octescape(rr->caa.value, rr->caa.value_size); + if (!value) return NULL; - r = asprintf(&ss, "%s\n" - "%*s-- Cert. usage: %s\n" - "%*s-- Selector: %s\n" - "%*s-- Matching type: %s", - s, - n - 6, "", cert_usage, - n - 6, "", selector, - n - 6, "", matching_type); + r = asprintf(&s, "%s %u %s \"%s\"%s%s%s%.0u", + k, + rr->caa.flags, + rr->caa.tag, + value, + rr->caa.flags ? "\n -- Flags:" : "", + rr->caa.flags & CAA_FLAG_CRITICAL ? " critical" : "", + rr->caa.flags & ~CAA_FLAG_CRITICAL ? " " : "", + rr->caa.flags & ~CAA_FLAG_CRITICAL); if (r < 0) return NULL; - free(s); - s = ss; break; } @@ -1173,6 +1199,47 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { return s; } +ssize_t dns_resource_record_payload(DnsResourceRecord *rr, void **out) { + assert(rr); + assert(out); + + switch(rr->unparseable ? _DNS_TYPE_INVALID : rr->key->type) { + case DNS_TYPE_SRV: + case DNS_TYPE_PTR: + case DNS_TYPE_NS: + case DNS_TYPE_CNAME: + case DNS_TYPE_DNAME: + case DNS_TYPE_HINFO: + case DNS_TYPE_SPF: + case DNS_TYPE_TXT: + case DNS_TYPE_A: + case DNS_TYPE_AAAA: + case DNS_TYPE_SOA: + case DNS_TYPE_MX: + case DNS_TYPE_LOC: + case DNS_TYPE_DS: + case DNS_TYPE_DNSKEY: + case DNS_TYPE_RRSIG: + case DNS_TYPE_NSEC: + case DNS_TYPE_NSEC3: + return -EINVAL; + + case DNS_TYPE_SSHFP: + *out = rr->sshfp.fingerprint; + return rr->sshfp.fingerprint_size; + + case DNS_TYPE_TLSA: + *out = rr->tlsa.data; + return rr->tlsa.data_size; + + + case DNS_TYPE_OPENPGPKEY: + default: + *out = rr->generic.data; + return rr->generic.data_size; + } +} + int dns_resource_record_to_wire_format(DnsResourceRecord *rr, bool canonical) { DnsPacket packet = { @@ -1230,7 +1297,7 @@ int dns_resource_record_signer(DnsResourceRecord *rr, const char **ret) { if (rr->n_skip_labels_signer == (unsigned) -1) return -ENODATA; - n = DNS_RESOURCE_KEY_NAME(rr->key); + n = dns_resource_key_name(rr->key); r = dns_name_skip(n, rr->n_skip_labels_signer, &n); if (r < 0) return r; @@ -1253,7 +1320,7 @@ int dns_resource_record_source(DnsResourceRecord *rr, const char **ret) { if (rr->n_skip_labels_source == (unsigned) -1) return -ENODATA; - n = DNS_RESOURCE_KEY_NAME(rr->key); + n = dns_resource_key_name(rr->key); r = dns_name_skip(n, rr->n_skip_labels_source, &n); if (r < 0) return r; @@ -1293,14 +1360,14 @@ int dns_resource_record_is_synthetic(DnsResourceRecord *rr) { if (rr->n_skip_labels_source > 1) return 1; - r = dns_name_startswith(DNS_RESOURCE_KEY_NAME(rr->key), "*"); + r = dns_name_startswith(dns_resource_key_name(rr->key), "*"); if (r < 0) return r; return !r; } -static void dns_resource_record_hash_func(const void *i, struct siphash *state) { +void dns_resource_record_hash_func(const void *i, struct siphash *state) { const DnsResourceRecord *rr = i; assert(rr); @@ -1427,7 +1494,13 @@ static void dns_resource_record_hash_func(const void *i, struct siphash *state) siphash24_compress(&rr->tlsa.cert_usage, sizeof(rr->tlsa.cert_usage), state); siphash24_compress(&rr->tlsa.selector, sizeof(rr->tlsa.selector), state); siphash24_compress(&rr->tlsa.matching_type, sizeof(rr->tlsa.matching_type), state); - siphash24_compress(&rr->tlsa.data, rr->tlsa.data_size, state); + siphash24_compress(rr->tlsa.data, rr->tlsa.data_size, state); + break; + + case DNS_TYPE_CAA: + siphash24_compress(&rr->caa.flags, sizeof(rr->caa.flags), state); + string_hash_func(rr->caa.tag, state); + siphash24_compress(rr->caa.value, rr->caa.value_size, state); break; case DNS_TYPE_OPENPGPKEY: diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index 2e0dfbaba3..020a2abd77 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -26,6 +26,7 @@ #include "hashmap.h" #include "in-addr-util.h" #include "list.h" +#include "string-util.h" typedef struct DnsResourceKey DnsResourceKey; typedef struct DnsResourceRecord DnsResourceRecord; @@ -81,7 +82,7 @@ enum { struct DnsResourceKey { unsigned n_ref; /* (unsigned -1) for const keys, see below */ uint16_t class, type; - char *_name; /* don't access directy, use DNS_RESOURCE_KEY_NAME()! */ + char *_name; /* don't access directly, use dns_resource_key_name()! */ }; /* Creates a temporary resource key. This is only useful to quickly @@ -249,19 +250,17 @@ struct DnsResourceRecord { void *data; size_t data_size; } tlsa; + + /* https://tools.ietf.org/html/rfc6844 */ + struct { + uint8_t flags; + char *tag; + void *value; + size_t value_size; + } caa; }; }; -static inline const char* DNS_RESOURCE_KEY_NAME(const DnsResourceKey *key) { - if (!key) - return NULL; - - if (key->_name) - return key->_name; - - return (char*) key + sizeof(DnsResourceKey); -} - static inline const void* DNS_RESOURCE_RECORD_RDATA(DnsResourceRecord *rr) { if (!rr) return NULL; @@ -289,12 +288,20 @@ int dns_resource_key_new_append_suffix(DnsResourceKey **ret, DnsResourceKey *key DnsResourceKey* dns_resource_key_new_consume(uint16_t class, uint16_t type, char *name); DnsResourceKey* dns_resource_key_ref(DnsResourceKey *key); DnsResourceKey* dns_resource_key_unref(DnsResourceKey *key); +const char* dns_resource_key_name(const DnsResourceKey *key); bool dns_resource_key_is_address(const DnsResourceKey *key); int dns_resource_key_equal(const DnsResourceKey *a, const DnsResourceKey *b); int dns_resource_key_match_rr(const DnsResourceKey *key, DnsResourceRecord *rr, const char *search_domain); int dns_resource_key_match_cname_or_dname(const DnsResourceKey *key, const DnsResourceKey *cname, const char *search_domain); int dns_resource_key_match_soa(const DnsResourceKey *key, const DnsResourceKey *soa); -int dns_resource_key_to_string(const DnsResourceKey *key, char **ret); + +/* _DNS_{CLASS,TYPE}_STRING_MAX include one byte for NUL, which we use for space instead below. + * DNS_HOSTNAME_MAX does not include the NUL byte, so we need to add 1. */ +#define DNS_RESOURCE_KEY_STRING_MAX (_DNS_CLASS_STRING_MAX + _DNS_TYPE_STRING_MAX + DNS_HOSTNAME_MAX + 1) + +char* dns_resource_key_to_string(const DnsResourceKey *key, char *buf, size_t buf_size); +ssize_t dns_resource_record_payload(DnsResourceRecord *rr, void **out); + DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceKey*, dns_resource_key_unref); static inline bool dns_key_is_shared(const DnsResourceKey *key) { @@ -323,6 +330,8 @@ int dns_resource_record_is_synthetic(DnsResourceRecord *rr); DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i); bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b); +void dns_resource_record_hash_func(const void *i, struct siphash *state); + extern const struct hash_ops dns_resource_key_hash_ops; extern const struct hash_ops dns_resource_record_hash_ops; diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index a406872a38..66e4585c18 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -514,8 +514,8 @@ bool dns_scope_good_key(DnsScope *s, const DnsResourceKey *key) { * that those should be resolved via LLMNR or search * path only, and should not be leaked onto the * internet. */ - return !(dns_name_is_single_label(DNS_RESOURCE_KEY_NAME(key)) || - dns_name_is_root(DNS_RESOURCE_KEY_NAME(key))); + return !(dns_name_is_single_label(dns_resource_key_name(key)) || + dns_name_is_root(dns_resource_key_name(key))); } /* On mDNS and LLMNR, send A and AAAA queries only on the diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index 27342a0e04..3095c042db 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -120,7 +120,7 @@ DnsServer* dns_server_ref(DnsServer *s) { return NULL; assert(s->n_ref > 0); - s->n_ref ++; + s->n_ref++; return s; } @@ -130,7 +130,7 @@ DnsServer* dns_server_unref(DnsServer *s) { return NULL; assert(s->n_ref > 0); - s->n_ref --; + s->n_ref--; if (s->n_ref > 0) return NULL; @@ -157,6 +157,7 @@ void dns_server_unlink(DnsServer *s) { assert(s->link); assert(s->link->n_dns_servers > 0); LIST_REMOVE(servers, s->link->dns_servers, s); + s->link->n_dns_servers--; break; case DNS_SERVER_SYSTEM: @@ -290,9 +291,9 @@ void dns_server_packet_lost(DnsServer *s, int protocol, DnsServerFeatureLevel le if (s->possible_feature_level == level) { if (protocol == IPPROTO_UDP) - s->n_failed_udp ++; + s->n_failed_udp++; else if (protocol == IPPROTO_TCP) - s->n_failed_tcp ++; + s->n_failed_tcp++; } if (s->resend_timeout > usec) diff --git a/src/resolve/resolved-dns-synthesize.c b/src/resolve/resolved-dns-synthesize.c index f4a43dee8c..e3003411f7 100644 --- a/src/resolve/resolved-dns-synthesize.c +++ b/src/resolve/resolved-dns-synthesize.c @@ -86,7 +86,7 @@ static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, int if if (IN_SET(key->type, DNS_TYPE_A, DNS_TYPE_ANY)) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, DNS_RESOURCE_KEY_NAME(key)); + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, dns_resource_key_name(key)); if (!rr) return -ENOMEM; @@ -100,7 +100,7 @@ static int synthesize_localhost_rr(Manager *m, const DnsResourceKey *key, int if if (IN_SET(key->type, DNS_TYPE_AAAA, DNS_TYPE_ANY)) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, DNS_RESOURCE_KEY_NAME(key)); + rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_AAAA, dns_resource_key_name(key)); if (!rr) return -ENOMEM; @@ -140,7 +140,7 @@ static int synthesize_localhost_ptr(Manager *m, const DnsResourceKey *key, int i if (r < 0) return r; - r = answer_add_ptr(answer, DNS_RESOURCE_KEY_NAME(key), "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); + r = answer_add_ptr(answer, dns_resource_key_name(key), "localhost", dns_synthesize_ifindex(ifindex), DNS_ANSWER_AUTHENTICATED); if (r < 0) return r; } @@ -254,11 +254,11 @@ static int synthesize_system_hostname_rr(Manager *m, const DnsResourceKey *key, .address.in6 = in6addr_loopback, }; - return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), buffer, n); + return answer_add_addresses_rr(answer, dns_resource_key_name(key), buffer, n); } } - return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), addresses, n); + return answer_add_addresses_rr(answer, dns_resource_key_name(key), addresses, n); } static int synthesize_system_hostname_ptr(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) { @@ -319,7 +319,7 @@ static int synthesize_gateway_rr(Manager *m, const DnsResourceKey *key, int ifin return n; } - return answer_add_addresses_rr(answer, DNS_RESOURCE_KEY_NAME(key), addresses, n); + return answer_add_addresses_rr(answer, dns_resource_key_name(key), addresses, n); } static int synthesize_gateway_ptr(Manager *m, int af, const union in_addr_union *address, int ifindex, DnsAnswer **answer) { @@ -360,7 +360,7 @@ int dns_synthesize_answer( key->class != DNS_CLASS_ANY) continue; - name = DNS_RESOURCE_KEY_NAME(key); + name = dns_resource_key_name(key); if (is_localhost(name)) { diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index d48fdd1281..a4a67623e7 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -52,6 +52,7 @@ static void dns_transaction_flush_dnssec_transactions(DnsTransaction *t) { while ((z = set_steal_first(t->dnssec_transactions))) { set_remove(z->notify_transactions, t); + set_remove(z->notify_transactions_done, t); dns_transaction_gc(z); } } @@ -100,20 +101,31 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) { set_remove(c->transactions, t); set_free(t->notify_query_candidates); + while ((c = set_steal_first(t->notify_query_candidates_done))) + set_remove(c->transactions, t); + set_free(t->notify_query_candidates_done); + while ((i = set_steal_first(t->notify_zone_items))) i->probe_transaction = NULL; set_free(t->notify_zone_items); + while ((i = set_steal_first(t->notify_zone_items_done))) + i->probe_transaction = NULL; + set_free(t->notify_zone_items_done); + while ((z = set_steal_first(t->notify_transactions))) set_remove(z->dnssec_transactions, t); set_free(t->notify_transactions); + while ((z = set_steal_first(t->notify_transactions_done))) + set_remove(z->dnssec_transactions, t); + set_free(t->notify_transactions_done); + dns_transaction_flush_dnssec_transactions(t); set_free(t->dnssec_transactions); dns_answer_unref(t->validated_keys); dns_resource_key_unref(t->key); - free(t->key_string); free(t); return NULL; @@ -128,8 +140,11 @@ bool dns_transaction_gc(DnsTransaction *t) { return true; if (set_isempty(t->notify_query_candidates) && + set_isempty(t->notify_query_candidates_done) && set_isempty(t->notify_zone_items) && - set_isempty(t->notify_transactions)) { + set_isempty(t->notify_zone_items_done) && + set_isempty(t->notify_transactions) && + set_isempty(t->notify_transactions_done)) { dns_transaction_free(t); return false; } @@ -210,7 +225,7 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) LIST_PREPEND(transactions_by_scope, s->transactions, t); t->scope = s; - s->manager->n_transactions_total ++; + s->manager->n_transactions_total++; if (ret) *ret = t; @@ -238,6 +253,7 @@ static void dns_transaction_shuffle_id(DnsTransaction *t) { static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) { _cleanup_free_ char *pretty = NULL; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; DnsZoneItem *z; assert(t); @@ -246,15 +262,15 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) { if (manager_our_packet(t->scope->manager, p) != 0) return; - in_addr_to_string(p->family, &p->sender, &pretty); + (void) in_addr_to_string(p->family, &p->sender, &pretty); log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s got tentative packet from %s.", t->id, - dns_transaction_key_string(t), + dns_resource_key_to_string(t->key, key_str, sizeof key_str), dns_protocol_to_string(t->scope->protocol), t->scope->link ? t->scope->link->name : "*", - t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family), - pretty); + af_to_name_short(t->scope->family), + strnull(pretty)); /* RFC 4795, Section 4.1 says that the peer with the * lexicographically smaller IP address loses */ @@ -266,6 +282,7 @@ static void dns_transaction_tentative(DnsTransaction *t, DnsPacket *p) { log_debug("We have the lexicographically larger IP address and thus lost in the conflict."); t->block_gc++; + while ((z = set_first(t->notify_zone_items))) { /* First, make sure the zone item drops the reference * to us */ @@ -284,22 +301,25 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { DnsQueryCandidate *c; DnsZoneItem *z; DnsTransaction *d; - Iterator i; const char *st; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; assert(t); assert(!DNS_TRANSACTION_IS_LIVE(state)); - if (state == DNS_TRANSACTION_DNSSEC_FAILED) + if (state == DNS_TRANSACTION_DNSSEC_FAILED) { + dns_resource_key_to_string(t->key, key_str, sizeof key_str); + log_struct(LOG_NOTICE, LOG_MESSAGE_ID(SD_MESSAGE_DNSSEC_FAILURE), - LOG_MESSAGE("DNSSEC validation failed for question %s: %s", dns_transaction_key_string(t), dnssec_result_to_string(t->answer_dnssec_result)), + LOG_MESSAGE("DNSSEC validation failed for question %s: %s", key_str, dnssec_result_to_string(t->answer_dnssec_result)), "DNS_TRANSACTION=%" PRIu16, t->id, - "DNS_QUESTION=%s", dns_transaction_key_string(t), + "DNS_QUESTION=%s", key_str, "DNSSEC_RESULT=%s", dnssec_result_to_string(t->answer_dnssec_result), "DNS_SERVER=%s", dns_server_string(t->server), "DNS_SERVER_FEATURE_LEVEL=%s", dns_server_feature_level_to_string(t->server->possible_feature_level), NULL); + } /* Note that this call might invalidate the query. Callers * should hence not attempt to access the query or transaction @@ -312,10 +332,10 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { log_debug("Transaction %" PRIu16 " for <%s> on scope %s on %s/%s now complete with <%s> from %s (%s).", t->id, - dns_transaction_key_string(t), + dns_resource_key_to_string(t->key, key_str, sizeof key_str), dns_protocol_to_string(t->scope->protocol), t->scope->link ? t->scope->link->name : "*", - t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family), + af_to_name_short(t->scope->family), st, t->answer_source < 0 ? "none" : dns_transaction_source_to_string(t->answer_source), t->answer_authenticated ? "authenticated" : "unsigned"); @@ -329,39 +349,17 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { * transaction isn't freed while we are still looking at it */ t->block_gc++; - SET_FOREACH(c, t->notify_query_candidates, i) + SET_FOREACH_MOVE(c, t->notify_query_candidates_done, t->notify_query_candidates) dns_query_candidate_notify(c); - SET_FOREACH(z, t->notify_zone_items, i) - dns_zone_item_notify(z); - - if (!set_isempty(t->notify_transactions)) { - DnsTransaction **nt; - unsigned j, n = 0; - - /* We need to be careful when notifying other - * transactions, as that might destroy other - * transactions in our list. Hence, in order to be - * able to safely iterate through the list of - * transactions, take a GC lock on all of them - * first. Then, in a second loop, notify them, but - * first unlock that specific transaction. */ - - nt = newa(DnsTransaction*, set_size(t->notify_transactions)); - SET_FOREACH(d, t->notify_transactions, i) { - nt[n++] = d; - d->block_gc++; - } + SWAP_TWO(t->notify_query_candidates, t->notify_query_candidates_done); - assert(n == set_size(t->notify_transactions)); - - for (j = 0; j < n; j++) { - if (set_contains(t->notify_transactions, nt[j])) - dns_transaction_notify(nt[j], t); + SET_FOREACH_MOVE(z, t->notify_zone_items_done, t->notify_zone_items) + dns_zone_item_notify(z); + SWAP_TWO(t->notify_zone_items, t->notify_zone_items_done); - nt[j]->block_gc--; - dns_transaction_gc(nt[j]); - } - } + SET_FOREACH_MOVE(d, t->notify_transactions_done, t->notify_transactions) + dns_transaction_notify(d, t); + SWAP_TWO(t->notify_transactions, t->notify_transactions_done); t->block_gc--; dns_transaction_gc(t); @@ -522,7 +520,7 @@ static int dns_transaction_open_tcp(DnsTransaction *t) { * the IP address, in case this is a reverse * PTR lookup */ - r = dns_name_address(DNS_RESOURCE_KEY_NAME(t->key), &family, &address); + r = dns_name_address(dns_resource_key_name(t->key), &family, &address); if (r < 0) return r; if (r == 0) @@ -1209,7 +1207,7 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) { return 0; } - if (dns_name_is_root(DNS_RESOURCE_KEY_NAME(t->key)) && + if (dns_name_is_root(dns_resource_key_name(t->key)) && t->key->type == DNS_TYPE_DS) { /* Hmm, this is a request for the root DS? A @@ -1237,8 +1235,7 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) { * might be DS RRs, but we don't know * them, and the DNS server won't tell * them to us (and even if it would, - * we couldn't validate it and trust - * it). */ + * we couldn't validate and trust them. */ dns_transaction_complete(t, DNS_TRANSACTION_NO_TRUST_ANCHOR); return 0; @@ -1372,7 +1369,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) { other->state = DNS_TRANSACTION_PENDING; other->next_attempt_after = ts; - qdcount ++; + qdcount++; if (dns_key_is_shared(other->key)) add_known_answers = true; @@ -1425,6 +1422,7 @@ static int dns_transaction_make_packet(DnsTransaction *t) { int dns_transaction_go(DnsTransaction *t) { usec_t ts; int r; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; assert(t); @@ -1434,12 +1432,12 @@ int dns_transaction_go(DnsTransaction *t) { if (r <= 0) return r; - log_debug("Excercising transaction %" PRIu16 " for <%s> on scope %s on %s/%s.", + log_debug("Transaction %" PRIu16 " for <%s> scope %s on %s/%s.", t->id, - dns_transaction_key_string(t), + dns_resource_key_to_string(t->key, key_str, sizeof key_str), dns_protocol_to_string(t->scope->protocol), t->scope->link ? t->scope->link->name : "*", - t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family)); + af_to_name_short(t->scope->family)); if (!t->initial_jitter_scheduled && (t->scope->protocol == DNS_PROTOCOL_LLMNR || @@ -1494,8 +1492,8 @@ int dns_transaction_go(DnsTransaction *t) { return r; if (t->scope->protocol == DNS_PROTOCOL_LLMNR && - (dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), "in-addr.arpa") > 0 || - dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), "ip6.arpa") > 0)) { + (dns_name_endswith(dns_resource_key_name(t->key), "in-addr.arpa") > 0 || + dns_name_endswith(dns_resource_key_name(t->key), "ip6.arpa") > 0)) { /* RFC 4795, Section 2.4. says reverse lookups shall * always be made via TCP on LLMNR */ @@ -1602,11 +1600,14 @@ static int dns_transaction_add_dnssec_transaction(DnsTransaction *t, DnsResource if (r < 0) return r; if (r > 0) { - log_debug("Detected potential cyclic dependency, refusing to add transaction %" PRIu16 " (%s) as dependency for %" PRIu16 " (%s).", + char s[DNS_RESOURCE_KEY_STRING_MAX], saux[DNS_RESOURCE_KEY_STRING_MAX]; + + log_debug("Potential cyclic dependency, refusing to add transaction %" PRIu16 " (%s) as dependency for %" PRIu16 " (%s).", aux->id, - strna(dns_transaction_key_string(aux)), + dns_resource_key_to_string(t->key, s, sizeof s), t->id, - strna(dns_transaction_key_string(t))); + dns_resource_key_to_string(aux->key, saux, sizeof saux)); + return -ELOOP; } } @@ -1619,6 +1620,10 @@ static int dns_transaction_add_dnssec_transaction(DnsTransaction *t, DnsResource if (r < 0) goto gc; + r = set_ensure_allocated(&aux->notify_transactions_done, NULL); + if (r < 0) + goto gc; + r = set_put(t->dnssec_transactions, aux); if (r < 0) goto gc; @@ -1678,7 +1683,7 @@ static int dns_transaction_negative_trust_anchor_lookup(DnsTransaction *t, const assert(t); - /* Check whether the specified name is in the the NTA + /* Check whether the specified name is in the NTA * database, either in the global one, or the link-local * one. */ @@ -1708,7 +1713,7 @@ static int dns_transaction_has_unsigned_negative_answer(DnsTransaction *t) { /* Is this key explicitly listed as a negative trust anchor? * If so, it's nothing we need to care about */ - r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(t->key)); + r = dns_transaction_negative_trust_anchor_lookup(t, dns_resource_key_name(t->key)); if (r < 0) return r; if (r > 0) @@ -1799,7 +1804,8 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { * - For unsigned SOA/NS we get the matching DS * - For unsigned CNAME/DNAME/DS we get the parent SOA RR * - For other unsigned RRs we get the matching SOA RR - * - For SOA/NS/DS queries with no matching response RRs, and no NSEC/NSEC3, the parent's SOA RR + * - For SOA/NS queries with no matching response RR, and no NSEC/NSEC3, the DS RR + * - For DS queries with no matching response RRs, and no NSEC/NSEC3, the parent's SOA RR * - For other queries with no matching response RRs, and no NSEC/NSEC3, the SOA RR */ @@ -1816,7 +1822,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { continue; /* If this RR is in the negative trust anchor, we don't need to validate it. */ - r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(rr->key)); + r = dns_transaction_negative_trust_anchor_lookup(t, dns_resource_key_name(rr->key)); if (r < 0) return r; if (r > 0) @@ -1833,7 +1839,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { * already have the DNSKEY, and we don't have * to look for more. */ if (rr->rrsig.type_covered == DNS_TYPE_DNSKEY) { - r = dns_name_equal(rr->rrsig.signer, DNS_RESOURCE_KEY_NAME(rr->key)); + r = dns_name_equal(rr->rrsig.signer, dns_resource_key_name(rr->key)); if (r < 0) return r; if (r > 0) @@ -1851,7 +1857,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { * in another transaction whose additonal RRs * point back to the original transaction, and * we deadlock. */ - r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), rr->rrsig.signer); + r = dns_name_endswith(dns_resource_key_name(t->key), rr->rrsig.signer); if (r < 0) return r; if (r == 0) @@ -1861,7 +1867,8 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { if (!dnskey) return -ENOMEM; - log_debug("Requesting DNSKEY to validate transaction %" PRIu16" (%s, RRSIG with key tag: %" PRIu16 ").", t->id, DNS_RESOURCE_KEY_NAME(rr->key), rr->rrsig.key_tag); + log_debug("Requesting DNSKEY to validate transaction %" PRIu16" (%s, RRSIG with key tag: %" PRIu16 ").", + t->id, dns_resource_key_name(rr->key), rr->rrsig.key_tag); r = dns_transaction_request_dnssec_rr(t, dnskey); if (r < 0) return r; @@ -1879,17 +1886,18 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { * up in request loops, and want to keep * additional traffic down. */ - r = dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), DNS_RESOURCE_KEY_NAME(rr->key)); + r = dns_name_endswith(dns_resource_key_name(t->key), dns_resource_key_name(rr->key)); if (r < 0) return r; if (r == 0) continue; - ds = dns_resource_key_new(rr->key->class, DNS_TYPE_DS, DNS_RESOURCE_KEY_NAME(rr->key)); + ds = dns_resource_key_new(rr->key->class, DNS_TYPE_DS, dns_resource_key_name(rr->key)); if (!ds) return -ENOMEM; - log_debug("Requesting DS to validate transaction %" PRIu16" (%s, DNSKEY with key tag: %" PRIu16 ").", t->id, DNS_RESOURCE_KEY_NAME(rr->key), dnssec_keytag(rr, false)); + log_debug("Requesting DS to validate transaction %" PRIu16" (%s, DNSKEY with key tag: %" PRIu16 ").", + t->id, dns_resource_key_name(rr->key), dnssec_keytag(rr, false)); r = dns_transaction_request_dnssec_rr(t, ds); if (r < 0) return r; @@ -1920,11 +1928,12 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { if (r > 0) continue; - ds = dns_resource_key_new(rr->key->class, DNS_TYPE_DS, DNS_RESOURCE_KEY_NAME(rr->key)); + ds = dns_resource_key_new(rr->key->class, DNS_TYPE_DS, dns_resource_key_name(rr->key)); if (!ds) return -ENOMEM; - log_debug("Requesting DS to validate transaction %" PRIu16 " (%s, unsigned SOA/NS RRset).", t->id, DNS_RESOURCE_KEY_NAME(rr->key)); + log_debug("Requesting DS to validate transaction %" PRIu16 " (%s, unsigned SOA/NS RRset).", + t->id, dns_resource_key_name(rr->key)); r = dns_transaction_request_dnssec_rr(t, ds); if (r < 0) return r; @@ -1966,7 +1975,7 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { if (r > 0) continue; - name = DNS_RESOURCE_KEY_NAME(rr->key); + name = dns_resource_key_name(rr->key); r = dns_name_parent(&name); if (r < 0) return r; @@ -1977,7 +1986,8 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { if (!soa) return -ENOMEM; - log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned CNAME/DNAME/DS RRset).", t->id, DNS_RESOURCE_KEY_NAME(rr->key)); + log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned CNAME/DNAME/DS RRset).", + t->id, dns_resource_key_name(rr->key)); r = dns_transaction_request_dnssec_rr(t, soa); if (r < 0) return r; @@ -2007,11 +2017,12 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { if (r > 0) continue; - soa = dns_resource_key_new(rr->key->class, DNS_TYPE_SOA, DNS_RESOURCE_KEY_NAME(rr->key)); + soa = dns_resource_key_new(rr->key->class, DNS_TYPE_SOA, dns_resource_key_name(rr->key)); if (!soa) return -ENOMEM; - log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned non-SOA/NS RRset <%s>).", t->id, DNS_RESOURCE_KEY_NAME(rr->key), dns_resource_record_to_string(rr)); + log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned non-SOA/NS RRset <%s>).", + t->id, dns_resource_key_name(rr->key), dns_resource_record_to_string(rr)); r = dns_transaction_request_dnssec_rr(t, soa); if (r < 0) return r; @@ -2028,30 +2039,42 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) { return r; if (r > 0) { const char *name; + uint16_t type = 0; - name = DNS_RESOURCE_KEY_NAME(t->key); + name = dns_resource_key_name(t->key); - /* If this was a SOA or NS request, then this - * indicates that we are not at a zone apex, hence ask - * the parent name instead. If this was a DS request, - * then it's signed when the parent zone is signed, - * hence ask the parent in that case, too. */ + /* If this was a SOA or NS request, then check if there's a DS RR for the same domain. Note that this + * could also be used as indication that we are not at a zone apex, but in real world setups there are + * too many broken DNS servers (Hello, incapdns.net!) where non-terminal zones return NXDOMAIN even + * though they have further children. If this was a DS request, then it's signed when the parent zone + * is signed, hence ask the parent SOA in that case. If this was any other RR then ask for the SOA RR, + * to see if that is signed. */ - if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS, DNS_TYPE_DS)) { + if (t->key->type == DNS_TYPE_DS) { r = dns_name_parent(&name); - if (r < 0) - return r; - if (r > 0) - log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS/DS response).", t->id, DNS_RESOURCE_KEY_NAME(t->key)); - else + if (r > 0) { + type = DNS_TYPE_SOA; + log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned empty DS response).", + t->id, dns_resource_key_name(t->key)); + } else name = NULL; - } else - log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned empty non-SOA/NS/DS response).", t->id, DNS_RESOURCE_KEY_NAME(t->key)); + + } else if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS)) { + + type = DNS_TYPE_DS; + log_debug("Requesting DS to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS response).", + t->id, dns_resource_key_name(t->key)); + + } else { + type = DNS_TYPE_SOA; + log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned empty non-SOA/NS/DS response).", + t->id, dns_resource_key_name(t->key)); + } if (name) { _cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL; - soa = dns_resource_key_new(t->key->class, DNS_TYPE_SOA, name); + soa = dns_resource_key_new(t->key->class, type, name); if (!soa) return -ENOMEM; @@ -2118,7 +2141,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord * if (dns_type_is_pseudo(rr->key->type)) return -EINVAL; - r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(rr->key)); + r = dns_transaction_negative_trust_anchor_lookup(t, dns_resource_key_name(rr->key)); if (r < 0) return r; if (r > 0) @@ -2144,7 +2167,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord * if (dt->key->type != DNS_TYPE_DS) continue; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), DNS_RESOURCE_KEY_NAME(rr->key)); + r = dns_name_equal(dns_resource_key_name(dt->key), dns_resource_key_name(rr->key)); if (r < 0) return r; if (r == 0) @@ -2187,7 +2210,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord * continue; if (!parent) { - parent = DNS_RESOURCE_KEY_NAME(rr->key); + parent = dns_resource_key_name(rr->key); r = dns_name_parent(&parent); if (r < 0) return r; @@ -2201,7 +2224,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord * } } - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), parent); + r = dns_name_equal(dns_resource_key_name(dt->key), parent); if (r < 0) return r; if (r == 0) @@ -2226,7 +2249,7 @@ static int dns_transaction_requires_rrsig(DnsTransaction *t, DnsResourceRecord * if (dt->key->type != DNS_TYPE_SOA) continue; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), DNS_RESOURCE_KEY_NAME(rr->key)); + r = dns_name_equal(dns_resource_key_name(dt->key), dns_resource_key_name(rr->key)); if (r < 0) return r; if (r == 0) @@ -2273,7 +2296,7 @@ static int dns_transaction_in_private_tld(DnsTransaction *t, const DnsResourceKe if (t->scope->dnssec_mode != DNSSEC_ALLOW_DOWNGRADE) return false; /* In strict DNSSEC mode what doesn't exist, doesn't exist */ - tld = DNS_RESOURCE_KEY_NAME(key); + tld = dns_resource_key_name(key); r = dns_name_parent(&tld); if (r < 0) return r; @@ -2288,7 +2311,7 @@ static int dns_transaction_in_private_tld(DnsTransaction *t, const DnsResourceKe if (dt->key->class != key->class) continue; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), tld); + r = dns_name_equal(dns_resource_key_name(dt->key), tld); if (r < 0) return r; if (r == 0) @@ -2305,8 +2328,10 @@ static int dns_transaction_in_private_tld(DnsTransaction *t, const DnsResourceKe } static int dns_transaction_requires_nsec(DnsTransaction *t) { + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; DnsTransaction *dt; const char *name; + uint16_t type = 0; Iterator i; int r; @@ -2321,7 +2346,7 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) { if (dns_type_is_pseudo(t->key->type)) return -EINVAL; - r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(t->key)); + r = dns_transaction_negative_trust_anchor_lookup(t, dns_resource_key_name(t->key)); if (r < 0) return r; if (r > 0) @@ -2335,28 +2360,32 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) { * exist, and we are in downgrade mode, hence ignore * that fact that we didn't get any NSEC RRs.*/ - log_info("Detected a negative query %s in a private DNS zone, permitting unsigned response.", dns_transaction_key_string(t)); + log_info("Detected a negative query %s in a private DNS zone, permitting unsigned response.", + dns_resource_key_to_string(t->key, key_str, sizeof key_str)); return false; } - name = DNS_RESOURCE_KEY_NAME(t->key); + name = dns_resource_key_name(t->key); - if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS, DNS_TYPE_DS)) { + if (t->key->type == DNS_TYPE_DS) { - /* We got a negative reply for this SOA/NS lookup? If - * so, then we are not at a zone apex, and thus should - * look at the result of the parent SOA lookup. - * - * We got a negative reply for this DS lookup? DS RRs - * are signed when their parent zone is signed, hence - * also check the parent SOA in this case. */ + /* We got a negative reply for this DS lookup? DS RRs are signed when their parent zone is signed, + * hence check the parent SOA in this case. */ r = dns_name_parent(&name); if (r < 0) return r; if (r == 0) return true; - } + + type = DNS_TYPE_SOA; + + } else if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS)) + /* We got a negative reply for this SOA/NS lookup? If so, check if there's a DS RR for this */ + type = DNS_TYPE_DS; + else + /* For all other negative replies, check for the SOA lookup */ + type = DNS_TYPE_SOA; /* For all other RRs we check the SOA on the same level to see * if it's signed. */ @@ -2365,10 +2394,10 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) { if (dt->key->class != t->key->class) continue; - if (dt->key->type != DNS_TYPE_SOA) + if (dt->key->type != type) continue; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), name); + r = dns_name_equal(dns_resource_key_name(dt->key), name); if (r < 0) return r; if (r == 0) @@ -2390,7 +2419,7 @@ static int dns_transaction_dnskey_authenticated(DnsTransaction *t, DnsResourceRe * the specified RRset is authenticated (i.e. has a matching * DS RR). */ - r = dns_transaction_negative_trust_anchor_lookup(t, DNS_RESOURCE_KEY_NAME(rr->key)); + r = dns_transaction_negative_trust_anchor_lookup(t, dns_resource_key_name(rr->key)); if (r < 0) return r; if (r > 0) @@ -2413,7 +2442,7 @@ static int dns_transaction_dnskey_authenticated(DnsTransaction *t, DnsResourceRe if (dt->key->type == DNS_TYPE_DNSKEY) { - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), rrsig->rrsig.signer); + r = dns_name_equal(dns_resource_key_name(dt->key), rrsig->rrsig.signer); if (r < 0) return r; if (r == 0) @@ -2430,7 +2459,7 @@ static int dns_transaction_dnskey_authenticated(DnsTransaction *t, DnsResourceRe } else if (dt->key->type == DNS_TYPE_DS) { - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(dt->key), rrsig->rrsig.signer); + r = dns_name_equal(dns_resource_key_name(dt->key), rrsig->rrsig.signer); if (r < 0) return r; if (r == 0) @@ -2460,7 +2489,7 @@ static int dns_transaction_known_signed(DnsTransaction *t, DnsResourceRecord *rr * not to be signed, there's a problem with the DNS server */ return rr->key->class == DNS_CLASS_IN && - dns_name_is_root(DNS_RESOURCE_KEY_NAME(rr->key)); + dns_name_is_root(dns_resource_key_name(rr->key)); } static int dns_transaction_check_revoked_trust_anchors(DnsTransaction *t) { @@ -2541,343 +2570,347 @@ static int dns_transaction_copy_validated(DnsTransaction *t) { return 0; } -int dns_transaction_validate_dnssec(DnsTransaction *t) { - _cleanup_(dns_answer_unrefp) DnsAnswer *validated = NULL; - enum { - PHASE_DNSKEY, /* Phase #1, only validate DNSKEYs */ - PHASE_NSEC, /* Phase #2, only validate NSEC+NSEC3 */ - PHASE_ALL, /* Phase #3, validate everything else */ - } phase; +typedef enum { + DNSSEC_PHASE_DNSKEY, /* Phase #1, only validate DNSKEYs */ + DNSSEC_PHASE_NSEC, /* Phase #2, only validate NSEC+NSEC3 */ + DNSSEC_PHASE_ALL, /* Phase #3, validate everything else */ +} Phase; + +static int dnssec_validate_records( + DnsTransaction *t, + Phase phase, + bool *have_nsec, + DnsAnswer **validated) { + DnsResourceRecord *rr; - DnsAnswerFlags flags; int r; - assert(t); + /* Returns negative on error, 0 if validation failed, 1 to restart validation, 2 when finished. */ - /* We have now collected all DS and DNSKEY RRs in - * t->validated_keys, let's see which RRs we can now - * authenticate with that. */ + DNS_ANSWER_FOREACH(rr, t->answer) { + DnsResourceRecord *rrsig = NULL; + DnssecResult result; - if (t->scope->dnssec_mode == DNSSEC_NO) - return 0; + switch (rr->key->type) { + case DNS_TYPE_RRSIG: + continue; - /* Already validated */ - if (t->answer_dnssec_result != _DNSSEC_RESULT_INVALID) - return 0; + case DNS_TYPE_DNSKEY: + /* We validate DNSKEYs only in the DNSKEY and ALL phases */ + if (phase == DNSSEC_PHASE_NSEC) + continue; + break; - /* Our own stuff needs no validation */ - if (IN_SET(t->answer_source, DNS_TRANSACTION_ZONE, DNS_TRANSACTION_TRUST_ANCHOR)) { - t->answer_dnssec_result = DNSSEC_VALIDATED; - t->answer_authenticated = true; - return 0; - } + case DNS_TYPE_NSEC: + case DNS_TYPE_NSEC3: + *have_nsec = true; - /* Cached stuff is not affected by validation. */ - if (t->answer_source != DNS_TRANSACTION_NETWORK) - return 0; + /* We validate NSEC/NSEC3 only in the NSEC and ALL phases */ + if (phase == DNSSEC_PHASE_DNSKEY) + continue; + break; - if (!dns_transaction_dnssec_supported_full(t)) { - /* The server does not support DNSSEC, or doesn't augment responses with RRSIGs. */ - t->answer_dnssec_result = DNSSEC_INCOMPATIBLE_SERVER; - log_debug("Not validating response for %" PRIu16 ", server lacks DNSSEC support.", t->id); - return 0; - } + default: + /* We validate all other RRs only in the ALL phases */ + if (phase != DNSSEC_PHASE_ALL) + continue; + } - log_debug("Validating response from transaction %" PRIu16 " (%s).", t->id, dns_transaction_key_string(t)); + r = dnssec_verify_rrset_search(t->answer, rr->key, t->validated_keys, USEC_INFINITY, &result, &rrsig); + if (r < 0) + return r; - /* First, see if this response contains any revoked trust - * anchors we care about */ - r = dns_transaction_check_revoked_trust_anchors(t); - if (r < 0) - return r; + log_debug("Looking at %s: %s", strna(dns_resource_record_to_string(rr)), dnssec_result_to_string(result)); - /* Third, copy all RRs we acquired successfully from auxiliary RRs over. */ - r = dns_transaction_copy_validated(t); - if (r < 0) - return r; + if (result == DNSSEC_VALIDATED) { - /* Second, see if there are DNSKEYs we already know a - * validated DS for. */ - r = dns_transaction_validate_dnskey_by_ds(t); - if (r < 0) - return r; + if (rr->key->type == DNS_TYPE_DNSKEY) { + /* If we just validated a DNSKEY RRset, then let's add these keys to + * the set of validated keys for this transaction. */ - /* Fourth, remove all DNSKEY and DS RRs again that our trust - * anchor says are revoked. After all we might have marked - * some keys revoked above, but they might still be lingering - * in our validated_keys list. */ - r = dns_transaction_invalidate_revoked_keys(t); - if (r < 0) - return r; + r = dns_answer_copy_by_key(&t->validated_keys, t->answer, rr->key, DNS_ANSWER_AUTHENTICATED); + if (r < 0) + return r; - phase = PHASE_DNSKEY; - for (;;) { - bool changed = false, have_nsec = false; + /* Some of the DNSKEYs we just added might already have been revoked, + * remove them again in that case. */ + r = dns_transaction_invalidate_revoked_keys(t); + if (r < 0) + return r; + } - DNS_ANSWER_FOREACH(rr, t->answer) { - DnsResourceRecord *rrsig = NULL; - DnssecResult result; + /* Add the validated RRset to the new list of validated + * RRsets, and remove it from the unvalidated RRsets. + * We mark the RRset as authenticated and cacheable. */ + r = dns_answer_move_by_key(validated, &t->answer, rr->key, DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE); + if (r < 0) + return r; - switch (rr->key->type) { + manager_dnssec_verdict(t->scope->manager, DNSSEC_SECURE, rr->key); - case DNS_TYPE_RRSIG: - continue; + /* Exit the loop, we dropped something from the answer, start from the beginning */ + return 1; + } - case DNS_TYPE_DNSKEY: - /* We validate DNSKEYs only in the DNSKEY and ALL phases */ - if (phase == PHASE_NSEC) - continue; - break; + /* If we haven't read all DNSKEYs yet a negative result of the validation is irrelevant, as + * there might be more DNSKEYs coming. Similar, if we haven't read all NSEC/NSEC3 RRs yet, + * we cannot do positive wildcard proofs yet, as those require the NSEC/NSEC3 RRs. */ + if (phase != DNSSEC_PHASE_ALL) + continue; - case DNS_TYPE_NSEC: - case DNS_TYPE_NSEC3: - have_nsec = true; + if (result == DNSSEC_VALIDATED_WILDCARD) { + bool authenticated = false; + const char *source; - /* We validate NSEC/NSEC3 only in the NSEC and ALL phases */ - if (phase == PHASE_DNSKEY) - continue; + /* This RRset validated, but as a wildcard. This means we need + * to prove via NSEC/NSEC3 that no matching non-wildcard RR exists.*/ - break; + /* First step, determine the source of synthesis */ + r = dns_resource_record_source(rrsig, &source); + if (r < 0) + return r; - default: - /* We validate all other RRs only in the ALL phases */ - if (phase != PHASE_ALL) - continue; + r = dnssec_test_positive_wildcard(*validated, + dns_resource_key_name(rr->key), + source, + rrsig->rrsig.signer, + &authenticated); - break; + /* Unless the NSEC proof showed that the key really doesn't exist something is off. */ + if (r == 0) + result = DNSSEC_INVALID; + else { + r = dns_answer_move_by_key(validated, &t->answer, rr->key, + authenticated ? (DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE) : 0); + if (r < 0) + return r; + + manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, rr->key); + + /* Exit the loop, we dropped something from the answer, start from the beginning */ + return 1; } + } - r = dnssec_verify_rrset_search(t->answer, rr->key, t->validated_keys, USEC_INFINITY, &result, &rrsig); + if (result == DNSSEC_NO_SIGNATURE) { + r = dns_transaction_requires_rrsig(t, rr); if (r < 0) return r; + if (r == 0) { + /* Data does not require signing. In that case, just copy it over, + * but remember that this is by no means authenticated.*/ + r = dns_answer_move_by_key(validated, &t->answer, rr->key, 0); + if (r < 0) + return r; + + manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); + return 1; + } - log_debug("Looking at %s: %s", strna(dns_resource_record_to_string(rr)), dnssec_result_to_string(result)); + r = dns_transaction_known_signed(t, rr); + if (r < 0) + return r; + if (r > 0) { + /* This is an RR we know has to be signed. If it isn't this means + * the server is not attaching RRSIGs, hence complain. */ - if (result == DNSSEC_VALIDATED) { + dns_server_packet_rrsig_missing(t->server, t->current_feature_level); - if (rr->key->type == DNS_TYPE_DNSKEY) { - /* If we just validated a - * DNSKEY RRset, then let's - * add these keys to the set - * of validated keys for this - * transaction. */ + if (t->scope->dnssec_mode == DNSSEC_ALLOW_DOWNGRADE) { - r = dns_answer_copy_by_key(&t->validated_keys, t->answer, rr->key, DNS_ANSWER_AUTHENTICATED); - if (r < 0) - return r; + /* Downgrading is OK? If so, just consider the information unsigned */ - /* some of the DNSKEYs we just - * added might already have - * been revoked, remove them - * again in that case. */ - r = dns_transaction_invalidate_revoked_keys(t); + r = dns_answer_move_by_key(validated, &t->answer, rr->key, 0); if (r < 0) return r; - } - /* Add the validated RRset to the new - * list of validated RRsets, and - * remove it from the unvalidated - * RRsets. We mark the RRset as - * authenticated and cacheable. */ - r = dns_answer_move_by_key(&validated, &t->answer, rr->key, DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE); - if (r < 0) - return r; - - manager_dnssec_verdict(t->scope->manager, DNSSEC_SECURE, rr->key); + manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); + return 1; + } - /* Exit the loop, we dropped something from the answer, start from the beginning */ - changed = true; - break; + /* Otherwise, fail */ + t->answer_dnssec_result = DNSSEC_INCOMPATIBLE_SERVER; + return 0; } - /* If we haven't read all DNSKEYs yet a negative result of the validation is irrelevant, as - * there might be more DNSKEYs coming. Similar, if we haven't read all NSEC/NSEC3 RRs yet, we - * cannot do positive wildcard proofs yet, as those require the NSEC/NSEC3 RRs. */ - if (phase != PHASE_ALL) - continue; + r = dns_transaction_in_private_tld(t, rr->key); + if (r < 0) + return r; + if (r > 0) { + char s[DNS_RESOURCE_KEY_STRING_MAX]; - if (result == DNSSEC_VALIDATED_WILDCARD) { - bool authenticated = false; - const char *source; + /* The data is from a TLD that is proven not to exist, and we are in downgrade + * mode, hence ignore the fact that this was not signed. */ - /* This RRset validated, but as a wildcard. This means we need to prove via NSEC/NSEC3 - * that no matching non-wildcard RR exists.*/ + log_info("Detected RRset %s is in a private DNS zone, permitting unsigned RRs.", + dns_resource_key_to_string(rr->key, s, sizeof s)); - /* First step, determine the source of synthesis */ - r = dns_resource_record_source(rrsig, &source); + r = dns_answer_move_by_key(validated, &t->answer, rr->key, 0); if (r < 0) return r; - r = dnssec_test_positive_wildcard( - validated, - DNS_RESOURCE_KEY_NAME(rr->key), - source, - rrsig->rrsig.signer, - &authenticated); - - /* Unless the NSEC proof showed that the key really doesn't exist something is off. */ - if (r == 0) - result = DNSSEC_INVALID; - else { - r = dns_answer_move_by_key(&validated, &t->answer, rr->key, authenticated ? (DNS_ANSWER_AUTHENTICATED|DNS_ANSWER_CACHEABLE) : 0); - if (r < 0) - return r; + manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); + return 1; + } + } - manager_dnssec_verdict(t->scope->manager, authenticated ? DNSSEC_SECURE : DNSSEC_INSECURE, rr->key); + if (IN_SET(result, + DNSSEC_MISSING_KEY, + DNSSEC_SIGNATURE_EXPIRED, + DNSSEC_UNSUPPORTED_ALGORITHM)) { - /* Exit the loop, we dropped something from the answer, start from the beginning */ - changed = true; - break; - } - } + r = dns_transaction_dnskey_authenticated(t, rr); + if (r < 0 && r != -ENXIO) + return r; + if (r == 0) { + /* The DNSKEY transaction was not authenticated, this means there's + * no DS for this, which means it's OK if no keys are found for this signature. */ - if (result == DNSSEC_NO_SIGNATURE) { - r = dns_transaction_requires_rrsig(t, rr); + r = dns_answer_move_by_key(validated, &t->answer, rr->key, 0); if (r < 0) return r; - if (r == 0) { - /* Data does not require signing. In that case, just copy it over, - * but remember that this is by no means authenticated.*/ - r = dns_answer_move_by_key(&validated, &t->answer, rr->key, 0); - if (r < 0) - return r; - manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); - changed = true; - break; - } + manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); + return 1; + } + } - r = dns_transaction_known_signed(t, rr); + r = dns_transaction_is_primary_response(t, rr); + if (r < 0) + return r; + if (r > 0) { + /* Look for a matching DNAME for this CNAME */ + r = dns_answer_has_dname_for_cname(t->answer, rr); + if (r < 0) + return r; + if (r == 0) { + /* Also look among the stuff we already validated */ + r = dns_answer_has_dname_for_cname(*validated, rr); if (r < 0) return r; - if (r > 0) { - /* This is an RR we know has to be signed. If it isn't this means - * the server is not attaching RRSIGs, hence complain. */ - - dns_server_packet_rrsig_missing(t->server, t->current_feature_level); + } - if (t->scope->dnssec_mode == DNSSEC_ALLOW_DOWNGRADE) { + if (r == 0) { + if (IN_SET(result, + DNSSEC_INVALID, + DNSSEC_SIGNATURE_EXPIRED, + DNSSEC_NO_SIGNATURE)) + manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, rr->key); + else /* DNSSEC_MISSING_KEY or DNSSEC_UNSUPPORTED_ALGORITHM */ + manager_dnssec_verdict(t->scope->manager, DNSSEC_INDETERMINATE, rr->key); + + /* This is a primary response to our question, and it failed validation. + * That's fatal. */ + t->answer_dnssec_result = result; + return 0; + } - /* Downgrading is OK? If so, just consider the information unsigned */ + /* This is a primary response, but we do have a DNAME RR + * in the RR that can replay this CNAME, hence rely on + * that, and we can remove the CNAME in favour of it. */ + } - r = dns_answer_move_by_key(&validated, &t->answer, rr->key, 0); - if (r < 0) - return r; + /* This is just some auxiliary data. Just remove the RRset and continue. */ + r = dns_answer_remove_by_key(&t->answer, rr->key); + if (r < 0) + return r; - manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); - changed = true; - break; - } + /* We dropped something from the answer, start from the beginning. */ + return 1; + } - /* Otherwise, fail */ - t->answer_dnssec_result = DNSSEC_INCOMPATIBLE_SERVER; - return 0; - } + return 2; /* Finito. */ +} - r = dns_transaction_in_private_tld(t, rr->key); - if (r < 0) - return r; - if (r > 0) { - _cleanup_free_ char *s = NULL; +int dns_transaction_validate_dnssec(DnsTransaction *t) { + _cleanup_(dns_answer_unrefp) DnsAnswer *validated = NULL; + Phase phase; + DnsAnswerFlags flags; + int r; + char key_str[DNS_RESOURCE_KEY_STRING_MAX]; - /* The data is from a TLD that is proven not to exist, and we are in downgrade - * mode, hence ignore the fact that this was not signed. */ + assert(t); - (void) dns_resource_key_to_string(rr->key, &s); - log_info("Detected RRset %s is in a private DNS zone, permitting unsigned RRs.", strna(s ? strstrip(s) : NULL)); + /* We have now collected all DS and DNSKEY RRs in + * t->validated_keys, let's see which RRs we can now + * authenticate with that. */ - r = dns_answer_move_by_key(&validated, &t->answer, rr->key, 0); - if (r < 0) - return r; + if (t->scope->dnssec_mode == DNSSEC_NO) + return 0; - manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); - changed = true; - break; - } - } + /* Already validated */ + if (t->answer_dnssec_result != _DNSSEC_RESULT_INVALID) + return 0; - if (IN_SET(result, - DNSSEC_MISSING_KEY, - DNSSEC_SIGNATURE_EXPIRED, - DNSSEC_UNSUPPORTED_ALGORITHM)) { + /* Our own stuff needs no validation */ + if (IN_SET(t->answer_source, DNS_TRANSACTION_ZONE, DNS_TRANSACTION_TRUST_ANCHOR)) { + t->answer_dnssec_result = DNSSEC_VALIDATED; + t->answer_authenticated = true; + return 0; + } - r = dns_transaction_dnskey_authenticated(t, rr); - if (r < 0 && r != -ENXIO) - return r; - if (r == 0) { - /* The DNSKEY transaction was not authenticated, this means there's - * no DS for this, which means it's OK if no keys are found for this signature. */ + /* Cached stuff is not affected by validation. */ + if (t->answer_source != DNS_TRANSACTION_NETWORK) + return 0; - r = dns_answer_move_by_key(&validated, &t->answer, rr->key, 0); - if (r < 0) - return r; + if (!dns_transaction_dnssec_supported_full(t)) { + /* The server does not support DNSSEC, or doesn't augment responses with RRSIGs. */ + t->answer_dnssec_result = DNSSEC_INCOMPATIBLE_SERVER; + log_debug("Not validating response for %" PRIu16 ", server lacks DNSSEC support.", t->id); + return 0; + } - manager_dnssec_verdict(t->scope->manager, DNSSEC_INSECURE, rr->key); - changed = true; - break; - } - } + log_debug("Validating response from transaction %" PRIu16 " (%s).", + t->id, + dns_resource_key_to_string(t->key, key_str, sizeof key_str)); - r = dns_transaction_is_primary_response(t, rr); - if (r < 0) - return r; - if (r > 0) { + /* First, see if this response contains any revoked trust + * anchors we care about */ + r = dns_transaction_check_revoked_trust_anchors(t); + if (r < 0) + return r; - /* Look for a matching DNAME for this CNAME */ - r = dns_answer_has_dname_for_cname(t->answer, rr); - if (r < 0) - return r; - if (r == 0) { - /* Also look among the stuff we already validated */ - r = dns_answer_has_dname_for_cname(validated, rr); - if (r < 0) - return r; - } + /* Third, copy all RRs we acquired successfully from auxiliary RRs over. */ + r = dns_transaction_copy_validated(t); + if (r < 0) + return r; - if (r == 0) { - if (IN_SET(result, - DNSSEC_INVALID, - DNSSEC_SIGNATURE_EXPIRED, - DNSSEC_NO_SIGNATURE)) - manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, rr->key); - else /* DNSSEC_MISSING_KEY or DNSSEC_UNSUPPORTED_ALGORITHM */ - manager_dnssec_verdict(t->scope->manager, DNSSEC_INDETERMINATE, rr->key); - - /* This is a primary response to our question, and it failed validation. That's - * fatal. */ - t->answer_dnssec_result = result; - return 0; - } + /* Second, see if there are DNSKEYs we already know a + * validated DS for. */ + r = dns_transaction_validate_dnskey_by_ds(t); + if (r < 0) + return r; - /* This is a primary response, but we do have a DNAME RR in the RR that can replay this - * CNAME, hence rely on that, and we can remove the CNAME in favour of it. */ - } + /* Fourth, remove all DNSKEY and DS RRs again that our trust + * anchor says are revoked. After all we might have marked + * some keys revoked above, but they might still be lingering + * in our validated_keys list. */ + r = dns_transaction_invalidate_revoked_keys(t); + if (r < 0) + return r; - /* This is just some auxiliary data. Just remove the RRset and continue. */ - r = dns_answer_remove_by_key(&t->answer, rr->key); - if (r < 0) - return r; + phase = DNSSEC_PHASE_DNSKEY; + for (;;) { + bool have_nsec = false; - /* Exit the loop, we dropped something from the answer, start from the beginning */ - changed = true; - break; - } + r = dnssec_validate_records(t, phase, &have_nsec, &validated); + if (r <= 0) + return r; - /* Restart the inner loop as long as we managed to achieve something */ - if (changed) + /* Try again as long as we managed to achieve something */ + if (r == 1) continue; - if (phase == PHASE_DNSKEY && have_nsec) { + if (phase == DNSSEC_PHASE_DNSKEY && have_nsec) { /* OK, we processed all DNSKEYs, and there are NSEC/NSEC3 RRs, look at those now. */ - phase = PHASE_NSEC; + phase = DNSSEC_PHASE_NSEC; continue; } - if (phase != PHASE_ALL) { - /* OK, we processed all DNSKEYs and NSEC/NSEC3 RRs, look at all the rest now. Note that in this - * third phase we start to remove RRs we couldn't validate. */ - phase = PHASE_ALL; + if (phase != DNSSEC_PHASE_ALL) { + /* OK, we processed all DNSKEYs and NSEC/NSEC3 RRs, look at all the rest now. + * Note that in this third phase we start to remove RRs we couldn't validate. */ + phase = DNSSEC_PHASE_ALL; continue; } @@ -2921,7 +2954,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { case DNSSEC_NSEC_NXDOMAIN: /* NSEC proves the domain doesn't exist. Very good. */ - log_debug("Proved NXDOMAIN via NSEC/NSEC3 for transaction %u (%s)", t->id, dns_transaction_key_string(t)); + log_debug("Proved NXDOMAIN via NSEC/NSEC3 for transaction %u (%s)", t->id, key_str); t->answer_dnssec_result = DNSSEC_VALIDATED; t->answer_rcode = DNS_RCODE_NXDOMAIN; t->answer_authenticated = authenticated; @@ -2931,7 +2964,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { case DNSSEC_NSEC_NODATA: /* NSEC proves that there's no data here, very good. */ - log_debug("Proved NODATA via NSEC/NSEC3 for transaction %u (%s)", t->id, dns_transaction_key_string(t)); + log_debug("Proved NODATA via NSEC/NSEC3 for transaction %u (%s)", t->id, key_str); t->answer_dnssec_result = DNSSEC_VALIDATED; t->answer_rcode = DNS_RCODE_SUCCESS; t->answer_authenticated = authenticated; @@ -2941,7 +2974,7 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { case DNSSEC_NSEC_OPTOUT: /* NSEC3 says the data might not be signed */ - log_debug("Data is NSEC3 opt-out via NSEC/NSEC3 for transaction %u (%s)", t->id, dns_transaction_key_string(t)); + log_debug("Data is NSEC3 opt-out via NSEC/NSEC3 for transaction %u (%s)", t->id, key_str); t->answer_dnssec_result = DNSSEC_UNSIGNED; t->answer_authenticated = false; @@ -2986,17 +3019,6 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) { return 1; } -const char *dns_transaction_key_string(DnsTransaction *t) { - assert(t); - - if (!t->key_string) { - if (dns_resource_key_to_string(t->key, &t->key_string) < 0) - return "n/a"; - } - - return strstrip(t->key_string); -} - static const char* const dns_transaction_state_table[_DNS_TRANSACTION_STATE_MAX] = { [DNS_TRANSACTION_NULL] = "null", [DNS_TRANSACTION_PENDING] = "pending", diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index 4617194711..eaece91533 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -64,7 +64,6 @@ struct DnsTransaction { DnsScope *scope; DnsResourceKey *key; - char *key_string; DnsTransactionState state; @@ -119,17 +118,17 @@ struct DnsTransaction { /* Query candidates this transaction is referenced by and that * shall be notified about this specific transaction * completing. */ - Set *notify_query_candidates; + Set *notify_query_candidates, *notify_query_candidates_done; /* Zone items this transaction is referenced by and that shall * be notified about completion. */ - Set *notify_zone_items; + Set *notify_zone_items, *notify_zone_items_done; /* Other transactions that this transactions is referenced by * and that shall be notified about completion. This is used * when transactions want to validate their RRsets, but need * another DNSKEY or DS RR to do so. */ - Set *notify_transactions; + Set *notify_transactions, *notify_transactions_done; /* The opposite direction: the transactions this transaction * created in order to request DNSKEY or DS RRs. */ @@ -153,8 +152,6 @@ void dns_transaction_notify(DnsTransaction *t, DnsTransaction *source); int dns_transaction_validate_dnssec(DnsTransaction *t); int dns_transaction_request_dnssec_keys(DnsTransaction *t); -const char *dns_transaction_key_string(DnsTransaction *t); - const char* dns_transaction_state_to_string(DnsTransactionState p) _const_; DnsTransactionState dns_transaction_state_from_string(const char *s) _pure_; diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index a75337eb6a..77370e7dd5 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -651,7 +651,7 @@ static int dns_trust_anchor_check_revoked_one(DnsTrustAnchor *d, DnsResourceReco } } - a = hashmap_get(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(revoked_dnskey->key->class, DNS_TYPE_DS, DNS_RESOURCE_KEY_NAME(revoked_dnskey->key))); + a = hashmap_get(d->positive_by_key, &DNS_RESOURCE_KEY_CONST(revoked_dnskey->key->class, DNS_TYPE_DS, dns_resource_key_name(revoked_dnskey->key))); if (a) { DnsResourceRecord *anchor; @@ -698,7 +698,7 @@ int dns_trust_anchor_check_revoked(DnsTrustAnchor *d, DnsResourceRecord *dnskey, /* Could this be interesting to us at all? If not, * there's no point in looking for and verifying a * self-signed RRSIG. */ - if (!dns_trust_anchor_knows_domain_positive(d, DNS_RESOURCE_KEY_NAME(dnskey->key))) + if (!dns_trust_anchor_knows_domain_positive(d, dns_resource_key_name(dnskey->key))) return 0; /* Look for a self-signed RRSIG in the other rrs belonging to this DNSKEY */ diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index f52383cfd1..850eed8cb8 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -38,6 +38,7 @@ void dns_zone_item_probe_stop(DnsZoneItem *i) { i->probe_transaction = NULL; set_remove(t->notify_zone_items, i); + set_remove(t->notify_zone_items_done, i); dns_transaction_gc(t); } @@ -68,12 +69,12 @@ static void dns_zone_item_remove_and_free(DnsZone *z, DnsZoneItem *i) { else hashmap_remove(z->by_key, i->rr->key); - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key)); + first = hashmap_get(z->by_name, dns_resource_key_name(i->rr->key)); LIST_REMOVE(by_name, first, i); if (first) - assert_se(hashmap_replace(z->by_name, DNS_RESOURCE_KEY_NAME(first->rr->key), first) >= 0); + assert_se(hashmap_replace(z->by_name, dns_resource_key_name(first->rr->key), first) >= 0); else - hashmap_remove(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key)); + hashmap_remove(z->by_name, dns_resource_key_name(i->rr->key)); dns_zone_item_free(i); } @@ -147,12 +148,12 @@ static int dns_zone_link_item(DnsZone *z, DnsZoneItem *i) { return r; } - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key)); + first = hashmap_get(z->by_name, dns_resource_key_name(i->rr->key)); if (first) { LIST_PREPEND(by_name, first, i); - assert_se(hashmap_replace(z->by_name, DNS_RESOURCE_KEY_NAME(first->rr->key), first) >= 0); + assert_se(hashmap_replace(z->by_name, dns_resource_key_name(first->rr->key), first) >= 0); } else { - r = hashmap_put(z->by_name, DNS_RESOURCE_KEY_NAME(i->rr->key), i); + r = hashmap_put(z->by_name, dns_resource_key_name(i->rr->key), i); if (r < 0) return r; } @@ -169,11 +170,11 @@ static int dns_zone_item_probe_start(DnsZoneItem *i) { if (i->probe_transaction) return 0; - t = dns_scope_find_transaction(i->scope, &DNS_RESOURCE_KEY_CONST(i->rr->key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(i->rr->key)), false); + t = dns_scope_find_transaction(i->scope, &DNS_RESOURCE_KEY_CONST(i->rr->key->class, DNS_TYPE_ANY, dns_resource_key_name(i->rr->key)), false); if (!t) { _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; - key = dns_resource_key_new(i->rr->key->class, DNS_TYPE_ANY, DNS_RESOURCE_KEY_NAME(i->rr->key)); + key = dns_resource_key_new(i->rr->key->class, DNS_TYPE_ANY, dns_resource_key_name(i->rr->key)); if (!key) return -ENOMEM; @@ -186,6 +187,10 @@ static int dns_zone_item_probe_start(DnsZoneItem *i) { if (r < 0) goto gc; + r = set_ensure_allocated(&t->notify_zone_items_done, NULL); + if (r < 0) + goto gc; + r = set_put(t->notify_zone_items, i); if (r < 0) goto gc; @@ -303,7 +308,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns * go through the list by the name and look * for everything manually */ - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key)); + first = hashmap_get(z->by_name, dns_resource_key_name(key)); LIST_FOREACH(by_name, j, first) { if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) continue; @@ -339,7 +344,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns } if (!found) { - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key)); + first = hashmap_get(z->by_name, dns_resource_key_name(key)); LIST_FOREACH(by_name, j, first) { if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) continue; @@ -370,7 +375,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns bool found = false, added = false; int k; - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key)); + first = hashmap_get(z->by_name, dns_resource_key_name(key)); LIST_FOREACH(by_name, j, first) { if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) continue; @@ -393,7 +398,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns } if (found && !added) { - r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(key), LLMNR_DEFAULT_TTL); + r = dns_answer_add_soa(soa, dns_resource_key_name(key), LLMNR_DEFAULT_TTL); if (r < 0) return r; } @@ -418,7 +423,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns if (!found) { bool add_soa = false; - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key)); + first = hashmap_get(z->by_name, dns_resource_key_name(key)); LIST_FOREACH(by_name, j, first) { if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) continue; @@ -430,7 +435,7 @@ int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, Dns } if (add_soa) { - r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(key), LLMNR_DEFAULT_TTL); + r = dns_answer_add_soa(soa, dns_resource_key_name(key), LLMNR_DEFAULT_TTL); if (r < 0) return r; } @@ -482,7 +487,7 @@ void dns_zone_item_conflict(DnsZoneItem *i) { i->state = DNS_ZONE_ITEM_WITHDRAWN; /* Maybe change the hostname */ - if (manager_is_own_hostname(i->scope->manager, DNS_RESOURCE_KEY_NAME(i->rr->key)) > 0) + if (manager_is_own_hostname(i->scope->manager, dns_resource_key_name(i->rr->key)) > 0) manager_next_hostname(i->scope->manager); } @@ -562,7 +567,7 @@ int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr) { * so, we'll verify our RRs. */ /* No conflict if we don't have the name at all. */ - first = hashmap_get(zone->by_name, DNS_RESOURCE_KEY_NAME(rr->key)); + first = hashmap_get(zone->by_name, dns_resource_key_name(rr->key)); if (!first) return 0; @@ -593,7 +598,7 @@ int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key) { /* Somebody else notified us about a possible conflict. Let's * verify if that's true. */ - first = hashmap_get(zone->by_name, DNS_RESOURCE_KEY_NAME(key)); + first = hashmap_get(zone->by_name, dns_resource_key_name(key)); if (!first) return 0; diff --git a/src/resolve/resolved-etc-hosts.c b/src/resolve/resolved-etc-hosts.c index ee82c96822..40d650949d 100644 --- a/src/resolve/resolved-etc-hosts.c +++ b/src/resolve/resolved-etc-hosts.c @@ -301,7 +301,7 @@ int manager_etc_hosts_read(Manager *m) { FOREACH_LINE(line, f, return log_error_errno(errno, "Failed to read /etc/hosts: %m")) { char *l; - nr ++; + nr++; l = strstrip(line); if (isempty(l)) @@ -363,7 +363,7 @@ int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) { if (!IN_SET(t->class, DNS_CLASS_IN, DNS_CLASS_ANY)) continue; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(t), name); + r = dns_name_equal(dns_resource_key_name(t), name); if (r < 0) return r; if (r > 0) { @@ -413,7 +413,7 @@ int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) { if (!IN_SET(t->class, DNS_CLASS_IN, DNS_CLASS_ANY)) continue; - r = dns_name_equal(DNS_RESOURCE_KEY_NAME(t), name); + r = dns_name_equal(dns_resource_key_name(t), name); if (r < 0) return r; if (r == 0) diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c index df7516f4f4..7f21891819 100644 --- a/src/resolve/resolved-link-bus.c +++ b/src/resolve/resolved-link-bus.c @@ -239,7 +239,7 @@ clear: return r; } -int bus_link_method_set_search_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) { +int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error) { Link *l = userdata; int r; @@ -457,10 +457,10 @@ const sd_bus_vtable link_vtable[] = { SD_BUS_PROPERTY("MulticastDNS", "s", property_get_resolve_support, offsetof(Link, mdns_support), 0), SD_BUS_PROPERTY("DNSSEC", "s", property_get_dnssec_mode, offsetof(Link, dnssec_mode), 0), SD_BUS_PROPERTY("DNSSECNegativeTrustAnchors", "as", property_get_ntas, 0, 0), - SD_BUS_PROPERTY("DNSSECSupport", "b", property_get_dnssec_supported, 0, 0), + SD_BUS_PROPERTY("DNSSECSupported", "b", property_get_dnssec_supported, 0, 0), SD_BUS_METHOD("SetDNS", "a(iay)", NULL, bus_link_method_set_dns_servers, 0), - SD_BUS_METHOD("SetDomains", "a(sb)", NULL, bus_link_method_set_search_domains, 0), + SD_BUS_METHOD("SetDomains", "a(sb)", NULL, bus_link_method_set_domains, 0), SD_BUS_METHOD("SetLLMNR", "s", NULL, bus_link_method_set_llmnr, 0), SD_BUS_METHOD("SetMulticastDNS", "s", NULL, bus_link_method_set_mdns, 0), SD_BUS_METHOD("SetDNSSEC", "s", NULL, bus_link_method_set_dnssec, 0), diff --git a/src/resolve/resolved-link-bus.h b/src/resolve/resolved-link-bus.h index 31e6cd2b45..646031b631 100644 --- a/src/resolve/resolved-link-bus.h +++ b/src/resolve/resolved-link-bus.h @@ -30,7 +30,7 @@ char *link_bus_path(Link *link); int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); int bus_link_method_set_dns_servers(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_link_method_set_search_domains(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_link_method_set_domains(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_link_method_set_llmnr(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_link_method_set_mdns(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index c5863b3aa2..b0dc65036d 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -468,7 +468,7 @@ static void link_read_settings(Link *l) { } if (r > 0) { - /* If this link used to be managed, but is now unmanaged, flush all our settings -- but only once. */ + /* If this link used to be managed, but is now unmanaged, flush all our settings — but only once. */ if (l->is_managed) link_flush_settings(l); diff --git a/src/resolve/resolved-llmnr.c b/src/resolve/resolved-llmnr.c index ef12abfbb5..8b1d71a3eb 100644 --- a/src/resolve/resolved-llmnr.c +++ b/src/resolve/resolved-llmnr.c @@ -286,7 +286,7 @@ static int on_llmnr_stream_packet(DnsStream *s) { scope = manager_find_scope(s->manager, s->read_packet); if (!scope) { - log_warning("Got LLMNR TCP packet on unknown scope. Ignroing."); + log_warning("Got LLMNR TCP packet on unknown scope. Ignoring."); return 0; } diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index bf5efe4cfa..b3ff46b5da 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -485,7 +485,7 @@ int manager_new(Manager **ret) { m->llmnr_support = RESOLVE_SUPPORT_YES; m->mdns_support = RESOLVE_SUPPORT_NO; - m->dnssec_mode = DNSSEC_NO; + m->dnssec_mode = DEFAULT_DNSSEC_MODE; m->read_resolv_conf = true; m->need_builtin_fallbacks = true; m->etc_hosts_last = m->etc_hosts_mtime = USEC_INFINITY; @@ -617,18 +617,16 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) { struct msghdr mh = {}; struct cmsghdr *cmsg; struct iovec iov; - int ms = 0, r; - ssize_t l; + ssize_t ms, l; + int r; assert(m); assert(fd >= 0); assert(ret); - r = ioctl(fd, FIONREAD, &ms); - if (r < 0) - return -errno; + ms = next_datagram_size_fd(fd); if (ms < 0) - return -EIO; + return ms; r = dns_packet_new(&p, protocol, ms); if (r < 0) @@ -1215,11 +1213,11 @@ void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResource assert(verdict < _DNSSEC_VERDICT_MAX); if (log_get_max_level() >= LOG_DEBUG) { - _cleanup_free_ char *s = NULL; - - (void) dns_resource_key_to_string(key, &s); + char s[DNS_RESOURCE_KEY_STRING_MAX]; - log_debug("Found verdict for lookup %s: %s", s ? strstrip(s) : "n/a", dnssec_verdict_to_string(verdict)); + log_debug("Found verdict for lookup %s: %s", + dns_resource_key_to_string(key, s, sizeof s), + dnssec_verdict_to_string(verdict)); } m->n_dnssec_verdict[verdict]++; diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c index bc8b8b809b..b13b1d0144 100644 --- a/src/resolve/resolved-mdns.c +++ b/src/resolve/resolved-mdns.c @@ -106,7 +106,7 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us dns_scope_check_conflicts(scope, p); DNS_ANSWER_FOREACH(rr, p->answer) { - const char *name = DNS_RESOURCE_KEY_NAME(rr->key); + const char *name = dns_resource_key_name(rr->key); DnsTransaction *t; /* If the received reply packet contains ANY record that is not .local or .in-addr.arpa, diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c index 065427b690..ff03acc772 100644 --- a/src/resolve/resolved-resolv-conf.c +++ b/src/resolve/resolved-resolv-conf.c @@ -158,7 +158,7 @@ static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) { if (*count == MAXNS) fputs("# Too many DNS servers configured, the following entries may be ignored.\n", f); - (*count) ++; + (*count)++; fprintf(f, "nameserver %s\n", s->server_string); } @@ -184,7 +184,7 @@ static void write_resolv_conf_search( } (*length) += strlen(domain); - (*count) ++; + (*count)++; fputc(' ', f); fputs(domain, f); diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c index c7e2ab14d6..161ea03412 100644 --- a/src/resolve/resolved.c +++ b/src/resolve/resolved.c @@ -48,7 +48,7 @@ int main(int argc, char *argv[]) { umask(0022); - r = mac_selinux_init(NULL); + r = mac_selinux_init(); if (r < 0) { log_error_errno(r, "SELinux setup failed: %m"); goto finish; diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in index efc9c6733a..a288588924 100644 --- a/src/resolve/resolved.conf.in +++ b/src/resolve/resolved.conf.in @@ -16,4 +16,4 @@ #FallbackDNS=@DNS_SERVERS@ #Domains= #LLMNR=yes -#DNSSEC=no +#DNSSEC=@DEFAULT_DNSSEC_MODE@ diff --git a/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts b/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts Binary files differnew file mode 100644 index 0000000000..a383c6286d --- /dev/null +++ b/src/resolve/test-data/_443._tcp.fedoraproject.org.pkts diff --git a/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts b/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts Binary files differnew file mode 100644 index 0000000000..15de02e997 --- /dev/null +++ b/src/resolve/test-data/_openpgpkey.fedoraproject.org.pkts diff --git a/src/resolve/test-data/fake-caa.pkts b/src/resolve/test-data/fake-caa.pkts Binary files differnew file mode 100644 index 0000000000..1c3ecc5491 --- /dev/null +++ b/src/resolve/test-data/fake-caa.pkts diff --git a/src/resolve/test-data/fedoraproject.org.pkts b/src/resolve/test-data/fedoraproject.org.pkts Binary files differnew file mode 100644 index 0000000000..17874844d9 --- /dev/null +++ b/src/resolve/test-data/fedoraproject.org.pkts diff --git a/src/resolve/test-data/gandi.net.pkts b/src/resolve/test-data/gandi.net.pkts Binary files differnew file mode 100644 index 0000000000..5ef51e0c8e --- /dev/null +++ b/src/resolve/test-data/gandi.net.pkts diff --git a/src/resolve/test-data/google.com.pkts b/src/resolve/test-data/google.com.pkts Binary files differnew file mode 100644 index 0000000000..f98c4cd855 --- /dev/null +++ b/src/resolve/test-data/google.com.pkts diff --git a/src/resolve/test-data/kyhwana.org.pkts b/src/resolve/test-data/kyhwana.org.pkts Binary files differnew file mode 100644 index 0000000000..e28a725c9a --- /dev/null +++ b/src/resolve/test-data/kyhwana.org.pkts diff --git a/src/resolve/test-data/root.pkts b/src/resolve/test-data/root.pkts Binary files differnew file mode 100644 index 0000000000..54ba668c75 --- /dev/null +++ b/src/resolve/test-data/root.pkts diff --git a/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts b/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts Binary files differnew file mode 100644 index 0000000000..a854249532 --- /dev/null +++ b/src/resolve/test-data/sw1a1aa-sw1a2aa-sw1a2ab-sw1a2ac.find.me.uk.pkts diff --git a/src/resolve/test-data/teamits.com.pkts b/src/resolve/test-data/teamits.com.pkts Binary files differnew file mode 100644 index 0000000000..11deb39677 --- /dev/null +++ b/src/resolve/test-data/teamits.com.pkts diff --git a/src/resolve/test-data/zbyszek@fedoraproject.org.pkts b/src/resolve/test-data/zbyszek@fedoraproject.org.pkts Binary files differnew file mode 100644 index 0000000000..f0a6f982df --- /dev/null +++ b/src/resolve/test-data/zbyszek@fedoraproject.org.pkts diff --git a/src/resolve/test-dns-packet.c b/src/resolve/test-dns-packet.c new file mode 100644 index 0000000000..c232a69ce1 --- /dev/null +++ b/src/resolve/test-dns-packet.c @@ -0,0 +1,114 @@ +/*** + 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 <net/if.h> +#include <glob.h> + +#include "alloc-util.h" +#include "fileio.h" +#include "glob-util.h" +#include "log.h" +#include "macro.h" +#include "resolved-dns-packet.h" +#include "resolved-dns-rr.h" +#include "string-util.h" +#include "strv.h" + +#define HASH_KEY SD_ID128_MAKE(d3,1e,48,90,4b,fa,4c,fe,af,9d,d5,a1,d7,2e,8a,b1) + +static uint64_t hash(DnsResourceRecord *rr) { + struct siphash state; + + siphash24_init(&state, HASH_KEY.bytes); + dns_resource_record_hash_func(rr, &state); + return siphash24_finalize(&state); +} + +static void test_packet_from_file(const char* filename, bool canonical) { + _cleanup_free_ char *data = NULL; + size_t data_size, packet_size, offset; + + assert_se(read_full_file(filename, &data, &data_size) >= 0); + assert_se(data); + assert_se(data_size > 8); + + log_info("============== %s %s==============", filename, canonical ? "canonical " : ""); + + for (offset = 0; offset < data_size; offset += 8 + packet_size) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL, *p2 = NULL; + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL, *rr2 = NULL; + const char *s, *s2; + uint64_t hash1, hash2; + + packet_size = le64toh( *(uint64_t*)(data + offset) ); + assert_se(packet_size > 0); + assert_se(offset + 8 + packet_size <= data_size); + + assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, 0) >= 0); + + assert_se(dns_packet_append_blob(p, data + offset + 8, packet_size, NULL) >= 0); + assert_se(dns_packet_read_rr(p, &rr, NULL, NULL) >= 0); + + s = dns_resource_record_to_string(rr); + assert_se(s); + puts(s); + + hash1 = hash(rr); + + assert_se(dns_resource_record_to_wire_format(rr, canonical) >= 0); + + assert_se(dns_packet_new(&p2, DNS_PROTOCOL_DNS, 0) >= 0); + assert_se(dns_packet_append_blob(p2, rr->wire_format, rr->wire_format_size, NULL) >= 0); + assert_se(dns_packet_read_rr(p2, &rr2, NULL, NULL) >= 0); + + s2 = dns_resource_record_to_string(rr); + assert_se(s2); + assert_se(streq(s, s2)); + + hash2 = hash(rr); + assert_se(hash1 == hash2); + } +} + +int main(int argc, char **argv) { + int i, N; + _cleanup_globfree_ glob_t g = {}; + char **fnames; + + log_parse_environment(); + + if (argc >= 2) { + N = argc - 1; + fnames = argv + 1; + } else { + assert_se(glob(RESOLVE_TEST_DIR "/*.pkts", GLOB_NOSORT, NULL, &g) == 0); + N = g.gl_pathc; + fnames = g.gl_pathv; + } + + for (i = 0; i < N; i++) { + test_packet_from_file(fnames[i], false); + puts(""); + test_packet_from_file(fnames[i], true); + if (i + 1 < N) + puts(""); + } + + return EXIT_SUCCESS; +} diff --git a/src/resolve/test-dnssec.c b/src/resolve/test-dnssec.c index a093d86a91..b3018e8239 100644 --- a/src/resolve/test-dnssec.c +++ b/src/resolve/test-dnssec.c @@ -27,77 +27,89 @@ #include "string-util.h" #include "hexdecoct.h" -static void test_dnssec_verify_rrset2(void) { +static void test_dnssec_canonicalize_one(const char *original, const char *canonical, int r) { + char canonicalized[DNSSEC_CANONICAL_HOSTNAME_MAX]; - static const uint8_t signature_blob[] = { - 0x48, 0x45, 0xc8, 0x8b, 0xc0, 0x14, 0x92, 0xf5, 0x15, 0xc6, 0x84, 0x9d, 0x2f, 0xe3, 0x32, 0x11, - 0x7d, 0xf1, 0xe6, 0x87, 0xb9, 0x42, 0xd3, 0x8b, 0x9e, 0xaf, 0x92, 0x31, 0x0a, 0x53, 0xad, 0x8b, - 0xa7, 0x5c, 0x83, 0x39, 0x8c, 0x28, 0xac, 0xce, 0x6e, 0x9c, 0x18, 0xe3, 0x31, 0x16, 0x6e, 0xca, - 0x38, 0x31, 0xaf, 0xd9, 0x94, 0xf1, 0x84, 0xb1, 0xdf, 0x5a, 0xc2, 0x73, 0x22, 0xf6, 0xcb, 0xa2, - 0xe7, 0x8c, 0x77, 0x0c, 0x74, 0x2f, 0xc2, 0x13, 0xb0, 0x93, 0x51, 0xa9, 0x4f, 0xae, 0x0a, 0xda, - 0x45, 0xcc, 0xfd, 0x43, 0x99, 0x36, 0x9a, 0x0d, 0x21, 0xe0, 0xeb, 0x30, 0x65, 0xd4, 0xa0, 0x27, - 0x37, 0x3b, 0xe4, 0xc1, 0xc5, 0xa1, 0x2a, 0xd1, 0x76, 0xc4, 0x7e, 0x64, 0x0e, 0x5a, 0xa6, 0x50, - 0x24, 0xd5, 0x2c, 0xcc, 0x6d, 0xe5, 0x37, 0xea, 0xbd, 0x09, 0x34, 0xed, 0x24, 0x06, 0xa1, 0x22, - }; + assert_se(dnssec_canonicalize(original, canonicalized, sizeof(canonicalized)) == r); + if (r < 0) + return; + + assert_se(streq(canonicalized, canonical)); +} +static void test_dnssec_canonicalize(void) { + test_dnssec_canonicalize_one("", ".", 1); + test_dnssec_canonicalize_one(".", ".", 1); + test_dnssec_canonicalize_one("foo", "foo.", 4); + test_dnssec_canonicalize_one("foo.", "foo.", 4); + test_dnssec_canonicalize_one("FOO.", "foo.", 4); + test_dnssec_canonicalize_one("FOO.bar.", "foo.bar.", 8); + test_dnssec_canonicalize_one("FOO..bar.", NULL, -EINVAL); +} + +#ifdef HAVE_GCRYPT + +static void test_dnssec_verify_dns_key(void) { + + static const uint8_t ds1_fprint[] = { + 0x46, 0x8B, 0xC8, 0xDD, 0xC7, 0xE8, 0x27, 0x03, 0x40, 0xBB, 0x8A, 0x1F, 0x3B, 0x2E, 0x45, 0x9D, + 0x80, 0x67, 0x14, 0x01, + }; + static const uint8_t ds2_fprint[] = { + 0x8A, 0xEE, 0x80, 0x47, 0x05, 0x5F, 0x83, 0xD1, 0x48, 0xBA, 0x8F, 0xF6, 0xDD, 0xA7, 0x60, 0xCE, + 0x94, 0xF7, 0xC7, 0x5E, 0x52, 0x4C, 0xF2, 0xE9, 0x50, 0xB9, 0x2E, 0xCB, 0xEF, 0x96, 0xB9, 0x98, + }; static const uint8_t dnskey_blob[] = { - 0x03, 0x01, 0x00, 0x01, 0xc3, 0x7f, 0x1d, 0xd1, 0x1c, 0x97, 0xb1, 0x13, 0x34, 0x3a, 0x9a, 0xea, - 0xee, 0xd9, 0x5a, 0x11, 0x1b, 0x17, 0xc7, 0xe3, 0xd4, 0xda, 0x20, 0xbc, 0x5d, 0xba, 0x74, 0xe3, - 0x37, 0x99, 0xec, 0x25, 0xce, 0x93, 0x7f, 0xbd, 0x22, 0x73, 0x7e, 0x14, 0x71, 0xe0, 0x60, 0x07, - 0xd4, 0x39, 0x8b, 0x5e, 0xe9, 0xba, 0x25, 0xe8, 0x49, 0xe9, 0x34, 0xef, 0xfe, 0x04, 0x5c, 0xa5, - 0x27, 0xcd, 0xa9, 0xda, 0x70, 0x05, 0x21, 0xab, 0x15, 0x82, 0x24, 0xc3, 0x94, 0xf5, 0xd7, 0xb7, - 0xc4, 0x66, 0xcb, 0x32, 0x6e, 0x60, 0x2b, 0x55, 0x59, 0x28, 0x89, 0x8a, 0x72, 0xde, 0x88, 0x56, - 0x27, 0x95, 0xd9, 0xac, 0x88, 0x4f, 0x65, 0x2b, 0x68, 0xfc, 0xe6, 0x41, 0xc1, 0x1b, 0xef, 0x4e, - 0xd6, 0xc2, 0x0f, 0x64, 0x88, 0x95, 0x5e, 0xdd, 0x3a, 0x02, 0x07, 0x50, 0xa9, 0xda, 0xa4, 0x49, - 0x74, 0x62, 0xfe, 0xd7, + 0x03, 0x01, 0x00, 0x01, 0xa8, 0x12, 0xda, 0x4f, 0xd2, 0x7d, 0x54, 0x14, 0x0e, 0xcc, 0x5b, 0x5e, + 0x45, 0x9c, 0x96, 0x98, 0xc0, 0xc0, 0x85, 0x81, 0xb1, 0x47, 0x8c, 0x7d, 0xe8, 0x39, 0x50, 0xcc, + 0xc5, 0xd0, 0xf2, 0x00, 0x81, 0x67, 0x79, 0xf6, 0xcc, 0x9d, 0xad, 0x6c, 0xbb, 0x7b, 0x6f, 0x48, + 0x97, 0x15, 0x1c, 0xfd, 0x0b, 0xfe, 0xd3, 0xd7, 0x7d, 0x9f, 0x81, 0x26, 0xd3, 0xc5, 0x65, 0x49, + 0xcf, 0x46, 0x62, 0xb0, 0x55, 0x6e, 0x47, 0xc7, 0x30, 0xef, 0x51, 0xfb, 0x3e, 0xc6, 0xef, 0xde, + 0x27, 0x3f, 0xfa, 0x57, 0x2d, 0xa7, 0x1d, 0x80, 0x46, 0x9a, 0x5f, 0x14, 0xb3, 0xb0, 0x2c, 0xbe, + 0x72, 0xca, 0xdf, 0xb2, 0xff, 0x36, 0x5b, 0x4f, 0xec, 0x58, 0x8e, 0x8d, 0x01, 0xe9, 0xa9, 0xdf, + 0xb5, 0x60, 0xad, 0x52, 0x4d, 0xfc, 0xa9, 0x3e, 0x8d, 0x35, 0x95, 0xb3, 0x4e, 0x0f, 0xca, 0x45, + 0x1b, 0xf7, 0xef, 0x3a, 0x88, 0x25, 0x08, 0xc7, 0x4e, 0x06, 0xc1, 0x62, 0x1a, 0xce, 0xd8, 0x77, + 0xbd, 0x02, 0x65, 0xf8, 0x49, 0xfb, 0xce, 0xf6, 0xa8, 0x09, 0xfc, 0xde, 0xb2, 0x09, 0x9d, 0x39, + 0xf8, 0x63, 0x9c, 0x32, 0x42, 0x7c, 0xa0, 0x30, 0x86, 0x72, 0x7a, 0x4a, 0xc6, 0xd4, 0xb3, 0x2d, + 0x24, 0xef, 0x96, 0x3f, 0xc2, 0xda, 0xd3, 0xf2, 0x15, 0x6f, 0xda, 0x65, 0x4b, 0x81, 0x28, 0x68, + 0xf4, 0xfe, 0x3e, 0x71, 0x4f, 0x50, 0x96, 0x72, 0x58, 0xa1, 0x89, 0xdd, 0x01, 0x61, 0x39, 0x39, + 0xc6, 0x76, 0xa4, 0xda, 0x02, 0x70, 0x3d, 0xc0, 0xdc, 0x8d, 0x70, 0x72, 0x04, 0x90, 0x79, 0xd4, + 0xec, 0x65, 0xcf, 0x49, 0x35, 0x25, 0x3a, 0x14, 0x1a, 0x45, 0x20, 0xeb, 0x31, 0xaf, 0x92, 0xba, + 0x20, 0xd3, 0xcd, 0xa7, 0x13, 0x44, 0xdc, 0xcf, 0xf0, 0x27, 0x34, 0xb9, 0xe7, 0x24, 0x6f, 0x73, + 0xe7, 0xea, 0x77, 0x03, }; - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *nsec = NULL, *rrsig = NULL, *dnskey = NULL; - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - DnssecResult result; - - nsec = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_NSEC, "nasa.gov"); - assert_se(nsec); + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *dnskey = NULL, *ds1 = NULL, *ds2 = NULL; - nsec->nsec.next_domain_name = strdup("3D-Printing.nasa.gov"); - assert_se(nsec->nsec.next_domain_name); + /* The two DS RRs in effect for nasa.gov on 2015-12-01. */ + ds1 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "nasa.gov"); + assert_se(ds1); - nsec->nsec.types = bitmap_new(); - assert_se(nsec->nsec.types); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_A) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_NS) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_SOA) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_MX) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_TXT) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_RRSIG) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_NSEC) >= 0); - assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_DNSKEY) >= 0); - assert_se(bitmap_set(nsec->nsec.types, 65534) >= 0); + ds1->ds.key_tag = 47857; + ds1->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256; + ds1->ds.digest_type = DNSSEC_DIGEST_SHA1; + ds1->ds.digest_size = sizeof(ds1_fprint); + ds1->ds.digest = memdup(ds1_fprint, ds1->ds.digest_size); + assert_se(ds1->ds.digest); - log_info("NSEC: %s", strna(dns_resource_record_to_string(nsec))); + log_info("DS1: %s", strna(dns_resource_record_to_string(ds1))); - rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "NaSa.GOV."); - assert_se(rrsig); + ds2 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "NASA.GOV"); + assert_se(ds2); - rrsig->rrsig.type_covered = DNS_TYPE_NSEC; - rrsig->rrsig.algorithm = DNSSEC_ALGORITHM_RSASHA256; - rrsig->rrsig.labels = 2; - rrsig->rrsig.original_ttl = 300; - rrsig->rrsig.expiration = 0x5689002f; - rrsig->rrsig.inception = 0x56617230; - rrsig->rrsig.key_tag = 30390; - rrsig->rrsig.signer = strdup("Nasa.Gov."); - assert_se(rrsig->rrsig.signer); - rrsig->rrsig.signature_size = sizeof(signature_blob); - rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size); - assert_se(rrsig->rrsig.signature); + ds2->ds.key_tag = 47857; + ds2->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256; + ds2->ds.digest_type = DNSSEC_DIGEST_SHA256; + ds2->ds.digest_size = sizeof(ds2_fprint); + ds2->ds.digest = memdup(ds2_fprint, ds2->ds.digest_size); + assert_se(ds2->ds.digest); - log_info("RRSIG: %s", strna(dns_resource_record_to_string(rrsig))); + log_info("DS2: %s", strna(dns_resource_record_to_string(ds2))); - dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nASA.gOV"); + dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nasa.GOV"); assert_se(dnskey); - dnskey->dnskey.flags = 256; + dnskey->dnskey.flags = 257; dnskey->dnskey.protocol = 3; dnskey->dnskey.algorithm = DNSSEC_ALGORITHM_RSASHA256; dnskey->dnskey.key_size = sizeof(dnskey_blob); @@ -107,16 +119,8 @@ static void test_dnssec_verify_rrset2(void) { log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey))); log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false)); - assert_se(dnssec_key_match_rrsig(nsec->key, rrsig) > 0); - assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey, false) > 0); - - answer = dns_answer_new(1); - assert_se(answer); - assert_se(dns_answer_add(answer, nsec, 0, DNS_ANSWER_AUTHENTICATED) >= 0); - - /* Validate the RR as it if was 2015-12-11 today */ - assert_se(dnssec_verify_rrset(answer, nsec->key, rrsig, dnskey, 1449849318*USEC_PER_SEC, &result) >= 0); - assert_se(result == DNSSEC_VALIDATED); + assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds1, false) > 0); + assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds2, false) > 0); } static void test_dnssec_verify_rrset(void) { @@ -198,67 +202,77 @@ static void test_dnssec_verify_rrset(void) { assert_se(result == DNSSEC_VALIDATED); } -static void test_dnssec_verify_dns_key(void) { +static void test_dnssec_verify_rrset2(void) { - static const uint8_t ds1_fprint[] = { - 0x46, 0x8B, 0xC8, 0xDD, 0xC7, 0xE8, 0x27, 0x03, 0x40, 0xBB, 0x8A, 0x1F, 0x3B, 0x2E, 0x45, 0x9D, - 0x80, 0x67, 0x14, 0x01, - }; - static const uint8_t ds2_fprint[] = { - 0x8A, 0xEE, 0x80, 0x47, 0x05, 0x5F, 0x83, 0xD1, 0x48, 0xBA, 0x8F, 0xF6, 0xDD, 0xA7, 0x60, 0xCE, - 0x94, 0xF7, 0xC7, 0x5E, 0x52, 0x4C, 0xF2, 0xE9, 0x50, 0xB9, 0x2E, 0xCB, 0xEF, 0x96, 0xB9, 0x98, + static const uint8_t signature_blob[] = { + 0x48, 0x45, 0xc8, 0x8b, 0xc0, 0x14, 0x92, 0xf5, 0x15, 0xc6, 0x84, 0x9d, 0x2f, 0xe3, 0x32, 0x11, + 0x7d, 0xf1, 0xe6, 0x87, 0xb9, 0x42, 0xd3, 0x8b, 0x9e, 0xaf, 0x92, 0x31, 0x0a, 0x53, 0xad, 0x8b, + 0xa7, 0x5c, 0x83, 0x39, 0x8c, 0x28, 0xac, 0xce, 0x6e, 0x9c, 0x18, 0xe3, 0x31, 0x16, 0x6e, 0xca, + 0x38, 0x31, 0xaf, 0xd9, 0x94, 0xf1, 0x84, 0xb1, 0xdf, 0x5a, 0xc2, 0x73, 0x22, 0xf6, 0xcb, 0xa2, + 0xe7, 0x8c, 0x77, 0x0c, 0x74, 0x2f, 0xc2, 0x13, 0xb0, 0x93, 0x51, 0xa9, 0x4f, 0xae, 0x0a, 0xda, + 0x45, 0xcc, 0xfd, 0x43, 0x99, 0x36, 0x9a, 0x0d, 0x21, 0xe0, 0xeb, 0x30, 0x65, 0xd4, 0xa0, 0x27, + 0x37, 0x3b, 0xe4, 0xc1, 0xc5, 0xa1, 0x2a, 0xd1, 0x76, 0xc4, 0x7e, 0x64, 0x0e, 0x5a, 0xa6, 0x50, + 0x24, 0xd5, 0x2c, 0xcc, 0x6d, 0xe5, 0x37, 0xea, 0xbd, 0x09, 0x34, 0xed, 0x24, 0x06, 0xa1, 0x22, }; + static const uint8_t dnskey_blob[] = { - 0x03, 0x01, 0x00, 0x01, 0xa8, 0x12, 0xda, 0x4f, 0xd2, 0x7d, 0x54, 0x14, 0x0e, 0xcc, 0x5b, 0x5e, - 0x45, 0x9c, 0x96, 0x98, 0xc0, 0xc0, 0x85, 0x81, 0xb1, 0x47, 0x8c, 0x7d, 0xe8, 0x39, 0x50, 0xcc, - 0xc5, 0xd0, 0xf2, 0x00, 0x81, 0x67, 0x79, 0xf6, 0xcc, 0x9d, 0xad, 0x6c, 0xbb, 0x7b, 0x6f, 0x48, - 0x97, 0x15, 0x1c, 0xfd, 0x0b, 0xfe, 0xd3, 0xd7, 0x7d, 0x9f, 0x81, 0x26, 0xd3, 0xc5, 0x65, 0x49, - 0xcf, 0x46, 0x62, 0xb0, 0x55, 0x6e, 0x47, 0xc7, 0x30, 0xef, 0x51, 0xfb, 0x3e, 0xc6, 0xef, 0xde, - 0x27, 0x3f, 0xfa, 0x57, 0x2d, 0xa7, 0x1d, 0x80, 0x46, 0x9a, 0x5f, 0x14, 0xb3, 0xb0, 0x2c, 0xbe, - 0x72, 0xca, 0xdf, 0xb2, 0xff, 0x36, 0x5b, 0x4f, 0xec, 0x58, 0x8e, 0x8d, 0x01, 0xe9, 0xa9, 0xdf, - 0xb5, 0x60, 0xad, 0x52, 0x4d, 0xfc, 0xa9, 0x3e, 0x8d, 0x35, 0x95, 0xb3, 0x4e, 0x0f, 0xca, 0x45, - 0x1b, 0xf7, 0xef, 0x3a, 0x88, 0x25, 0x08, 0xc7, 0x4e, 0x06, 0xc1, 0x62, 0x1a, 0xce, 0xd8, 0x77, - 0xbd, 0x02, 0x65, 0xf8, 0x49, 0xfb, 0xce, 0xf6, 0xa8, 0x09, 0xfc, 0xde, 0xb2, 0x09, 0x9d, 0x39, - 0xf8, 0x63, 0x9c, 0x32, 0x42, 0x7c, 0xa0, 0x30, 0x86, 0x72, 0x7a, 0x4a, 0xc6, 0xd4, 0xb3, 0x2d, - 0x24, 0xef, 0x96, 0x3f, 0xc2, 0xda, 0xd3, 0xf2, 0x15, 0x6f, 0xda, 0x65, 0x4b, 0x81, 0x28, 0x68, - 0xf4, 0xfe, 0x3e, 0x71, 0x4f, 0x50, 0x96, 0x72, 0x58, 0xa1, 0x89, 0xdd, 0x01, 0x61, 0x39, 0x39, - 0xc6, 0x76, 0xa4, 0xda, 0x02, 0x70, 0x3d, 0xc0, 0xdc, 0x8d, 0x70, 0x72, 0x04, 0x90, 0x79, 0xd4, - 0xec, 0x65, 0xcf, 0x49, 0x35, 0x25, 0x3a, 0x14, 0x1a, 0x45, 0x20, 0xeb, 0x31, 0xaf, 0x92, 0xba, - 0x20, 0xd3, 0xcd, 0xa7, 0x13, 0x44, 0xdc, 0xcf, 0xf0, 0x27, 0x34, 0xb9, 0xe7, 0x24, 0x6f, 0x73, - 0xe7, 0xea, 0x77, 0x03, + 0x03, 0x01, 0x00, 0x01, 0xc3, 0x7f, 0x1d, 0xd1, 0x1c, 0x97, 0xb1, 0x13, 0x34, 0x3a, 0x9a, 0xea, + 0xee, 0xd9, 0x5a, 0x11, 0x1b, 0x17, 0xc7, 0xe3, 0xd4, 0xda, 0x20, 0xbc, 0x5d, 0xba, 0x74, 0xe3, + 0x37, 0x99, 0xec, 0x25, 0xce, 0x93, 0x7f, 0xbd, 0x22, 0x73, 0x7e, 0x14, 0x71, 0xe0, 0x60, 0x07, + 0xd4, 0x39, 0x8b, 0x5e, 0xe9, 0xba, 0x25, 0xe8, 0x49, 0xe9, 0x34, 0xef, 0xfe, 0x04, 0x5c, 0xa5, + 0x27, 0xcd, 0xa9, 0xda, 0x70, 0x05, 0x21, 0xab, 0x15, 0x82, 0x24, 0xc3, 0x94, 0xf5, 0xd7, 0xb7, + 0xc4, 0x66, 0xcb, 0x32, 0x6e, 0x60, 0x2b, 0x55, 0x59, 0x28, 0x89, 0x8a, 0x72, 0xde, 0x88, 0x56, + 0x27, 0x95, 0xd9, 0xac, 0x88, 0x4f, 0x65, 0x2b, 0x68, 0xfc, 0xe6, 0x41, 0xc1, 0x1b, 0xef, 0x4e, + 0xd6, 0xc2, 0x0f, 0x64, 0x88, 0x95, 0x5e, 0xdd, 0x3a, 0x02, 0x07, 0x50, 0xa9, 0xda, 0xa4, 0x49, + 0x74, 0x62, 0xfe, 0xd7, }; - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *dnskey = NULL, *ds1 = NULL, *ds2 = NULL; + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *nsec = NULL, *rrsig = NULL, *dnskey = NULL; + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; + DnssecResult result; - /* The two DS RRs in effect for nasa.gov on 2015-12-01. */ - ds1 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "nasa.gov"); - assert_se(ds1); + nsec = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_NSEC, "nasa.gov"); + assert_se(nsec); - ds1->ds.key_tag = 47857; - ds1->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256; - ds1->ds.digest_type = DNSSEC_DIGEST_SHA1; - ds1->ds.digest_size = sizeof(ds1_fprint); - ds1->ds.digest = memdup(ds1_fprint, ds1->ds.digest_size); - assert_se(ds1->ds.digest); + nsec->nsec.next_domain_name = strdup("3D-Printing.nasa.gov"); + assert_se(nsec->nsec.next_domain_name); - log_info("DS1: %s", strna(dns_resource_record_to_string(ds1))); + nsec->nsec.types = bitmap_new(); + assert_se(nsec->nsec.types); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_A) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_NS) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_SOA) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_MX) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_TXT) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_RRSIG) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_NSEC) >= 0); + assert_se(bitmap_set(nsec->nsec.types, DNS_TYPE_DNSKEY) >= 0); + assert_se(bitmap_set(nsec->nsec.types, 65534) >= 0); - ds2 = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "NASA.GOV"); - assert_se(ds2); + log_info("NSEC: %s", strna(dns_resource_record_to_string(nsec))); - ds2->ds.key_tag = 47857; - ds2->ds.algorithm = DNSSEC_ALGORITHM_RSASHA256; - ds2->ds.digest_type = DNSSEC_DIGEST_SHA256; - ds2->ds.digest_size = sizeof(ds2_fprint); - ds2->ds.digest = memdup(ds2_fprint, ds2->ds.digest_size); - assert_se(ds2->ds.digest); + rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "NaSa.GOV."); + assert_se(rrsig); - log_info("DS2: %s", strna(dns_resource_record_to_string(ds2))); + rrsig->rrsig.type_covered = DNS_TYPE_NSEC; + rrsig->rrsig.algorithm = DNSSEC_ALGORITHM_RSASHA256; + rrsig->rrsig.labels = 2; + rrsig->rrsig.original_ttl = 300; + rrsig->rrsig.expiration = 0x5689002f; + rrsig->rrsig.inception = 0x56617230; + rrsig->rrsig.key_tag = 30390; + rrsig->rrsig.signer = strdup("Nasa.Gov."); + assert_se(rrsig->rrsig.signer); + rrsig->rrsig.signature_size = sizeof(signature_blob); + rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size); + assert_se(rrsig->rrsig.signature); - dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nasa.GOV"); + log_info("RRSIG: %s", strna(dns_resource_record_to_string(rrsig))); + + dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "nASA.gOV"); assert_se(dnskey); - dnskey->dnskey.flags = 257; + dnskey->dnskey.flags = 256; dnskey->dnskey.protocol = 3; dnskey->dnskey.algorithm = DNSSEC_ALGORITHM_RSASHA256; dnskey->dnskey.key_size = sizeof(dnskey_blob); @@ -268,28 +282,16 @@ static void test_dnssec_verify_dns_key(void) { log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey))); log_info("DNSKEY keytag: %u", dnssec_keytag(dnskey, false)); - assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds1, false) > 0); - assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds2, false) > 0); -} - -static void test_dnssec_canonicalize_one(const char *original, const char *canonical, int r) { - char canonicalized[DNSSEC_CANONICAL_HOSTNAME_MAX]; - - assert_se(dnssec_canonicalize(original, canonicalized, sizeof(canonicalized)) == r); - if (r < 0) - return; + assert_se(dnssec_key_match_rrsig(nsec->key, rrsig) > 0); + assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey, false) > 0); - assert_se(streq(canonicalized, canonical)); -} + answer = dns_answer_new(1); + assert_se(answer); + assert_se(dns_answer_add(answer, nsec, 0, DNS_ANSWER_AUTHENTICATED) >= 0); -static void test_dnssec_canonicalize(void) { - test_dnssec_canonicalize_one("", ".", 1); - test_dnssec_canonicalize_one(".", ".", 1); - test_dnssec_canonicalize_one("foo", "foo.", 4); - test_dnssec_canonicalize_one("foo.", "foo.", 4); - test_dnssec_canonicalize_one("FOO.", "foo.", 4); - test_dnssec_canonicalize_one("FOO.bar.", "foo.bar.", 8); - test_dnssec_canonicalize_one("FOO..bar.", NULL, -EINVAL); + /* Validate the RR as it if was 2015-12-11 today */ + assert_se(dnssec_verify_rrset(answer, nsec->key, rrsig, dnskey, 1449849318*USEC_PER_SEC, &result) >= 0); + assert_se(result == DNSSEC_VALIDATED); } static void test_dnssec_nsec3_hash(void) { @@ -324,13 +326,18 @@ static void test_dnssec_nsec3_hash(void) { assert_se(strcasecmp(b, "PJ8S08RR45VIQDAQGE7EN3VHKNROTBMM") == 0); } +#endif + int main(int argc, char*argv[]) { test_dnssec_canonicalize(); + +#ifdef HAVE_GCRYPT test_dnssec_verify_dns_key(); test_dnssec_verify_rrset(); test_dnssec_verify_rrset2(); test_dnssec_nsec3_hash(); +#endif return 0; } diff --git a/src/resolve/test-resolve-tables.c b/src/resolve/test-resolve-tables.c index 63660afc87..2d615130e1 100644 --- a/src/resolve/test-resolve-tables.c +++ b/src/resolve/test-resolve-tables.c @@ -21,7 +21,44 @@ #include "test-tables.h" int main(int argc, char **argv) { + uint16_t i; + test_table_sparse(dns_type, DNS_TYPE); + log_info("/* DNS_TYPE */"); + for (i = 0; i < _DNS_TYPE_MAX; i++) { + const char *s; + + s = dns_type_to_string(i); + assert_se(s == NULL || strlen(s) < _DNS_TYPE_STRING_MAX); + + if (s) + log_info("%-*s %s%s%s%s%s%s%s%s%s", + (int) _DNS_TYPE_STRING_MAX - 1, s, + dns_type_is_pseudo(i) ? "pseudo " : "", + dns_type_is_valid_query(i) ? "valid_query " : "", + dns_type_is_valid_rr(i) ? "is_valid_rr " : "", + dns_type_may_redirect(i) ? "may_redirect " : "", + dns_type_is_dnssec(i) ? "dnssec " : "", + dns_type_is_obsolete(i) ? "obsolete " : "", + dns_type_may_wildcard(i) ? "wildcard " : "", + dns_type_apex_only(i) ? "apex_only " : "", + dns_type_needs_authentication(i) ? "needs_authentication" : ""); + } + + log_info("/* DNS_CLASS */"); + for (i = 0; i < _DNS_CLASS_MAX; i++) { + const char *s; + + s = dns_class_to_string(i); + assert_se(s == NULL || strlen(s) < _DNS_CLASS_STRING_MAX); + + if (s) + log_info("%-*s %s%s", + (int) _DNS_CLASS_STRING_MAX - 1, s, + dns_class_is_pseudo(i) ? "is_pseudo " : "", + dns_class_is_valid_rr(i) ? "is_valid_rr " : ""); + } + return EXIT_SUCCESS; } diff --git a/src/run/run.c b/src/run/run.c index e7f4c21f73..d6c9b6d37a 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -25,6 +25,7 @@ #include "alloc-util.h" #include "bus-error.h" +#include "bus-unit-util.h" #include "bus-util.h" #include "calendarspec.h" #include "env-util.h" @@ -83,8 +84,8 @@ static void polkit_agent_open_if_enabled(void) { static void help(void) { printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n" "Run the specified command in a transient scope or service or timer\n" - "unit. If timer option is specified and unit is exist which is\n" - "specified with --unit option then command can be omitted.\n\n" + "unit. If a timer option is specified and the unit specified with\n" + "the --unit option exists, the command can be omitted.\n\n" " -h --help Show this help\n" " --version Show package version\n" " --no-ask-password Do not prompt for password\n" @@ -103,7 +104,7 @@ static void help(void) { " --uid=USER Run as system user\n" " --gid=GROUP Run as system group\n" " --nice=NICE Nice level\n" - " --setenv=NAME=VALUE Set environment\n" + " -E --setenv=NAME=VALUE Set environment\n" " -t --pty Run service on pseudo tty\n" " -q --quiet Suppress information messages during runtime\n\n" "Timer options:\n\n" @@ -125,7 +126,6 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, - ARG_NO_ASK_PASSWORD, ARG_USER, ARG_SYSTEM, ARG_SCOPE, @@ -133,12 +133,10 @@ static int parse_argv(int argc, char *argv[]) { ARG_DESCRIPTION, ARG_SLICE, ARG_SEND_SIGHUP, + ARG_SERVICE_TYPE, ARG_EXEC_USER, ARG_EXEC_GROUP, - ARG_SERVICE_TYPE, ARG_NICE, - ARG_SETENV, - ARG_TTY, ARG_ON_ACTIVE, ARG_ON_BOOT, ARG_ON_STARTUP, @@ -147,6 +145,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_ON_CALENDAR, ARG_TIMER_PROPERTY, ARG_NO_BLOCK, + ARG_NO_ASK_PASSWORD, }; static const struct option options[] = { @@ -166,9 +165,10 @@ static int parse_argv(int argc, char *argv[]) { { "uid", required_argument, NULL, ARG_EXEC_USER }, { "gid", required_argument, NULL, ARG_EXEC_GROUP }, { "nice", required_argument, NULL, ARG_NICE }, - { "setenv", required_argument, NULL, ARG_SETENV }, + { "setenv", required_argument, NULL, 'E' }, { "property", required_argument, NULL, 'p' }, - { "tty", no_argument, NULL, 't' }, + { "tty", no_argument, NULL, 't' }, /* deprecated */ + { "pty", no_argument, NULL, 't' }, { "quiet", no_argument, NULL, 'q' }, { "on-active", required_argument, NULL, ARG_ON_ACTIVE }, { "on-boot", required_argument, NULL, ARG_ON_BOOT }, @@ -266,7 +266,7 @@ static int parse_argv(int argc, char *argv[]) { arg_nice_set = true; break; - case ARG_SETENV: + case 'E': if (strv_extend(&arg_environment, optarg) < 0) return log_oom(); @@ -621,6 +621,10 @@ static int transient_scope_set_properties(sd_bus_message *m) { if (r < 0) return r; + r = transient_cgroup_set_properties(m); + if (r < 0) + return r; + r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid()); if (r < 0) return r; @@ -756,6 +760,7 @@ static int start_transient_service( } else if (arg_transport == BUS_TRANSPORT_MACHINE) { _cleanup_(sd_bus_unrefp) sd_bus *system_bus = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *pty_reply = NULL; const char *s; r = sd_bus_default_system(&system_bus); @@ -768,19 +773,17 @@ static int start_transient_service( "org.freedesktop.machine1.Manager", "OpenMachinePTY", &error, - &reply, + &pty_reply, "s", arg_host); if (r < 0) { log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r)); return r; } - r = sd_bus_message_read(reply, "hs", &master, &s); + r = sd_bus_message_read(pty_reply, "hs", &master, &s); if (r < 0) return bus_log_parse_error(r); - reply = sd_bus_message_unref(reply); - master = fcntl(master, F_DUPFD_CLOEXEC, 3); if (master < 0) return log_error_errno(errno, "Failed to duplicate master fd: %m"); @@ -878,7 +881,7 @@ static int start_transient_service( (void) sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); if (!arg_quiet) - log_info("Running as unit %s.\nPress ^] three times within 1s to disconnect TTY.", service); + log_info("Running as unit: %s\nPress ^] three times within 1s to disconnect TTY.", service); r = pty_forward_new(event, master, PTY_FORWARD_IGNORE_INITIAL_VHANGUP, &forward); if (r < 0) @@ -896,7 +899,7 @@ static int start_transient_service( fputc('\n', stdout); } else if (!arg_quiet) - log_info("Running as unit %s.", service); + log_info("Running as unit: %s", service); return 0; } @@ -1038,7 +1041,7 @@ static int start_transient_scope( return r; if (!arg_quiet) - log_info("Running scope as unit %s.", scope); + log_info("Running scope as unit: %s", scope); execvpe(argv[0], argv, env); @@ -1189,9 +1192,9 @@ static int start_transient_timer( if (r < 0) return r; - log_info("Running timer as unit %s.", timer); + log_info("Running timer as unit: %s", timer); if (argv[0]) - log_info("Will run service as unit %s.", service); + log_info("Will run service as unit: %s", service); return 0; } diff --git a/src/shared/acpi-fpdt.c b/src/shared/acpi-fpdt.c index 3cb9e781fd..6779691c28 100644 --- a/src/shared/acpi-fpdt.c +++ b/src/shared/acpi-fpdt.c @@ -119,7 +119,7 @@ int acpi_get_boot_usec(usec_t *loader_start, usec_t *loader_exit) { } if (ptr == 0) - return -EINVAL; + return -ENODATA; /* read Firmware Basic Boot Performance Data Record */ fd = open("/dev/mem", O_CLOEXEC|O_RDONLY); @@ -146,6 +146,10 @@ int acpi_get_boot_usec(usec_t *loader_start, usec_t *loader_exit) { if (brec.type != ACPI_FPDT_BOOT_REC) return -EINVAL; + if (brec.exit_services_exit == 0) + /* Non-UEFI compatible boot. */ + return -ENODATA; + if (brec.startup_start == 0 || brec.exit_services_exit < brec.startup_start) return -EINVAL; if (brec.exit_services_exit > NSEC_PER_HOUR) diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index 6805873f9e..4a4bd8d3b8 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -431,7 +431,7 @@ static int create_socket(char **name) { snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%" PRIx64, random_u64()); RUN_WITH_UMASK(0177) { - if (bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) + if (bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) return -errno; } diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c new file mode 100644 index 0000000000..f6559cd854 --- /dev/null +++ b/src/shared/bus-unit-util.c @@ -0,0 +1,1302 @@ +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "alloc-util.h" +#include "bus-internal.h" +#include "bus-unit-util.h" +#include "bus-util.h" +#include "cgroup-util.h" +#include "env-util.h" +#include "escape.h" +#include "hashmap.h" +#include "list.h" +#include "locale-util.h" +#include "parse-util.h" +#include "path-util.h" +#include "process-util.h" +#include "rlimit-util.h" +#include "signal-util.h" +#include "string-util.h" +#include "syslog-util.h" +#include "terminal-util.h" +#include "utf8.h" +#include "util.h" + +int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) { + assert(message); + assert(u); + + u->machine = NULL; + + return sd_bus_message_read( + message, + "(ssssssouso)", + &u->id, + &u->description, + &u->load_state, + &u->active_state, + &u->sub_state, + &u->following, + &u->unit_path, + &u->job_id, + &u->job_type, + &u->job_path); +} + +int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) { + const char *eq, *field; + int r, rl; + + assert(m); + assert(assignment); + + eq = strchr(assignment, '='); + if (!eq) { + log_error("Not an assignment: %s", assignment); + return -EINVAL; + } + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + field = strndupa(assignment, eq - assignment); + eq++; + + if (streq(field, "CPUQuota")) { + + if (isempty(eq)) + r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY); + else if (endswith(eq, "%")) { + double percent; + + if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) { + log_error("CPU quota '%s' invalid.", eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) percent * USEC_PER_SEC / 100); + } else { + log_error("CPU quota needs to be in percent."); + return -EINVAL; + } + + goto finish; + + } else if (streq(field, "EnvironmentFile")) { + + r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1, + eq[0] == '-' ? eq + 1 : eq, + eq[0] == '-'); + goto finish; + + } else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) { + char *n; + usec_t t; + size_t l; + r = parse_sec(eq, &t); + if (r < 0) + return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq); + + l = strlen(field); + n = newa(char, l + 2); + if (!n) + return log_oom(); + + /* Change suffix Sec → USec */ + strcpy(mempcpy(n, field, l - 3), "USec"); + r = sd_bus_message_append(m, "sv", n, "t", t); + goto finish; + } + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); + + rl = rlimit_from_string(field); + if (rl >= 0) { + const char *sn; + struct rlimit l; + + r = rlimit_parse(rl, eq, &l); + if (r < 0) + return log_error_errno(r, "Failed to parse resource limit: %s", eq); + + r = sd_bus_message_append(m, "v", "t", l.rlim_max); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + sn = strjoina(field, "Soft"); + r = sd_bus_message_append(m, "sv", sn, "t", l.rlim_cur); + + } else if (STR_IN_SET(field, + "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting", "TasksAccounting", + "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", + "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", + "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges", + "SyslogLevelPrefix", "Delegate", "RemainAfterElapse")) { + + r = parse_boolean(eq); + if (r < 0) + return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment); + + r = sd_bus_message_append(m, "v", "b", r); + + } else if (streq(field, "MemoryLimit")) { + uint64_t bytes; + + if (isempty(eq) || streq(eq, "infinity")) + bytes = (uint64_t) -1; + else { + r = parse_size(eq, 1024, &bytes); + if (r < 0) { + log_error("Failed to parse bytes specification %s", assignment); + return -EINVAL; + } + } + + r = sd_bus_message_append(m, "v", "t", bytes); + + } else if (streq(field, "TasksMax")) { + uint64_t n; + + if (isempty(eq) || streq(eq, "infinity")) + n = (uint64_t) -1; + else { + r = safe_atou64(eq, &n); + if (r < 0) { + log_error("Failed to parse maximum tasks specification %s", assignment); + return -EINVAL; + } + } + + r = sd_bus_message_append(m, "v", "t", n); + + } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) { + uint64_t u; + + r = cg_cpu_shares_parse(eq, &u); + if (r < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "t", u); + + } else if (STR_IN_SET(field, "IOWeight", "StartupIOWeight")) { + uint64_t u; + + r = cg_weight_parse(eq, &u); + if (r < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "t", u); + + } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) { + uint64_t u; + + r = cg_blkio_weight_parse(eq, &u); + if (r < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "t", u); + + } else if (STR_IN_SET(field, + "User", "Group", "DevicePolicy", "KillMode", + "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath", + "StandardInput", "StandardOutput", "StandardError", + "Description", "Slice", "Type", "WorkingDirectory", + "RootDirectory", "SyslogIdentifier", "ProtectSystem", + "ProtectHome", "SELinuxContext")) + r = sd_bus_message_append(m, "v", "s", eq); + + else if (streq(field, "SyslogLevel")) { + int level; + + level = log_level_from_string(eq); + if (level < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "i", level); + + } else if (streq(field, "SyslogFacility")) { + int facility; + + facility = log_facility_unshifted_from_string(eq); + if (facility < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "i", facility); + + } else if (streq(field, "DeviceAllow")) { + + if (isempty(eq)) + r = sd_bus_message_append(m, "v", "a(ss)", 0); + else { + const char *path, *rwm, *e; + + e = strchr(eq, ' '); + if (e) { + path = strndupa(eq, e - eq); + rwm = e+1; + } else { + path = eq; + rwm = ""; + } + + if (!path_startswith(path, "/dev")) { + log_error("%s is not a device file in /dev.", path); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm); + } + + } else if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) { + + if (isempty(eq)) + r = sd_bus_message_append(m, "v", "a(st)", 0); + else { + const char *path, *bandwidth, *e; + uint64_t bytes; + + e = strchr(eq, ' '); + if (e) { + path = strndupa(eq, e - eq); + bandwidth = e+1; + } else { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + if (!path_startswith(path, "/dev")) { + log_error("%s is not a device file in /dev.", path); + return -EINVAL; + } + + if (streq(bandwidth, "max")) { + bytes = CGROUP_LIMIT_MAX; + } else { + r = parse_size(bandwidth, 1000, &bytes); + if (r < 0) { + log_error("Failed to parse byte value %s.", bandwidth); + return -EINVAL; + } + } + + r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes); + } + + } else if (STR_IN_SET(field, "IODeviceWeight", "BlockIODeviceWeight")) { + + if (isempty(eq)) + r = sd_bus_message_append(m, "v", "a(st)", 0); + else { + const char *path, *weight, *e; + uint64_t u; + + e = strchr(eq, ' '); + if (e) { + path = strndupa(eq, e - eq); + weight = e+1; + } else { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + if (!path_startswith(path, "/dev")) { + log_error("%s is not a device file in /dev.", path); + return -EINVAL; + } + + r = safe_atou64(weight, &u); + if (r < 0) { + log_error("Failed to parse %s value %s.", field, weight); + return -EINVAL; + } + r = sd_bus_message_append(m, "v", "a(st)", 1, path, u); + } + + } else if (streq(field, "Nice")) { + int32_t i; + + r = safe_atoi32(eq, &i); + if (r < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "i", i); + + } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) { + const char *p; + + r = sd_bus_message_open_container(m, 'v', "as"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return bus_log_create_error(r); + + p = eq; + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); + if (r < 0) { + log_error("Failed to parse Environment value %s", eq); + return -EINVAL; + } + if (r == 0) + break; + + if (streq(field, "Environment")) { + if (!env_assignment_is_valid(word)) { + log_error("Invalid environment assignment: %s", word); + return -EINVAL; + } + } else { /* PassEnvironment */ + if (!env_name_is_valid(word)) { + log_error("Invalid environment variable name: %s", word); + return -EINVAL; + } + } + + r = sd_bus_message_append_basic(m, 's', word); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + + } else if (streq(field, "KillSignal")) { + int sig; + + sig = signal_from_string_try_harder(eq); + if (sig < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "i", sig); + + } else if (streq(field, "TimerSlackNSec")) { + nsec_t n; + + r = parse_nsec(eq, &n); + if (r < 0) { + log_error("Failed to parse %s value %s", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "t", n); + } else if (streq(field, "OOMScoreAdjust")) { + int oa; + + r = safe_atoi(eq, &oa); + if (r < 0) { + log_error("Failed to parse %s value %s", field, eq); + return -EINVAL; + } + + if (!oom_score_adjust_is_valid(oa)) { + log_error("OOM score adjust value out of range"); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "i", oa); + } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories")) { + const char *p; + + r = sd_bus_message_open_container(m, 'v', "as"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return bus_log_create_error(r); + + p = eq; + + for (;;) { + _cleanup_free_ char *word = NULL; + int offset; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); + if (r < 0) { + log_error("Failed to parse %s value %s", field, eq); + return -EINVAL; + } + if (r == 0) + break; + + if (!utf8_is_valid(word)) { + log_error("Failed to parse %s value %s", field, eq); + return -EINVAL; + } + + offset = word[0] == '-'; + if (!path_is_absolute(word + offset)) { + log_error("Failed to parse %s value %s", field, eq); + return -EINVAL; + } + + path_kill_slashes(word + offset); + + r = sd_bus_message_append_basic(m, 's', word); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + + } else if (streq(field, "RuntimeDirectory")) { + const char *p; + + r = sd_bus_message_open_container(m, 'v', "as"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return bus_log_create_error(r); + + p = eq; + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); + if (r < 0) + return log_error_errno(r, "Failed to parse %s value %s", field, eq); + + if (r == 0) + break; + + r = sd_bus_message_append_basic(m, 's', word); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + + } else { + log_error("Unknown assignment %s.", assignment); + return -EINVAL; + } + +finish: + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + return 0; +} + +typedef struct BusWaitForJobs { + sd_bus *bus; + Set *jobs; + + char *name; + char *result; + + sd_bus_slot *slot_job_removed; + sd_bus_slot *slot_disconnected; +} BusWaitForJobs; + +static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) { + assert(m); + + log_error("Warning! D-Bus connection terminated."); + sd_bus_close(sd_bus_message_get_bus(m)); + + return 0; +} + +static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { + const char *path, *unit, *result; + BusWaitForJobs *d = userdata; + uint32_t id; + char *found; + int r; + + assert(m); + assert(d); + + r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result); + if (r < 0) { + bus_log_parse_error(r); + return 0; + } + + found = set_remove(d->jobs, (char*) path); + if (!found) + return 0; + + free(found); + + if (!isempty(result)) + d->result = strdup(result); + + if (!isempty(unit)) + d->name = strdup(unit); + + return 0; +} + +void bus_wait_for_jobs_free(BusWaitForJobs *d) { + if (!d) + return; + + set_free_free(d->jobs); + + sd_bus_slot_unref(d->slot_disconnected); + sd_bus_slot_unref(d->slot_job_removed); + + sd_bus_unref(d->bus); + + free(d->name); + free(d->result); + + free(d); +} + +int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) { + _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL; + int r; + + assert(bus); + assert(ret); + + d = new0(BusWaitForJobs, 1); + if (!d) + return -ENOMEM; + + d->bus = sd_bus_ref(bus); + + /* When we are a bus client we match by sender. Direct + * connections OTOH have no initialized sender field, and + * hence we ignore the sender then */ + r = sd_bus_add_match( + bus, + &d->slot_job_removed, + bus->bus_client ? + "type='signal'," + "sender='org.freedesktop.systemd1'," + "interface='org.freedesktop.systemd1.Manager'," + "member='JobRemoved'," + "path='/org/freedesktop/systemd1'" : + "type='signal'," + "interface='org.freedesktop.systemd1.Manager'," + "member='JobRemoved'," + "path='/org/freedesktop/systemd1'", + match_job_removed, d); + if (r < 0) + return r; + + r = sd_bus_add_match( + bus, + &d->slot_disconnected, + "type='signal'," + "sender='org.freedesktop.DBus.Local'," + "interface='org.freedesktop.DBus.Local'," + "member='Disconnected'", + match_disconnected, d); + if (r < 0) + return r; + + *ret = d; + d = NULL; + + return 0; +} + +static int bus_process_wait(sd_bus *bus) { + int r; + + for (;;) { + r = sd_bus_process(bus, NULL); + if (r < 0) + return r; + if (r > 0) + return 0; + + r = sd_bus_wait(bus, (uint64_t) -1); + if (r < 0) + return r; + } +} + +static int bus_job_get_service_result(BusWaitForJobs *d, char **result) { + _cleanup_free_ char *dbus_path = NULL; + + assert(d); + assert(d->name); + assert(result); + + dbus_path = unit_dbus_path_from_name(d->name); + if (!dbus_path) + return -ENOMEM; + + return sd_bus_get_property_string(d->bus, + "org.freedesktop.systemd1", + dbus_path, + "org.freedesktop.systemd1.Service", + "Result", + NULL, + result); +} + +static const struct { + const char *result, *explanation; +} explanations [] = { + { "resources", "of unavailable resources or another system error" }, + { "timeout", "a timeout was exceeded" }, + { "exit-code", "the control process exited with error code" }, + { "signal", "a fatal signal was delivered to the control process" }, + { "core-dump", "a fatal signal was delivered causing the control process to dump core" }, + { "watchdog", "the service failed to send watchdog ping" }, + { "start-limit", "start of the service was attempted too often" } +}; + +static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) { + _cleanup_free_ char *service_shell_quoted = NULL; + const char *systemctl = "systemctl", *journalctl = "journalctl"; + + assert(service); + + service_shell_quoted = shell_maybe_quote(service); + + if (extra_args && extra_args[1]) { + _cleanup_free_ char *t; + + t = strv_join((char**) extra_args, " "); + systemctl = strjoina("systemctl ", t ? : "<args>"); + journalctl = strjoina("journalctl ", t ? : "<args>"); + } + + if (!isempty(result)) { + unsigned i; + + for (i = 0; i < ELEMENTSOF(explanations); ++i) + if (streq(result, explanations[i].result)) + break; + + if (i < ELEMENTSOF(explanations)) { + log_error("Job for %s failed because %s.\n" + "See \"%s status %s\" and \"%s -xe\" for details.\n", + service, + explanations[i].explanation, + systemctl, + service_shell_quoted ?: "<service>", + journalctl); + goto finish; + } + } + + log_error("Job for %s failed.\n" + "See \"%s status %s\" and \"%s -xe\" for details.\n", + service, + systemctl, + service_shell_quoted ?: "<service>", + journalctl); + +finish: + /* For some results maybe additional explanation is required */ + if (streq_ptr(result, "start-limit")) + log_info("To force a start use \"%1$s reset-failed %2$s\"\n" + "followed by \"%1$s start %2$s\" again.", + systemctl, + service_shell_quoted ?: "<service>"); +} + +static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) { + int r = 0; + + assert(d->result); + + if (!quiet) { + if (streq(d->result, "canceled")) + log_error("Job for %s canceled.", strna(d->name)); + else if (streq(d->result, "timeout")) + log_error("Job for %s timed out.", strna(d->name)); + else if (streq(d->result, "dependency")) + log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name)); + else if (streq(d->result, "invalid")) + log_error("%s is not active, cannot reload.", strna(d->name)); + else if (streq(d->result, "assert")) + log_error("Assertion failed on job for %s.", strna(d->name)); + else if (streq(d->result, "unsupported")) + log_error("Operation on or unit type of %s not supported on this system.", strna(d->name)); + else if (!streq(d->result, "done") && !streq(d->result, "skipped")) { + if (d->name) { + int q; + _cleanup_free_ char *result = NULL; + + q = bus_job_get_service_result(d, &result); + if (q < 0) + log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name); + + log_job_error_with_service_result(d->name, result, extra_args); + } else + log_error("Job failed. See \"journalctl -xe\" for details."); + } + } + + if (streq(d->result, "canceled")) + r = -ECANCELED; + else if (streq(d->result, "timeout")) + r = -ETIME; + else if (streq(d->result, "dependency")) + r = -EIO; + else if (streq(d->result, "invalid")) + r = -ENOEXEC; + else if (streq(d->result, "assert")) + r = -EPROTO; + else if (streq(d->result, "unsupported")) + r = -EOPNOTSUPP; + else if (!streq(d->result, "done") && !streq(d->result, "skipped")) + r = -EIO; + + return r; +} + +int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) { + int r = 0; + + assert(d); + + while (!set_isempty(d->jobs)) { + int q; + + q = bus_process_wait(d->bus); + if (q < 0) + return log_error_errno(q, "Failed to wait for response: %m"); + + if (d->result) { + q = check_wait_response(d, quiet, extra_args); + /* Return the first error as it is most likely to be + * meaningful. */ + if (q < 0 && r == 0) + r = q; + + log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name)); + } + + d->name = mfree(d->name); + d->result = mfree(d->result); + } + + return r; +} + +int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) { + int r; + + assert(d); + + r = set_ensure_allocated(&d->jobs, &string_hash_ops); + if (r < 0) + return r; + + return set_put_strdup(d->jobs, path); +} + +int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) { + int r; + + r = bus_wait_for_jobs_add(d, path); + if (r < 0) + return log_oom(); + + return bus_wait_for_jobs(d, quiet, NULL); +} + +int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) { + const char *type, *path, *source; + int r; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) { + /* We expect only "success" changes to be sent over the bus. + Hence, reject anything negative. */ + UnitFileChangeType ch = unit_file_change_type_from_string(type); + + if (ch < 0) { + log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path); + continue; + } + + r = unit_file_changes_add(changes, n_changes, ch, path, source); + if (r < 0) + return r; + } + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + unit_file_dump_changes(0, NULL, *changes, *n_changes, false); + return 0; +} + +struct CGroupInfo { + char *cgroup_path; + bool is_const; /* If false, cgroup_path should be free()'d */ + + Hashmap *pids; /* PID → process name */ + bool done; + + struct CGroupInfo *parent; + LIST_FIELDS(struct CGroupInfo, siblings); + LIST_HEAD(struct CGroupInfo, children); + size_t n_children; +}; + +static bool IS_ROOT(const char *p) { + return isempty(p) || streq(p, "/"); +} + +static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) { + struct CGroupInfo *parent = NULL, *cg; + int r; + + assert(cgroups); + assert(ret); + + if (IS_ROOT(path)) + path = "/"; + + cg = hashmap_get(cgroups, path); + if (cg) { + *ret = cg; + return 0; + } + + if (!IS_ROOT(path)) { + const char *e, *pp; + + e = strrchr(path, '/'); + if (!e) + return -EINVAL; + + pp = strndupa(path, e - path); + if (!pp) + return -ENOMEM; + + r = add_cgroup(cgroups, pp, false, &parent); + if (r < 0) + return r; + } + + cg = new0(struct CGroupInfo, 1); + if (!cg) + return -ENOMEM; + + if (is_const) + cg->cgroup_path = (char*) path; + else { + cg->cgroup_path = strdup(path); + if (!cg->cgroup_path) { + free(cg); + return -ENOMEM; + } + } + + cg->is_const = is_const; + cg->parent = parent; + + r = hashmap_put(cgroups, cg->cgroup_path, cg); + if (r < 0) { + if (!is_const) + free(cg->cgroup_path); + free(cg); + return r; + } + + if (parent) { + LIST_PREPEND(siblings, parent->children, cg); + parent->n_children++; + } + + *ret = cg; + return 1; +} + +static int add_process( + Hashmap *cgroups, + const char *path, + pid_t pid, + const char *name) { + + struct CGroupInfo *cg; + int r; + + assert(cgroups); + assert(name); + assert(pid > 0); + + r = add_cgroup(cgroups, path, true, &cg); + if (r < 0) + return r; + + r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops); + if (r < 0) + return r; + + return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name); +} + +static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) { + assert(cgroups); + assert(cg); + + while (cg->children) + remove_cgroup(cgroups, cg->children); + + hashmap_remove(cgroups, cg->cgroup_path); + + if (!cg->is_const) + free(cg->cgroup_path); + + hashmap_free(cg->pids); + + if (cg->parent) + LIST_REMOVE(siblings, cg->parent->children, cg); + + free(cg); +} + +static int cgroup_info_compare_func(const void *a, const void *b) { + const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b; + + assert(x); + assert(y); + + return strcmp(x->cgroup_path, y->cgroup_path); +} + +static int dump_processes( + Hashmap *cgroups, + const char *cgroup_path, + const char *prefix, + unsigned n_columns, + OutputFlags flags) { + + struct CGroupInfo *cg; + int r; + + assert(prefix); + + if (IS_ROOT(cgroup_path)) + cgroup_path = "/"; + + cg = hashmap_get(cgroups, cgroup_path); + if (!cg) + return 0; + + if (!hashmap_isempty(cg->pids)) { + const char *name; + size_t n = 0, i; + pid_t *pids; + void *pidp; + Iterator j; + int width; + + /* Order processes by their PID */ + pids = newa(pid_t, hashmap_size(cg->pids)); + + HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) + pids[n++] = PTR_TO_PID(pidp); + + assert(n == hashmap_size(cg->pids)); + qsort_safe(pids, n, sizeof(pid_t), pid_compare_func); + + width = DECIMAL_STR_WIDTH(pids[n-1]); + + for (i = 0; i < n; i++) { + _cleanup_free_ char *e = NULL; + const char *special; + bool more; + + name = hashmap_get(cg->pids, PID_TO_PTR(pids[i])); + assert(name); + + if (n_columns != 0) { + unsigned k; + + k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U); + + e = ellipsize(name, k, 100); + if (e) + name = e; + } + + more = i+1 < n || cg->children; + special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT); + + fprintf(stdout, "%s%s%*"PID_PRI" %s\n", + prefix, + special, + width, pids[i], + name); + } + } + + if (cg->children) { + struct CGroupInfo **children, *child; + size_t n = 0, i; + + /* Order subcgroups by their name */ + children = newa(struct CGroupInfo*, cg->n_children); + LIST_FOREACH(siblings, child, cg->children) + children[n++] = child; + assert(n == cg->n_children); + qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func); + + n_columns = MAX(LESS_BY(n_columns, 2U), 20U); + + for (i = 0; i < n; i++) { + _cleanup_free_ char *pp = NULL; + const char *name, *special; + bool more; + + child = children[i]; + + name = strrchr(child->cgroup_path, '/'); + if (!name) + return -EINVAL; + name++; + + more = i+1 < n; + special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT); + + fputs(prefix, stdout); + fputs(special, stdout); + fputs(name, stdout); + fputc('\n', stdout); + + special = special_glyph(more ? TREE_VERTICAL : TREE_SPACE); + + pp = strappend(prefix, special); + if (!pp) + return -ENOMEM; + + r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags); + if (r < 0) + return r; + } + } + + cg->done = true; + return 0; +} + +static int dump_extra_processes( + Hashmap *cgroups, + const char *prefix, + unsigned n_columns, + OutputFlags flags) { + + _cleanup_free_ pid_t *pids = NULL; + _cleanup_hashmap_free_ Hashmap *names = NULL; + struct CGroupInfo *cg; + size_t n_allocated = 0, n = 0, k; + Iterator i; + int width, r; + + /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as + * combined, sorted, linear list. */ + + HASHMAP_FOREACH(cg, cgroups, i) { + const char *name; + void *pidp; + Iterator j; + + if (cg->done) + continue; + + if (hashmap_isempty(cg->pids)) + continue; + + r = hashmap_ensure_allocated(&names, &trivial_hash_ops); + if (r < 0) + return r; + + if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids))) + return -ENOMEM; + + HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) { + pids[n++] = PTR_TO_PID(pidp); + + r = hashmap_put(names, pidp, (void*) name); + if (r < 0) + return r; + } + } + + if (n == 0) + return 0; + + qsort_safe(pids, n, sizeof(pid_t), pid_compare_func); + width = DECIMAL_STR_WIDTH(pids[n-1]); + + for (k = 0; k < n; k++) { + _cleanup_free_ char *e = NULL; + const char *name; + + name = hashmap_get(names, PID_TO_PTR(pids[k])); + assert(name); + + if (n_columns != 0) { + unsigned z; + + z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U); + + e = ellipsize(name, z, 100); + if (e) + name = e; + } + + fprintf(stdout, "%s%s %*" PID_PRI " %s\n", + prefix, + special_glyph(TRIANGULAR_BULLET), + width, pids[k], + name); + } + + return 0; +} + +int unit_show_processes( + sd_bus *bus, + const char *unit, + const char *cgroup_path, + const char *prefix, + unsigned n_columns, + OutputFlags flags, + sd_bus_error *error) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Hashmap *cgroups = NULL; + struct CGroupInfo *cg; + int r; + + assert(bus); + assert(unit); + + if (flags & OUTPUT_FULL_WIDTH) + n_columns = 0; + else if (n_columns <= 0) + n_columns = columns(); + + prefix = strempty(prefix); + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnitProcesses", + error, + &reply, + "s", + unit); + if (r < 0) + return r; + + cgroups = hashmap_new(&string_hash_ops); + if (!cgroups) + return -ENOMEM; + + r = sd_bus_message_enter_container(reply, 'a', "(sus)"); + if (r < 0) + goto finish; + + for (;;) { + const char *path = NULL, *name = NULL; + uint32_t pid; + + r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name); + if (r < 0) + goto finish; + if (r == 0) + break; + + r = add_process(cgroups, path, pid, name); + if (r < 0) + goto finish; + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + goto finish; + + r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags); + if (r < 0) + goto finish; + + r = dump_extra_processes(cgroups, prefix, n_columns, flags); + +finish: + while ((cg = hashmap_first(cgroups))) + remove_cgroup(cgroups, cg); + + hashmap_free(cgroups); + + return r; +} diff --git a/src/shared/bus-unit-util.h b/src/shared/bus-unit-util.h new file mode 100644 index 0000000000..c0c172f336 --- /dev/null +++ b/src/shared/bus-unit-util.h @@ -0,0 +1,57 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2016 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "sd-bus.h" + +#include "output-mode.h" +#include "install.h" + +typedef struct UnitInfo { + const char *machine; + const char *id; + const char *description; + const char *load_state; + const char *active_state; + const char *sub_state; + const char *following; + const char *unit_path; + uint32_t job_id; + const char *job_type; + const char *job_path; +} UnitInfo; + +int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u); + +int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment); + +typedef struct BusWaitForJobs BusWaitForJobs; + +int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret); +void bus_wait_for_jobs_free(BusWaitForJobs *d); +int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path); +int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args); +int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet); + +DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free); + +int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes); + +int unit_show_processes(sd_bus *bus, const char *unit, const char *cgroup_path, const char *prefix, unsigned n_columns, OutputFlags flags, sd_bus_error *error); diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 38557f0b8d..4efbf3710f 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -39,34 +39,16 @@ #include "bus-label.h" #include "bus-message.h" #include "bus-util.h" -#include "cgroup-util.h" #include "def.h" -#include "env-util.h" #include "escape.h" -#include "extract-word.h" #include "fd-util.h" -#include "hashmap.h" -#include "install.h" -#include "kdbus.h" -#include "log.h" -#include "macro.h" #include "missing.h" #include "parse-util.h" -#include "path-util.h" #include "proc-cmdline.h" -#include "process-util.h" #include "rlimit-util.h" -#include "set.h" -#include "signal-util.h" #include "stdio-util.h" -#include "string-util.h" #include "strv.h" -#include "syslog-util.h" -#include "time-util.h" -#include "unit-name.h" #include "user-util.h" -#include "utf8.h" -#include "util.h" static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { sd_event *e = userdata; @@ -712,7 +694,15 @@ int bus_connect_user_systemd(sd_bus **_bus) { return 0; } -int bus_print_property(const char *name, sd_bus_message *property, bool all) { +#define print_property(name, fmt, ...) \ + do { \ + if (value) \ + printf(fmt "\n", __VA_ARGS__); \ + else \ + printf("%s=" fmt "\n", name, __VA_ARGS__); \ + } while(0) + +int bus_print_property(const char *name, sd_bus_message *property, bool value, bool all) { char type; const char *contents; int r; @@ -740,7 +730,7 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) { if (!escaped) return -ENOMEM; - printf("%s=%s\n", name, escaped); + print_property(name, "%s", escaped); } return 1; @@ -753,7 +743,7 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) { if (r < 0) return r; - printf("%s=%s\n", name, yes_no(b)); + print_property(name, "%s", yes_no(b)); return 1; } @@ -773,14 +763,14 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) { t = format_timestamp(timestamp, sizeof(timestamp), u); if (t || all) - printf("%s=%s\n", name, strempty(t)); + print_property(name, "%s", strempty(t)); } else if (strstr(name, "USec")) { char timespan[FORMAT_TIMESPAN_MAX]; - printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u, 0)); + print_property(name, "%s", format_timespan(timespan, sizeof(timespan), u, 0)); } else - printf("%s=%llu\n", name, (unsigned long long) u); + print_property(name, "%"PRIu64, u); return 1; } @@ -792,7 +782,7 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) { if (r < 0) return r; - printf("%s=%lld\n", name, (long long) i); + print_property(name, "%"PRIi64, i); return 1; } @@ -805,9 +795,9 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) { return r; if (strstr(name, "UMask") || strstr(name, "Mode")) - printf("%s=%04o\n", name, u); + print_property(name, "%04o", u); else - printf("%s=%u\n", name, (unsigned) u); + print_property(name, "%"PRIu32, u); return 1; } @@ -819,7 +809,7 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) { if (r < 0) return r; - printf("%s=%i\n", name, (int) i); + print_property(name, "%"PRIi32, i); return 1; } @@ -830,7 +820,7 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) { if (r < 0) return r; - printf("%s=%g\n", name, d); + print_property(name, "%g", d); return 1; } @@ -843,10 +833,10 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) { if (r < 0) return r; - while((r = sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) > 0) { + while ((r = sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) > 0) { _cleanup_free_ char *escaped = NULL; - if (first) + if (first && !value) printf("%s=", name); escaped = xescape(str, "\n "); @@ -860,7 +850,7 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) { if (r < 0) return r; - if (first && all) + if (first && all && !value) printf("%s=", name); if (!first || all) puts(""); @@ -882,7 +872,8 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) { if (all || n > 0) { unsigned int i; - printf("%s=", name); + if (!value) + printf("%s=", name); for (i = 0; i < n; i++) printf("%02x", u[i]); @@ -903,7 +894,8 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) { if (all || n > 0) { unsigned int i; - printf("%s=", name); + if (!value) + printf("%s=", name); for (i = 0; i < n; i++) printf("%08x", u[i]); @@ -920,7 +912,7 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) { return 0; } -int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool all) { +int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool value, bool all) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; @@ -960,7 +952,7 @@ int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, ch if (r < 0) return r; - r = bus_print_property(name, reply, all); + r = bus_print_property(name, reply, value, all); if (r < 0) return r; if (r == 0) { @@ -1068,7 +1060,7 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_ } case SD_BUS_TYPE_UINT32: { - uint64_t u; + uint32_t u; uint32_t *p = userdata; r = sd_bus_message_read_basic(m, type, &u); @@ -1373,839 +1365,6 @@ int bus_log_create_error(int r) { return log_error_errno(r, "Failed to create bus message: %m"); } -int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) { - assert(message); - assert(u); - - u->machine = NULL; - - return sd_bus_message_read( - message, - "(ssssssouso)", - &u->id, - &u->description, - &u->load_state, - &u->active_state, - &u->sub_state, - &u->following, - &u->unit_path, - &u->job_id, - &u->job_type, - &u->job_path); -} - -int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) { - const char *eq, *field; - int r, rl; - - assert(m); - assert(assignment); - - eq = strchr(assignment, '='); - if (!eq) { - log_error("Not an assignment: %s", assignment); - return -EINVAL; - } - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); - if (r < 0) - return bus_log_create_error(r); - - field = strndupa(assignment, eq - assignment); - eq ++; - - if (streq(field, "CPUQuota")) { - - if (isempty(eq)) - r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY); - else if (endswith(eq, "%")) { - double percent; - - if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) { - log_error("CPU quota '%s' invalid.", eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) percent * USEC_PER_SEC / 100); - } else { - log_error("CPU quota needs to be in percent."); - return -EINVAL; - } - - goto finish; - - } else if (streq(field, "EnvironmentFile")) { - - r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1, - eq[0] == '-' ? eq + 1 : eq, - eq[0] == '-'); - goto finish; - - } else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) { - char *n; - usec_t t; - size_t l; - r = parse_sec(eq, &t); - if (r < 0) - return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq); - - l = strlen(field); - n = newa(char, l + 2); - if (!n) - return log_oom(); - - /* Change suffix Sec → USec */ - strcpy(mempcpy(n, field, l - 3), "USec"); - r = sd_bus_message_append(m, "sv", n, "t", t); - goto finish; - } - - r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); - if (r < 0) - return bus_log_create_error(r); - - rl = rlimit_from_string(field); - if (rl >= 0) { - const char *sn; - struct rlimit l; - - r = rlimit_parse(rl, eq, &l); - if (r < 0) - return log_error_errno(r, "Failed to parse resource limit: %s", eq); - - r = sd_bus_message_append(m, "v", "t", l.rlim_max); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); - if (r < 0) - return bus_log_create_error(r); - - sn = strjoina(field, "Soft"); - r = sd_bus_message_append(m, "sv", sn, "t", l.rlim_cur); - - } else if (STR_IN_SET(field, - "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting", - "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", - "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", - "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges", - "SyslogLevelPrefix", "Delegate", "RemainAfterElapse")) { - - r = parse_boolean(eq); - if (r < 0) - return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment); - - r = sd_bus_message_append(m, "v", "b", r); - - } else if (streq(field, "MemoryLimit")) { - uint64_t bytes; - - if (isempty(eq) || streq(eq, "infinity")) - bytes = (uint64_t) -1; - else { - r = parse_size(eq, 1024, &bytes); - if (r < 0) { - log_error("Failed to parse bytes specification %s", assignment); - return -EINVAL; - } - } - - r = sd_bus_message_append(m, "v", "t", bytes); - - } else if (streq(field, "TasksMax")) { - uint64_t n; - - if (isempty(eq) || streq(eq, "infinity")) - n = (uint64_t) -1; - else { - r = safe_atou64(eq, &n); - if (r < 0) { - log_error("Failed to parse maximum tasks specification %s", assignment); - return -EINVAL; - } - } - - r = sd_bus_message_append(m, "v", "t", n); - - } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) { - uint64_t u; - - r = cg_cpu_shares_parse(eq, &u); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "t", u); - - } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) { - uint64_t u; - - r = cg_cpu_shares_parse(eq, &u); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "t", u); - - } else if (STR_IN_SET(field, - "User", "Group", "DevicePolicy", "KillMode", - "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath", - "StandardInput", "StandardOutput", "StandardError", - "Description", "Slice", "Type", "WorkingDirectory", - "RootDirectory", "SyslogIdentifier", "ProtectSystem", - "ProtectHome")) - r = sd_bus_message_append(m, "v", "s", eq); - - else if (streq(field, "SyslogLevel")) { - int level; - - level = log_level_from_string(eq); - if (level < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "i", level); - - } else if (streq(field, "SyslogFacility")) { - int facility; - - facility = log_facility_unshifted_from_string(eq); - if (facility < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "i", facility); - - } else if (streq(field, "DeviceAllow")) { - - if (isempty(eq)) - r = sd_bus_message_append(m, "v", "a(ss)", 0); - else { - const char *path, *rwm, *e; - - e = strchr(eq, ' '); - if (e) { - path = strndupa(eq, e - eq); - rwm = e+1; - } else { - path = eq; - rwm = ""; - } - - if (!path_startswith(path, "/dev")) { - log_error("%s is not a device file in /dev.", path); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm); - } - - } else if (STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) { - - if (isempty(eq)) - r = sd_bus_message_append(m, "v", "a(st)", 0); - else { - const char *path, *bandwidth, *e; - uint64_t bytes; - - e = strchr(eq, ' '); - if (e) { - path = strndupa(eq, e - eq); - bandwidth = e+1; - } else { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - if (!path_startswith(path, "/dev")) { - log_error("%s is not a device file in /dev.", path); - return -EINVAL; - } - - r = parse_size(bandwidth, 1000, &bytes); - if (r < 0) { - log_error("Failed to parse byte value %s.", bandwidth); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes); - } - - } else if (streq(field, "BlockIODeviceWeight")) { - - if (isempty(eq)) - r = sd_bus_message_append(m, "v", "a(st)", 0); - else { - const char *path, *weight, *e; - uint64_t u; - - e = strchr(eq, ' '); - if (e) { - path = strndupa(eq, e - eq); - weight = e+1; - } else { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - if (!path_startswith(path, "/dev")) { - log_error("%s is not a device file in /dev.", path); - return -EINVAL; - } - - r = safe_atou64(weight, &u); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, weight); - return -EINVAL; - } - r = sd_bus_message_append(m, "v", "a(st)", path, u); - } - - } else if (streq(field, "Nice")) { - int32_t i; - - r = safe_atoi32(eq, &i); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "i", i); - - } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) { - const char *p; - - r = sd_bus_message_open_container(m, 'v', "as"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - return bus_log_create_error(r); - - p = eq; - - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); - if (r < 0) { - log_error("Failed to parse Environment value %s", eq); - return -EINVAL; - } - if (r == 0) - break; - - if (streq(field, "Environment")) { - if (!env_assignment_is_valid(word)) { - log_error("Invalid environment assignment: %s", word); - return -EINVAL; - } - } else { /* PassEnvironment */ - if (!env_name_is_valid(word)) { - log_error("Invalid environment variable name: %s", word); - return -EINVAL; - } - } - - r = sd_bus_message_append_basic(m, 's', word); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - - } else if (streq(field, "KillSignal")) { - int sig; - - sig = signal_from_string_try_harder(eq); - if (sig < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "i", sig); - - } else if (streq(field, "TimerSlackNSec")) { - nsec_t n; - - r = parse_nsec(eq, &n); - if (r < 0) { - log_error("Failed to parse %s value %s", field, eq); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "t", n); - } else if (streq(field, "OOMScoreAdjust")) { - int oa; - - r = safe_atoi(eq, &oa); - if (r < 0) { - log_error("Failed to parse %s value %s", field, eq); - return -EINVAL; - } - - if (!oom_score_adjust_is_valid(oa)) { - log_error("OOM score adjust value out of range"); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "i", oa); - } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories")) { - const char *p; - - r = sd_bus_message_open_container(m, 'v', "as"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - return bus_log_create_error(r); - - p = eq; - - for (;;) { - _cleanup_free_ char *word = NULL; - int offset; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); - if (r < 0) { - log_error("Failed to parse %s value %s", field, eq); - return -EINVAL; - } - if (r == 0) - break; - - if (!utf8_is_valid(word)) { - log_error("Failed to parse %s value %s", field, eq); - return -EINVAL; - } - - offset = word[0] == '-'; - if (!path_is_absolute(word + offset)) { - log_error("Failed to parse %s value %s", field, eq); - return -EINVAL; - } - - path_kill_slashes(word + offset); - - r = sd_bus_message_append_basic(m, 's', word); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - - } else if (streq(field, "RuntimeDirectory")) { - const char *p; - - r = sd_bus_message_open_container(m, 'v', "as"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - return bus_log_create_error(r); - - p = eq; - - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s", field, eq); - - if (r == 0) - break; - - r = sd_bus_message_append_basic(m, 's', word); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - - } else { - log_error("Unknown assignment %s.", assignment); - return -EINVAL; - } - -finish: - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - return 0; -} - -typedef struct BusWaitForJobs { - sd_bus *bus; - Set *jobs; - - char *name; - char *result; - - sd_bus_slot *slot_job_removed; - sd_bus_slot *slot_disconnected; -} BusWaitForJobs; - -static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) { - assert(m); - - log_error("Warning! D-Bus connection terminated."); - sd_bus_close(sd_bus_message_get_bus(m)); - - return 0; -} - -static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { - const char *path, *unit, *result; - BusWaitForJobs *d = userdata; - uint32_t id; - char *found; - int r; - - assert(m); - assert(d); - - r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result); - if (r < 0) { - bus_log_parse_error(r); - return 0; - } - - found = set_remove(d->jobs, (char*) path); - if (!found) - return 0; - - free(found); - - if (!isempty(result)) - d->result = strdup(result); - - if (!isempty(unit)) - d->name = strdup(unit); - - return 0; -} - -void bus_wait_for_jobs_free(BusWaitForJobs *d) { - if (!d) - return; - - set_free_free(d->jobs); - - sd_bus_slot_unref(d->slot_disconnected); - sd_bus_slot_unref(d->slot_job_removed); - - sd_bus_unref(d->bus); - - free(d->name); - free(d->result); - - free(d); -} - -int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) { - _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL; - int r; - - assert(bus); - assert(ret); - - d = new0(BusWaitForJobs, 1); - if (!d) - return -ENOMEM; - - d->bus = sd_bus_ref(bus); - - /* When we are a bus client we match by sender. Direct - * connections OTOH have no initialized sender field, and - * hence we ignore the sender then */ - r = sd_bus_add_match( - bus, - &d->slot_job_removed, - bus->bus_client ? - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='JobRemoved'," - "path='/org/freedesktop/systemd1'" : - "type='signal'," - "interface='org.freedesktop.systemd1.Manager'," - "member='JobRemoved'," - "path='/org/freedesktop/systemd1'", - match_job_removed, d); - if (r < 0) - return r; - - r = sd_bus_add_match( - bus, - &d->slot_disconnected, - "type='signal'," - "sender='org.freedesktop.DBus.Local'," - "interface='org.freedesktop.DBus.Local'," - "member='Disconnected'", - match_disconnected, d); - if (r < 0) - return r; - - *ret = d; - d = NULL; - - return 0; -} - -static int bus_process_wait(sd_bus *bus) { - int r; - - for (;;) { - r = sd_bus_process(bus, NULL); - if (r < 0) - return r; - if (r > 0) - return 0; - - r = sd_bus_wait(bus, (uint64_t) -1); - if (r < 0) - return r; - } -} - -static int bus_job_get_service_result(BusWaitForJobs *d, char **result) { - _cleanup_free_ char *dbus_path = NULL; - - assert(d); - assert(d->name); - assert(result); - - dbus_path = unit_dbus_path_from_name(d->name); - if (!dbus_path) - return -ENOMEM; - - return sd_bus_get_property_string(d->bus, - "org.freedesktop.systemd1", - dbus_path, - "org.freedesktop.systemd1.Service", - "Result", - NULL, - result); -} - -static const struct { - const char *result, *explanation; -} explanations [] = { - { "resources", "a configured resource limit was exceeded" }, - { "timeout", "a timeout was exceeded" }, - { "exit-code", "the control process exited with error code" }, - { "signal", "a fatal signal was delivered to the control process" }, - { "core-dump", "a fatal signal was delivered causing the control process to dump core" }, - { "watchdog", "the service failed to send watchdog ping" }, - { "start-limit", "start of the service was attempted too often" } -}; - -static void log_job_error_with_service_result(const char* service, const char *result, const char *extra_args) { - _cleanup_free_ char *service_shell_quoted = NULL, *systemctl_extra_args = NULL; - - assert(service); - - service_shell_quoted = shell_maybe_quote(service); - - systemctl_extra_args = strjoin("systemctl ", extra_args, " ", NULL); - if (!systemctl_extra_args) { - log_oom(); - return; - } - - systemctl_extra_args = strstrip(systemctl_extra_args); - - if (!isempty(result)) { - unsigned i; - - for (i = 0; i < ELEMENTSOF(explanations); ++i) - if (streq(result, explanations[i].result)) - break; - - if (i < ELEMENTSOF(explanations)) { - log_error("Job for %s failed because %s. See \"%s status %s\" and \"journalctl -xe\" for details.\n", - service, - explanations[i].explanation, - systemctl_extra_args, - strna(service_shell_quoted)); - - goto finish; - } - } - - log_error("Job for %s failed. See \"%s status %s\" and \"journalctl -xe\" for details.\n", - service, - systemctl_extra_args, - strna(service_shell_quoted)); - -finish: - /* For some results maybe additional explanation is required */ - if (streq_ptr(result, "start-limit")) - log_info("To force a start use \"%1$s reset-failed %2$s\" followed by \"%1$s start %2$s\" again.", - systemctl_extra_args, - strna(service_shell_quoted)); -} - -static int check_wait_response(BusWaitForJobs *d, bool quiet, const char *extra_args) { - int r = 0; - - assert(d->result); - - if (!quiet) { - if (streq(d->result, "canceled")) - log_error("Job for %s canceled.", strna(d->name)); - else if (streq(d->result, "timeout")) - log_error("Job for %s timed out.", strna(d->name)); - else if (streq(d->result, "dependency")) - log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name)); - else if (streq(d->result, "invalid")) - log_error("%s is not active, cannot reload.", strna(d->name)); - else if (streq(d->result, "assert")) - log_error("Assertion failed on job for %s.", strna(d->name)); - else if (streq(d->result, "unsupported")) - log_error("Operation on or unit type of %s not supported on this system.", strna(d->name)); - else if (!streq(d->result, "done") && !streq(d->result, "skipped")) { - if (d->name) { - int q; - _cleanup_free_ char *result = NULL; - - q = bus_job_get_service_result(d, &result); - if (q < 0) - log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name); - - log_job_error_with_service_result(d->name, result, extra_args); - } else - log_error("Job failed. See \"journalctl -xe\" for details."); - } - } - - if (streq(d->result, "canceled")) - r = -ECANCELED; - else if (streq(d->result, "timeout")) - r = -ETIME; - else if (streq(d->result, "dependency")) - r = -EIO; - else if (streq(d->result, "invalid")) - r = -ENOEXEC; - else if (streq(d->result, "assert")) - r = -EPROTO; - else if (streq(d->result, "unsupported")) - r = -EOPNOTSUPP; - else if (!streq(d->result, "done") && !streq(d->result, "skipped")) - r = -EIO; - - return r; -} - -int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char *extra_args) { - int r = 0; - - assert(d); - - while (!set_isempty(d->jobs)) { - int q; - - q = bus_process_wait(d->bus); - if (q < 0) - return log_error_errno(q, "Failed to wait for response: %m"); - - if (d->result) { - q = check_wait_response(d, quiet, extra_args); - /* Return the first error as it is most likely to be - * meaningful. */ - if (q < 0 && r == 0) - r = q; - - log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name)); - } - - d->name = mfree(d->name); - d->result = mfree(d->result); - } - - return r; -} - -int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) { - int r; - - assert(d); - - r = set_ensure_allocated(&d->jobs, &string_hash_ops); - if (r < 0) - return r; - - return set_put_strdup(d->jobs, path); -} - -int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) { - int r; - - r = bus_wait_for_jobs_add(d, path); - if (r < 0) - return log_oom(); - - return bus_wait_for_jobs(d, quiet, NULL); -} - -int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) { - const char *type, *path, *source; - int r; - - r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) { - if (!quiet) { - if (streq(type, "symlink")) - log_info("Created symlink from %s to %s.", path, source); - else - log_info("Removed symlink %s.", path); - } - - r = unit_file_changes_add(changes, n_changes, streq(type, "symlink") ? UNIT_FILE_SYMLINK : UNIT_FILE_UNLINK, path, source); - if (r < 0) - return r; - } - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(m); - if (r < 0) - return bus_log_parse_error(r); - - return 0; -} - /** * bus_path_encode_unique() - encode unique object path * @b: bus connection or NULL diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h index 204da55682..d792258ecd 100644 --- a/src/shared/bus-util.h +++ b/src/shared/bus-util.h @@ -24,15 +24,12 @@ #include <stdint.h> #include <sys/types.h> -#include "sd-bus-vtable.h" #include "sd-bus.h" #include "sd-event.h" #include "hashmap.h" -#include "install.h" #include "macro.h" #include "string-util.h" -#include "time-util.h" typedef enum BusTransport { BUS_TRANSPORT_LOCAL, @@ -78,8 +75,8 @@ int bus_connect_user_systemd(sd_bus **_bus); int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **bus); int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus); -int bus_print_property(const char *name, sd_bus_message *property, bool all); -int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool all); +int bus_print_property(const char *name, sd_bus_message *property, bool value, bool all); +int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool value, bool all); int bus_property_get_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); @@ -126,22 +123,6 @@ assert_cc(sizeof(mode_t) == sizeof(uint32_t)); int bus_log_parse_error(int r); int bus_log_create_error(int r); -typedef struct UnitInfo { - const char *machine; - const char *id; - const char *description; - const char *load_state; - const char *active_state; - const char *sub_state; - const char *following; - const char *unit_path; - uint32_t job_id; - const char *job_type; - const char *job_path; -} UnitInfo; - -int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u); - #define BUS_DEFINE_PROPERTY_GET_ENUM(function, name, type) \ int function(sd_bus *bus, \ const char *path, \ @@ -173,20 +154,6 @@ int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u); SD_BUS_PROPERTY(name, "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, realtime), (flags)), \ SD_BUS_PROPERTY(name "Monotonic", "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, monotonic), (flags)) -int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment); - -typedef struct BusWaitForJobs BusWaitForJobs; - -int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret); -void bus_wait_for_jobs_free(BusWaitForJobs *d); -int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path); -int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char *extra_args); -int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet); - -DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free); - -int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes); - int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path); int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external); diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c index f3039b23f7..3e451db715 100644 --- a/src/shared/cgroup-show.c +++ b/src/shared/cgroup-show.c @@ -37,23 +37,21 @@ #include "string-util.h" #include "terminal-util.h" -static int compare(const void *a, const void *b) { - const pid_t *p = a, *q = b; +static void show_pid_array( + pid_t pids[], + unsigned n_pids, + const char *prefix, + unsigned n_columns, + bool extra, + bool more, + OutputFlags flags) { - if (*p < *q) - return -1; - if (*p > *q) - return 1; - return 0; -} - -static void show_pid_array(pid_t pids[], unsigned n_pids, const char *prefix, unsigned n_columns, bool extra, bool more, bool kernel_threads, OutputFlags flags) { unsigned i, j, pid_width; if (n_pids == 0) return; - qsort(pids, n_pids, sizeof(pid_t), compare); + qsort(pids, n_pids, sizeof(pid_t), pid_compare_func); /* Filter duplicates */ for (j = 0, i = 1; i < n_pids; i++) { @@ -78,16 +76,21 @@ static void show_pid_array(pid_t pids[], unsigned n_pids, const char *prefix, un get_process_cmdline(pids[i], n_columns, true, &t); if (extra) - printf("%s%s ", prefix, draw_special_char(DRAW_TRIANGULAR_BULLET)); + printf("%s%s ", prefix, special_glyph(TRIANGULAR_BULLET)); else - printf("%s%s", prefix, draw_special_char(((more || i < n_pids-1) ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT))); + printf("%s%s", prefix, special_glyph(((more || i < n_pids-1) ? TREE_BRANCH : TREE_RIGHT))); printf("%*"PID_PRI" %s\n", pid_width, pids[i], strna(t)); } } +static int show_cgroup_one_by_path( + const char *path, + const char *prefix, + unsigned n_columns, + bool more, + OutputFlags flags) { -static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigned n_columns, bool more, bool kernel_threads, OutputFlags flags) { char *fn; _cleanup_fclose_ FILE *f = NULL; size_t n = 0, n_allocated = 0; @@ -107,7 +110,7 @@ static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigne while ((r = cg_read_pid(f, &pid)) > 0) { - if (!kernel_threads && is_kernel_thread(pid) > 0) + if (!(flags & OUTPUT_KERNEL_THREADS) && is_kernel_thread(pid) > 0) continue; if (!GREEDY_REALLOC(pids, n_allocated, n + 1)) @@ -120,12 +123,17 @@ static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigne if (r < 0) return r; - show_pid_array(pids, n, prefix, n_columns, false, more, kernel_threads, flags); + show_pid_array(pids, n, prefix, n_columns, false, more, flags); return 0; } -int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, OutputFlags flags) { +int show_cgroup_by_path( + const char *path, + const char *prefix, + unsigned n_columns, + OutputFlags flags) { + _cleanup_free_ char *fn = NULL, *p1 = NULL, *last = NULL, *p2 = NULL; _cleanup_closedir_ DIR *d = NULL; char *gn = NULL; @@ -137,8 +145,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns if (n_columns <= 0) n_columns = columns(); - if (!prefix) - prefix = ""; + prefix = strempty(prefix); r = cg_mangle_path(path, &fn); if (r < 0) @@ -160,20 +167,20 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns continue; if (!shown_pids) { - show_cgroup_one_by_path(path, prefix, n_columns, true, kernel_threads, flags); + show_cgroup_one_by_path(path, prefix, n_columns, true, flags); shown_pids = true; } if (last) { - printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_BRANCH), cg_unescape(basename(last))); + printf("%s%s%s\n", prefix, special_glyph(TREE_BRANCH), cg_unescape(basename(last))); if (!p1) { - p1 = strappend(prefix, draw_special_char(DRAW_TREE_VERTICAL)); + p1 = strappend(prefix, special_glyph(TREE_VERTICAL)); if (!p1) return -ENOMEM; } - show_cgroup_by_path(last, p1, n_columns-2, kernel_threads, flags); + show_cgroup_by_path(last, p1, n_columns-2, flags); free(last); } @@ -185,10 +192,10 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns return r; if (!shown_pids) - show_cgroup_one_by_path(path, prefix, n_columns, !!last, kernel_threads, flags); + show_cgroup_one_by_path(path, prefix, n_columns, !!last, flags); if (last) { - printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_RIGHT), cg_unescape(basename(last))); + printf("%s%s%s\n", prefix, special_glyph(TREE_RIGHT), cg_unescape(basename(last))); if (!p2) { p2 = strappend(prefix, " "); @@ -196,13 +203,17 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns return -ENOMEM; } - show_cgroup_by_path(last, p2, n_columns-2, kernel_threads, flags); + show_cgroup_by_path(last, p2, n_columns-2, flags); } return 0; } -int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, OutputFlags flags) { +int show_cgroup(const char *controller, + const char *path, + const char *prefix, + unsigned n_columns, + OutputFlags flags) { _cleanup_free_ char *p = NULL; int r; @@ -212,10 +223,18 @@ int show_cgroup(const char *controller, const char *path, const char *prefix, un if (r < 0) return r; - return show_cgroup_by_path(p, prefix, n_columns, kernel_threads, flags); + return show_cgroup_by_path(p, prefix, n_columns, flags); } -static int show_extra_pids(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t pids[], unsigned n_pids, OutputFlags flags) { +static int show_extra_pids( + const char *controller, + const char *path, + const char *prefix, + unsigned n_columns, + const pid_t pids[], + unsigned n_pids, + OutputFlags flags) { + _cleanup_free_ pid_t *copy = NULL; unsigned i, j; int r; @@ -247,24 +266,39 @@ static int show_extra_pids(const char *controller, const char *path, const char copy[j++] = pids[i]; } - show_pid_array(copy, j, prefix, n_columns, true, false, false, flags); + show_pid_array(copy, j, prefix, n_columns, true, false, flags); return 0; } -int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags) { +int show_cgroup_and_extra( + const char *controller, + const char *path, + const char *prefix, + unsigned n_columns, + const pid_t extra_pids[], + unsigned n_extra_pids, + OutputFlags flags) { + int r; assert(path); - r = show_cgroup(controller, path, prefix, n_columns, kernel_threads, flags); + r = show_cgroup(controller, path, prefix, n_columns, flags); if (r < 0) return r; return show_extra_pids(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags); } -int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags) { +int show_cgroup_and_extra_by_spec( + const char *spec, + const char *prefix, + unsigned n_columns, + const pid_t extra_pids[], + unsigned n_extra_pids, + OutputFlags flags) { + _cleanup_free_ char *controller = NULL, *path = NULL; int r; @@ -274,5 +308,5 @@ int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned if (r < 0) return r; - return show_cgroup_and_extra(controller, path, prefix, n_columns, kernel_threads, extra_pids, n_extra_pids, flags); + return show_cgroup_and_extra(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags); } diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h index 3ab7dfb33c..5c1d6e6d98 100644 --- a/src/shared/cgroup-show.h +++ b/src/shared/cgroup-show.h @@ -25,8 +25,8 @@ #include "logs-show.h" #include "output-mode.h" -int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, bool kernel_threads, OutputFlags flags); -int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, bool kernel_threads, OutputFlags flags); +int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, OutputFlags flags); +int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, OutputFlags flags); -int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags); -int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags); +int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags); +int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags); diff --git a/src/shared/condition.c b/src/shared/condition.c index f93785865e..3a45ed265c 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -295,7 +295,7 @@ static int condition_test_needs_update(Condition *c) { return false; /* Any other failure means we should allow the condition to be true, - * so that we rather invoke too many update tools then too + * so that we rather invoke too many update tools than too * few. */ if (!path_is_absolute(c->parameter)) diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index e7fe9ac21e..83be79a4f5 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -37,6 +37,7 @@ #include "path-util.h" #include "process-util.h" #include "signal-util.h" +#include "socket-util.h" #include "string-util.h" #include "strv.h" #include "syslog-util.h" @@ -294,7 +295,7 @@ int config_parse(const char *unit, _cleanup_free_ char *section = NULL, *continuation = NULL; _cleanup_fclose_ FILE *ours = NULL; unsigned line = 0, section_line = 0; - bool section_ignored = false; + bool section_ignored = false, allow_bom = true; int r; assert(filename); @@ -314,11 +315,11 @@ int config_parse(const char *unit, fd_warn_permissions(filename, fileno(f)); - while (!feof(f)) { - char l[LINE_MAX], *p, *c = NULL, *e; + for (;;) { + char buf[LINE_MAX], *l, *p, *c = NULL, *e; bool escaped = false; - if (!fgets(l, sizeof(l), f)) { + if (!fgets(buf, sizeof buf, f)) { if (feof(f)) break; @@ -326,6 +327,11 @@ int config_parse(const char *unit, return -errno; } + l = buf; + if (allow_bom && startswith(l, UTF8_BYTE_ORDER_MARK)) + l += strlen(UTF8_BYTE_ORDER_MARK); + allow_bom = false; + truncate_nl(l); if (continuation) { @@ -727,7 +733,7 @@ int config_parse_strv(const char *unit, for (;;) { char *word = NULL; int r; - r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES); + r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES|EXTRACT_RETAIN_ESCAPE); if (r == 0) break; if (r == -ENOMEM) @@ -868,3 +874,40 @@ int config_parse_personality( *personality = p; return 0; } + +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) { + + char **s = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + *s = mfree(*s); + return 0; + } + + if (!ifname_valid(rvalue)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue); + return 0; + } + + r = free_and_strdup(s, rvalue); + if (r < 0) + return log_oom(); + + return 0; +} diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index a91c94c322..f6964e3fd4 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -125,6 +125,7 @@ int config_parse_log_facility(const char *unit, const char *filename, unsigned l int config_parse_log_level(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_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); #define DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg) \ int function(const char *unit, \ @@ -178,7 +179,7 @@ int config_parse_personality(const char *unit, const char *filename, unsigned li assert(data); \ \ xs = new0(type, 1); \ - if(!xs) \ + if (!xs) \ return -ENOMEM; \ \ *xs = invalid; \ diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 45d24c0079..835557c6b2 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -180,7 +180,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha unsigned slashes = 0; for (y = terminal - 1; y >= name && *y == '\\'; y--) - slashes ++; + slashes++; if (slashes % 2 == 0) { /* The '.' was not escaped */ @@ -192,7 +192,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha } } - terminal --; + terminal--; } r = dns_label_unescape(&name, dest, sz); @@ -331,7 +331,7 @@ int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded l = strlen(buffer); - /* Verify that the the result is not longer than one DNS label. */ + /* Verify that the result is not longer than one DNS label. */ if (l <= 0 || l > DNS_LABEL_MAX) return -EINVAL; if (l > decoded_max) @@ -1172,7 +1172,7 @@ int dns_name_skip(const char *a, unsigned n_labels, const char **ret) { assert(a); assert(ret); - for (; n_labels > 0; n_labels --) { + for (; n_labels > 0; n_labels--) { r = dns_name_parent(&a); if (r < 0) return r; diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index 2de3642cb3..af780f0b8b 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,9 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - - #include <errno.h> #include <stdbool.h> #include <stddef.h> diff --git a/src/shared/dropin.c b/src/shared/dropin.c index cc1acd6f23..b9cd952ac8 100644 --- a/src/shared/dropin.c +++ b/src/shared/dropin.c @@ -160,7 +160,7 @@ static int iterate_dir( if (!de) break; - if (hidden_file(de->d_name)) + if (hidden_or_backup_file(de->d_name)) continue; f = strjoin(path, "/", de->d_name, NULL); diff --git a/src/shared/firewall-util.c b/src/shared/firewall-util.c index 0d3da2e6d2..f73108eaa3 100644 --- a/src/shared/firewall-util.c +++ b/src/shared/firewall-util.c @@ -17,14 +17,22 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#warning "Temporary work-around for broken glibc vs. linux kernel header definitions" +#warning "This really should be removed sooner rather than later, when this is fixed upstream" +#define _NET_IF_H 1 + #include <alloca.h> #include <arpa/inet.h> #include <endian.h> #include <errno.h> -#include <net/if.h> #include <stddef.h> #include <string.h> #include <sys/socket.h> +#include <net/if.h> +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif +#include <linux/if.h> #include <linux/netfilter_ipv4/ip_tables.h> #include <linux/netfilter/nf_nat.h> #include <linux/netfilter/xt_addrtype.h> @@ -34,6 +42,7 @@ #include "firewall-util.h" #include "in-addr-util.h" #include "macro.h" +#include "socket-util.h" DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle*, iptc_free); @@ -49,10 +58,9 @@ static int entry_fill_basics( assert(entry); - if (out_interface && strlen(out_interface) >= IFNAMSIZ) + if (out_interface && !ifname_valid(out_interface)) return -EINVAL; - - if (in_interface && strlen(in_interface) >= IFNAMSIZ) + if (in_interface && !ifname_valid(in_interface)) return -EINVAL; entry->ip.proto = protocol; diff --git a/src/shared/gcrypt-util.c b/src/shared/gcrypt-util.c new file mode 100644 index 0000000000..39b544b6f0 --- /dev/null +++ b/src/shared/gcrypt-util.c @@ -0,0 +1,71 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#ifdef HAVE_GCRYPT +#include <gcrypt.h> + +#include "gcrypt-util.h" +#include "hexdecoct.h" + +void initialize_libgcrypt(bool secmem) { + const char *p; + if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) + return; + + p = gcry_check_version("1.4.5"); + assert(p); + + /* Turn off "secmem". Clients which wish to make use of this + * feature should initialize the library manually */ + if (!secmem) + gcry_control(GCRYCTL_DISABLE_SECMEM); + gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); +} + +int string_hashsum(const char *s, size_t len, int md_algorithm, char **out) { + gcry_md_hd_t md = NULL; + size_t hash_size; + void *hash; + char *enc; + + initialize_libgcrypt(false); + + hash_size = gcry_md_get_algo_dlen(md_algorithm); + assert(hash_size > 0); + + gcry_md_open(&md, md_algorithm, 0); + if (!md) + return -EIO; + + gcry_md_write(md, s, len); + + hash = gcry_md_read(md, 0); + if (!hash) + return -EIO; + + enc = hexmem(hash, hash_size); + if (!enc) + return -ENOMEM; + + *out = enc; + return 0; +} +#endif diff --git a/src/shared/gcrypt-util.h b/src/shared/gcrypt-util.h new file mode 100644 index 0000000000..cf33b3c59c --- /dev/null +++ b/src/shared/gcrypt-util.h @@ -0,0 +1,39 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + 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 <errno.h> +#include <stdbool.h> +#include <stddef.h> + +#ifdef HAVE_GCRYPT +#include <gcrypt.h> + +void initialize_libgcrypt(bool secmem); +int string_hashsum(const char *s, size_t len, int md_algorithm, char **out); +#endif + +static inline int string_hashsum_sha224(const char *s, size_t len, char **out) { +#ifdef HAVE_GCRYPT + return string_hashsum(s, len, GCRY_MD_SHA224, out); +#else + return -EOPNOTSUPP; +#endif +} diff --git a/src/shared/generator.c b/src/shared/generator.c index cd3c35cd55..70afc6a285 100644 --- a/src/shared/generator.c +++ b/src/shared/generator.c @@ -65,7 +65,7 @@ static int write_fsck_sysroot_service(const char *dir, const char *what) { "Description=File System Check on %2$s\n" "DefaultDependencies=no\n" "BindsTo=%3$s\n" - "After=%3$s local-fs-pre.target\n" + "After=initrd-root-device.target local-fs-pre.target\n" "Before=shutdown.target\n" "\n" "[Service]\n" @@ -191,3 +191,17 @@ int generator_write_timeouts( "[Unit]\nJobTimeoutSec=%s", program_invocation_short_name, timeout); } + +int generator_write_initrd_root_device_deps(const char *dir, const char *what) { + _cleanup_free_ char *unit = NULL; + int r; + + r = unit_name_from_path(what, ".device", &unit); + if (r < 0) + return log_error_errno(r, "Failed to make unit name from path: %m"); + + return write_drop_in_format(dir, SPECIAL_INITRD_ROOT_DEVICE_TARGET, 50, "root-device", + "# Automatically generated by %s\n\n" + "[Unit]\nRequires=%s\nAfter=%s", + program_invocation_short_name, unit, unit); +} diff --git a/src/shared/generator.h b/src/shared/generator.h index a734e13970..a6017c1b76 100644 --- a/src/shared/generator.h +++ b/src/shared/generator.h @@ -34,3 +34,7 @@ int generator_write_timeouts( const char *where, const char *opts, char **filtered); + +int generator_write_initrd_root_device_deps( + const char *dir, + const char *what); diff --git a/src/shared/gpt.h b/src/shared/gpt.h index 52ab29ed5f..55b41bbcd8 100644 --- a/src/shared/gpt.h +++ b/src/shared/gpt.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - #include <endian.h> #include "sd-id128.h" diff --git a/src/shared/install-printf.h b/src/shared/install-printf.h index acf519f4f7..8a570fc265 100644 --- a/src/shared/install-printf.h +++ b/src/shared/install-printf.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - #include "install.h" int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret); diff --git a/src/shared/install.c b/src/shared/install.c index ef8f485cae..64d66a45d3 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -40,11 +40,13 @@ #include "hashmap.h" #include "install-printf.h" #include "install.h" +#include "locale-util.h" #include "log.h" #include "macro.h" #include "mkdir.h" #include "path-lookup.h" #include "path-util.h" +#include "rm-rf.h" #include "set.h" #include "special.h" #include "stat-util.h" @@ -65,7 +67,65 @@ typedef struct { OrderedHashmap *have_processed; } InstallContext; -static int in_search_path(const char *path, char **search) { +typedef enum { + PRESET_UNKNOWN, + PRESET_ENABLE, + PRESET_DISABLE, +} PresetAction; + +typedef struct { + char *pattern; + PresetAction action; +} PresetRule; + +typedef struct { + PresetRule *rules; + size_t n_rules; +} Presets; + +static inline void presets_freep(Presets *p) { + size_t i; + + if (!p) + return; + + for (i = 0; i < p->n_rules; i++) + free(p->rules[i].pattern); + + free(p->rules); + p->n_rules = 0; +} + +static int unit_file_lookup_state(UnitFileScope scope, const LookupPaths *paths, const char *name, UnitFileState *ret); + +bool unit_type_may_alias(UnitType type) { + return IN_SET(type, + UNIT_SERVICE, + UNIT_SOCKET, + UNIT_TARGET, + UNIT_DEVICE, + UNIT_TIMER, + UNIT_PATH); +} + +bool unit_type_may_template(UnitType type) { + return IN_SET(type, + UNIT_SERVICE, + UNIT_SOCKET, + UNIT_TARGET, + UNIT_TIMER, + UNIT_PATH); +} + +static const char *unit_file_type_table[_UNIT_FILE_TYPE_MAX] = { + [UNIT_FILE_TYPE_REGULAR] = "regular", + [UNIT_FILE_TYPE_SYMLINK] = "symlink", + [UNIT_FILE_TYPE_MASKED] = "masked", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(unit_file_type, UnitFileType); + +static int in_search_path(const LookupPaths *p, const char *path) { _cleanup_free_ char *parent = NULL; char **i; @@ -75,141 +135,141 @@ static int in_search_path(const char *path, char **search) { if (!parent) return -ENOMEM; - STRV_FOREACH(i, search) + STRV_FOREACH(i, p->search_path) if (path_equal(parent, *i)) return true; return false; } -static int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) { - char *p = NULL; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(ret); +static const char* skip_root(const LookupPaths *p, const char *path) { + char *e; - /* This determines where we shall create or remove our - * installation ("configuration") symlinks */ + assert(p); + assert(path); - switch (scope) { + if (!p->root_dir) + return path; - case UNIT_FILE_SYSTEM: + e = path_startswith(path, p->root_dir); + if (!e) + return NULL; - if (runtime) - p = path_join(root_dir, "/run/systemd/system", NULL); - else - p = path_join(root_dir, SYSTEM_CONFIG_UNIT_PATH, NULL); - break; + /* Make sure the returned path starts with a slash */ + if (e[0] != '/') { + if (e == path || e[-1] != '/') + return NULL; - case UNIT_FILE_GLOBAL: + e--; + } - if (root_dir) - return -EINVAL; + return e; +} - if (runtime) - p = strdup("/run/systemd/user"); - else - p = strdup(USER_CONFIG_UNIT_PATH); - break; +static int path_is_generator(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; - case UNIT_FILE_USER: + assert(p); + assert(path); - if (root_dir) - return -EINVAL; + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; - if (runtime) - r = user_runtime_dir(&p); - else - r = user_config_home(&p); - if (r < 0) - return r; - if (r == 0) - return -ENOENT; + return path_equal_ptr(parent, p->generator) || + path_equal_ptr(parent, p->generator_early) || + path_equal_ptr(parent, p->generator_late); +} - break; +static int path_is_transient(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; - default: - assert_not_reached("Bad scope"); - } + assert(p); + assert(path); - if (!p) + parent = dirname_malloc(path); + if (!parent) return -ENOMEM; - *ret = p; - return 0; + return path_equal_ptr(parent, p->transient); } -static bool is_config_path(UnitFileScope scope, const char *path) { - int r; +static int path_is_control(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(p); assert(path); - /* Checks whether the specified path is intended for - * configuration or is outside of it */ + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; - switch (scope) { + return path_equal_ptr(parent, p->persistent_control) || + path_equal_ptr(parent, p->runtime_control); +} - case UNIT_FILE_SYSTEM: - case UNIT_FILE_GLOBAL: - return path_startswith(path, "/etc") || - path_startswith(path, SYSTEM_CONFIG_UNIT_PATH) || - path_startswith(path, "/run"); +static int path_is_config(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; + assert(p); + assert(path); - case UNIT_FILE_USER: { - _cleanup_free_ char *p = NULL; + /* Note that we do *not* have generic checks for /etc or /run in place, since with them we couldn't discern + * configuration from transient or generated units */ - r = user_config_home(&p); - if (r < 0) - return r; - if (r > 0 && path_startswith(path, p)) - return true; + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; - p = mfree(p); + return path_equal_ptr(parent, p->persistent_config) || + path_equal_ptr(parent, p->runtime_config); +} - r = user_runtime_dir(&p); - if (r < 0) - return r; - if (r > 0 && path_startswith(path, p)) - return true; +static int path_is_runtime(const LookupPaths *p, const char *path) { + _cleanup_free_ char *parent = NULL; + const char *rpath; - return false; - } + assert(p); + assert(path); - default: - assert_not_reached("Bad scope"); - } -} + /* Everything in /run is considered runtime. On top of that we also add explicit checks for the various runtime + * directories, as safety net. */ + rpath = skip_root(p, path); + if (rpath && path_startswith(rpath, "/run")) + return true; -static int verify_root_dir(UnitFileScope scope, const char **root_dir) { - int r; + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; + + return path_equal_ptr(parent, p->runtime_config) || + path_equal_ptr(parent, p->generator) || + path_equal_ptr(parent, p->generator_early) || + path_equal_ptr(parent, p->generator_late) || + path_equal_ptr(parent, p->transient) || + path_equal_ptr(parent, p->runtime_control); +} - assert(root_dir); +static int path_is_vendor(const LookupPaths *p, const char *path) { + const char *rpath; - /* Verifies that the specified root directory to operate on - * makes sense. Reset it to NULL if it is the root directory - * or set to empty */ + assert(p); + assert(path); - if (isempty(*root_dir) || path_equal(*root_dir, "/")) { - *root_dir = NULL; + rpath = skip_root(p, path); + if (!rpath) return 0; - } - if (scope != UNIT_FILE_SYSTEM) - return -EINVAL; + if (path_startswith(rpath, "/usr")) + return true; - r = is_dir(*root_dir, true); - if (r < 0) - return r; - if (r == 0) - return -ENOTDIR; +#ifdef HAVE_SPLIT_USR + if (path_startswith(rpath, "/lib")) + return true; +#endif - return 0; + return path_equal(rpath, SYSTEM_DATA_UNIT_PATH); } int unit_file_changes_add( @@ -219,8 +279,8 @@ int unit_file_changes_add( const char *path, const char *source) { + _cleanup_free_ char *p = NULL, *s = NULL; UnitFileChange *c; - unsigned i; assert(path); assert(!changes == !n_changes); @@ -231,29 +291,22 @@ int unit_file_changes_add( c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange)); if (!c) return -ENOMEM; - *changes = c; - i = *n_changes; - - c[i].type = type; - c[i].path = strdup(path); - if (!c[i].path) - return -ENOMEM; - path_kill_slashes(c[i].path); + p = strdup(path); + if (source) + s = strdup(source); - if (source) { - c[i].source = strdup(source); - if (!c[i].source) { - free(c[i].path); - return -ENOMEM; - } + if (!p || (source && !s)) + return -ENOMEM; - path_kill_slashes(c[i].path); - } else - c[i].source = NULL; + path_kill_slashes(p); + if (s) + path_kill_slashes(s); - *n_changes = i+1; + c[*n_changes] = (UnitFileChange) { type, p, s }; + p = s = NULL; + (*n_changes) ++; return 0; } @@ -262,9 +315,6 @@ void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) { assert(changes || n_changes == 0); - if (!changes) - return; - for (i = 0; i < n_changes; i++) { free(changes[i].path); free(changes[i].source); @@ -273,6 +323,76 @@ void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) { free(changes); } +void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, unsigned n_changes, bool quiet) { + unsigned i; + bool logged = false; + + assert(changes || n_changes == 0); + /* If verb is not specified, errors are not allowed! */ + assert(verb || r >= 0); + + for (i = 0; i < n_changes; i++) { + assert(verb || changes[i].type >= 0); + + switch(changes[i].type) { + case UNIT_FILE_SYMLINK: + if (!quiet) + log_info("Created symlink %s %s %s.", + changes[i].path, + special_glyph(ARROW), + changes[i].source); + break; + case UNIT_FILE_UNLINK: + if (!quiet) + log_info("Removed %s.", changes[i].path); + break; + case UNIT_FILE_IS_MASKED: + if (!quiet) + log_info("Unit %s is masked, ignoring.", changes[i].path); + break; + case UNIT_FILE_IS_DANGLING: + if (!quiet) + log_info("Unit %s is an alias to a unit that is not present, ignoring.", + changes[i].path); + break; + case -EEXIST: + if (changes[i].source) + log_error_errno(changes[i].type, + "Failed to %s unit, file %s already exists and is a symlink to %s.", + verb, changes[i].path, changes[i].source); + else + log_error_errno(changes[i].type, + "Failed to %s unit, file %s already exists.", + verb, changes[i].path); + logged = true; + break; + case -ERFKILL: + log_error_errno(changes[i].type, "Failed to %s unit, unit %s is masked.", + verb, changes[i].path); + logged = true; + break; + case -EADDRNOTAVAIL: + log_error_errno(changes[i].type, "Failed to %s unit, unit %s is transient or generated.", + verb, changes[i].path); + logged = true; + break; + case -ELOOP: + log_error_errno(changes[i].type, "Failed to %s unit, refusing to operate on linked unit file %s", + verb, changes[i].path); + logged = true; + break; + default: + assert(changes[i].type < 0); + log_error_errno(changes[i].type, "Failed to %s unit, file %s: %m.", + verb, changes[i].path); + logged = true; + } + } + + if (r < 0 && !logged) + log_error_errno(r, "Failed to %s: %m.", verb); +} + static int create_symlink( const char *old_path, const char *new_path, @@ -288,36 +408,52 @@ static int create_symlink( /* Actually create a symlink, and remember that we did. Is * smart enough to check if there's already a valid symlink in - * place. */ + * place. + * + * Returns 1 if a symlink was created or already exists and points to + * the right place, or negative on error. + */ mkdir_parents_label(new_path, 0755); if (symlink(old_path, new_path) >= 0) { unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); - return 0; + return 1; } - if (errno != EEXIST) + if (errno != EEXIST) { + unit_file_changes_add(changes, n_changes, -errno, new_path, NULL); return -errno; + } r = readlink_malloc(new_path, &dest); - if (r < 0) + if (r < 0) { + /* translate EINVAL (non-symlink exists) to EEXIST */ + if (r == -EINVAL) + r = -EEXIST; + + unit_file_changes_add(changes, n_changes, r, new_path, NULL); return r; + } if (path_equal(dest, old_path)) - return 0; + return 1; - if (!force) + if (!force) { + unit_file_changes_add(changes, n_changes, -EEXIST, new_path, dest); return -EEXIST; + } r = symlink_atomic(old_path, new_path); - if (r < 0) + if (r < 0) { + unit_file_changes_add(changes, n_changes, r, new_path, NULL); return r; + } unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL); unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); - return 0; + return 1; } static int mark_symlink_for_removal( @@ -353,6 +489,7 @@ static int remove_marked_symlinks_fd( int fd, const char *path, const char *config_path, + const LookupPaths *lp, bool *restart, UnitFileChange **changes, unsigned *n_changes) { @@ -365,6 +502,7 @@ static int remove_marked_symlinks_fd( assert(fd >= 0); assert(path); assert(config_path); + assert(lp); assert(restart); d = fdopendir(fd); @@ -400,12 +538,13 @@ static int remove_marked_symlinks_fd( } /* This will close nfd, regardless whether it succeeds or not */ - q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, restart, changes, n_changes); + q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, lp, restart, changes, n_changes); if (q < 0 && r == 0) r = q; } else if (de->d_type == DT_LNK) { _cleanup_free_ char *p = NULL, *dest = NULL; + const char *rp; bool found; int q; @@ -415,42 +554,43 @@ static int remove_marked_symlinks_fd( p = path_make_absolute(de->d_name, path); if (!p) return -ENOMEM; + path_kill_slashes(p); q = readlink_malloc(p, &dest); + if (q == -ENOENT) + continue; if (q < 0) { - if (q == -ENOENT) - continue; - if (r == 0) r = q; continue; } - /* We remove all links pointing to a file or - * path that is marked, as well as all files - * sharing the same name as a file that is - * marked. */ + /* We remove all links pointing to a file or path that is marked, as well as all files sharing + * the same name as a file that is marked. */ - found = - set_contains(remove_symlinks_to, dest) || + found = set_contains(remove_symlinks_to, dest) || set_contains(remove_symlinks_to, basename(dest)) || set_contains(remove_symlinks_to, de->d_name); if (!found) continue; - if (unlink(p) < 0 && errno != ENOENT) { + if (unlinkat(fd, de->d_name, 0) < 0 && errno != ENOENT) { if (r == 0) r = -errno; + unit_file_changes_add(changes, n_changes, -errno, p, NULL); continue; } - path_kill_slashes(p); (void) rmdir_parents(p, config_path); unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL); - q = mark_symlink_for_removal(&remove_symlinks_to, p); + /* Now, remember the full path (but with the root prefix removed) of + * the symlink we just removed, and remove any symlinks to it, too. */ + + rp = skip_root(lp, p); + q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: p); if (q < 0) return q; if (q > 0) @@ -464,6 +604,7 @@ static int remove_marked_symlinks_fd( static int remove_marked_symlinks( Set *remove_symlinks_to, const char *config_path, + const LookupPaths *lp, UnitFileChange **changes, unsigned *n_changes) { @@ -472,6 +613,7 @@ static int remove_marked_symlinks( int r = 0; assert(config_path); + assert(lp); if (set_size(remove_symlinks_to) <= 0) return 0; @@ -489,7 +631,7 @@ static int remove_marked_symlinks( return -errno; /* This takes possession of cfd and closes it */ - q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &restart, changes, n_changes); + q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, lp, &restart, changes, n_changes); if (r == 0) r = q; } while (restart); @@ -503,6 +645,7 @@ static int find_symlinks_fd( int fd, const char *path, const char *config_path, + const LookupPaths *lp, bool *same_name_link) { _cleanup_closedir_ DIR *d = NULL; @@ -513,6 +656,7 @@ static int find_symlinks_fd( assert(fd >= 0); assert(path); assert(config_path); + assert(lp); assert(same_name_link); d = fdopendir(fd); @@ -546,7 +690,7 @@ static int find_symlinks_fd( } /* This will close nfd, regardless whether it succeeds or not */ - q = find_symlinks_fd(root_dir, name, nfd, p, config_path, same_name_link); + q = find_symlinks_fd(root_dir, name, nfd, p, config_path, lp, same_name_link); if (q > 0) return 1; if (r == 0) @@ -624,6 +768,7 @@ static int find_symlinks( const char *root_dir, const char *name, const char *config_path, + const LookupPaths *lp, bool *same_name_link) { int fd; @@ -640,29 +785,25 @@ static int find_symlinks( } /* This takes possession of fd and closes it */ - return find_symlinks_fd(root_dir, name, fd, config_path, config_path, same_name_link); + return find_symlinks_fd(root_dir, name, fd, config_path, config_path, lp, same_name_link); } static int find_symlinks_in_scope( UnitFileScope scope, - const char *root_dir, + const LookupPaths *paths, const char *name, UnitFileState *state) { - _cleanup_free_ char *normal_path = NULL, *runtime_path = NULL; bool same_name_link_runtime = false, same_name_link = false; int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(paths); assert(name); - /* First look in the normal config path */ - r = get_config_path(scope, false, root_dir, &normal_path); - if (r < 0) - return r; - - r = find_symlinks(root_dir, name, normal_path, &same_name_link); + /* First look in the persistent config path */ + r = find_symlinks(paths->root_dir, name, paths->persistent_config, paths, &same_name_link); if (r < 0) return r; if (r > 0) { @@ -671,11 +812,7 @@ static int find_symlinks_in_scope( } /* Then look in runtime config path */ - r = get_config_path(scope, true, root_dir, &runtime_path); - if (r < 0) - return r; - - r = find_symlinks(root_dir, name, runtime_path, &same_name_link_runtime); + r = find_symlinks(paths->root_dir, name, paths->runtime_config, paths, &same_name_link_runtime); if (r < 0) return r; if (r > 0) { @@ -742,6 +879,30 @@ static UnitFileInstallInfo *install_info_find(InstallContext *c, const char *nam return ordered_hashmap_get(c->will_process, name); } +static int install_info_may_process( + UnitFileInstallInfo *i, + const LookupPaths *paths, + UnitFileChange **changes, + unsigned *n_changes) { + assert(i); + assert(paths); + + /* Checks whether the loaded unit file is one we should process, or is masked, transient or generated and thus + * not subject to enable/disable operations. */ + + if (i->type == UNIT_FILE_TYPE_MASKED) { + unit_file_changes_add(changes, n_changes, -ERFKILL, i->path, NULL); + return -ERFKILL; + } + if (path_is_generator(paths, i->path) || + path_is_transient(paths, i->path)) { + unit_file_changes_add(changes, n_changes, -EADDRNOTAVAIL, i->path, NULL); + return -EADDRNOTAVAIL; + } + + return 0; +} + static int install_info_add( InstallContext *c, const char *name, @@ -804,18 +965,34 @@ fail: return r; } -static int install_info_add_auto( - InstallContext *c, - const char *name_or_path, - UnitFileInstallInfo **ret) { +static int config_parse_alias( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { - assert(c); - assert(name_or_path); + const char *name; + UnitType type; - if (path_is_absolute(name_or_path)) - return install_info_add(c, NULL, name_or_path, ret); - else - return install_info_add(c, name_or_path, NULL, ret); + assert(filename); + assert(lvalue); + assert(rvalue); + + name = basename(filename); + type = unit_name_to_type(name); + if (!unit_type_may_alias(type)) + return log_syntax(unit, LOG_WARNING, filename, line, 0, + "Aliases are not allowed for %s units, ignoring.", + unit_type_to_string(type)); + + return config_parse_strv(unit, filename, line, section, section_line, + lvalue, ltype, rvalue, data, userdata); } static int config_parse_also( @@ -874,6 +1051,7 @@ static int config_parse_default_instance( void *userdata) { UnitFileInstallInfo *i = data; + const char *name; char *printed; int r; @@ -881,6 +1059,15 @@ static int config_parse_default_instance( assert(lvalue); assert(rvalue); + name = basename(filename); + if (unit_name_is_valid(name, UNIT_NAME_INSTANCE)) + /* When enabling an instance, we might be using a template unit file, + * but we should ignore DefaultInstance silently. */ + return 0; + if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) + return log_syntax(unit, LOG_WARNING, filename, line, 0, + "DefaultInstance only makes sense for template units, ignoring."); + r = install_full_printf(i, rvalue, &printed); if (r < 0) return r; @@ -900,11 +1087,10 @@ static int unit_file_load( InstallContext *c, UnitFileInstallInfo *info, const char *path, - const char *root_dir, SearchFlags flags) { const ConfigTableItem items[] = { - { "Install", "Alias", config_parse_strv, 0, &info->aliases }, + { "Install", "Alias", config_parse_alias, 0, &info->aliases }, { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by }, { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by }, { "Install", "DefaultInstance", config_parse_default_instance, 0, info }, @@ -912,6 +1098,8 @@ static int unit_file_load( {} }; + const char *name; + UnitType type; _cleanup_fclose_ FILE *f = NULL; _cleanup_close_ int fd = -1; struct stat st; @@ -921,7 +1109,11 @@ static int unit_file_load( assert(info); assert(path); - path = prefix_roota(root_dir, path); + name = basename(path); + type = unit_name_to_type(name); + if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && + !unit_type_may_template(type)) + return log_error_errno(EINVAL, "Unit type %s cannot be templated.", unit_type_to_string(type)); if (!(flags & SEARCH_LOAD)) { r = lstat(path, &st); @@ -983,26 +1175,26 @@ static int unit_file_load_or_readlink( const char *root_dir, SearchFlags flags) { - _cleanup_free_ char *np = NULL; + _cleanup_free_ char *target = NULL; int r; - r = unit_file_load(c, info, path, root_dir, flags); + r = unit_file_load(c, info, path, flags); if (r != -ELOOP) return r; /* This is a symlink, let's read it. */ - r = readlink_and_make_absolute_root(root_dir, path, &np); + r = readlink_malloc(path, &target); if (r < 0) return r; - if (path_equal(np, "/dev/null")) + if (path_equal(target, "/dev/null")) info->type = UNIT_FILE_TYPE_MASKED; else { const char *bn; UnitType a, b; - bn = basename(np); + bn = basename(target); if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN)) { @@ -1029,9 +1221,16 @@ static int unit_file_load_or_readlink( if (a < 0 || b < 0 || a != b) return -EINVAL; + if (path_is_absolute(target)) + /* This is an absolute path, prefix the root so that we always deal with fully qualified paths */ + info->symlink_target = prefix_root(root_dir, target); + else + /* This is a relative path, take it relative to the dir the symlink is located in. */ + info->symlink_target = file_in_same_dir(path, target); + if (!info->symlink_target) + return -ENOMEM; + info->type = UNIT_FILE_TYPE_SYMLINK; - info->symlink_target = np; - np = NULL; } return 0; @@ -1041,9 +1240,9 @@ static int unit_file_search( InstallContext *c, UnitFileInstallInfo *info, const LookupPaths *paths, - const char *root_dir, SearchFlags flags) { + _cleanup_free_ char *template = NULL; char **p; int r; @@ -1056,59 +1255,53 @@ static int unit_file_search( return 0; if (info->path) - return unit_file_load_or_readlink(c, info, info->path, root_dir, flags); + return unit_file_load_or_readlink(c, info, info->path, paths->root_dir, flags); assert(info->name); - STRV_FOREACH(p, paths->unit_path) { + STRV_FOREACH(p, paths->search_path) { _cleanup_free_ char *path = NULL; path = strjoin(*p, "/", info->name, NULL); if (!path) return -ENOMEM; - r = unit_file_load_or_readlink(c, info, path, root_dir, flags); - if (r < 0) { - if (r != -ENOENT) - return r; - } else { + r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags); + if (r >= 0) { info->path = path; path = NULL; return r; - } + } else if (r != -ENOENT) + return r; } if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { - /* Unit file doesn't exist, however instance * enablement was requested. We will check if it is * possible to load template unit file. */ - _cleanup_free_ char *template = NULL; - r = unit_name_template(info->name, &template); if (r < 0) return r; - STRV_FOREACH(p, paths->unit_path) { + STRV_FOREACH(p, paths->search_path) { _cleanup_free_ char *path = NULL; path = strjoin(*p, "/", template, NULL); if (!path) return -ENOMEM; - r = unit_file_load_or_readlink(c, info, path, root_dir, flags); - if (r < 0) { - if (r != -ENOENT) - return r; - } else { + r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags); + if (r >= 0) { info->path = path; path = NULL; return r; - } + } else if (r != -ENOENT) + return r; } } + log_debug("Cannot find unit %s%s%s.", info->name, template ? " or " : "", strempty(template)); return -ENOENT; } @@ -1140,10 +1333,14 @@ static int install_info_follow( return unit_file_load_or_readlink(c, i, i->path, root_dir, flags); } +/** + * Search for the unit file. If the unit name is a symlink, + * follow the symlink to the target, maybe more than once. + * Propagate the instance name if present. + */ static int install_info_traverse( UnitFileScope scope, InstallContext *c, - const char *root_dir, const LookupPaths *paths, UnitFileInstallInfo *start, SearchFlags flags, @@ -1157,7 +1354,7 @@ static int install_info_traverse( assert(start); assert(c); - r = unit_file_search(c, start, paths, root_dir, flags); + r = unit_file_search(c, start, paths, flags); if (r < 0) return r; @@ -1168,17 +1365,19 @@ static int install_info_traverse( if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX) return -ELOOP; - if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS) && is_config_path(scope, i->path)) - return -ELOOP; + if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS)) { + r = path_is_config(paths, i->path); + if (r < 0) + return r; + if (r > 0) + return -ELOOP; + } - r = install_info_follow(c, i, root_dir, flags); - if (r < 0) { + r = install_info_follow(c, i, paths->root_dir, flags); + if (r == -EXDEV) { _cleanup_free_ char *buffer = NULL; const char *bn; - if (r != -EXDEV) - return r; - /* Target has a different name, create a new * install info object for that, and continue * with that. */ @@ -1205,12 +1404,15 @@ static int install_info_traverse( if (r < 0) return r; - r = unit_file_search(c, i, paths, root_dir, flags); - if (r < 0) - return r; + /* Try again, with the new target we found. */ + r = unit_file_search(c, i, paths, flags); + if (r == -ENOENT) + /* Translate error code to highlight this specific case */ + return -ENOLINK; } - /* Try again, with the new target we found. */ + if (r < 0) + return r; } if (ret) @@ -1219,10 +1421,28 @@ static int install_info_traverse( return 0; } +static int install_info_add_auto( + InstallContext *c, + const LookupPaths *paths, + const char *name_or_path, + UnitFileInstallInfo **ret) { + + assert(c); + assert(name_or_path); + + if (path_is_absolute(name_or_path)) { + const char *pp; + + pp = prefix_roota(paths->root_dir, name_or_path); + + return install_info_add(c, NULL, pp, ret); + } else + return install_info_add(c, name_or_path, NULL, ret); +} + static int install_info_discover( UnitFileScope scope, InstallContext *c, - const char *root_dir, const LookupPaths *paths, const char *name, SearchFlags flags, @@ -1235,15 +1455,16 @@ static int install_info_discover( assert(paths); assert(name); - r = install_info_add_auto(c, name, &i); + r = install_info_add_auto(c, paths, name, &i); if (r < 0) return r; - return install_info_traverse(scope, c, root_dir, paths, i, flags, ret); + return install_info_traverse(scope, c, paths, i, flags, ret); } static int install_info_symlink_alias( UnitFileInstallInfo *i, + const LookupPaths *paths, const char *config_path, bool force, UnitFileChange **changes, @@ -1253,10 +1474,12 @@ static int install_info_symlink_alias( int r = 0, q; assert(i); + assert(paths); assert(config_path); STRV_FOREACH(s, i->aliases) { _cleanup_free_ char *alias_path = NULL, *dst = NULL; + const char *rp; q = install_full_printf(i, *s, &dst); if (q < 0) @@ -1266,7 +1489,9 @@ static int install_info_symlink_alias( if (!alias_path) return -ENOMEM; - q = create_symlink(i->path, alias_path, force, changes, n_changes); + rp = skip_root(paths, i->path); + + q = create_symlink(rp ?: i->path, alias_path, force, changes, n_changes); if (r == 0) r = q; } @@ -1276,10 +1501,10 @@ static int install_info_symlink_alias( static int install_info_symlink_wants( UnitFileInstallInfo *i, + const LookupPaths *paths, const char *config_path, char **list, const char *suffix, - bool force, UnitFileChange **changes, unsigned *n_changes) { @@ -1289,6 +1514,7 @@ static int install_info_symlink_wants( int r = 0, q; assert(i); + assert(paths); assert(config_path); if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) { @@ -1309,6 +1535,7 @@ static int install_info_symlink_wants( STRV_FOREACH(s, list) { _cleanup_free_ char *path = NULL, *dst = NULL; + const char *rp; q = install_full_printf(i, *s, &dst); if (q < 0) @@ -1323,7 +1550,9 @@ static int install_info_symlink_wants( if (!path) return -ENOMEM; - q = create_symlink(i->path, path, force, changes, n_changes); + rp = skip_root(paths, i->path); + + q = create_symlink(rp ?: i->path, path, true, changes, n_changes); if (r == 0) r = q; } @@ -1335,12 +1564,12 @@ static int install_info_symlink_link( UnitFileInstallInfo *i, const LookupPaths *paths, const char *config_path, - const char *root_dir, bool force, UnitFileChange **changes, unsigned *n_changes) { _cleanup_free_ char *path = NULL; + const char *rp; int r; assert(i); @@ -1348,22 +1577,25 @@ static int install_info_symlink_link( assert(config_path); assert(i->path); - r = in_search_path(i->path, paths->unit_path); - if (r != 0) + r = in_search_path(paths, i->path); + if (r < 0) return r; + if (r > 0) + return 0; path = strjoin(config_path, "/", i->name, NULL); if (!path) return -ENOMEM; - return create_symlink(i->path, path, force, changes, n_changes); + rp = skip_root(paths, i->path); + + return create_symlink(rp ?: i->path, path, force, changes, n_changes); } static int install_info_apply( UnitFileInstallInfo *i, const LookupPaths *paths, const char *config_path, - const char *root_dir, bool force, UnitFileChange **changes, unsigned *n_changes) { @@ -1377,18 +1609,19 @@ static int install_info_apply( if (i->type != UNIT_FILE_TYPE_REGULAR) return 0; - r = install_info_symlink_alias(i, config_path, force, changes, n_changes); + r = install_info_symlink_alias(i, paths, config_path, force, changes, n_changes); - q = install_info_symlink_wants(i, config_path, i->wanted_by, ".wants/", force, changes, n_changes); + q = install_info_symlink_wants(i, paths, config_path, i->wanted_by, ".wants/", changes, n_changes); if (r == 0) r = q; - q = install_info_symlink_wants(i, config_path, i->required_by, ".requires/", force, changes, n_changes); + q = install_info_symlink_wants(i, paths, config_path, i->required_by, ".requires/", changes, n_changes); if (r == 0) r = q; - q = install_info_symlink_link(i, paths, config_path, root_dir, force, changes, n_changes); - if (r == 0) + q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes); + /* Do not count links to the unit file towards the "carries_install_info" count */ + if (r == 0 && q < 0) r = q; return r; @@ -1399,7 +1632,6 @@ static int install_context_apply( InstallContext *c, const LookupPaths *paths, const char *config_path, - const char *root_dir, bool force, SearchFlags flags, UnitFileChange **changes, @@ -1427,19 +1659,19 @@ static int install_context_apply( if (q < 0) return q; - r = install_info_traverse(scope, c, root_dir, paths, i, flags, NULL); + r = install_info_traverse(scope, c, paths, i, flags, NULL); if (r < 0) return r; if (i->type != UNIT_FILE_TYPE_REGULAR) continue; - q = install_info_apply(i, paths, config_path, root_dir, force, changes, n_changes); + q = install_info_apply(i, paths, config_path, force, changes, n_changes); if (r >= 0) { if (q < 0) r = q; else - r+= q; + r += q; } } @@ -1451,8 +1683,7 @@ static int install_context_mark_for_removal( InstallContext *c, const LookupPaths *paths, Set **remove_symlinks_to, - const char *config_path, - const char *root_dir) { + const char *config_path) { UnitFileInstallInfo *i; int r; @@ -1476,12 +1707,18 @@ static int install_context_mark_for_removal( if (r < 0) return r; - r = install_info_traverse(scope, c, root_dir, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL); - if (r < 0) + r = install_info_traverse(scope, c, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL); + if (r == -ENOLINK) + return 0; + else if (r < 0) return r; - if (i->type != UNIT_FILE_TYPE_REGULAR) + if (i->type != UNIT_FILE_TYPE_REGULAR) { + log_debug("Unit %s has type %s, ignoring.", + i->name, + unit_file_type_to_string(i->type) ?: "invalid"); continue; + } r = mark_symlink_for_removal(remove_symlinks_to, i->name); if (r < 0) @@ -1500,20 +1737,19 @@ int unit_file_mask( UnitFileChange **changes, unsigned *n_changes) { - _cleanup_free_ char *prefix = NULL; + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + const char *config_path; char **i; int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = get_config_path(scope, runtime, root_dir, &prefix); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; STRV_FOREACH(i, files) { _cleanup_free_ char *path = NULL; @@ -1525,7 +1761,7 @@ int unit_file_mask( continue; } - path = path_make_absolute(*i, prefix); + path = path_make_absolute(*i, config_path); if (!path) return -ENOMEM; @@ -1545,23 +1781,22 @@ int unit_file_unmask( UnitFileChange **changes, unsigned *n_changes) { + _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - _cleanup_free_ char *config_path = NULL; _cleanup_free_ char **todo = NULL; size_t n_todo = 0, n_allocated = 0; + const char *config_path; char **i; int r, q; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; STRV_FOREACH(i, files) { _cleanup_free_ char *path = NULL; @@ -1592,24 +1827,31 @@ int unit_file_unmask( r = 0; STRV_FOREACH(i, todo) { _cleanup_free_ char *path = NULL; + const char *rp; path = path_make_absolute(*i, config_path); if (!path) return -ENOMEM; if (unlink(path) < 0) { - if (errno != -ENOENT && r >= 0) - r = -errno; - } else { - q = mark_symlink_for_removal(&remove_symlinks_to, path); - if (q < 0) - return q; + if (errno != ENOENT) { + if (r >= 0) + r = -errno; + unit_file_changes_add(changes, n_changes, -errno, path, NULL); + } - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); + continue; } + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); + + rp = skip_root(&paths, path); + q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path); + if (q < 0) + return q; } - q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + q = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, changes, n_changes); if (r >= 0) r = q; @@ -1626,26 +1868,20 @@ int unit_file_link( unsigned *n_changes) { _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_free_ char *config_path = NULL; _cleanup_free_ char **todo = NULL; size_t n_todo = 0, n_allocated = 0; + const char *config_path; char **i; int r, q; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - r = verify_root_dir(scope, &root_dir); - if (r < 0) - return r; - - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; STRV_FOREACH(i, files) { _cleanup_free_ char *full = NULL; @@ -1659,7 +1895,7 @@ int unit_file_link( if (!unit_name_is_valid(fn, UNIT_NAME_ANY)) return -EINVAL; - full = prefix_root(root_dir, *i); + full = prefix_root(paths.root_dir, *i); if (!full) return -ENOMEM; @@ -1672,7 +1908,7 @@ int unit_file_link( if (!S_ISREG(st.st_mode)) return -ENOTTY; - q = in_search_path(*i, paths.unit_path); + q = in_search_path(&paths, *i); if (q < 0) return q; if (q > 0) @@ -1688,13 +1924,15 @@ int unit_file_link( r = 0; STRV_FOREACH(i, todo) { - _cleanup_free_ char *path = NULL; + _cleanup_free_ char *new_path = NULL; + const char *old_path; - path = path_make_absolute(basename(*i), config_path); - if (!path) + old_path = skip_root(&paths, *i); + new_path = path_make_absolute(basename(*i), config_path); + if (!new_path) return -ENOMEM; - q = create_symlink(*i, path, force, changes, n_changes); + q = create_symlink(old_path ?: *i, new_path, force, changes, n_changes); if (q < 0 && r >= 0) r = q; } @@ -1702,6 +1940,182 @@ int unit_file_link( return r; } +static int path_shall_revert(const LookupPaths *paths, const char *path) { + int r; + + assert(paths); + assert(path); + + /* Checks whether the path is one where the drop-in directories shall be removed. */ + + r = path_is_config(paths, path); + if (r != 0) + return r; + + r = path_is_control(paths, path); + if (r != 0) + return r; + + return path_is_transient(paths, path); +} + +int unit_file_revert( + UnitFileScope scope, + const char *root_dir, + char **files, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; + /* _cleanup_(install_context_done) InstallContext c = {}; */ + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + _cleanup_strv_free_ char **todo = NULL; + size_t n_todo = 0, n_allocated = 0; + char **i; + int r, q; + + /* Puts a unit file back into vendor state. This means: + * + * a) we remove all drop-in snippets added by the user ("config"), add to transient units ("transient"), and + * added via "systemctl set-property" ("control"), but not if the drop-in is generated ("generated"). + * + * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files (i.e. in + * "config", but not in "transient" or "control" or even "generated"). + * + * We remove all that in both the runtime and the persistent directories, if that applies. + */ + + r = lookup_paths_init(&paths, scope, 0, root_dir); + if (r < 0) + return r; + + STRV_FOREACH(i, files) { + bool has_vendor = false; + char **p; + + if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) + return -EINVAL; + + STRV_FOREACH(p, paths.search_path) { + _cleanup_free_ char *path = NULL, *dropin = NULL; + struct stat st; + + path = path_make_absolute(*i, *p); + if (!path) + return -ENOMEM; + + r = lstat(path, &st); + if (r < 0) { + if (errno != ENOENT) + return -errno; + } else if (S_ISREG(st.st_mode)) { + /* Check if there's a vendor version */ + r = path_is_vendor(&paths, path); + if (r < 0) + return r; + if (r > 0) + has_vendor = true; + } + + dropin = strappend(path, ".d"); + if (!dropin) + return -ENOMEM; + + r = lstat(dropin, &st); + if (r < 0) { + if (errno != ENOENT) + return -errno; + } else if (S_ISDIR(st.st_mode)) { + /* Remove the drop-ins */ + r = path_shall_revert(&paths, dropin); + if (r < 0) + return r; + if (r > 0) { + if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) + return -ENOMEM; + + todo[n_todo++] = dropin; + dropin = NULL; + } + } + } + + if (!has_vendor) + continue; + + /* OK, there's a vendor version, hence drop all configuration versions */ + STRV_FOREACH(p, paths.search_path) { + _cleanup_free_ char *path = NULL; + struct stat st; + + path = path_make_absolute(*i, *p); + if (!path) + return -ENOMEM; + + r = lstat(path, &st); + if (r < 0) { + if (errno != ENOENT) + return -errno; + } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { + r = path_is_config(&paths, path); + if (r < 0) + return r; + if (r > 0) { + if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) + return -ENOMEM; + + todo[n_todo++] = path; + path = NULL; + } + } + } + } + + strv_uniq(todo); + + r = 0; + STRV_FOREACH(i, todo) { + _cleanup_strv_free_ char **fs = NULL; + const char *rp; + char **j; + + (void) get_files_in_directory(*i, &fs); + + q = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL); + if (q < 0 && q != -ENOENT && r >= 0) { + r = q; + continue; + } + + STRV_FOREACH(j, fs) { + _cleanup_free_ char *t = NULL; + + t = strjoin(*i, "/", *j, NULL); + if (!t) + return -ENOMEM; + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, t, NULL); + } + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, *i, NULL); + + rp = skip_root(&paths, *i); + q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: *i); + if (q < 0) + return q; + } + + q = remove_marked_symlinks(remove_symlinks_to, paths.runtime_config, &paths, changes, n_changes); + if (r >= 0) + r = q; + + q = remove_marked_symlinks(remove_symlinks_to, paths.persistent_config, &paths, changes, n_changes); + if (r >= 0) + r = q; + + return r; +} + int unit_file_add_dependency( UnitFileScope scope, bool runtime, @@ -1715,8 +2129,8 @@ int unit_file_add_dependency( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; - _cleanup_free_ char *config_path = NULL; UnitFileInstallInfo *i, *target_info; + const char *config_path; char **f; int r; @@ -1730,34 +2144,30 @@ int unit_file_add_dependency( if (!unit_name_is_valid(target, UNIT_NAME_ANY)) return -EINVAL; - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; - r = get_config_path(scope, runtime, root_dir, &config_path); + r = install_info_discover(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info); if (r < 0) return r; - - r = install_info_discover(scope, &c, root_dir, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info); + r = install_info_may_process(target_info, &paths, changes, n_changes); if (r < 0) return r; - if (target_info->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; assert(target_info->type == UNIT_FILE_TYPE_REGULAR); STRV_FOREACH(f, files) { char ***l; - r = install_info_discover(scope, &c, root_dir, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + r = install_info_discover(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + if (r < 0) + return r; + r = install_info_may_process(i, &paths, changes, n_changes); if (r < 0) return r; - if (i->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; assert(i->type == UNIT_FILE_TYPE_REGULAR); @@ -1776,7 +2186,7 @@ int unit_file_add_dependency( return -ENOMEM; } - return install_context_apply(scope, &c, &paths, config_path, root_dir, force, SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes); + return install_context_apply(scope, &c, &paths, config_path, force, SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes); } int unit_file_enable( @@ -1790,7 +2200,7 @@ int unit_file_enable( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; - _cleanup_free_ char *config_path = NULL; + const char *config_path; UnitFileInstallInfo *i; char **f; int r; @@ -1798,24 +2208,19 @@ int unit_file_enable( assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - r = verify_root_dir(scope, &root_dir); - if (r < 0) - return r; - - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; STRV_FOREACH(f, files) { - r = install_info_discover(scope, &c, root_dir, &paths, *f, SEARCH_LOAD, &i); + r = install_info_discover(scope, &c, &paths, *f, SEARCH_LOAD, &i); + if (r < 0) + return r; + r = install_info_may_process(i, &paths, changes, n_changes); if (r < 0) return r; - if (i->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; assert(i->type == UNIT_FILE_TYPE_REGULAR); } @@ -1825,7 +2230,7 @@ int unit_file_enable( is useful to determine whether the passed files had any installation data at all. */ - return install_context_apply(scope, &c, &paths, config_path, root_dir, force, SEARCH_LOAD, changes, n_changes); + return install_context_apply(scope, &c, &paths, config_path, force, SEARCH_LOAD, changes, n_changes); } int unit_file_disable( @@ -1838,25 +2243,19 @@ int unit_file_disable( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; - _cleanup_free_ char *config_path = NULL; _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; + const char *config_path; char **i; int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - r = verify_root_dir(scope, &root_dir); - if (r < 0) - return r; - - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; STRV_FOREACH(i, files) { if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) @@ -1867,11 +2266,11 @@ int unit_file_disable( return r; } - r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, root_dir); + r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path); if (r < 0) return r; - return remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + return remove_marked_symlinks(remove_symlinks_to, config_path, &paths, changes, n_changes); } int unit_file_reenable( @@ -1912,41 +2311,34 @@ int unit_file_set_default( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; - _cleanup_free_ char *config_path = NULL; UnitFileInstallInfo *i; - const char *path; + const char *new_path, *old_path; int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); - if (unit_name_to_type(name) != UNIT_TARGET) + if (unit_name_to_type(name) != UNIT_TARGET) /* this also validates the name */ return -EINVAL; if (streq(name, SPECIAL_DEFAULT_TARGET)) return -EINVAL; - r = verify_root_dir(scope, &root_dir); - if (r < 0) - return r; - - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = get_config_path(scope, false, root_dir, &config_path); + r = install_info_discover(scope, &c, &paths, name, 0, &i); if (r < 0) return r; - - r = install_info_discover(scope, &c, root_dir, &paths, name, 0, &i); + r = install_info_may_process(i, &paths, changes, n_changes); if (r < 0) return r; - if (i->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; - path = strjoina(config_path, "/" SPECIAL_DEFAULT_TARGET); + old_path = skip_root(&paths, i->path); + new_path = strjoina(paths.persistent_config, "/" SPECIAL_DEFAULT_TARGET); - return create_symlink(i->path, path, force, changes, n_changes); + return create_symlink(old_path ?: i->path, new_path, force, changes, n_changes); } int unit_file_get_default( @@ -1964,19 +2356,16 @@ int unit_file_get_default( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + r = install_info_discover(scope, &c, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); if (r < 0) return r; - - r = install_info_discover(scope, &c, root_dir, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + r = install_info_may_process(i, &paths, NULL, 0); if (r < 0) return r; - if (i->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; n = strdup(i->name); if (!n) @@ -1986,9 +2375,8 @@ int unit_file_get_default( return 0; } -int unit_file_lookup_state( +static int unit_file_lookup_state( UnitFileScope scope, - const char *root_dir, const LookupPaths *paths, const char *name, UnitFileState *ret) { @@ -2004,11 +2392,7 @@ int unit_file_lookup_state( if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; - r = verify_root_dir(scope, &root_dir); - if (r < 0) - return r; - - r = install_info_discover(scope, &c, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + r = install_info_discover(scope, &c, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); if (r < 0) return r; @@ -2019,11 +2403,31 @@ int unit_file_lookup_state( switch (i->type) { case UNIT_FILE_TYPE_MASKED: - state = path_startswith(i->path, "/run") ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; + r = path_is_runtime(paths, i->path); + if (r < 0) + return r; + + state = r > 0 ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; break; case UNIT_FILE_TYPE_REGULAR: - r = find_symlinks_in_scope(scope, root_dir, i->name, &state); + r = path_is_generator(paths, i->path); + if (r < 0) + return r; + if (r > 0) { + state = UNIT_FILE_GENERATED; + break; + } + + r = path_is_transient(paths, i->path); + if (r < 0) + return r; + if (r > 0) { + state = UNIT_FILE_TRANSIENT; + break; + } + + r = find_symlinks_in_scope(scope, paths, i->name, &state); if (r < 0) return r; if (r == 0) { @@ -2058,32 +2462,42 @@ int unit_file_get_state( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + return unit_file_lookup_state(scope, &paths, name, ret); +} + +int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name) { + _cleanup_(install_context_done) InstallContext c = {}; + int r; + + assert(paths); + assert(name); + + if (!unit_name_is_valid(name, UNIT_NAME_ANY)) + return -EINVAL; + + r = install_info_discover(scope, &c, paths, name, 0, NULL); + if (r == -ENOENT) + return 0; if (r < 0) return r; - return unit_file_lookup_state(scope, root_dir, &paths, name, ret); + return 1; } -int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) { +static int read_presets(UnitFileScope scope, const char *root_dir, Presets *presets) { + _cleanup_(presets_freep) Presets ps = {}; + size_t n_allocated = 0; _cleanup_strv_free_ char **files = NULL; char **p; int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(name); - - r = verify_root_dir(scope, &root_dir); - if (r < 0) - return r; - - if (!unit_name_is_valid(name, UNIT_NAME_ANY)) - return -EINVAL; + assert(presets); if (scope == UNIT_FILE_SYSTEM) r = conf_files_list(&files, ".preset", root_dir, @@ -2100,8 +2514,11 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char "/usr/local/lib/systemd/user-preset", "/usr/lib/systemd/user-preset", NULL); - else - return 1; /* Default is "enable" */ + else { + *presets = (Presets){}; + + return 0; + } if (r < 0) return r; @@ -2109,6 +2526,7 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char STRV_FOREACH(p, files) { _cleanup_fclose_ FILE *f; char line[LINE_MAX]; + int n = 0; f = fopen(*p, "re"); if (!f) { @@ -2119,10 +2537,12 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char } FOREACH_LINE(line, f, return -errno) { + PresetRule rule = {}; const char *parameter; char *l; l = strstrip(line); + n++; if (isempty(l)) continue; @@ -2131,31 +2551,87 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char parameter = first_word(l, "enable"); if (parameter) { - if (fnmatch(parameter, name, FNM_NOESCAPE) == 0) { - log_debug("Preset file says enable %s.", name); - return 1; - } + char *pattern; - continue; + pattern = strdup(parameter); + if (!pattern) + return -ENOMEM; + + rule = (PresetRule) { + .pattern = pattern, + .action = PRESET_ENABLE, + }; } parameter = first_word(l, "disable"); if (parameter) { - if (fnmatch(parameter, name, FNM_NOESCAPE) == 0) { - log_debug("Preset file says disable %s.", name); - return 0; - } + char *pattern; + + pattern = strdup(parameter); + if (!pattern) + return -ENOMEM; + + rule = (PresetRule) { + .pattern = pattern, + .action = PRESET_DISABLE, + }; + } + + if (rule.action) { + if (!GREEDY_REALLOC(ps.rules, n_allocated, ps.n_rules + 1)) + return -ENOMEM; + ps.rules[ps.n_rules++] = rule; continue; } - log_debug("Couldn't parse line '%s'", l); + log_syntax(NULL, LOG_WARNING, *p, n, 0, "Couldn't parse line '%s'. Ignoring.", line); } } - /* Default is "enable" */ - log_debug("Preset file doesn't say anything about %s, enabling.", name); - return 1; + *presets = ps; + ps = (Presets){}; + + return 0; +} + +static int query_presets(const char *name, const Presets presets) { + PresetAction action = PRESET_UNKNOWN; + size_t i; + + if (!unit_name_is_valid(name, UNIT_NAME_ANY)) + return -EINVAL; + + for (i = 0; i < presets.n_rules; i++) + if (fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) { + action = presets.rules[i].action; + break; + } + + switch (action) { + case PRESET_UNKNOWN: + log_debug("Preset files don't specify rule for %s. Enabling.", name); + return 1; + case PRESET_ENABLE: + log_debug("Preset files say enable %s.", name); + return 1; + case PRESET_DISABLE: + log_debug("Preset files say disable %s.", name); + return 0; + default: + assert_not_reached("invalid preset action"); + } +} + +int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) { + _cleanup_(presets_freep) Presets presets = {}; + int r; + + r = read_presets(scope, root_dir, &presets); + if (r < 0) + return r; + + return query_presets(name, presets); } static int execute_preset( @@ -2164,7 +2640,6 @@ static int execute_preset( InstallContext *minus, const LookupPaths *paths, const char *config_path, - const char *root_dir, char **files, UnitFilePresetMode mode, bool force, @@ -2181,11 +2656,11 @@ static int execute_preset( if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) { _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, root_dir); + r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path); if (r < 0) return r; - r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + r = remove_marked_symlinks(remove_symlinks_to, config_path, paths, changes, n_changes); } else r = 0; @@ -2193,12 +2668,12 @@ static int execute_preset( int q; /* Returns number of symlinks that where supposed to be installed. */ - q = install_context_apply(scope, plus, paths, config_path, root_dir, force, SEARCH_LOAD, changes, n_changes); + q = install_context_apply(scope, plus, paths, config_path, force, SEARCH_LOAD, changes, n_changes); if (r >= 0) { if (q < 0) r = q; else - r+= q; + r += q; } } @@ -2210,9 +2685,11 @@ static int preset_prepare_one( InstallContext *plus, InstallContext *minus, LookupPaths *paths, - const char *root_dir, UnitFilePresetMode mode, - const char *name) { + const char *name, + Presets presets, + UnitFileChange **changes, + unsigned *n_changes) { UnitFileInstallInfo *i; int r; @@ -2221,19 +2698,20 @@ static int preset_prepare_one( install_info_find(minus, name)) return 0; - r = unit_file_query_preset(scope, root_dir, name); + r = query_presets(name, presets); if (r < 0) return r; if (r > 0) { - r = install_info_discover(scope, plus, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); if (r < 0) return r; - if (i->type == UNIT_FILE_TYPE_MASKED) - return -ESHUTDOWN; + r = install_info_may_process(i, paths, changes, n_changes); + if (r < 0) + return r; } else - r = install_info_discover(scope, minus, root_dir, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); return r; } @@ -2250,7 +2728,8 @@ int unit_file_preset( _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_free_ char *config_path = NULL; + _cleanup_(presets_freep) Presets presets = {}; + const char *config_path; char **i; int r; @@ -2258,28 +2737,23 @@ int unit_file_preset( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(mode < _UNIT_FILE_PRESET_MAX); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; - r = get_config_path(scope, runtime, root_dir, &config_path); + r = read_presets(scope, root_dir, &presets); if (r < 0) return r; STRV_FOREACH(i, files) { - if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) - return -EINVAL; - - r = preset_prepare_one(scope, &plus, &minus, &paths, root_dir, mode, *i); + r = preset_prepare_one(scope, &plus, &minus, &paths, mode, *i, presets, changes, n_changes); if (r < 0) return r; } - return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, files, mode, force, changes, n_changes); + return execute_preset(scope, &plus, &minus, &paths, config_path, files, mode, force, changes, n_changes); } int unit_file_preset_all( @@ -2293,7 +2767,8 @@ int unit_file_preset_all( _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_free_ char *config_path = NULL; + _cleanup_(presets_freep) Presets presets = {}; + const char *config_path = NULL; char **i; int r; @@ -2301,28 +2776,21 @@ int unit_file_preset_all( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(mode < _UNIT_FILE_PRESET_MAX); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); - if (r < 0) - return r; + config_path = runtime ? paths.runtime_config : paths.persistent_config; - r = get_config_path(scope, runtime, root_dir, &config_path); + r = read_presets(scope, root_dir, &presets); if (r < 0) return r; - STRV_FOREACH(i, paths.unit_path) { + STRV_FOREACH(i, paths.search_path) { _cleanup_closedir_ DIR *d = NULL; - _cleanup_free_ char *units_dir; struct dirent *de; - units_dir = path_join(root_dir, *i, NULL); - if (!units_dir) - return -ENOMEM; - - d = opendir(units_dir); + d = opendir(*i); if (!d) { if (errno == ENOENT) continue; @@ -2340,13 +2808,20 @@ int unit_file_preset_all( if (!IN_SET(de->d_type, DT_LNK, DT_REG)) continue; - r = preset_prepare_one(scope, &plus, &minus, &paths, root_dir, mode, de->d_name); + /* we don't pass changes[] in, because we want to handle errors on our own */ + r = preset_prepare_one(scope, &plus, &minus, &paths, mode, de->d_name, presets, NULL, 0); + if (r == -ERFKILL) + r = unit_file_changes_add(changes, n_changes, + UNIT_FILE_IS_MASKED, de->d_name, NULL); + else if (r == -ENOLINK) + r = unit_file_changes_add(changes, n_changes, + UNIT_FILE_IS_DANGLING, de->d_name, NULL); if (r < 0) return r; } } - return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, NULL, mode, force, changes, n_changes); + return execute_preset(scope, &plus, &minus, &paths, config_path, NULL, mode, force, changes, n_changes); } static void unit_file_list_free_one(UnitFileList *f) { @@ -2371,7 +2846,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one); int unit_file_get_list( UnitFileScope scope, const char *root_dir, - Hashmap *h) { + Hashmap *h, + char **states, + char **patterns) { _cleanup_lookup_paths_free_ LookupPaths paths = {}; char **i; @@ -2381,24 +2858,15 @@ int unit_file_get_list( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(h); - r = verify_root_dir(scope, &root_dir); + r = lookup_paths_init(&paths, scope, 0, root_dir); if (r < 0) return r; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); - if (r < 0) - return r; - - STRV_FOREACH(i, paths.unit_path) { + STRV_FOREACH(i, paths.search_path) { _cleanup_closedir_ DIR *d = NULL; - _cleanup_free_ char *units_dir; struct dirent *de; - units_dir = path_join(root_dir, *i, NULL); - if (!units_dir) - return -ENOMEM; - - d = opendir(units_dir); + d = opendir(*i); if (!d) { if (errno == ENOENT) continue; @@ -2412,6 +2880,9 @@ int unit_file_get_list( if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) continue; + if (!strv_fnmatch_or_empty(patterns, de->d_name, FNM_NOESCAPE)) + continue; + if (hashmap_get(h, de->d_name)) continue; @@ -2424,14 +2895,18 @@ int unit_file_get_list( if (!f) return -ENOMEM; - f->path = path_make_absolute(de->d_name, units_dir); + f->path = path_make_absolute(de->d_name, *i); if (!f->path) return -ENOMEM; - r = unit_file_lookup_state(scope, root_dir, &paths, basename(f->path), &f->state); + r = unit_file_lookup_state(scope, &paths, de->d_name, &f->state); if (r < 0) f->state = UNIT_FILE_BAD; + if (!strv_isempty(states) && + !strv_contains(states, unit_file_state_to_string(f->state))) + continue; + r = hashmap_put(h, basename(f->path), f); if (r < 0) return r; @@ -2453,6 +2928,8 @@ static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = { [UNIT_FILE_STATIC] = "static", [UNIT_FILE_DISABLED] = "disabled", [UNIT_FILE_INDIRECT] = "indirect", + [UNIT_FILE_GENERATED] = "generated", + [UNIT_FILE_TRANSIENT] = "transient", [UNIT_FILE_BAD] = "bad", }; @@ -2461,6 +2938,8 @@ DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState); static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = { [UNIT_FILE_SYMLINK] = "symlink", [UNIT_FILE_UNLINK] = "unlink", + [UNIT_FILE_IS_MASKED] = "masked", + [UNIT_FILE_IS_DANGLING] = "dangling", }; DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType); diff --git a/src/shared/install.h b/src/shared/install.h index c1a43e23e7..c6aa4f6ef1 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -54,6 +54,8 @@ enum UnitFileState { UNIT_FILE_STATIC, UNIT_FILE_DISABLED, UNIT_FILE_INDIRECT, + UNIT_FILE_GENERATED, + UNIT_FILE_TRANSIENT, UNIT_FILE_BAD, _UNIT_FILE_STATE_MAX, _UNIT_FILE_STATE_INVALID = -1 @@ -70,16 +72,30 @@ enum UnitFilePresetMode { enum UnitFileChangeType { UNIT_FILE_SYMLINK, UNIT_FILE_UNLINK, + UNIT_FILE_IS_MASKED, + UNIT_FILE_IS_DANGLING, _UNIT_FILE_CHANGE_TYPE_MAX, - _UNIT_FILE_CHANGE_TYPE_INVALID = -1 + _UNIT_FILE_CHANGE_INVALID = INT_MIN }; +/* type can either one of the UnitFileChangeTypes listed above, or a negative error. + * If source is specified, it should be the contents of the path symlink. + * In case of an error, source should be the existing symlink contents or NULL + */ struct UnitFileChange { - UnitFileChangeType type; + int type; /* UnitFileChangeType or bust */ char *path; char *source; }; +static inline bool unit_file_changes_have_modification(const UnitFileChange* changes, unsigned n_changes) { + unsigned i; + for (i = 0; i < n_changes; i++) + if (IN_SET(changes[i].type, UNIT_FILE_SYMLINK, UNIT_FILE_UNLINK)) + return true; + return false; +} + struct UnitFileList { char *path; UnitFileState state; @@ -123,31 +139,115 @@ static inline bool UNIT_FILE_INSTALL_INFO_HAS_ALSO(UnitFileInstallInfo *i) { return !strv_isempty(i->also); } -int unit_file_enable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes); -int unit_file_disable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes); -int unit_file_reenable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes); -int unit_file_link(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes); -int unit_file_preset(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFilePresetMode mode, bool force, UnitFileChange **changes, unsigned *n_changes); -int unit_file_preset_all(UnitFileScope scope, bool runtime, const char *root_dir, UnitFilePresetMode mode, bool force, UnitFileChange **changes, unsigned *n_changes); -int unit_file_mask(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes); -int unit_file_unmask(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes); -int unit_file_set_default(UnitFileScope scope, const char *root_dir, const char *file, bool force, UnitFileChange **changes, unsigned *n_changes); -int unit_file_get_default(UnitFileScope scope, const char *root_dir, char **name); -int unit_file_add_dependency(UnitFileScope scope, bool runtime, const char *root_dir, char **files, const char *target, UnitDependency dep, bool force, UnitFileChange **changes, unsigned *n_changes); - -int unit_file_lookup_state(UnitFileScope scope, const char *root_dir,const LookupPaths *paths, const char *name, UnitFileState *ret); +bool unit_type_may_alias(UnitType type) _const_; +bool unit_type_may_template(UnitType type) _const_; + +int unit_file_enable( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + bool force, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_disable( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_reenable( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + bool force, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_preset( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + UnitFilePresetMode mode, + bool force, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_preset_all( + UnitFileScope scope, + bool runtime, + const char *root_dir, + UnitFilePresetMode mode, + bool force, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_mask( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + bool force, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_unmask( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_link( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + bool force, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_revert( + UnitFileScope scope, + const char *root_dir, + char **files, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_set_default( + UnitFileScope scope, + const char *root_dir, + const char *file, + bool force, + UnitFileChange **changes, + unsigned *n_changes); +int unit_file_get_default( + UnitFileScope scope, + const char *root_dir, + char **name); +int unit_file_add_dependency( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + const char *target, + UnitDependency dep, + bool force, + UnitFileChange **changes, + unsigned *n_changes); + int unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename, UnitFileState *ret); +int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name); -int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h); +int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h, char **states, char **patterns); Hashmap* unit_file_list_free(Hashmap *h); int unit_file_changes_add(UnitFileChange **changes, unsigned *n_changes, UnitFileChangeType type, const char *path, const char *source); void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes); +void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, unsigned n_changes, bool quiet); int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name); const char *unit_file_state_to_string(UnitFileState s) _const_; UnitFileState unit_file_state_from_string(const char *s) _pure_; +/* from_string conversion is unreliable because of the overlap between -EPERM and -1 for error. */ const char *unit_file_change_type_to_string(UnitFileChangeType s) _const_; UnitFileChangeType unit_file_change_type_from_string(const char *s) _pure_; diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 5eb3bd35c7..9351b85eed 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -287,7 +287,10 @@ static int output_short( if (r < 0) return r; } - + if (r == -EBADMSG) { + log_debug_errno(r, "Skipping message we can't read: %m"); + return 0; + } if (r < 0) return log_error_errno(r, "Failed to get journal fields: %m"); @@ -344,16 +347,22 @@ static int output_short( t = (time_t) (x / USEC_PER_SEC); - switch(mode) { + switch (mode) { + + case OUTPUT_SHORT_UNIX: + r = snprintf(buf, sizeof(buf), "%10llu.%06llu", (unsigned long long) t, (unsigned long long) (x % USEC_PER_SEC)); + break; + case OUTPUT_SHORT_ISO: r = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm)); break; + case OUTPUT_SHORT_PRECISE: r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)); if (r > 0) - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), - ".%06llu", (unsigned long long) (x % USEC_PER_SEC)); + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%06llu", (unsigned long long) (x % USEC_PER_SEC)); break; + default: r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)); } @@ -367,6 +376,12 @@ static int output_short( n += strlen(buf); } + if (hostname && (flags & OUTPUT_NO_HOSTNAME)) { + /* Suppress display of the hostname if this is requested. */ + hostname = NULL; + hostname_len = 0; + } + if (hostname && shall_print(hostname, hostname_len, flags)) { fprintf(f, " %.*s", (int) hostname_len, hostname); n += hostname_len + 1; @@ -894,6 +909,7 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])( [OUTPUT_SHORT_ISO] = output_short, [OUTPUT_SHORT_PRECISE] = output_short, [OUTPUT_SHORT_MONOTONIC] = output_short, + [OUTPUT_SHORT_UNIX] = output_short, [OUTPUT_VERBOSE] = output_verbose, [OUTPUT_EXPORT] = output_export, [OUTPUT_JSON] = output_json, @@ -997,7 +1013,7 @@ static int show_journal(FILE *f, continue; } - line ++; + line++; maybe_print_begin_newline(f, &flags); r = output_journal(f, j, mode, n_columns, flags, ellipsized); @@ -1040,8 +1056,8 @@ static int show_journal(FILE *f, } int add_matches_for_unit(sd_journal *j, const char *unit) { + const char *m1, *m2, *m3, *m4; int r; - char *m1, *m2, *m3, *m4; assert(j); assert(unit); @@ -1073,7 +1089,9 @@ int add_matches_for_unit(sd_journal *j, const char *unit) { ); if (r == 0 && endswith(unit, ".slice")) { - char *m5 = strappend("_SYSTEMD_SLICE=", unit); + const char *m5; + + m5 = strjoina("_SYSTEMD_SLICE=", unit); /* Show all messages belonging to a slice */ (void)( @@ -1123,7 +1141,9 @@ int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) { ); if (r == 0 && endswith(unit, ".slice")) { - char *m5 = strappend("_SYSTEMD_SLICE=", unit); + const char *m5; + + m5 = strjoina("_SYSTEMD_SLICE=", unit); /* Show all messages belonging to a slice */ (void)( @@ -1288,18 +1308,3 @@ int show_journal_by_unit( return show_journal(f, j, mode, n_columns, not_before, how_many, flags, ellipsized); } - -static const char *const output_mode_table[_OUTPUT_MODE_MAX] = { - [OUTPUT_SHORT] = "short", - [OUTPUT_SHORT_ISO] = "short-iso", - [OUTPUT_SHORT_PRECISE] = "short-precise", - [OUTPUT_SHORT_MONOTONIC] = "short-monotonic", - [OUTPUT_VERBOSE] = "verbose", - [OUTPUT_EXPORT] = "export", - [OUTPUT_JSON] = "json", - [OUTPUT_JSON_PRETTY] = "json-pretty", - [OUTPUT_JSON_SSE] = "json-sse", - [OUTPUT_CAT] = "cat" -}; - -DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode); diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h index 9765a24ff2..6643440881 100644 --- a/src/shared/logs-show.h +++ b/src/shared/logs-show.h @@ -68,6 +68,3 @@ void json_escape( const char* p, size_t l, OutputFlags flags); - -const char* output_mode_to_string(OutputMode m) _const_; -OutputMode output_mode_from_string(const char *s) _pure_; diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index ed8a29c575..529d89ee2a 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -23,6 +23,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/file.h> #include <sys/stat.h> #include <unistd.h> #include <linux/fs.h> @@ -400,8 +401,7 @@ int image_remove(Image *i) { assert(i); - if (path_equal(i->path, "/") || - path_startswith(i->path, "/usr")) + if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) return -EROFS; settings = image_settings_path(i); @@ -423,7 +423,7 @@ int image_remove(Image *i) { case IMAGE_DIRECTORY: /* Allow deletion of read-only directories */ - (void) chattr_path(i->path, false, FS_IMMUTABLE_FL); + (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL); r = rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); if (r < 0) return r; @@ -473,8 +473,7 @@ int image_rename(Image *i, const char *new_name) { if (!image_name_is_valid(new_name)) return -EINVAL; - if (path_equal(i->path, "/") || - path_startswith(i->path, "/usr")) + if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) return -EROFS; settings = image_settings_path(i); @@ -488,7 +487,7 @@ int image_rename(Image *i, const char *new_name) { /* Make sure nobody takes the new name, between the time we * checked it is currently unused in all search paths, and the - * time we take possesion of it */ + * time we take possession of it */ r = image_name_lock(new_name, LOCK_EX|LOCK_NB, &name_lock); if (r < 0) return r; @@ -506,7 +505,7 @@ int image_rename(Image *i, const char *new_name) { (void) read_attr_path(i->path, &file_attr); if (file_attr & FS_IMMUTABLE_FL) - (void) chattr_path(i->path, false, FS_IMMUTABLE_FL); + (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL); /* fall through */ @@ -539,7 +538,7 @@ int image_rename(Image *i, const char *new_name) { /* Restore the immutable bit, if it was set before */ if (file_attr & FS_IMMUTABLE_FL) - (void) chattr_path(new_path, true, FS_IMMUTABLE_FL); + (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL); free(i->path); i->path = new_path; @@ -589,7 +588,7 @@ int image_clone(Image *i, const char *new_name, bool read_only) { /* Make sure nobody takes the new name, between the time we * checked it is currently unused in all search paths, and the - * time we take possesion of it */ + * time we take possession of it */ r = image_name_lock(new_name, LOCK_EX|LOCK_NB, &name_lock); if (r < 0) return r; @@ -604,13 +603,21 @@ int image_clone(Image *i, const char *new_name, bool read_only) { case IMAGE_SUBVOLUME: case IMAGE_DIRECTORY: + /* If we can we'll always try to create a new btrfs subvolume here, even if the source is a plain + * directory.*/ + new_path = strjoina("/var/lib/machines/", new_name); r = btrfs_subvol_snapshot(i->path, new_path, (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE | BTRFS_SNAPSHOT_QUOTA); + if (r == -EOPNOTSUPP) { + /* No btrfs snapshots supported, create a normal directory then. */ - /* Enable "subtree" quotas for the copy, if we didn't - * copy any quota from the source. */ - (void) btrfs_subvol_auto_qgroup(i->path, 0, true); + r = copy_directory(i->path, new_path, false); + if (r >= 0) + (void) chattr_path(new_path, read_only ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL); + } else if (r >= 0) + /* Enable "subtree" quotas for the copy, if we didn't copy any quota from the source. */ + (void) btrfs_subvol_auto_qgroup(new_path, 0, true); break; @@ -641,8 +648,7 @@ int image_read_only(Image *i, bool b) { int r; assert(i); - if (path_equal(i->path, "/") || - path_startswith(i->path, "/usr")) + if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) return -EROFS; /* Make sure we don't interfere with a running nspawn */ @@ -672,7 +678,7 @@ int image_read_only(Image *i, bool b) { a read-only subvolume, but at least something, and we can read the value back.*/ - r = chattr_path(i->path, b, FS_IMMUTABLE_FL); + r = chattr_path(i->path, b ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL); if (r < 0) return r; @@ -750,8 +756,7 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile int image_set_limit(Image *i, uint64_t referenced_max) { assert(i); - if (path_equal(i->path, "/") || - path_startswith(i->path, "/usr")) + if (IMAGE_IS_VENDOR(i) || IMAGE_IS_HOST(i)) return -EROFS; if (i->type != IMAGE_SUBVOLUME) diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h index 31b720d50c..7410168c4f 100644 --- a/src/shared/machine-image.h +++ b/src/shared/machine-image.h @@ -25,6 +25,8 @@ #include "hashmap.h" #include "lockfile-util.h" #include "macro.h" +#include "path-util.h" +#include "string-util.h" #include "time-util.h" typedef enum ImageType { @@ -75,3 +77,27 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile int image_name_lock(const char *name, int operation, LockFile *ret); int image_set_limit(Image *i, uint64_t referenced_max); + +static inline bool IMAGE_IS_HIDDEN(const struct Image *i) { + assert(i); + + return i->name && i->name[0] == '.'; +} + +static inline bool IMAGE_IS_VENDOR(const struct Image *i) { + assert(i); + + return i->path && path_startswith(i->path, "/usr"); +} + +static inline bool IMAGE_IS_HOST(const struct Image *i) { + assert(i); + + if (i->name && streq(i->name, ".host")) + return true; + + if (i->path && path_equal(i->path, "/")) + return true; + + return false; +} diff --git a/src/shared/machine-pool.c b/src/shared/machine-pool.c index e5674e4137..23890c63a0 100644 --- a/src/shared/machine-pool.c +++ b/src/shared/machine-pool.c @@ -24,6 +24,7 @@ #include <stdbool.h> #include <stdio.h> #include <stdlib.h> +#include <sys/file.h> #include <sys/ioctl.h> #include <sys/mount.h> #include <sys/prctl.h> @@ -138,7 +139,7 @@ static int setup_machine_raw(uint64_t size, sd_bus_error *error) { execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp, NULL); if (errno == ENOENT) - return 99; + _exit(99); _exit(EXIT_FAILURE); } @@ -238,10 +239,8 @@ int setup_machine_directory(uint64_t size, sd_bus_error *error) { } r = mkfs_exists("btrfs"); - if (r == -ENOENT) { - log_debug("mkfs.btrfs is missing, cannot create loopback file for /var/lib/machines."); - return 0; - } + if (r == 0) + return sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing"); if (r < 0) return r; diff --git a/src/shared/output-mode.c b/src/shared/output-mode.c new file mode 100644 index 0000000000..bec53ee0ae --- /dev/null +++ b/src/shared/output-mode.c @@ -0,0 +1,37 @@ +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "output-mode.h" +#include "string-table.h" + +static const char *const output_mode_table[_OUTPUT_MODE_MAX] = { + [OUTPUT_SHORT] = "short", + [OUTPUT_SHORT_ISO] = "short-iso", + [OUTPUT_SHORT_PRECISE] = "short-precise", + [OUTPUT_SHORT_MONOTONIC] = "short-monotonic", + [OUTPUT_SHORT_UNIX] = "short-unix", + [OUTPUT_VERBOSE] = "verbose", + [OUTPUT_EXPORT] = "export", + [OUTPUT_JSON] = "json", + [OUTPUT_JSON_PRETTY] = "json-pretty", + [OUTPUT_JSON_SSE] = "json-sse", + [OUTPUT_CAT] = "cat" +}; + +DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode); diff --git a/src/shared/output-mode.h b/src/shared/output-mode.h index c5470e7c1b..f37189e57f 100644 --- a/src/shared/output-mode.h +++ b/src/shared/output-mode.h @@ -19,11 +19,14 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "macro.h" + typedef enum OutputMode { OUTPUT_SHORT, OUTPUT_SHORT_ISO, OUTPUT_SHORT_PRECISE, OUTPUT_SHORT_MONOTONIC, + OUTPUT_SHORT_UNIX, OUTPUT_VERBOSE, OUTPUT_EXPORT, OUTPUT_JSON, @@ -34,6 +37,9 @@ typedef enum OutputMode { _OUTPUT_MODE_INVALID = -1 } OutputMode; +/* The output flags definitions are shared by the logs and process tree output. Some apply to both, some only to the + * logs output, others only to the process tree output. */ + typedef enum OutputFlags { OUTPUT_SHOW_ALL = 1 << 0, OUTPUT_FOLLOW = 1 << 1, @@ -43,4 +49,9 @@ typedef enum OutputFlags { OUTPUT_CATALOG = 1 << 5, OUTPUT_BEGIN_NEWLINE = 1 << 6, OUTPUT_UTC = 1 << 7, + OUTPUT_KERNEL_THREADS = 1 << 8, + OUTPUT_NO_HOSTNAME = 1 << 9, } OutputFlags; + +const char* output_mode_to_string(OutputMode m) _const_; +OutputMode output_mode_from_string(const char *s) _pure_; diff --git a/src/shared/pager.c b/src/shared/pager.c index 05b2b15e40..c16bc027be 100644 --- a/src/shared/pager.c +++ b/src/shared/pager.c @@ -52,11 +52,14 @@ noreturn static void pager_fallback(void) { _exit(EXIT_SUCCESS); } -int pager_open(bool jump_to_end) { +int pager_open(bool no_pager, bool jump_to_end) { _cleanup_close_pair_ int fd[2] = { -1, -1 }; const char *pager; pid_t parent_pid; + if (no_pager) + return 0; + if (pager_pid > 0) return 1; diff --git a/src/shared/pager.h b/src/shared/pager.h index 9fb05796bb..893e1d2bb6 100644 --- a/src/shared/pager.h +++ b/src/shared/pager.h @@ -23,7 +23,7 @@ #include "macro.h" -int pager_open(bool jump_to_end); +int pager_open(bool no_pager, bool jump_to_end); void pager_close(void); bool pager_have(void) _pure_; diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c index 5410620725..ca593b6963 100644 --- a/src/shared/path-lookup.c +++ b/src/shared/path-lookup.c @@ -26,61 +26,66 @@ #include "install.h" #include "log.h" #include "macro.h" +#include "mkdir.h" #include "path-lookup.h" #include "path-util.h" +#include "rm-rf.h" +#include "stat-util.h" #include "string-util.h" #include "strv.h" #include "util.h" -int user_config_home(char **config_home) { +static int user_runtime_dir(char **ret, const char *suffix) { const char *e; - char *r; + char *j; - e = getenv("XDG_CONFIG_HOME"); - if (e) { - r = strappend(e, "/systemd/user"); - if (!r) - return -ENOMEM; - - *config_home = r; - return 1; - } else { - const char *home; + assert(ret); + assert(suffix); - home = getenv("HOME"); - if (home) { - r = strappend(home, "/.config/systemd/user"); - if (!r) - return -ENOMEM; + e = getenv("XDG_RUNTIME_DIR"); + if (!e) + return -ENXIO; - *config_home = r; - return 1; - } - } + j = strappend(e, suffix); + if (!j) + return -ENOMEM; + *ret = j; return 0; } -int user_runtime_dir(char **runtime_dir) { +static int user_config_dir(char **ret, const char *suffix) { const char *e; - char *r; + char *j; - e = getenv("XDG_RUNTIME_DIR"); - if (e) { - r = strappend(e, "/systemd/user"); - if (!r) - return -ENOMEM; + assert(ret); + + e = getenv("XDG_CONFIG_HOME"); + if (e) + j = strappend(e, suffix); + else { + const char *home; - *runtime_dir = r; - return 1; + home = getenv("HOME"); + if (!home) + return -ENXIO; + + j = strjoin(home, "/.config", suffix, NULL); } + if (!j) + return -ENOMEM; + + *ret = j; return 0; } -static int user_data_home_dir(char **dir, const char *suffix) { +static int user_data_dir(char **ret, const char *suffix) { const char *e; - char *res; + char *j; + + assert(ret); + assert(suffix); /* We don't treat /etc/xdg/systemd here as the spec * suggests because we assume that that is a link to @@ -88,27 +93,33 @@ static int user_data_home_dir(char **dir, const char *suffix) { e = getenv("XDG_DATA_HOME"); if (e) - res = strappend(e, suffix); + j = strappend(e, suffix); else { const char *home; home = getenv("HOME"); - if (home) - res = strjoin(home, "/.local/share", suffix, NULL); - else - return 0; + if (!home) + return -ENXIO; + + + j = strjoin(home, "/.local/share", suffix, NULL); } - if (!res) + if (!j) return -ENOMEM; - *dir = res; - return 0; + *ret = j; + return 1; } static char** user_dirs( + const char *persistent_config, + const char *runtime_config, const char *generator, const char *generator_early, - const char *generator_late) { + const char *generator_late, + const char *transient, + const char *persistent_control, + const char *runtime_control) { const char * const config_unit_paths[] = { USER_CONFIG_UNIT_PATH, @@ -116,8 +127,6 @@ static char** user_dirs( NULL }; - const char * const runtime_unit_path = "/run/systemd/user"; - const char * const data_unit_paths[] = { "/usr/local/lib/systemd/user", "/usr/local/share/systemd/user", @@ -128,8 +137,8 @@ static char** user_dirs( }; const char *e; - _cleanup_free_ char *config_home = NULL, *runtime_dir = NULL, *data_home = NULL; _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL; + _cleanup_free_ char *data_home = NULL; _cleanup_free_ char **res = NULL; char **tmp; int r; @@ -143,12 +152,6 @@ static char** user_dirs( * as data, and allow overriding as configuration. */ - if (user_config_home(&config_home) < 0) - return NULL; - - if (user_runtime_dir(&runtime_dir) < 0) - return NULL; - e = getenv("XDG_CONFIG_DIRS"); if (e) { config_dirs = strv_split(e, ":"); @@ -156,8 +159,8 @@ static char** user_dirs( return NULL; } - r = user_data_home_dir(&data_home, "/systemd/user"); - if (r < 0) + r = user_data_dir(&data_home, "/systemd/user"); + if (r < 0 && r != -ENXIO) return NULL; e = getenv("XDG_DATA_DIRS"); @@ -171,35 +174,36 @@ static char** user_dirs( return NULL; /* Now merge everything we found. */ - if (generator_early) - if (strv_extend(&res, generator_early) < 0) - return NULL; + if (strv_extend(&res, persistent_control) < 0) + return NULL; - if (config_home) - if (strv_extend(&res, config_home) < 0) - return NULL; + if (strv_extend(&res, runtime_control) < 0) + return NULL; + + if (strv_extend(&res, transient) < 0) + return NULL; + + if (strv_extend(&res, generator_early) < 0) + return NULL; if (!strv_isempty(config_dirs)) if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0) return NULL; - if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0) + if (strv_extend(&res, persistent_config) < 0) return NULL; - if (runtime_dir) - if (strv_extend(&res, runtime_dir) < 0) - return NULL; + if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0) + return NULL; - if (strv_extend(&res, runtime_unit_path) < 0) + if (strv_extend(&res, runtime_config) < 0) return NULL; - if (generator) - if (strv_extend(&res, generator) < 0) - return NULL; + if (strv_extend(&res, generator) < 0) + return NULL; - if (data_home) - if (strv_extend(&res, data_home) < 0) - return NULL; + if (strv_extend(&res, data_home) < 0) + return NULL; if (!strv_isempty(data_dirs)) if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0) @@ -208,9 +212,8 @@ static char** user_dirs( if (strv_extend_strv(&res, (char**) data_unit_paths, false) < 0) return NULL; - if (generator_late) - if (strv_extend(&res, generator_late) < 0) - return NULL; + if (strv_extend(&res, generator_late) < 0) + return NULL; if (path_strv_make_absolute_cwd(res) < 0) return NULL; @@ -220,58 +223,298 @@ static char** user_dirs( return tmp; } -char **generator_paths(ManagerRunningAs running_as) { - if (running_as == MANAGER_USER) - return strv_new("/run/systemd/user-generators", - "/etc/systemd/user-generators", - "/usr/local/lib/systemd/user-generators", - USER_GENERATOR_PATH, - NULL); - else - return strv_new("/run/systemd/system-generators", - "/etc/systemd/system-generators", - "/usr/local/lib/systemd/system-generators", - SYSTEM_GENERATOR_PATH, - NULL); +static int acquire_generator_dirs( + UnitFileScope scope, + char **generator, + char **generator_early, + char **generator_late) { + + _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL; + const char *prefix; + + assert(generator); + assert(generator_early); + assert(generator_late); + + switch (scope) { + + case UNIT_FILE_SYSTEM: + prefix = "/run/systemd/"; + break; + + case UNIT_FILE_USER: { + const char *e; + + e = getenv("XDG_RUNTIME_DIR"); + if (!e) + return -ENXIO; + + prefix = strjoina(e, "/systemd/"); + break; + } + + case UNIT_FILE_GLOBAL: + return -EOPNOTSUPP; + + default: + assert_not_reached("Hmm, unexpected scope value."); + } + + x = strappend(prefix, "generator"); + if (!x) + return -ENOMEM; + + y = strappend(prefix, "generator.early"); + if (!y) + return -ENOMEM; + + z = strappend(prefix, "generator.late"); + if (!z) + return -ENOMEM; + + *generator = x; + *generator_early = y; + *generator_late = z; + + x = y = z = NULL; + return 0; +} + +static int acquire_transient_dir(UnitFileScope scope, char **ret) { + assert(ret); + + switch (scope) { + + case UNIT_FILE_SYSTEM: { + char *transient; + + transient = strdup("/run/systemd/transient"); + if (!transient) + return -ENOMEM; + + *ret = transient; + return 0; + } + + case UNIT_FILE_USER: + return user_runtime_dir(ret, "/systemd/transient"); + + case UNIT_FILE_GLOBAL: + return -EOPNOTSUPP; + + default: + assert_not_reached("Hmm, unexpected scope value."); + } +} + +static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **runtime) { + _cleanup_free_ char *a = NULL, *b = NULL; + int r; + + assert(persistent); + assert(runtime); + + switch (scope) { + + case UNIT_FILE_SYSTEM: + a = strdup(SYSTEM_CONFIG_UNIT_PATH); + b = strdup("/run/systemd/system"); + break; + + case UNIT_FILE_GLOBAL: + a = strdup(USER_CONFIG_UNIT_PATH); + b = strdup("/run/systemd/user"); + break; + + case UNIT_FILE_USER: + r = user_config_dir(&a, "/systemd/user"); + if (r < 0) + return r; + + r = user_runtime_dir(runtime, "/systemd/user"); + if (r < 0) + return r; + + *persistent = a; + a = NULL; + + return 0; + + default: + assert_not_reached("Hmm, unexpected scope value."); + } + + if (!a || !b) + return -ENOMEM; + + *persistent = a; + *runtime = b; + a = b = NULL; + + return 0; +} + +static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **runtime) { + _cleanup_free_ char *a = NULL; + int r; + + assert(persistent); + assert(runtime); + + switch (scope) { + + case UNIT_FILE_SYSTEM: { + _cleanup_free_ char *b = NULL; + + a = strdup("/etc/systemd/system.control"); + if (!a) + return -ENOMEM; + + b = strdup("/run/systemd/system.control"); + if (!b) + return -ENOMEM; + + *runtime = b; + b = NULL; + + break; + } + + case UNIT_FILE_USER: + r = user_config_dir(&a, "/systemd/system.control"); + if (r < 0) + return r; + + r = user_runtime_dir(runtime, "/systemd/system.control"); + if (r < 0) + return r; + + break; + + case UNIT_FILE_GLOBAL: + return -EOPNOTSUPP; + + default: + assert_not_reached("Hmm, unexpected scope value."); + } + + *persistent = a; + a = NULL; + + return 0; +} + +static int patch_root_prefix(char **p, const char *root_dir) { + char *c; + + assert(p); + + if (!*p) + return 0; + + c = prefix_root(root_dir, *p); + if (!c) + return -ENOMEM; + + free(*p); + *p = c; + + return 0; +} + +static int patch_root_prefix_strv(char **l, const char *root_dir) { + char **i; + int r; + + if (!root_dir) + return 0; + + STRV_FOREACH(i, l) { + r = patch_root_prefix(i, root_dir); + if (r < 0) + return r; + } + + return 0; } int lookup_paths_init( LookupPaths *p, - ManagerRunningAs running_as, - bool personal, - const char *root_dir, - const char *generator, - const char *generator_early, - const char *generator_late) { - - const char *e; + UnitFileScope scope, + LookupPathsFlags flags, + const char *root_dir) { + + _cleanup_free_ char + *root = NULL, + *persistent_config = NULL, *runtime_config = NULL, + *generator = NULL, *generator_early = NULL, *generator_late = NULL, + *transient = NULL, + *persistent_control = NULL, *runtime_control = NULL; bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */ + _cleanup_strv_free_ char **paths = NULL; + const char *e; int r; assert(p); + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + if (!isempty(root_dir) && !path_equal(root_dir, "/")) { + if (scope == UNIT_FILE_USER) + return -EINVAL; + + r = is_dir(root_dir, true); + if (r < 0) + return r; + if (r == 0) + return -ENOTDIR; - /* First priority is whatever has been passed to us via env - * vars */ + root = strdup(root_dir); + if (!root) + return -ENOMEM; + } + + r = acquire_config_dirs(scope, &persistent_config, &runtime_config); + if (r < 0 && r != -ENXIO) + return r; + + if ((flags & LOOKUP_PATHS_EXCLUDE_GENERATED) == 0) { + r = acquire_generator_dirs(scope, &generator, &generator_early, &generator_late); + if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO) + return r; + } + + r = acquire_transient_dir(scope, &transient); + if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO) + return r; + + r = acquire_control_dirs(scope, &persistent_control, &runtime_control); + if (r < 0 && r != -EOPNOTSUPP && r != -ENXIO) + return r; + + /* First priority is whatever has been passed to us via env vars */ e = getenv("SYSTEMD_UNIT_PATH"); if (e) { - if (endswith(e, ":")) { - e = strndupa(e, strlen(e) - 1); + const char *k; + + k = endswith(e, ":"); + if (k) { + e = strndupa(e, k - e); append = true; } /* FIXME: empty components in other places should be * rejected. */ - r = path_split_and_make_absolute(e, &p->unit_path); + r = path_split_and_make_absolute(e, &paths); if (r < 0) return r; - } else - p->unit_path = NULL; + } - if (!p->unit_path || append) { + if (!paths || append) { /* Let's figure something out. */ - _cleanup_strv_free_ char **unit_path; + _cleanup_strv_free_ char **add = NULL; /* For the user units we include share/ in the search * path in order to comply with the XDG basedir spec. @@ -279,17 +522,45 @@ int lookup_paths_init( * we include /lib in the search path for the system * stuff but avoid it for user stuff. */ - if (running_as == MANAGER_USER) { - if (personal) - unit_path = user_dirs(generator, generator_early, generator_late); - else - unit_path = strv_new( + switch (scope) { + + case UNIT_FILE_SYSTEM: + add = strv_new( + /* If you modify this you also want to modify + * systemdsystemunitpath= in systemd.pc.in! */ + STRV_IFNOTNULL(persistent_control), + STRV_IFNOTNULL(runtime_control), + STRV_IFNOTNULL(transient), + STRV_IFNOTNULL(generator_early), + persistent_config, + SYSTEM_CONFIG_UNIT_PATH, + "/etc/systemd/system", + runtime_config, + "/run/systemd/system", + STRV_IFNOTNULL(generator), + "/usr/local/lib/systemd/system", + SYSTEM_DATA_UNIT_PATH, + "/usr/lib/systemd/system", +#ifdef HAVE_SPLIT_USR + "/lib/systemd/system", +#endif + STRV_IFNOTNULL(generator_late), + NULL); + break; + + case UNIT_FILE_GLOBAL: + add = strv_new( /* If you modify this you also want to modify * systemduserunitpath= in systemd.pc.in, and * the arrays in user_dirs() above! */ + STRV_IFNOTNULL(persistent_control), + STRV_IFNOTNULL(runtime_control), + STRV_IFNOTNULL(transient), STRV_IFNOTNULL(generator_early), + persistent_config, USER_CONFIG_UNIT_PATH, "/etc/systemd/user", + runtime_config, "/run/systemd/user", STRV_IFNOTNULL(generator), "/usr/local/lib/systemd/user", @@ -299,143 +570,253 @@ int lookup_paths_init( "/usr/share/systemd/user", STRV_IFNOTNULL(generator_late), NULL); - } else - unit_path = strv_new( - /* If you modify this you also want to modify - * systemdsystemunitpath= in systemd.pc.in! */ - STRV_IFNOTNULL(generator_early), - SYSTEM_CONFIG_UNIT_PATH, - "/etc/systemd/system", - "/run/systemd/system", - STRV_IFNOTNULL(generator), - "/usr/local/lib/systemd/system", - SYSTEM_DATA_UNIT_PATH, - "/usr/lib/systemd/system", -#ifdef HAVE_SPLIT_USR - "/lib/systemd/system", -#endif - STRV_IFNOTNULL(generator_late), - NULL); + break; + + case UNIT_FILE_USER: + add = user_dirs(persistent_config, runtime_config, + generator, generator_early, generator_late, + transient, + persistent_config, runtime_control); + break; - if (!unit_path) + default: + assert_not_reached("Hmm, unexpected scope?"); + } + + if (!add) return -ENOMEM; - r = strv_extend_strv(&p->unit_path, unit_path, false); - if (r < 0) - return r; + if (paths) { + r = strv_extend_strv(&paths, add, true); + if (r < 0) + return r; + } else { + /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it, + * and don't have to copy anything */ + paths = add; + add = NULL; + } } - if (!path_strv_resolve_uniq(p->unit_path, root_dir)) + r = patch_root_prefix(&persistent_config, root); + if (r < 0) + return r; + r = patch_root_prefix(&runtime_config, root); + if (r < 0) + return r; + + r = patch_root_prefix(&generator, root); + if (r < 0) + return r; + r = patch_root_prefix(&generator_early, root); + if (r < 0) + return r; + r = patch_root_prefix(&generator_late, root); + if (r < 0) + return r; + + r = patch_root_prefix(&transient, root); + if (r < 0) + return r; + + r = patch_root_prefix(&persistent_control, root); + if (r < 0) + return r; + + r = patch_root_prefix(&runtime_control, root); + if (r < 0) + return r; + + r = patch_root_prefix_strv(paths, root); + if (r < 0) return -ENOMEM; - if (!strv_isempty(p->unit_path)) { - _cleanup_free_ char *t = strv_join(p->unit_path, "\n\t"); - if (!t) - return -ENOMEM; - log_debug("Looking for unit files in (higher priority first):\n\t%s", t); - } else { - log_debug("Ignoring unit files."); - p->unit_path = strv_free(p->unit_path); - } + p->search_path = strv_uniq(paths); + paths = NULL; - if (running_as == MANAGER_SYSTEM) { -#ifdef HAVE_SYSV_COMPAT - /* /etc/init.d/ compatibility does not matter to users */ + p->persistent_config = persistent_config; + p->runtime_config = runtime_config; + persistent_config = runtime_config = NULL; - e = getenv("SYSTEMD_SYSVINIT_PATH"); - if (e) { - r = path_split_and_make_absolute(e, &p->sysvinit_path); - if (r < 0) - return r; - } else - p->sysvinit_path = NULL; + p->generator = generator; + p->generator_early = generator_early; + p->generator_late = generator_late; + generator = generator_early = generator_late = NULL; - if (strv_isempty(p->sysvinit_path)) { - strv_free(p->sysvinit_path); + p->transient = transient; + transient = NULL; - p->sysvinit_path = strv_new( - SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */ - NULL); - if (!p->sysvinit_path) - return -ENOMEM; - } + p->persistent_control = persistent_control; + p->runtime_control = runtime_control; + persistent_control = runtime_control = NULL; - e = getenv("SYSTEMD_SYSVRCND_PATH"); - if (e) { - r = path_split_and_make_absolute(e, &p->sysvrcnd_path); - if (r < 0) - return r; - } else - p->sysvrcnd_path = NULL; + p->root_dir = root; + root = NULL; - if (strv_isempty(p->sysvrcnd_path)) { - strv_free(p->sysvrcnd_path); + return 0; +} - p->sysvrcnd_path = strv_new( - SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */ - NULL); - if (!p->sysvrcnd_path) - return -ENOMEM; +void lookup_paths_free(LookupPaths *p) { + if (!p) + return; + + p->search_path = strv_free(p->search_path); + + p->persistent_config = mfree(p->persistent_config); + p->runtime_config = mfree(p->runtime_config); + + p->generator = mfree(p->generator); + p->generator_early = mfree(p->generator_early); + p->generator_late = mfree(p->generator_late); + + p->transient = mfree(p->transient); + + p->persistent_control = mfree(p->persistent_control); + p->runtime_control = mfree(p->runtime_control); + + p->root_dir = mfree(p->root_dir); +} + +int lookup_paths_reduce(LookupPaths *p) { + _cleanup_free_ struct stat *stats = NULL; + size_t n_stats = 0, allocated = 0; + unsigned c = 0; + int r; + + assert(p); + + /* Drop duplicates and non-existing directories from the search path. We figure out whether two directories are + * the same by comparing their device and inode numbers. Note one special tweak: when we have a root path set, + * we do not follow symlinks when retrieving them, because the kernel wouldn't take the root prefix into + * account when following symlinks. When we have no root path set this restriction does not apply however. */ + + if (!p->search_path) + return 0; + + while (p->search_path[c]) { + struct stat st; + unsigned k; + + if (p->root_dir) + r = lstat(p->search_path[c], &st); + else + r = stat(p->search_path[c], &st); + if (r < 0) { + if (errno == ENOENT) + goto remove_item; + + /* If something we don't grok happened, let's better leave it in. */ + log_debug_errno(errno, "Failed to stat %s: %m", p->search_path[c]); + c++; + continue; } - if (!path_strv_resolve_uniq(p->sysvinit_path, root_dir)) - return -ENOMEM; + for (k = 0; k < n_stats; k++) { + if (stats[k].st_dev == st.st_dev && + stats[k].st_ino == st.st_ino) + break; + } + + if (k < n_stats) /* Is there already an entry with the same device/inode? */ + goto remove_item; - if (!path_strv_resolve_uniq(p->sysvrcnd_path, root_dir)) + if (!GREEDY_REALLOC(stats, allocated, n_stats+1)) return -ENOMEM; - if (!strv_isempty(p->sysvinit_path)) { - _cleanup_free_ char *t = strv_join(p->sysvinit_path, "\n\t"); - if (!t) - return -ENOMEM; - log_debug("Looking for SysV init scripts in:\n\t%s", t); - } else { - log_debug("Ignoring SysV init scripts."); - p->sysvinit_path = strv_free(p->sysvinit_path); - } + stats[n_stats++] = st; + c++; + continue; - if (!strv_isempty(p->sysvrcnd_path)) { - _cleanup_free_ char *t = - strv_join(p->sysvrcnd_path, "\n\t"); - if (!t) - return -ENOMEM; + remove_item: + free(p->search_path[c]); + memmove(p->search_path + c, + p->search_path + c + 1, + (strv_length(p->search_path + c + 1) + 1) * sizeof(char*)); + } - log_debug("Looking for SysV rcN.d links in:\n\t%s", t); - } else { - log_debug("Ignoring SysV rcN.d links."); - p->sysvrcnd_path = strv_free(p->sysvrcnd_path); - } -#else - log_debug("SysV init scripts and rcN.d links support disabled"); -#endif + if (strv_isempty(p->search_path)) { + log_debug("Ignoring unit files."); + p->search_path = strv_free(p->search_path); + } else { + _cleanup_free_ char *t; + + t = strv_join(p->search_path, "\n\t"); + if (!t) + return -ENOMEM; + + log_debug("Looking for unit files in (higher priority first):\n\t%s", t); } return 0; } -void lookup_paths_free(LookupPaths *p) { +int lookup_paths_mkdir_generator(LookupPaths *p) { + int r, q; + assert(p); - p->unit_path = strv_free(p->unit_path); + if (!p->generator || !p->generator_early || !p->generator_late) + return -EINVAL; -#ifdef HAVE_SYSV_COMPAT - p->sysvinit_path = strv_free(p->sysvinit_path); - p->sysvrcnd_path = strv_free(p->sysvrcnd_path); -#endif + r = mkdir_p_label(p->generator, 0755); + + q = mkdir_p_label(p->generator_early, 0755); + if (q < 0 && r >= 0) + r = q; + + q = mkdir_p_label(p->generator_late, 0755); + if (q < 0 && r >= 0) + r = q; + + return r; } -int lookup_paths_init_from_scope(LookupPaths *paths, - UnitFileScope scope, - const char *root_dir) { - assert(paths); - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); +void lookup_paths_trim_generator(LookupPaths *p) { + assert(p); - zero(*paths); + /* Trim empty dirs */ + + if (p->generator) + (void) rmdir(p->generator); + if (p->generator_early) + (void) rmdir(p->generator_early); + if (p->generator_late) + (void) rmdir(p->generator_late); +} + +void lookup_paths_flush_generator(LookupPaths *p) { + assert(p); - return lookup_paths_init(paths, - scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER, - scope == UNIT_FILE_USER, - root_dir, - NULL, NULL, NULL); + /* Flush the generated unit files in full */ + + if (p->generator) + (void) rm_rf(p->generator, REMOVE_ROOT); + if (p->generator_early) + (void) rm_rf(p->generator_early, REMOVE_ROOT); + if (p->generator_late) + (void) rm_rf(p->generator_late, REMOVE_ROOT); +} + +char **generator_binary_paths(UnitFileScope scope) { + + switch (scope) { + + case UNIT_FILE_SYSTEM: + return strv_new("/run/systemd/system-generators", + "/etc/systemd/system-generators", + "/usr/local/lib/systemd/system-generators", + SYSTEM_GENERATOR_PATH, + NULL); + + case UNIT_FILE_GLOBAL: + case UNIT_FILE_USER: + return strv_new("/run/systemd/user-generators", + "/etc/systemd/user-generators", + "/usr/local/lib/systemd/user-generators", + USER_GENERATOR_PATH, + NULL); + + default: + assert_not_reached("Hmm, unexpected scope."); + } } diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h index 26c83d6111..f9bb2fe237 100644 --- a/src/shared/path-lookup.h +++ b/src/shared/path-lookup.h @@ -20,41 +20,57 @@ ***/ #include <stdbool.h> -#include "macro.h" -typedef struct LookupPaths { - char **unit_path; -#ifdef HAVE_SYSV_COMPAT - char **sysvinit_path; - char **sysvrcnd_path; -#endif -} LookupPaths; - -typedef enum ManagerRunningAs { - MANAGER_SYSTEM, - MANAGER_USER, - _MANAGER_RUNNING_AS_MAX, - _MANAGER_RUNNING_AS_INVALID = -1 -} ManagerRunningAs; - -int user_config_home(char **config_home); -int user_runtime_dir(char **runtime_dir); - -char **generator_paths(ManagerRunningAs running_as); - -int lookup_paths_init(LookupPaths *p, - ManagerRunningAs running_as, - bool personal, - const char *root_dir, - const char *generator, - const char *generator_early, - const char *generator_late); +typedef struct LookupPaths LookupPaths; #include "install.h" +#include "macro.h" + +typedef enum LookupPathsFlags { + LOOKUP_PATHS_EXCLUDE_GENERATED = 1, +} LookupPathsFlags; + +struct LookupPaths { + /* Where we look for unit files. This includes the individual special paths below, but also any vendor + * supplied, static unit file paths. */ + char **search_path; + + /* Where we shall create or remove our installation symlinks, aka "configuration", and where the user/admin + * shall place his own unit files. */ + char *persistent_config; + char *runtime_config; + + /* Where to place generated unit files (i.e. those a "generator" tool generated). Note the special semantics of + * this directory: the generators are flushed each time a "systemctl daemon-reload" is issued. The user should + * not alter these directories directly. */ + char *generator; + char *generator_early; + char *generator_late; -int lookup_paths_init_from_scope(LookupPaths *paths, - UnitFileScope scope, - const char *root_dir); + /* Where to place transient unit files (i.e. those created dynamically via the bus API). Note the special + * semantics of this directory: all units created transiently have their unit files removed as the transient + * unit is unloaded. The user should not alter this directory directly. */ + char *transient; + + /* Where the snippets created by "systemctl set-property" are placed. Note that for transient units, the + * snippets are placed in the transient directory though (see above). The user should not alter this directory + * directly. */ + char *persistent_control; + char *runtime_control; + + /* The root directory prepended to all items above, or NULL */ + char *root_dir; +}; + +int lookup_paths_init(LookupPaths *p, UnitFileScope scope, LookupPathsFlags flags, const char *root_dir); + +int lookup_paths_reduce(LookupPaths *p); + +int lookup_paths_mkdir_generator(LookupPaths *p); +void lookup_paths_trim_generator(LookupPaths *p); +void lookup_paths_flush_generator(LookupPaths *p); void lookup_paths_free(LookupPaths *p); #define _cleanup_lookup_paths_free_ _cleanup_(lookup_paths_free) + +char **generator_binary_paths(UnitFileScope scope); diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c index 061d31f4de..02c03b98d8 100644 --- a/src/shared/ptyfwd.c +++ b/src/shared/ptyfwd.c @@ -461,10 +461,7 @@ int pty_forward_set_ignore_vhangup(PTYForward *f, bool b) { if (!!(f->flags & PTY_FORWARD_IGNORE_VHANGUP) == b) return 0; - if (b) - f->flags |= PTY_FORWARD_IGNORE_VHANGUP; - else - f->flags &= ~PTY_FORWARD_IGNORE_VHANGUP; + SET_FLAG(f->flags, PTY_FORWARD_IGNORE_VHANGUP, b); if (!ignore_vhangup(f)) { diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index a0aef66bc8..f00624d0f2 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -28,6 +28,7 @@ #include "alloc-util.h" #include "conf-parser.h" #include "def.h" +#include "env-util.h" #include "fd-util.h" #include "fileio.h" #include "log.h" @@ -37,7 +38,7 @@ #include "string-util.h" #include "strv.h" -#define USE(x, y) do{ (x) = (y); (y) = NULL; } while(0) +#define USE(x, y) do { (x) = (y); (y) = NULL; } while (0) int parse_sleep_config(const char *verb, char ***_modes, char ***_states) { @@ -231,6 +232,9 @@ static bool enough_memory_for_hibernation(void) { size_t size = 0, used = 0; int r; + if (getenv_bool("SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK") > 0) + return true; + r = hibernation_partition_size(&size, &used); if (r < 0) return false; diff --git a/src/shared/sleep-config.h b/src/shared/sleep-config.h index 51f4621844..ad10039ff4 100644 --- a/src/shared/sleep-config.h +++ b/src/shared/sleep-config.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - int parse_sleep_config(const char *verb, char ***modes, char ***states); int can_sleep(const char *verb); diff --git a/src/shared/spawn-polkit-agent.c b/src/shared/spawn-polkit-agent.c index cf3c8ad5a3..7dae4d14fe 100644 --- a/src/shared/spawn-polkit-agent.c +++ b/src/shared/spawn-polkit-agent.c @@ -44,6 +44,10 @@ int polkit_agent_open(void) { if (agent_pid > 0) return 0; + /* Clients that run as root don't need to activate/query polkit */ + if (geteuid() == 0) + return 0; + /* We check STDIN here, not STDOUT, since this is about input, * not output */ if (!isatty(STDIN_FILENO)) diff --git a/src/bootchart/store.h b/src/shared/tests.c index 6e9acf2a6f..409116290d 100644 --- a/src/bootchart/store.h +++ b/src/shared/tests.c @@ -1,12 +1,7 @@ -#pragma once - /*** This file is part of systemd. - Copyright (C) 2009-2013 Intel Corporation - - Authors: - Auke Kok <auke-jan.h.kok@intel.com> + Copyright 2016 Lennart Poettering systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -22,15 +17,17 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <dirent.h> +#include <stdlib.h> +#include <util.h> + +#include "tests.h" + +char* setup_fake_runtime_dir(void) { + char t[] = "/tmp/fake-xdg-runtime-XXXXXX", *p; -#include "bootchart.h" + assert_se(mkdtemp(t)); + assert_se(setenv("XDG_RUNTIME_DIR", t, 1) >= 0); + assert_se(p = strdup(t)); -double gettime_ns(void); -void log_uptime(void); -int log_sample(DIR *proc, - int sample, - struct ps_struct *ps_first, - struct list_sample_data **ptr, - int *pscount, - int *cpus); + return p; +} diff --git a/src/import/aufs-util.h b/src/shared/tests.h index e474a50897..93f09013a1 100644 --- a/src/import/aufs-util.h +++ b/src/shared/tests.h @@ -3,7 +3,7 @@ /*** This file is part of systemd. - Copyright 2014 Lennart Poettering + Copyright 2016 Lennart Poettering systemd is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -19,4 +19,4 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -int aufs_resolve(const char *path); +char* setup_fake_runtime_dir(void); diff --git a/src/shared/uid-range.c b/src/shared/uid-range.c index eb251492c3..b6ec474390 100644 --- a/src/shared/uid-range.c +++ b/src/shared/uid-range.c @@ -54,7 +54,7 @@ static void uid_range_coalesce(UidRange **p, unsigned *n) { if (*n > j+1) memmove(y, y+1, sizeof(UidRange) * (*n - j -1)); - (*n) --; + (*n)--; j--; } } diff --git a/src/socket-proxy/socket-proxyd.c b/src/socket-proxy/socket-proxyd.c index 99d4b62139..52b4db8875 100644 --- a/src/socket-proxy/socket-proxyd.c +++ b/src/socket-proxy/socket-proxyd.c @@ -400,34 +400,25 @@ static int resolve_remote(Connection *c) { union sockaddr_union sa = {}; const char *node, *service; - socklen_t salen; int r; if (path_is_absolute(arg_remote_host)) { sa.un.sun_family = AF_UNIX; - strncpy(sa.un.sun_path, arg_remote_host, sizeof(sa.un.sun_path)-1); - sa.un.sun_path[sizeof(sa.un.sun_path)-1] = 0; - - salen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path); - - return connection_start(c, &sa.sa, salen); + strncpy(sa.un.sun_path, arg_remote_host, sizeof(sa.un.sun_path)); + return connection_start(c, &sa.sa, SOCKADDR_UN_LEN(sa.un)); } if (arg_remote_host[0] == '@') { sa.un.sun_family = AF_UNIX; sa.un.sun_path[0] = 0; - strncpy(sa.un.sun_path+1, arg_remote_host+1, sizeof(sa.un.sun_path)-2); - sa.un.sun_path[sizeof(sa.un.sun_path)-1] = 0; - - salen = offsetof(union sockaddr_union, un.sun_path) + 1 + strlen(sa.un.sun_path + 1); - - return connection_start(c, &sa.sa, salen); + strncpy(sa.un.sun_path+1, arg_remote_host+1, sizeof(sa.un.sun_path)-1); + return connection_start(c, &sa.sa, SOCKADDR_UN_LEN(sa.un)); } service = strrchr(arg_remote_host, ':'); if (service) { node = strndupa(arg_remote_host, service - arg_remote_host); - service ++; + service++; } else { node = arg_remote_host; service = "80"; diff --git a/src/stdio-bridge/stdio-bridge.c b/src/stdio-bridge/stdio-bridge.c new file mode 100644 index 0000000000..ce8efce3d5 --- /dev/null +++ b/src/stdio-bridge/stdio-bridge.c @@ -0,0 +1,302 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <errno.h> +#include <getopt.h> +#include <poll.h> +#include <stddef.h> +#include <string.h> +#include <unistd.h> + +#include "sd-bus.h" +#include "sd-daemon.h" + +#include "bus-internal.h" +#include "bus-util.h" +#include "build.h" +#include "log.h" +#include "util.h" + +#define DEFAULT_BUS_PATH "unix:path=/run/dbus/system_bus_socket" + +const char *arg_bus_path = DEFAULT_BUS_PATH; + +static int help(void) { + + printf("%s [OPTIONS...]\n\n" + "STDIO or socket-activatable proxy to a given DBus endpoint.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --bus-path=PATH Path to the kernel bus (default: %s)\n", + program_invocation_short_name, DEFAULT_BUS_PATH); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "bus-path", required_argument, NULL, 'p' }, + { NULL, 0, NULL, 0 } + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hsup:", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + return version(); + + case '?': + return -EINVAL; + + case 'p': + arg_bus_path = optarg; + break; + + default: + log_error("Unknown option code %c", c); + return -EINVAL; + } + } + + return 1; +} + +int main(int argc, char *argv[]) { + _cleanup_(sd_bus_unrefp) sd_bus *a = NULL, *b = NULL; + sd_id128_t server_id; + bool is_unix; + int r, in_fd, out_fd; + + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + r = sd_listen_fds(0); + if (r == 0) { + in_fd = STDIN_FILENO; + out_fd = STDOUT_FILENO; + } else if (r == 1) { + in_fd = SD_LISTEN_FDS_START; + out_fd = SD_LISTEN_FDS_START; + } else { + log_error("Illegal number of file descriptors passed\n"); + goto finish; + } + + is_unix = + sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 && + sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0; + + r = sd_bus_new(&a); + if (r < 0) { + log_error_errno(r, "Failed to allocate bus: %m"); + goto finish; + } + + r = sd_bus_set_address(a, arg_bus_path); + if (r < 0) { + log_error_errno(r, "Failed to set address to connect to: %m"); + goto finish; + } + + r = sd_bus_negotiate_fds(a, is_unix); + if (r < 0) { + log_error_errno(r, "Failed to set FD negotiation: %m"); + goto finish; + } + + r = sd_bus_start(a); + if (r < 0) { + log_error_errno(r, "Failed to start bus client: %m"); + goto finish; + } + + r = sd_bus_get_bus_id(a, &server_id); + if (r < 0) { + log_error_errno(r, "Failed to get server ID: %m"); + goto finish; + } + + r = sd_bus_new(&b); + if (r < 0) { + log_error_errno(r, "Failed to allocate bus: %m"); + goto finish; + } + + r = sd_bus_set_fd(b, in_fd, out_fd); + if (r < 0) { + log_error_errno(r, "Failed to set fds: %m"); + goto finish; + } + + r = sd_bus_set_server(b, 1, server_id); + if (r < 0) { + log_error_errno(r, "Failed to set server mode: %m"); + goto finish; + } + + r = sd_bus_negotiate_fds(b, is_unix); + if (r < 0) { + log_error_errno(r, "Failed to set FD negotiation: %m"); + goto finish; + } + + r = sd_bus_set_anonymous(b, true); + if (r < 0) { + log_error_errno(r, "Failed to set anonymous authentication: %m"); + goto finish; + } + + r = sd_bus_start(b); + if (r < 0) { + log_error_errno(r, "Failed to start bus client: %m"); + goto finish; + } + + for (;;) { + _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; + + r = sd_bus_process(a, &m); + if (r < 0) { + log_error_errno(r, "Failed to process bus a: %m"); + goto finish; + } + + if (m) { + r = sd_bus_send(b, m, NULL); + if (r < 0) { + log_error_errno(r, "Failed to send message: %m"); + goto finish; + } + } + + if (r > 0) + continue; + + r = sd_bus_process(b, &m); + if (r < 0) { + /* treat 'connection reset by peer' as clean exit condition */ + if (r == -ECONNRESET) + r = 0; + + goto finish; + } + + if (m) { + r = sd_bus_send(a, m, NULL); + if (r < 0) { + log_error_errno(r, "Failed to send message: %m"); + goto finish; + } + } + + if (r > 0) + continue; + + fd = sd_bus_get_fd(a); + if (fd < 0) { + r = fd; + log_error_errno(r, "Failed to get fd: %m"); + goto finish; + } + + events_a = sd_bus_get_events(a); + if (events_a < 0) { + r = events_a; + log_error_errno(r, "Failed to get events mask: %m"); + goto finish; + } + + r = sd_bus_get_timeout(a, &timeout_a); + if (r < 0) { + log_error_errno(r, "Failed to get timeout: %m"); + goto finish; + } + + events_b = sd_bus_get_events(b); + if (events_b < 0) { + r = events_b; + log_error_errno(r, "Failed to get events mask: %m"); + goto finish; + } + + r = sd_bus_get_timeout(b, &timeout_b); + if (r < 0) { + log_error_errno(r, "Failed to get timeout: %m"); + goto finish; + } + + t = timeout_a; + if (t == (uint64_t) -1 || (timeout_b != (uint64_t) -1 && timeout_b < timeout_a)) + t = timeout_b; + + if (t == (uint64_t) -1) + ts = NULL; + else { + usec_t nw; + + nw = now(CLOCK_MONOTONIC); + if (t > nw) + t -= nw; + else + t = 0; + + ts = timespec_store(&_ts, t); + } + + { + struct pollfd p[3] = { + {.fd = fd, .events = events_a, }, + {.fd = STDIN_FILENO, .events = events_b & POLLIN, }, + {.fd = STDOUT_FILENO, .events = events_b & POLLOUT, }}; + + r = ppoll(p, ELEMENTSOF(p), ts, NULL); + } + if (r < 0) { + log_error("ppoll() failed: %m"); + goto finish; + } + } + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 908ccabf8a..b943c68e1b 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -39,6 +39,7 @@ #include "bus-common-errors.h" #include "bus-error.h" #include "bus-message.h" +#include "bus-unit-util.h" #include "bus-util.h" #include "cgroup-show.h" #include "cgroup-util.h" @@ -101,8 +102,10 @@ static bool arg_no_block = false; static bool arg_no_legend = false; static bool arg_no_pager = false; static bool arg_no_wtmp = false; +static bool arg_no_sync = false; static bool arg_no_wall = false; static bool arg_no_reload = false; +static bool arg_value = false; static bool arg_show_types = false; static bool arg_ignore_inhibitors = false; static bool arg_dry = false; @@ -152,7 +155,7 @@ static bool arg_now = false; static int daemon_reload(int argc, char *argv[], void* userdata); static int halt_now(enum action a); -static int check_one_unit(sd_bus *bus, const char *name, const char *good_states, bool quiet); +static int get_state_one_unit(sd_bus *bus, const char *name, UnitActiveState *active_state); static bool original_stdout_is_tty; @@ -200,14 +203,6 @@ static void release_busses(void) { busses[w] = sd_bus_flush_close_unref(busses[w]); } -static void pager_open_if_enabled(void) { - - if (arg_no_pager) - return; - - pager_open(false); -} - static void ask_password_agent_open_if_enabled(void) { /* Open the password agent as a child process if necessary */ @@ -331,6 +326,8 @@ static int compare_unit_info(const void *a, const void *b) { } static bool output_show_unit(const UnitInfo *u, char **patterns) { + assert(u); + if (!strv_fnmatch_or_empty(patterns, u->id, FNM_NOESCAPE)) return false; @@ -348,10 +345,21 @@ static bool output_show_unit(const UnitInfo *u, char **patterns) { if (arg_all) return true; + /* Note that '--all' is not purely a state filter, but also a + * filter that hides units that "follow" other units (which is + * used for device units that appear under different names). */ + if (!isempty(u->following)) + return false; + + if (!strv_isempty(arg_states)) + return true; + + /* By default show all units except the ones in inactive + * state and with no pending job */ if (u->job_id > 0) return true; - if (streq(u->active_state, "inactive") || u->following[0]) + if (streq(u->active_state, "inactive")) return false; return true; @@ -476,7 +484,7 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) { } if (circle_len > 0) - printf("%s%s%s ", on_circle, circle ? draw_special_char(DRAW_BLACK_CIRCLE) : " ", off_circle); + printf("%s%s%s ", on_circle, circle ? special_glyph(BLACK_CIRCLE) : " ", off_circle); printf("%s%-*s%s %s%-*s%s %s%-*s %-*s%s %-*s", on_active, id_len, id, off_active, @@ -534,6 +542,7 @@ static int get_unit_list( size_t size = c; int r; UnitInfo u; + bool fallback = false; assert(bus); assert(unit_infos); @@ -545,8 +554,7 @@ static int get_unit_list( "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - "ListUnitsFiltered"); - + "ListUnitsByPatterns"); if (r < 0) return bus_log_create_error(r); @@ -554,7 +562,34 @@ static int get_unit_list( if (r < 0) return bus_log_create_error(r); + r = sd_bus_message_append_strv(m, patterns); + if (r < 0) + return bus_log_create_error(r); + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) { + /* Fallback to legacy ListUnitsFiltered method */ + fallback = true; + log_debug_errno(r, "Failed to list units: %s Falling back to ListUnitsFiltered method.", bus_error_message(&error, r)); + m = sd_bus_message_unref(m); + sd_bus_error_free(&error); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListUnitsFiltered"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, arg_states); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); + } if (r < 0) return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r)); @@ -565,7 +600,7 @@ static int get_unit_list( while ((r = bus_parse_unit_info(reply, &u)) > 0) { u.machine = machine; - if (!output_show_unit(&u, patterns)) + if (!output_show_unit(&u, fallback ? patterns : NULL)) continue; if (!GREEDY_REALLOC(*unit_infos, size, c+1)) @@ -678,7 +713,7 @@ static int list_units(int argc, char *argv[], void *userdata) { sd_bus *bus; int r; - pager_open_if_enabled(); + pager_open(arg_no_pager, false); r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) @@ -890,7 +925,7 @@ static int list_sockets(int argc, char *argv[], void *userdata) { int r = 0, n; sd_bus *bus; - pager_open_if_enabled(); + pager_open(arg_no_pager, false); r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) @@ -1197,7 +1232,7 @@ static int list_timers(int argc, char *argv[], void *userdata) { sd_bus *bus; int r = 0; - pager_open_if_enabled(); + pager_open(arg_no_pager, false); r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) @@ -1275,7 +1310,9 @@ static int compare_unit_file_list(const void *a, const void *b) { return strcasecmp(basename(u->path), basename(v->path)); } -static bool output_show_unit_file(const UnitFileList *u, char **patterns) { +static bool output_show_unit_file(const UnitFileList *u, char **states, char **patterns) { + assert(u); + if (!strv_fnmatch_or_empty(patterns, basename(u->path), FNM_NOESCAPE)) return false; @@ -1290,8 +1327,8 @@ static bool output_show_unit_file(const UnitFileList *u, char **patterns) { return false; } - if (!strv_isempty(arg_states) && - !strv_find(arg_states, unit_file_state_to_string(u->state))) + if (!strv_isempty(states) && + !strv_find(states, unit_file_state_to_string(u->state))) return false; return true; @@ -1319,7 +1356,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { } else id_cols = max_id_len; - if (!arg_no_legend) + if (!arg_no_legend && c > 0) printf("%-*s %-*s\n", id_cols, "UNIT FILE", state_cols, "STATE"); @@ -1364,8 +1401,9 @@ static int list_unit_files(int argc, char *argv[], void *userdata) { const char *state; char *path; int r; + bool fallback = false; - pager_open_if_enabled(); + pager_open(arg_no_pager, false); if (install_client_side()) { Hashmap *h; @@ -1377,7 +1415,7 @@ static int list_unit_files(int argc, char *argv[], void *userdata) { if (!h) return log_oom(); - r = unit_file_get_list(arg_scope, arg_root, h); + r = unit_file_get_list(arg_scope, arg_root, h, arg_states, strv_skip(argv, 1)); if (r < 0) { unit_file_list_free(h); return log_error_errno(r, "Failed to get unit file list: %m"); @@ -1385,14 +1423,14 @@ static int list_unit_files(int argc, char *argv[], void *userdata) { n_units = hashmap_size(h); - units = new(UnitFileList, n_units); - if (!units && n_units > 0) { + units = new(UnitFileList, n_units ?: 1); /* avoid malloc(0) */ + if (!units) { unit_file_list_free(h); return log_oom(); } HASHMAP_FOREACH(u, h, i) { - if (!output_show_unit_file(u, strv_skip(argv, 1))) + if (!output_show_unit_file(u, NULL, NULL)) continue; units[c++] = *u; @@ -1402,6 +1440,7 @@ static int list_unit_files(int argc, char *argv[], void *userdata) { assert(c <= n_units); hashmap_free(h); } else { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus; @@ -1409,15 +1448,44 @@ static int list_unit_files(int argc, char *argv[], void *userdata) { if (r < 0) return r; - r = sd_bus_call_method( + r = sd_bus_message_new_method_call( bus, + &m, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - "ListUnitFiles", - &error, - &reply, - NULL); + "ListUnitFilesByPatterns"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, arg_states); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, strv_skip(argv, 1)); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) { + /* Fallback to legacy ListUnitFiles method */ + fallback = true; + log_debug_errno(r, "Failed to list unit files: %s Falling back to ListUnitsFiles method.", bus_error_message(&error, r)); + m = sd_bus_message_unref(m); + sd_bus_error_free(&error); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListUnitFiles"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); + } if (r < 0) return log_error_errno(r, "Failed to list unit files: %s", bus_error_message(&error, r)); @@ -1435,8 +1503,10 @@ static int list_unit_files(int argc, char *argv[], void *userdata) { unit_file_state_from_string(state) }; - if (output_show_unit_file(&units[c], strv_skip(argv, 1))) - c ++; + if (output_show_unit_file(&units[c], + fallback ? arg_states : NULL, + fallback ? strv_skip(argv, 1) : NULL)) + c++; } if (r < 0) @@ -1450,10 +1520,9 @@ static int list_unit_files(int argc, char *argv[], void *userdata) { qsort_safe(units, c, sizeof(UnitFileList), compare_unit_file_list); output_unit_file_list(units, c); - if (install_client_side()) { + if (install_client_side()) for (unit = units; unit < units + c; unit++) free(unit->path); - } return 0; } @@ -1472,7 +1541,7 @@ static int list_dependencies_print(const char *name, int level, unsigned int bra printf("%s...\n",max_len % 2 ? "" : " "); return 0; } - printf("%s", draw_special_char(branches & (1 << i) ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE)); + printf("%s", special_glyph(branches & (1 << i) ? TREE_VERTICAL : TREE_SPACE)); } len += 2; @@ -1481,10 +1550,10 @@ static int list_dependencies_print(const char *name, int level, unsigned int bra return 0; } - printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH)); + printf("%s", special_glyph(last ? TREE_RIGHT : TREE_BRANCH)); } - if (arg_full){ + if (arg_full) { printf("%s\n", name); return 0; } @@ -1638,12 +1707,29 @@ static int list_dependencies_one( if (arg_plain) printf(" "); else { - int state; + UnitActiveState active_state = _UNIT_ACTIVE_STATE_INVALID; const char *on; - state = check_one_unit(bus, *c, "activating\0active\0reloading\0", true); - on = state > 0 ? ansi_highlight_green() : ansi_highlight_red(); - printf("%s%s%s ", on, draw_special_char(DRAW_BLACK_CIRCLE), ansi_normal()); + (void) get_state_one_unit(bus, *c, &active_state); + + switch (active_state) { + case UNIT_ACTIVE: + case UNIT_RELOADING: + case UNIT_ACTIVATING: + on = ansi_highlight_green(); + break; + + case UNIT_INACTIVE: + case UNIT_DEACTIVATING: + on = ansi_normal(); + break; + + default: + on = ansi_highlight_red(); + break; + } + + printf("%s%s%s ", on, special_glyph(BLACK_CIRCLE), ansi_normal()); } r = list_dependencies_print(*c, level, branches, c[1] == NULL); @@ -1679,7 +1765,7 @@ static int list_dependencies(int argc, char *argv[], void *userdata) { } else u = SPECIAL_DEFAULT_TARGET; - pager_open_if_enabled(); + pager_open(arg_no_pager, false); r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) @@ -1880,16 +1966,16 @@ static void output_machines_list(struct machine_info *machine_infos, unsigned n) on_failed = off_failed = ""; if (circle_len > 0) - printf("%s%s%s ", on_state, circle ? draw_special_char(DRAW_BLACK_CIRCLE) : " ", off_state); + printf("%s%s%s ", on_state, circle ? special_glyph(BLACK_CIRCLE) : " ", off_state); if (m->is_host) - printf("%-*s (host) %s%-*s%s %s%*u%s %*u\n", + printf("%-*s (host) %s%-*s%s %s%*" PRIu32 "%s %*" PRIu32 "\n", (int) (namelen - (sizeof(" (host)")-1)), strna(m->name), on_state, statelen, strna(m->state), off_state, on_failed, failedlen, m->n_failed_units, off_failed, jobslen, m->n_jobs); else - printf("%-*s %s%-*s%s %s%*u%s %*u\n", + printf("%-*s %s%-*s%s %s%*" PRIu32 "%s %*" PRIu32 "\n", namelen, strna(m->name), on_state, statelen, strna(m->state), off_state, on_failed, failedlen, m->n_failed_units, off_failed, @@ -1910,7 +1996,7 @@ static int list_machines(int argc, char *argv[], void *userdata) { return -EPERM; } - pager_open_if_enabled(); + pager_open(arg_no_pager, false); r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) @@ -1970,19 +2056,6 @@ static int get_default(int argc, char *argv[], void *userdata) { return 0; } -static void dump_unit_file_changes(const UnitFileChange *changes, unsigned n_changes) { - unsigned i; - - assert(changes || n_changes == 0); - - for (i = 0; i < n_changes; i++) { - if (changes[i].type == UNIT_FILE_SYMLINK) - log_info("Created symlink %s, pointing to %s.", changes[i].path, changes[i].source); - else - log_info("Removed symlink %s.", changes[i].path); - } -} - static int set_default(int argc, char *argv[], void *userdata) { _cleanup_free_ char *unit = NULL; int r; @@ -1999,14 +2072,9 @@ static int set_default(int argc, char *argv[], void *userdata) { unsigned n_changes = 0; r = unit_file_set_default(arg_scope, arg_root, unit, true, &changes, &n_changes); - if (r < 0) - return log_error_errno(r, "Failed to set default target: %m"); - - if (!arg_quiet) - dump_unit_file_changes(changes, n_changes); - + unit_file_dump_changes(r, "set default", changes, n_changes, arg_quiet); unit_file_changes_free(changes, n_changes); - r = 0; + return r; } else { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; @@ -2067,7 +2135,7 @@ static void output_jobs_list(const struct job_info* jobs, unsigned n, bool skipp return; } - pager_open_if_enabled(); + pager_open(arg_no_pager, false); id_len = strlen("JOB"); unit_len = strlen("UNIT"); @@ -2137,7 +2205,7 @@ static int list_jobs(int argc, char *argv[], void *userdata) { int r; bool skipped = false; - pager_open_if_enabled(); + pager_open(arg_no_pager, false); r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) @@ -2267,6 +2335,8 @@ static int need_daemon_reload(sd_bus *bus, const char *unit) { } static void warn_unit_file_changed(const char *name) { + assert(name); + log_warning("%sWarning:%s %s changed on disk. Run 'systemctl%s daemon-reload' to reload units.", ansi_highlight_red(), ansi_normal(), @@ -2281,7 +2351,7 @@ static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **un assert(unit_name); assert(unit_path); - STRV_FOREACH(p, lp->unit_path) { + STRV_FOREACH(p, lp->search_path) { _cleanup_free_ char *path; path = path_join(arg_root, *p, unit_name); @@ -2381,7 +2451,7 @@ static int unit_find_paths( } if (dropin_paths) { - r = unit_file_find_dropin_paths(lp->unit_path, NULL, names, &dropins); + r = unit_file_find_dropin_paths(lp->search_path, NULL, names, &dropins); if (r < 0) return r; } @@ -2407,18 +2477,19 @@ static int unit_find_paths( return r; } -static int check_one_unit(sd_bus *bus, const char *name, const char *good_states, bool quiet) { +static int get_state_one_unit(sd_bus *bus, const char *name, UnitActiveState *active_state) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_free_ char *buf = NULL; - const char *path, *state; + UnitActiveState state; + const char *path; int r; assert(name); + assert(active_state); /* We don't use unit_dbus_path_from_name() directly since we don't want to load the unit unnecessarily, if it * isn't loaded. */ - r = sd_bus_call_method( bus, "org.freedesktop.systemd1", @@ -2434,7 +2505,7 @@ static int check_one_unit(sd_bus *bus, const char *name, const char *good_states /* The unit is currently not loaded, hence say it's "inactive", since all units that aren't loaded are * considered inactive. */ - state = "inactive"; + state = UNIT_INACTIVE; } else { r = sd_bus_message_read(reply, "o", &path); @@ -2452,13 +2523,15 @@ static int check_one_unit(sd_bus *bus, const char *name, const char *good_states if (r < 0) return log_error_errno(r, "Failed to retrieve unit state: %s", bus_error_message(&error, r)); - state = buf; + state = unit_active_state_from_string(buf); + if (state == _UNIT_ACTIVE_STATE_INVALID) { + log_error("Invalid unit state '%s' for: %s", buf, name); + return -EINVAL; + } } - if (!quiet) - puts(state); - - return nulstr_contains(good_states, state); + *active_state = state; + return 0; } static int check_triggering_units( @@ -2466,9 +2539,10 @@ static int check_triggering_units( const char *name) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_free_ char *path = NULL, *n = NULL, *state = NULL; + _cleanup_free_ char *path = NULL, *n = NULL, *load_state = NULL; _cleanup_strv_free_ char **triggered_by = NULL; bool print_warning_label = true; + UnitActiveState active_state; char **i; int r; @@ -2487,11 +2561,11 @@ static int check_triggering_units( "org.freedesktop.systemd1.Unit", "LoadState", &error, - &state); + &load_state); if (r < 0) return log_error_errno(r, "Failed to get load state of %s: %s", n, bus_error_message(&error, r)); - if (streq(state, "masked")) + if (streq(load_state, "masked")) return 0; r = sd_bus_get_property_strv( @@ -2506,11 +2580,11 @@ static int check_triggering_units( return log_error_errno(r, "Failed to get triggered by array of %s: %s", n, bus_error_message(&error, r)); STRV_FOREACH(i, triggered_by) { - r = check_one_unit(bus, *i, "active\0reloading\0", true); + r = get_state_one_unit(bus, *i, &active_state); if (r < 0) - return log_error_errno(r, "Failed to check unit: %m"); + return r; - if (r == 0) + if (!IN_SET(active_state, UNIT_ACTIVE, UNIT_RELOADING)) continue; if (print_warning_label) { @@ -2604,7 +2678,10 @@ static int start_unit_one( if (!sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) && !sd_bus_error_has_name(error, BUS_ERROR_UNIT_MASKED)) - log_error("See system logs and 'systemctl status %s' for details.", name); + log_error("See %s logs and 'systemctl%s status %s' for details.", + arg_scope == UNIT_FILE_SYSTEM ? "system" : "user", + arg_scope == UNIT_FILE_SYSTEM ? "" : " --user", + name); return r; } @@ -2778,9 +2855,22 @@ static int start_unit(int argc, char *argv[], void *userdata) { } if (!arg_no_block) { - int q; + int q, arg_count = 0; + const char* extra_args[4] = {}; + + if (arg_scope != UNIT_FILE_SYSTEM) + extra_args[arg_count++] = "--user"; + + assert(IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_REMOTE, BUS_TRANSPORT_MACHINE)); + if (arg_transport == BUS_TRANSPORT_REMOTE) { + extra_args[arg_count++] = "-H"; + extra_args[arg_count++] = arg_host; + } else if (arg_transport == BUS_TRANSPORT_MACHINE) { + extra_args[arg_count++] = "-M"; + extra_args[arg_count++] = arg_host; + } - q = bus_wait_for_jobs(w, arg_quiet, arg_scope != UNIT_FILE_SYSTEM ? "--user" : NULL); + q = bus_wait_for_jobs(w, arg_quiet, extra_args); if (q < 0) return q; @@ -3075,7 +3165,7 @@ static int set_exit_code(uint8_t code) { NULL, "y", code); if (r < 0) - return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r)); + return log_error_errno(r, "Failed to set exit code: %s", bus_error_message(&error, r)); return 0; } @@ -3102,7 +3192,7 @@ static int start_special(int argc, char *argv[], void *userdata) { return r; if (a == ACTION_REBOOT && argc > 1) { - r = update_reboot_param_file(argv[1]); + r = update_reboot_parameter_and_warn(argv[1]); if (r < 0) return r; @@ -3158,11 +3248,12 @@ static int start_special(int argc, char *argv[], void *userdata) { return start_unit(argc, argv, userdata); } -static int check_unit_generic(int code, const char *good_states, char **args) { +static int check_unit_generic(int code, const UnitActiveState good_states[], int nb_states, char **args) { _cleanup_strv_free_ char **names = NULL; + UnitActiveState active_state; sd_bus *bus; char **name; - int r; + int r, i; bool found = false; r = acquire_bus(BUS_MANAGER, &bus); @@ -3174,13 +3265,16 @@ static int check_unit_generic(int code, const char *good_states, char **args) { return log_error_errno(r, "Failed to expand names: %m"); STRV_FOREACH(name, names) { - int state; + r = get_state_one_unit(bus, *name, &active_state); + if (r < 0) + return r; + + if (!arg_quiet) + puts(unit_active_state_to_string(active_state)); - state = check_one_unit(bus, *name, good_states, arg_quiet); - if (state < 0) - return state; - if (state > 0) - found = true; + for (i = 0; i < nb_states; ++i) + if (good_states[i] == active_state) + found = true; } /* use the given return code for the case that we won't find @@ -3189,12 +3283,14 @@ static int check_unit_generic(int code, const char *good_states, char **args) { } static int check_unit_active(int argc, char *argv[], void *userdata) { + const UnitActiveState states[] = { UNIT_ACTIVE, UNIT_RELOADING }; /* According to LSB: 3, "program is not running" */ - return check_unit_generic(3, "active\0reloading\0", strv_skip(argv, 1)); + return check_unit_generic(3, states, ELEMENTSOF(states), strv_skip(argv, 1)); } static int check_unit_failed(int argc, char *argv[], void *userdata) { - return check_unit_generic(1, "failed\0", strv_skip(argv, 1)); + const UnitActiveState states[] = { UNIT_FAILED }; + return check_unit_generic(1, states, ELEMENTSOF(states), strv_skip(argv, 1)); } static int kill_unit(int argc, char *argv[], void *userdata) { @@ -3214,7 +3310,7 @@ static int kill_unit(int argc, char *argv[], void *userdata) { /* --fail was specified */ if (streq(arg_job_mode, "fail")) - kill_who = strjoina(arg_kill_who, "-fail", NULL); + kill_who = strjoina(arg_kill_who, "-fail"); r = expand_names(bus, strv_skip(argv, 1), NULL, &names); if (r < 0) @@ -3406,6 +3502,7 @@ typedef struct UnitStatusInfo { } UnitStatusInfo; static void print_status_info( + sd_bus *bus, UnitStatusInfo *i, bool *ellipsized) { @@ -3416,6 +3513,7 @@ static void print_status_info( char since2[FORMAT_TIMESTAMP_MAX], *s2; const char *path; char **t, **t2; + int r; assert(i); @@ -3431,7 +3529,7 @@ static void print_status_info( } else active_on = active_off = ""; - printf("%s%s%s %s", active_on, draw_special_char(DRAW_BLACK_CIRCLE), active_off, strna(i->id)); + printf("%s%s%s %s", active_on, special_glyph(BLACK_CIRCLE), active_off, strna(i->id)); if (i->description && !streq_ptr(i->id, i->description)) printf(" - %s", i->description); @@ -3486,7 +3584,7 @@ static void print_status_info( } printf("%s\n %s", dir, - draw_special_char(DRAW_TREE_RIGHT)); + special_glyph(TREE_RIGHT)); } last = ! (*(dropin + 1) && startswith(*(dropin + 1), dir)); @@ -3688,25 +3786,26 @@ static void print_status_info( printf(" CPU: %s\n", format_timespan(buf, sizeof(buf), i->cpu_usage_nsec / NSEC_PER_USEC, USEC_PER_MSEC)); } - if (i->control_group && - (i->main_pid > 0 || i->control_pid > 0 || - (!IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_MACHINE) || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group) == 0))) { + if (i->control_group) + printf(" CGroup: %s\n", i->control_group); + + { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + static const char prefix[] = " "; unsigned c; - printf(" CGroup: %s\n", i->control_group); + c = columns(); + if (c > sizeof(prefix) - 1) + c -= sizeof(prefix) - 1; + else + c = 0; - if (IN_SET(arg_transport, - BUS_TRANSPORT_LOCAL, - BUS_TRANSPORT_MACHINE)) { + r = unit_show_processes(bus, i->id, i->control_group, prefix, c, get_output_flags(), &error); + if (r == -EBADR) { unsigned k = 0; pid_t extra[2]; - static const char prefix[] = " "; - c = columns(); - if (c > sizeof(prefix) - 1) - c -= sizeof(prefix) - 1; - else - c = 0; + /* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */ if (i->main_pid > 0) extra[k++] = i->main_pid; @@ -3714,8 +3813,9 @@ static void print_status_info( if (i->control_pid > 0) extra[k++] = i->control_pid; - show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, c, false, extra, k, get_output_flags()); - } + show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, c, extra, k, get_output_flags()); + } else if (r < 0) + log_warning_errno(r, "Failed to dump process list, ignoring: %s", bus_error_message(&error, r)); } if (i->id && arg_transport == BUS_TRANSPORT_LOCAL) @@ -4077,6 +4177,14 @@ skip: return 0; } +#define print_prop(name, fmt, ...) \ + do { \ + if (arg_value) \ + printf(fmt "\n", __VA_ARGS__); \ + else \ + printf("%s=" fmt "\n", name, __VA_ARGS__); \ + } while(0) + static int print_property(const char *name, sd_bus_message *m, const char *contents) { int r; @@ -4104,9 +4212,9 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte return bus_log_parse_error(r); if (u > 0) - printf("%s=%"PRIu32"\n", name, u); + print_prop(name, "%"PRIu32, u); else if (arg_all) - printf("%s=\n", name); + print_prop(name, "%s", ""); return 0; @@ -4118,7 +4226,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte return bus_log_parse_error(r); if (arg_all || !isempty(s)) - printf("%s=%s\n", name, s); + print_prop(name, "%s", s); return 0; @@ -4130,7 +4238,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte return bus_log_parse_error(r); if (arg_all || !isempty(a) || !isempty(b)) - printf("%s=%s \"%s\"\n", name, strempty(a), strempty(b)); + print_prop(name, "%s \"%s\"", strempty(a), strempty(b)); return 0; } else if (streq_ptr(name, "SystemCallFilter")) { @@ -4157,8 +4265,10 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte bool first = true; char **i; - fputs(name, stdout); - fputc('=', stdout); + if (!arg_value) { + fputs(name, stdout); + fputc('=', stdout); + } if (!whitelist) fputc('~', stdout); @@ -4190,7 +4300,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte return bus_log_parse_error(r); while ((r = sd_bus_message_read(m, "(sb)", &path, &ignore)) > 0) - printf("EnvironmentFile=%s (ignore_errors=%s)\n", path, yes_no(ignore)); + print_prop("EnvironmentFile", "%s (ignore_errors=%s)\n", path, yes_no(ignore)); if (r < 0) return bus_log_parse_error(r); @@ -4209,7 +4319,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte return bus_log_parse_error(r); while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0) - printf("%s=%s\n", type, path); + print_prop(type, "%s", path); if (r < 0) return bus_log_parse_error(r); @@ -4227,7 +4337,10 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte return bus_log_parse_error(r); while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0) - printf("Listen%s=%s\n", type, path); + if (arg_value) + puts(path); + else + printf("Listen%s=%s\n", type, path); if (r < 0) return bus_log_parse_error(r); @@ -4248,10 +4361,9 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte while ((r = sd_bus_message_read(m, "(stt)", &base, &value, &next_elapse)) > 0) { char timespan1[FORMAT_TIMESPAN_MAX], timespan2[FORMAT_TIMESPAN_MAX]; - printf("%s={ value=%s ; next_elapse=%s }\n", - base, - format_timespan(timespan1, sizeof(timespan1), value, 0), - format_timespan(timespan2, sizeof(timespan2), next_elapse, 0)); + print_prop(base, "{ value=%s ; next_elapse=%s }", + format_timespan(timespan1, sizeof(timespan1), value, 0), + format_timespan(timespan2, sizeof(timespan2), next_elapse, 0)); } if (r < 0) return bus_log_parse_error(r); @@ -4275,18 +4387,18 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte tt = strv_join(info.argv, " "); - printf("%s={ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid="PID_FMT" ; code=%s ; status=%i%s%s }\n", - name, - strna(info.path), - strna(tt), - yes_no(info.ignore), - strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)), - strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)), - info.pid, - sigchld_code_to_string(info.code), - info.status, - info.code == CLD_EXITED ? "" : "/", - strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status))); + print_prop(name, + "{ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid="PID_FMT" ; code=%s ; status=%i%s%s }", + strna(info.path), + strna(tt), + yes_no(info.ignore), + strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)), + strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)), + info.pid, + sigchld_code_to_string(info.code), + info.status, + info.code == CLD_EXITED ? "" : "/", + strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status))); free(info.path); strv_free(info.argv); @@ -4307,7 +4419,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte return bus_log_parse_error(r); while ((r = sd_bus_message_read(m, "(ss)", &path, &rwm)) > 0) - printf("%s=%s %s\n", name, strna(path), strna(rwm)); + print_prop(name, "%s %s", strna(path), strna(rwm)); if (r < 0) return bus_log_parse_error(r); @@ -4317,7 +4429,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte return 0; - } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "BlockIODeviceWeight")) { + } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && (streq(name, "IODeviceWeight") || streq(name, "BlockIODeviceWeight"))) { const char *path; uint64_t weight; @@ -4326,7 +4438,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte return bus_log_parse_error(r); while ((r = sd_bus_message_read(m, "(st)", &path, &weight)) > 0) - printf("%s=%s %" PRIu64 "\n", name, strna(path), weight); + print_prop(name, "%s %"PRIu64, strna(path), weight); if (r < 0) return bus_log_parse_error(r); @@ -4336,7 +4448,8 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte return 0; - } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth"))) { + } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && (cgroup_io_limit_type_from_string(name) >= 0 || + streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth"))) { const char *path; uint64_t bandwidth; @@ -4345,7 +4458,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte return bus_log_parse_error(r); while ((r = sd_bus_message_read(m, "(st)", &path, &bandwidth)) > 0) - printf("%s=%s %" PRIu64 "\n", name, strna(path), bandwidth); + print_prop(name, "%s %"PRIu64, strna(path), bandwidth); if (r < 0) return bus_log_parse_error(r); @@ -4359,7 +4472,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte break; } - r = bus_print_property(name, m, arg_all); + r = bus_print_property(name, m, arg_value, arg_all); if (r < 0) return bus_log_parse_error(r); @@ -4464,7 +4577,7 @@ static int show_one( if (streq(verb, "help")) show_unit_help(&info); else - print_status_info(&info, ellipsized); + print_status_info(bus, &info, ellipsized); } strv_free(info.documentation); @@ -4546,7 +4659,7 @@ static int show_all( if (r < 0) return r; - pager_open_if_enabled(); + pager_open(arg_no_pager, false); c = (unsigned) r; @@ -4593,13 +4706,13 @@ static int show_system_status(sd_bus *bus) { } else on = off = ""; - printf("%s%s%s %s\n", on, draw_special_char(DRAW_BLACK_CIRCLE), off, arg_host ? arg_host : hn); + printf("%s%s%s %s\n", on, special_glyph(BLACK_CIRCLE), off, arg_host ? arg_host : hn); printf(" State: %s%s%s\n", on, strna(mi.state), off); - printf(" Jobs: %u queued\n", mi.n_jobs); - printf(" Failed: %u units\n", mi.n_failed_units); + printf(" Jobs: %" PRIu32 " queued\n", mi.n_jobs); + printf(" Failed: %" PRIu32 " units\n", mi.n_failed_units); printf(" Since: %s; %s\n", format_timestamp(since2, sizeof(since2), mi.timestamp), @@ -4618,7 +4731,7 @@ static int show_system_status(sd_bus *bus) { else c = 0; - show_cgroup(SYSTEMD_CGROUP_CONTROLLER, strempty(mi.control_group), prefix, c, false, get_output_flags()); + show_cgroup(SYSTEMD_CGROUP_CONTROLLER, strempty(mi.control_group), prefix, c, get_output_flags()); } return 0; @@ -4641,7 +4754,7 @@ static int show(int argc, char *argv[], void *userdata) { return -EINVAL; } - pager_open_if_enabled(); + pager_open(arg_no_pager, false); if (show_status) /* Increase max number of open files to 16K if we can, we @@ -4659,7 +4772,7 @@ static int show(int argc, char *argv[], void *userdata) { if (show_status && argc <= 1) { - pager_open_if_enabled(); + pager_open(arg_no_pager, false); show_system_status(bus); new_line = true; @@ -4728,34 +4841,6 @@ static int show(int argc, char *argv[], void *userdata) { return ret; } -static int init_home_and_lookup_paths(char **user_home, char **user_runtime, LookupPaths *lp) { - int r; - - assert(user_home); - assert(user_runtime); - assert(lp); - - if (arg_scope == UNIT_FILE_USER) { - r = user_config_home(user_home); - if (r < 0) - return log_error_errno(r, "Failed to query XDG_CONFIG_HOME: %m"); - else if (r == 0) - return log_error_errno(ENOTDIR, "Cannot find units: $XDG_CONFIG_HOME and $HOME are not set."); - - r = user_runtime_dir(user_runtime); - if (r < 0) - return log_error_errno(r, "Failed to query XDG_CONFIG_HOME: %m"); - else if (r == 0) - return log_error_errno(ENOTDIR, "Cannot find units: $XDG_RUNTIME_DIR is not set."); - } - - r = lookup_paths_init_from_scope(lp, arg_scope, arg_root); - if (r < 0) - return log_error_errno(r, "Failed to query unit lookup paths: %m"); - - return 0; -} - static int cat_file(const char *filename, bool newline) { _cleanup_close_ int fd; @@ -4774,8 +4859,6 @@ static int cat_file(const char *filename, bool newline) { } static int cat(int argc, char *argv[], void *userdata) { - _cleanup_free_ char *user_home = NULL; - _cleanup_free_ char *user_runtime = NULL; _cleanup_lookup_paths_free_ LookupPaths lp = {}; _cleanup_strv_free_ char **names = NULL; char **name; @@ -4788,9 +4871,9 @@ static int cat(int argc, char *argv[], void *userdata) { return -EINVAL; } - r = init_home_and_lookup_paths(&user_home, &user_runtime, &lp); + r = lookup_paths_init(&lp, arg_scope, 0, arg_root); if (r < 0) - return r; + return log_error_errno(r, "Failed to determine unit paths: %m"); r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) @@ -4800,7 +4883,7 @@ static int cat(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to expand names: %m"); - pager_open_if_enabled(); + pager_open(arg_no_pager, false); STRV_FOREACH(name, names) { _cleanup_free_ char *fragment_path = NULL; @@ -4937,7 +5020,7 @@ static int daemon_reload(int argc, char *argv[], void *userdata) { * reply */ r = 0; else if (r < 0) - return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r)); + return log_error_errno(r, "Failed to reload daemon: %s", bus_error_message(&error, r)); return r < 0 ? r : 0; } @@ -4990,7 +5073,7 @@ static int show_environment(int argc, char *argv[], void *userdata) { sd_bus *bus; int r; - pager_open_if_enabled(); + pager_open(arg_no_pager, false); r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) @@ -5201,8 +5284,10 @@ static int enable_sysv_units(const char *verb, char **args) { int r = 0; #if defined(HAVE_SYSV_COMPAT) - unsigned f = 0; _cleanup_lookup_paths_free_ LookupPaths paths = {}; + unsigned f = 0; + + /* Processes all SysV units, and reshuffles the array so that afterwards only the native units remain */ if (arg_scope != UNIT_FILE_SYSTEM) return 0; @@ -5216,24 +5301,28 @@ static int enable_sysv_units(const char *verb, char **args) { "is-enabled")) return 0; - /* Processes all SysV units, and reshuffles the array so that - * afterwards only the native units remain */ - - r = lookup_paths_init(&paths, MANAGER_SYSTEM, false, arg_root, NULL, NULL, NULL); + r = lookup_paths_init(&paths, arg_scope, LOOKUP_PATHS_EXCLUDE_GENERATED, arg_root); if (r < 0) return r; r = 0; while (args[f]) { - const char *name; + + const char *argv[] = { + ROOTLIBEXECDIR "/systemd-sysv-install", + NULL, + NULL, + NULL, + NULL, + }; + _cleanup_free_ char *p = NULL, *q = NULL, *l = NULL; bool found_native = false, found_sysv; + siginfo_t status; + const char *name; unsigned c = 1; - const char *argv[6] = { ROOTLIBEXECDIR "/systemd-sysv-install", NULL, NULL, NULL, NULL }; - char **k; - int j; pid_t pid; - siginfo_t status; + int j; name = args[f++]; @@ -5243,21 +5332,13 @@ static int enable_sysv_units(const char *verb, char **args) { if (path_is_absolute(name)) continue; - STRV_FOREACH(k, paths.unit_path) { - _cleanup_free_ char *path = NULL; - - path = path_join(arg_root, *k, name); - if (!path) - return log_oom(); + j = unit_file_exists(arg_scope, &paths, name); + if (j < 0 && !IN_SET(j, -ELOOP, -ERFKILL, -EADDRNOTAVAIL)) + return log_error_errno(j, "Failed to lookup unit file state: %m"); + found_native = j != 0; - found_native = access(path, F_OK) >= 0; - if (found_native) - break; - } - - /* If we have both a native unit and a SysV script, - * enable/disable them both (below); for is-enabled, prefer the - * native unit */ + /* If we have both a native unit and a SysV script, enable/disable them both (below); for is-enabled, + * prefer the native unit */ if (found_native && streq(verb, "is-enabled")) continue; @@ -5271,9 +5352,9 @@ static int enable_sysv_units(const char *verb, char **args) { continue; if (found_native) - log_info("Synchronizing state of %s with SysV init with %s...", name, argv[0]); + log_info("Synchronizing state of %s with SysV service script with %s.", name, argv[0]); else - log_info("%s is not a native service, redirecting to systemd-sysv-install", name); + log_info("%s is not a native service, redirecting to systemd-sysv-install.", name); if (!isempty(arg_root)) argv[c++] = q = strappend("--root=", arg_root); @@ -5286,7 +5367,7 @@ static int enable_sysv_units(const char *verb, char **args) { if (!l) return log_oom(); - log_info("Executing %s", l); + log_info("Executing: %s", l); pid = fork(); if (pid < 0) @@ -5298,7 +5379,7 @@ static int enable_sysv_units(const char *verb, char **args) { (void) reset_signal_mask(); execv(argv[0], (char**) argv); - log_error_errno(r, "Failed to execute %s: %m", argv[0]); + log_error_errno(errno, "Failed to execute %s: %m", argv[0]); _exit(EXIT_FAILURE); } @@ -5320,9 +5401,11 @@ static int enable_sysv_units(const char *verb, char **args) { } } else if (status.si_status != 0) - return -EINVAL; - } else + return -EBADE; /* We don't warn here, under the assumption the script already showed an explanation */ + } else { + log_error("Unexpected waitid() result."); return -EPROTO; + } if (found_native) continue; @@ -5380,6 +5463,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) { UnitFileChange *changes = NULL; unsigned n_changes = 0; int carries_install_info = -1; + bool ignore_carries_install_info = arg_quiet; int r; if (!argv[1]) @@ -5393,10 +5477,12 @@ static int enable_unit(int argc, char *argv[], void *userdata) { if (r < 0) return r; - /* If the operation was fully executed by the SysV compat, - * let's finish early */ - if (strv_isempty(names)) - return 0; + /* If the operation was fully executed by the SysV compat, let's finish early */ + if (strv_isempty(names)) { + if (arg_no_reload || install_client_side()) + return 0; + return daemon_reload(argc, argv, userdata); + } if (install_client_side()) { if (streq(verb, "enable")) { @@ -5411,28 +5497,24 @@ static int enable_unit(int argc, char *argv[], void *userdata) { r = unit_file_link(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes); else if (streq(verb, "preset")) { r = unit_file_preset(arg_scope, arg_runtime, arg_root, names, arg_preset_mode, arg_force, &changes, &n_changes); - carries_install_info = r; } else if (streq(verb, "mask")) r = unit_file_mask(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes); else if (streq(verb, "unmask")) r = unit_file_unmask(arg_scope, arg_runtime, arg_root, names, &changes, &n_changes); + else if (streq(verb, "revert")) + r = unit_file_revert(arg_scope, arg_root, names, &changes, &n_changes); else assert_not_reached("Unknown verb"); - if (r == -ESHUTDOWN) - return log_error_errno(r, "Unit file is masked."); + unit_file_dump_changes(r, verb, changes, n_changes, arg_quiet); if (r < 0) - return log_error_errno(r, "Operation failed: %m"); - - if (!arg_quiet) - dump_unit_file_changes(changes, n_changes); - + return r; r = 0; } else { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int expect_carries_install_info = false; - bool send_force = true, send_preset_mode = false; + bool expect_carries_install_info = false; + bool send_runtime = true, send_force = true, send_preset_mode = false; const char *method; sd_bus *bus; @@ -5462,11 +5544,15 @@ static int enable_unit(int argc, char *argv[], void *userdata) { method = "PresetUnitFiles"; expect_carries_install_info = true; + ignore_carries_install_info = true; } else if (streq(verb, "mask")) method = "MaskUnitFiles"; else if (streq(verb, "unmask")) { method = "UnmaskUnitFiles"; send_force = false; + } else if (streq(verb, "revert")) { + method = "RevertUnitFiles"; + send_runtime = send_force = false; } else assert_not_reached("Unknown verb"); @@ -5490,9 +5576,11 @@ static int enable_unit(int argc, char *argv[], void *userdata) { return bus_log_create_error(r); } - r = sd_bus_message_append(m, "b", arg_runtime); - if (r < 0) - return bus_log_create_error(r); + if (send_runtime) { + r = sd_bus_message_append(m, "b", arg_runtime); + if (r < 0) + return bus_log_create_error(r); + } if (send_force) { r = sd_bus_message_append(m, "b", arg_force); @@ -5502,7 +5590,7 @@ static int enable_unit(int argc, char *argv[], void *userdata) { r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0) - return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r)); + return log_error_errno(r, "Failed to %s unit: %s", verb, bus_error_message(&error, r)); if (expect_carries_install_info) { r = sd_bus_message_read(reply, "b", &carries_install_info); @@ -5521,16 +5609,19 @@ static int enable_unit(int argc, char *argv[], void *userdata) { r = 0; } - if (carries_install_info == 0) - log_warning("The unit files have no [Install] section. They are not meant to be enabled\n" - "using systemctl.\n" + if (carries_install_info == 0 && !ignore_carries_install_info) + log_warning("The unit files have no installation config (WantedBy, RequiredBy, Also, Alias\n" + "settings in the [Install] section, and DefaultInstance for template units).\n" + "This means they are not meant to be enabled using systemctl.\n" "Possible reasons for having this kind of units are:\n" "1) A unit may be statically enabled by being symlinked from another unit's\n" " .wants/ or .requires/ directory.\n" "2) A unit's purpose may be to act as a helper for some other unit which has\n" " a requirement dependency on it.\n" "3) A unit may be started when needed via activation (socket, path, timer,\n" - " D-Bus, udev, scripted systemctl call, ...).\n"); + " D-Bus, udev, scripted systemctl call, ...).\n" + "4) In case of template units, the unit is meant to be enabled with some\n" + " instance name specified."); if (arg_now && n_changes > 0 && STR_IN_SET(argv[0], "enable", "disable", "mask")) { char *new_args[n_changes + 2]; @@ -5585,16 +5676,9 @@ static int add_dependency(int argc, char *argv[], void *userdata) { unsigned n_changes = 0; r = unit_file_add_dependency(arg_scope, arg_runtime, arg_root, names, target, dep, arg_force, &changes, &n_changes); - if (r == -ESHUTDOWN) - return log_error_errno(r, "Unit file is masked."); - if (r < 0) - return log_error_errno(r, "Can't add dependency: %m"); - - if (!arg_quiet) - dump_unit_file_changes(changes, n_changes); - + unit_file_dump_changes(r, "add dependency on", changes, n_changes, arg_quiet); unit_file_changes_free(changes, n_changes); - + return r; } else { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -5626,39 +5710,29 @@ static int add_dependency(int argc, char *argv[], void *userdata) { r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0) - return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r)); + return log_error_errno(r, "Failed to add dependency: %s", bus_error_message(&error, r)); r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL); if (r < 0) return r; - if (!arg_no_reload) - r = daemon_reload(argc, argv, userdata); - else - r = 0; + if (arg_no_reload) + return 0; + return daemon_reload(argc, argv, userdata); } - - return r; } static int preset_all(int argc, char *argv[], void *userdata) { - UnitFileChange *changes = NULL; - unsigned n_changes = 0; int r; if (install_client_side()) { + UnitFileChange *changes = NULL; + unsigned n_changes = 0; r = unit_file_preset_all(arg_scope, arg_runtime, arg_root, arg_preset_mode, arg_force, &changes, &n_changes); - if (r < 0) { - log_error_errno(r, "Operation failed: %m"); - goto finish; - } - - if (!arg_quiet) - dump_unit_file_changes(changes, n_changes); - - r = 0; - + unit_file_dump_changes(r, "preset", changes, n_changes, arg_quiet); + unit_file_changes_free(changes, n_changes); + return r; } else { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; @@ -5683,22 +5757,16 @@ static int preset_all(int argc, char *argv[], void *userdata) { arg_runtime, arg_force); if (r < 0) - return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r)); + return log_error_errno(r, "Failed to preset all units: %s", bus_error_message(&error, r)); r = bus_deserialize_and_dump_unit_file_changes(reply, arg_quiet, NULL, NULL); if (r < 0) return r; - if (!arg_no_reload) - r = daemon_reload(argc, argv, userdata); - else - r = 0; + if (arg_no_reload) + return 0; + return daemon_reload(argc, argv, userdata); } - -finish: - unit_file_changes_free(changes, n_changes); - - return r; } static int unit_is_enabled(int argc, char *argv[], void *userdata) { @@ -5731,7 +5799,8 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) { UNIT_FILE_ENABLED, UNIT_FILE_ENABLED_RUNTIME, UNIT_FILE_STATIC, - UNIT_FILE_INDIRECT)) + UNIT_FILE_INDIRECT, + UNIT_FILE_GENERATED)) enabled = true; if (!arg_quiet) @@ -5766,7 +5835,7 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_parse_error(r); - if (STR_IN_SET(s, "enabled", "enabled-runtime", "static", "indirect")) + if (STR_IN_SET(s, "enabled", "enabled-runtime", "static", "indirect", "generated")) enabled = true; if (!arg_quiet) @@ -5774,7 +5843,7 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) { } } - return !enabled; + return enabled ? EXIT_SUCCESS : EXIT_FAILURE; } static int is_system_running(int argc, char *argv[], void *userdata) { @@ -5844,52 +5913,32 @@ static int create_edit_temp_file(const char *new_path, const char *original_path return 0; } -static int get_file_to_edit(const char *name, const char *user_home, const char *user_runtime, char **ret_path) { - _cleanup_free_ char *path = NULL, *path2 = NULL, *run = NULL; +static int get_file_to_edit( + const LookupPaths *paths, + const char *name, + char **ret_path) { + + _cleanup_free_ char *path = NULL, *run = NULL; assert(name); assert(ret_path); - switch (arg_scope) { - case UNIT_FILE_SYSTEM: - path = path_join(arg_root, SYSTEM_CONFIG_UNIT_PATH, name); - if (arg_runtime) - run = path_join(arg_root, "/run/systemd/system/", name); - break; - case UNIT_FILE_GLOBAL: - path = path_join(arg_root, USER_CONFIG_UNIT_PATH, name); - if (arg_runtime) - run = path_join(arg_root, "/run/systemd/user/", name); - break; - case UNIT_FILE_USER: - assert(user_home); - assert(user_runtime); - - path = path_join(arg_root, user_home, name); - if (arg_runtime) { - path2 = path_join(arg_root, USER_CONFIG_UNIT_PATH, name); - if (!path2) - return log_oom(); - run = path_join(arg_root, user_runtime, name); - } - break; - default: - assert_not_reached("Invalid scope"); - } - if (!path || (arg_runtime && !run)) + path = strjoin(paths->persistent_config, "/", name, NULL); + if (!path) return log_oom(); if (arg_runtime) { + run = strjoin(paths->runtime_config, name, NULL); + if (!run) + return log_oom(); + } + + if (arg_runtime) { if (access(path, F_OK) >= 0) { log_error("Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.", run, path); return -EEXIST; } - if (path2 && access(path2, F_OK) >= 0) { - log_error("Refusing to create \"%s\" because it would be overridden by \"%s\" anyway.", run, path2); - return -EEXIST; - } - *ret_path = run; run = NULL; } else { @@ -5900,7 +5949,12 @@ static int get_file_to_edit(const char *name, const char *user_home, const char return 0; } -static int unit_file_create_dropin(const char *unit_name, const char *user_home, const char *user_runtime, char **ret_new_path, char **ret_tmp_path) { +static int unit_file_create_dropin( + const LookupPaths *paths, + const char *unit_name, + char **ret_new_path, + char **ret_tmp_path) { + char *tmp_new_path, *tmp_tmp_path, *ending; int r; @@ -5909,7 +5963,7 @@ static int unit_file_create_dropin(const char *unit_name, const char *user_home, assert(ret_tmp_path); ending = strjoina(unit_name, ".d/override.conf"); - r = get_file_to_edit(ending, user_home, user_runtime, &tmp_new_path); + r = get_file_to_edit(paths, ending, &tmp_new_path); if (r < 0) return r; @@ -5926,10 +5980,9 @@ static int unit_file_create_dropin(const char *unit_name, const char *user_home, } static int unit_file_create_copy( + const LookupPaths *paths, const char *unit_name, const char *fragment_path, - const char *user_home, - const char *user_runtime, char **ret_new_path, char **ret_tmp_path) { @@ -5941,7 +5994,7 @@ static int unit_file_create_copy( assert(ret_new_path); assert(ret_tmp_path); - r = get_file_to_edit(unit_name, user_home, user_runtime, &tmp_new_path); + r = get_file_to_edit(paths, unit_name, &tmp_new_path); if (r < 0) return r; @@ -6056,8 +6109,6 @@ static int run_editor(char **paths) { } static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) { - _cleanup_free_ char *user_home = NULL; - _cleanup_free_ char *user_runtime = NULL; _cleanup_lookup_paths_free_ LookupPaths lp = {}; char **name; int r; @@ -6065,13 +6116,12 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) { assert(names); assert(paths); - r = init_home_and_lookup_paths(&user_home, &user_runtime, &lp); + r = lookup_paths_init(&lp, arg_scope, 0, arg_root); if (r < 0) return r; STRV_FOREACH(name, names) { - _cleanup_free_ char *path = NULL; - char *new_path, *tmp_path; + _cleanup_free_ char *path = NULL, *new_path = NULL, *tmp_path = NULL; r = unit_find_paths(bus, *name, &lp, &path, NULL); if (r < 0) @@ -6085,15 +6135,16 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) { } if (arg_full) - r = unit_file_create_copy(*name, path, user_home, user_runtime, &new_path, &tmp_path); + r = unit_file_create_copy(&lp, *name, path, &new_path, &tmp_path); else - r = unit_file_create_dropin(*name, user_home, user_runtime, &new_path, &tmp_path); + r = unit_file_create_dropin(&lp, *name, &new_path, &tmp_path); if (r < 0) return r; r = strv_push_pair(paths, new_path, tmp_path); if (r < 0) return log_oom(); + new_path = tmp_path = NULL; } return 0; @@ -6157,15 +6208,30 @@ static int edit(int argc, char *argv[], void *userdata) { r = daemon_reload(argc, argv, userdata); end: - STRV_FOREACH_PAIR(original, tmp, paths) + STRV_FOREACH_PAIR(original, tmp, paths) { (void) unlink(*tmp); + /* Removing empty dropin dirs */ + if (!arg_full) { + _cleanup_free_ char *dir; + + dir = dirname_malloc(*original); + if (!dir) + return log_oom(); + + /* no need to check if the dir is empty, rmdir + * does nothing if it is not the case. + */ + (void) rmdir(dir); + } + } + return r; } static void systemctl_help(void) { - pager_open_if_enabled(); + pager_open(arg_no_pager, false); printf("%s [OPTIONS...] {COMMAND} ...\n\n" "Query or send control commands to the systemd manager.\n\n" @@ -6189,6 +6255,7 @@ static void systemctl_help(void) { " --job-mode=MODE Specify how to deal with already queued jobs, when\n" " queueing a new job\n" " --show-types When showing sockets, explicitly show their type\n" + " --value When showing properties, only print the value\n" " -i --ignore-inhibitors\n" " When shutting down or sleeping, ignore inhibitors\n" " --kill-who=WHO Who to send signal to\n" @@ -6256,6 +6323,8 @@ static void systemctl_help(void) { " unmask NAME... Unmask one or more units\n" " link PATH... Link one or more units files into\n" " the search path\n" + " revert NAME... Revert one or more unit files to vendor\n" + " version\n" " add-wants TARGET NAME... Add 'Wants' dependency for the target\n" " on specified one or more units\n" " add-requires TARGET NAME... Add 'Requires' dependency for the target\n" @@ -6440,6 +6509,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_SHOW_TYPES, ARG_IRREVERSIBLE, ARG_IGNORE_DEPENDENCIES, + ARG_VALUE, ARG_VERSION, ARG_USER, ARG_SYSTEM, @@ -6481,6 +6551,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "irreversible", no_argument, NULL, ARG_IRREVERSIBLE }, /* compatibility only */ { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES }, /* compatibility only */ { "ignore-inhibitors", no_argument, NULL, 'i' }, + { "value", no_argument, NULL, ARG_VALUE }, { "user", no_argument, NULL, ARG_USER }, { "system", no_argument, NULL, ARG_SYSTEM }, { "global", no_argument, NULL, ARG_GLOBAL }, @@ -6537,7 +6608,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { } p = optarg; - for(;;) { + for (;;) { _cleanup_free_ char *type = NULL; r = extract_first_word(&p, &type, ",", 0); @@ -6587,7 +6658,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { return log_oom(); } else { p = optarg; - for(;;) { + for (;;) { _cleanup_free_ char *prop = NULL; r = extract_first_word(&p, &prop, ",", 0); @@ -6632,6 +6703,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_show_types = true; break; + case ARG_VALUE: + arg_value = true; + break; + case ARG_JOB_MODE: arg_job_mode = optarg; break; @@ -6677,7 +6752,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { break; case ARG_ROOT: - r = parse_path_argument_and_warn(optarg, true, &arg_root); + r = parse_path_argument_and_warn(optarg, false, &arg_root); if (r < 0) return r; break; @@ -6697,11 +6772,11 @@ static int systemctl_parse_argv(int argc, char *argv[]) { break; case ARG_FORCE: - arg_force ++; + arg_force++; break; case 'f': - arg_force ++; + arg_force++; break; case ARG_NO_RELOAD: @@ -6772,7 +6847,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { } p = optarg; - for(;;) { + for (;;) { _cleanup_free_ char *s = NULL; r = extract_first_word(&p, &s, ",", 0); @@ -6855,6 +6930,7 @@ static int halt_parse_argv(int argc, char *argv[]) { { "force", no_argument, NULL, 'f' }, { "wtmp-only", no_argument, NULL, 'w' }, { "no-wtmp", no_argument, NULL, 'd' }, + { "no-sync", no_argument, NULL, 'n' }, { "no-wall", no_argument, NULL, ARG_NO_WALL }, {} }; @@ -6900,13 +6976,16 @@ static int halt_parse_argv(int argc, char *argv[]) { arg_no_wtmp = true; break; + case 'n': + arg_no_sync = true; + break; + case ARG_NO_WALL: arg_no_wall = true; break; case 'i': case 'h': - case 'n': /* Compatibility nops */ break; @@ -6918,7 +6997,7 @@ static int halt_parse_argv(int argc, char *argv[]) { } if (arg_action == ACTION_REBOOT && (argc == optind || argc == optind + 1)) { - r = update_reboot_param_file(argc == optind + 1 ? argv[optind] : NULL); + r = update_reboot_parameter_and_warn(argc == optind + 1 ? argv[optind] : NULL); if (r < 0) return r; } else if (optind < argc) { @@ -7167,7 +7246,7 @@ static int telinit_parse_argv(int argc, char *argv[]) { arg_action = table[i].to; - optind ++; + optind++; return 1; } @@ -7258,6 +7337,7 @@ static int parse_argv(int argc, char *argv[]) { return systemctl_parse_argv(argc, argv); } +#ifdef HAVE_SYSV_COMPAT _pure_ static int action_to_runlevel(void) { static const char table[_ACTION_MAX] = { @@ -7275,6 +7355,7 @@ _pure_ static int action_to_runlevel(void) { return table[arg_action]; } +#endif static int talk_initctl(void) { #ifdef HAVE_SYSV_COMPAT @@ -7371,6 +7452,7 @@ static int systemctl_main(int argc, char *argv[]) { { "mask", 2, VERB_ANY, 0, enable_unit }, { "unmask", 2, VERB_ANY, 0, enable_unit }, { "link", 2, VERB_ANY, 0, enable_unit }, + { "revert", 2, VERB_ANY, 0, enable_unit }, { "switch-root", 2, VERB_ANY, VERB_NOCHROOT, switch_root }, { "list-dependencies", VERB_ANY, 2, VERB_NOCHROOT, list_dependencies }, { "set-default", 2, 2, 0, set_default }, @@ -7417,11 +7499,13 @@ static int start_with_fallback(void) { } static int halt_now(enum action a) { + int r; /* The kernel will automaticall flush ATA disks and suchlike * on reboot(), but the file systems need to be synce'd * explicitly in advance. */ - (void) sync(); + if (!arg_no_sync) + (void) sync(); /* Make sure C-A-D is handled by the kernel from this point * on... */ @@ -7443,9 +7527,14 @@ static int halt_now(enum action a) { case ACTION_REBOOT: { _cleanup_free_ char *param = NULL; - if (read_one_line_file(REBOOT_PARAM_FILE, ¶m) >= 0) { + r = read_one_line_file("/run/systemd/reboot-param", ¶m); + if (r < 0) + log_warning_errno(r, "Failed to read reboot parameter file: %m"); + + if (!isempty(param)) { 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."); diff --git a/src/systemd/_sd-common.h b/src/systemd/_sd-common.h index 2d4e1f26e1..3bb886be75 100644 --- a/src/systemd/_sd-common.h +++ b/src/systemd/_sd-common.h @@ -74,7 +74,7 @@ #endif #define _SD_DEFINE_POINTER_CLEANUP_FUNC(type, func) \ - static inline void func##p(type **p) { \ + static __inline__ void func##p(type **p) { \ if (*p) \ func(*p); \ } \ diff --git a/src/systemd/sd-bus-protocol.h b/src/systemd/sd-bus-protocol.h index 47b256d5b9..623cee0c50 100644 --- a/src/systemd/sd-bus-protocol.h +++ b/src/systemd/sd-bus-protocol.h @@ -59,7 +59,7 @@ enum { SD_BUS_TYPE_STRUCT_END = ')', SD_BUS_TYPE_DICT_ENTRY = 'e', /* not actually used in signatures */ SD_BUS_TYPE_DICT_ENTRY_BEGIN = '{', - SD_BUS_TYPE_DICT_ENTRY_END = '}', + SD_BUS_TYPE_DICT_ENTRY_END = '}' }; /* Well-known errors. Note that this is only a sanitized subset of the diff --git a/src/systemd/sd-bus-vtable.h b/src/systemd/sd-bus-vtable.h index 6ad6d51979..e8f84eb545 100644 --- a/src/systemd/sd-bus-vtable.h +++ b/src/systemd/sd-bus-vtable.h @@ -34,7 +34,7 @@ enum { _SD_BUS_VTABLE_METHOD = 'M', _SD_BUS_VTABLE_SIGNAL = 'S', _SD_BUS_VTABLE_PROPERTY = 'P', - _SD_BUS_VTABLE_WRITABLE_PROPERTY = 'W', + _SD_BUS_VTABLE_WRITABLE_PROPERTY = 'W' }; enum { diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h index 2a2ef0eb98..295989cd69 100644 --- a/src/systemd/sd-bus.h +++ b/src/systemd/sd-bus.h @@ -89,13 +89,13 @@ enum { SD_BUS_CREDS_WELL_KNOWN_NAMES = 1ULL << 32, SD_BUS_CREDS_DESCRIPTION = 1ULL << 33, SD_BUS_CREDS_AUGMENT = 1ULL << 63, /* special flag, if on sd-bus will augment creds struct, in a potentially race-full way. */ - _SD_BUS_CREDS_ALL = (1ULL << 34) -1, + _SD_BUS_CREDS_ALL = (1ULL << 34) -1 }; enum { SD_BUS_NAME_REPLACE_EXISTING = 1ULL << 0, SD_BUS_NAME_ALLOW_REPLACEMENT = 1ULL << 1, - SD_BUS_NAME_QUEUE = 1ULL << 2, + SD_BUS_NAME_QUEUE = 1ULL << 2 }; /* Callbacks */ diff --git a/src/systemd/sd-device.h b/src/systemd/sd-device.h index 5bfca6ecec..c1d07561d7 100644 --- a/src/systemd/sd-device.h +++ b/src/systemd/sd-device.h @@ -22,6 +22,7 @@ ***/ #include <inttypes.h> +#include <sys/sysmacros.h> #include <sys/types.h> #include "_sd-common.h" diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index 2b865a80e1..20b8c2873f 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -84,26 +84,57 @@ enum { typedef struct sd_dhcp_client sd_dhcp_client; -typedef void (*sd_dhcp_client_cb_t)(sd_dhcp_client *client, int event, - void *userdata); -int sd_dhcp_client_set_callback(sd_dhcp_client *client, sd_dhcp_client_cb_t cb, - void *userdata); - -int sd_dhcp_client_set_request_option(sd_dhcp_client *client, uint8_t option); -int sd_dhcp_client_set_request_address(sd_dhcp_client *client, - const struct in_addr *last_address); -int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast); -int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index); -int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr, - size_t addr_len, uint16_t arp_type); -int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type, - const uint8_t *data, size_t data_len); -int sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type, - const uint8_t **data, size_t *data_len); -int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu); -int sd_dhcp_client_set_hostname(sd_dhcp_client *client, const char *hostname); -int sd_dhcp_client_set_vendor_class_identifier(sd_dhcp_client *client, const char *vci); -int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret); +typedef void (*sd_dhcp_client_callback_t)(sd_dhcp_client *client, int event, void *userdata); +int sd_dhcp_client_set_callback( + sd_dhcp_client *client, + sd_dhcp_client_callback_t cb, + void *userdata); + +int sd_dhcp_client_set_request_option( + sd_dhcp_client *client, + uint8_t option); +int sd_dhcp_client_set_request_address( + sd_dhcp_client *client, + const struct in_addr *last_address); +int sd_dhcp_client_set_request_broadcast( + sd_dhcp_client *client, + int broadcast); +int sd_dhcp_client_set_index( + sd_dhcp_client *client, + int interface_index); +int sd_dhcp_client_set_mac( + sd_dhcp_client *client, + const uint8_t *addr, + size_t addr_len, + uint16_t arp_type); +int sd_dhcp_client_set_client_id( + sd_dhcp_client *client, + uint8_t type, + const uint8_t *data, + size_t data_len); +int sd_dhcp_client_set_iaid_duid( + sd_dhcp_client *client, + uint32_t iaid, + uint16_t duid_type, + const void *duid, + size_t duid_len); +int sd_dhcp_client_get_client_id( + sd_dhcp_client *client, + uint8_t *type, + const uint8_t **data, + size_t *data_len); +int sd_dhcp_client_set_mtu( + sd_dhcp_client *client, + uint32_t mtu); +int sd_dhcp_client_set_hostname( + sd_dhcp_client *client, + const char *hostname); +int sd_dhcp_client_set_vendor_class_identifier( + sd_dhcp_client *client, + const char *vci); +int sd_dhcp_client_get_lease( + sd_dhcp_client *client, + sd_dhcp_lease **ret); int sd_dhcp_client_stop(sd_dhcp_client *client); int sd_dhcp_client_start(sd_dhcp_client *client); @@ -113,7 +144,10 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client); int sd_dhcp_client_new(sd_dhcp_client **ret); -int sd_dhcp_client_attach_event(sd_dhcp_client *client, sd_event *event, int priority); +int sd_dhcp_client_attach_event( + sd_dhcp_client *client, + sd_event *event, + int64_t priority); int sd_dhcp_client_detach_event(sd_dhcp_client *client); sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client); diff --git a/src/systemd/sd-dhcp-server.h b/src/systemd/sd-dhcp-server.h index 8658197e80..d4517a26d6 100644 --- a/src/systemd/sd-dhcp-server.h +++ b/src/systemd/sd-dhcp-server.h @@ -37,7 +37,7 @@ int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex); sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server); sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server); -int sd_dhcp_server_attach_event(sd_dhcp_server *client, sd_event *event, int priority); +int sd_dhcp_server_attach_event(sd_dhcp_server *client, sd_event *event, int64_t priority); int sd_dhcp_server_detach_event(sd_dhcp_server *client); sd_event *sd_dhcp_server_get_event(sd_dhcp_server *client); @@ -51,6 +51,7 @@ int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *addres int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *timezone); int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n); int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr dns[], unsigned n); +int sd_dhcp_server_set_emit_router(sd_dhcp_server *server, int enabled); int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t); int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t); diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index 9608060830..90f62eaca4 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -76,29 +76,52 @@ enum { typedef struct sd_dhcp6_client sd_dhcp6_client; -typedef void (*sd_dhcp6_client_cb_t)(sd_dhcp6_client *client, int event, - void *userdata); -int sd_dhcp6_client_set_callback(sd_dhcp6_client *client, - sd_dhcp6_client_cb_t cb, void *userdata); - -int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index); -int sd_dhcp6_client_set_local_address(sd_dhcp6_client *client, const struct in6_addr *local_address); -int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr, - size_t addr_len, uint16_t arp_type); -int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid, - size_t duid_len); -int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled); -int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, int *enabled); -int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, - uint16_t option); - -int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret); +typedef void (*sd_dhcp6_client_callback_t)(sd_dhcp6_client *client, int event, void *userdata); +int sd_dhcp6_client_set_callback( + sd_dhcp6_client *client, + sd_dhcp6_client_callback_t cb, + void *userdata); + +int sd_dhcp6_client_set_index( + sd_dhcp6_client *client, + int interface_index); +int sd_dhcp6_client_set_local_address( + sd_dhcp6_client *client, + const struct in6_addr *local_address); +int sd_dhcp6_client_set_mac( + sd_dhcp6_client *client, + const uint8_t *addr, + size_t addr_len, + uint16_t arp_type); +int sd_dhcp6_client_set_duid( + sd_dhcp6_client *client, + uint16_t duid_type, + const void *duid, + size_t duid_len); +int sd_dhcp6_client_set_iaid( + sd_dhcp6_client *client, + uint32_t iaid); +int sd_dhcp6_client_set_information_request( + sd_dhcp6_client *client, + int enabled); +int sd_dhcp6_client_get_information_request( + sd_dhcp6_client *client, + int *enabled); +int sd_dhcp6_client_set_request_option( + sd_dhcp6_client *client, + uint16_t option); + +int sd_dhcp6_client_get_lease( + sd_dhcp6_client *client, + sd_dhcp6_lease **ret); int sd_dhcp6_client_stop(sd_dhcp6_client *client); int sd_dhcp6_client_start(sd_dhcp6_client *client); int sd_dhcp6_client_is_running(sd_dhcp6_client *client); -int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, - int priority); +int sd_dhcp6_client_attach_event( + sd_dhcp6_client *client, + sd_event *event, + int64_t priority); int sd_dhcp6_client_detach_event(sd_dhcp6_client *client); sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client); sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client); diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h index 1ea97e47f8..531ace1c34 100644 --- a/src/systemd/sd-event.h +++ b/src/systemd/sd-event.h @@ -55,7 +55,7 @@ enum { SD_EVENT_RUNNING, SD_EVENT_EXITING, SD_EVENT_FINISHED, - SD_EVENT_PREPARING, + SD_EVENT_PREPARING }; enum { @@ -69,7 +69,11 @@ 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 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; +#endif int sd_event_default(sd_event **e); diff --git a/src/systemd/sd-id128.h b/src/systemd/sd-id128.h index a3bf5897b8..4dff0b9b81 100644 --- a/src/systemd/sd-id128.h +++ b/src/systemd/sd-id128.h @@ -100,11 +100,11 @@ int sd_id128_get_boot(sd_id128_t *ret); ((x).bytes[15] & 15) >= 10 ? 'a' + ((x).bytes[15] & 15) - 10 : '0' + ((x).bytes[15] & 15), \ 0 }) -_sd_pure_ static inline int sd_id128_equal(sd_id128_t a, sd_id128_t b) { +_sd_pure_ static __inline__ int sd_id128_equal(sd_id128_t a, sd_id128_t b) { return memcmp(&a, &b, 16) == 0; } -_sd_pure_ static inline int sd_id128_is_null(sd_id128_t a) { +_sd_pure_ static __inline__ int sd_id128_is_null(sd_id128_t a) { return a.qwords[0] == 0 && a.qwords[1] == 0; } diff --git a/src/systemd/sd-ipv4acd.h b/src/systemd/sd-ipv4acd.h index 3a2219c82c..9e3e14a30c 100644 --- a/src/systemd/sd-ipv4acd.h +++ b/src/systemd/sd-ipv4acd.h @@ -37,12 +37,12 @@ enum { }; typedef struct sd_ipv4acd sd_ipv4acd; -typedef void (*sd_ipv4acd_cb_t)(sd_ipv4acd *ll, int event, void *userdata); +typedef void (*sd_ipv4acd_callback_t)(sd_ipv4acd *ll, int event, void *userdata); int sd_ipv4acd_detach_event(sd_ipv4acd *ll); -int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int priority); +int sd_ipv4acd_attach_event(sd_ipv4acd *ll, sd_event *event, int64_t priority); int sd_ipv4acd_get_address(sd_ipv4acd *ll, struct in_addr *address); -int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_cb_t cb, void *userdata); +int sd_ipv4acd_set_callback(sd_ipv4acd *ll, sd_ipv4acd_callback_t cb, void *userdata); int sd_ipv4acd_set_mac(sd_ipv4acd *ll, const struct ether_addr *addr); int sd_ipv4acd_set_index(sd_ipv4acd *ll, int interface_index); int sd_ipv4acd_set_address(sd_ipv4acd *ll, const struct in_addr *address); diff --git a/src/systemd/sd-ipv4ll.h b/src/systemd/sd-ipv4ll.h index 67c566fe0d..6fa38a2243 100644 --- a/src/systemd/sd-ipv4ll.h +++ b/src/systemd/sd-ipv4ll.h @@ -36,12 +36,12 @@ enum { }; typedef struct sd_ipv4ll sd_ipv4ll; -typedef void (*sd_ipv4ll_cb_t)(sd_ipv4ll *ll, int event, void *userdata); +typedef void (*sd_ipv4ll_callback_t)(sd_ipv4ll *ll, int event, void *userdata); int sd_ipv4ll_detach_event(sd_ipv4ll *ll); -int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int priority); +int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority); int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address); -int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_cb_t cb, void *userdata); +int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata); int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr); int sd_ipv4ll_set_index(sd_ipv4ll *ll, int interface_index); int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address); diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h index abb9eca576..9c36b27157 100644 --- a/src/systemd/sd-journal.h +++ b/src/systemd/sd-journal.h @@ -67,12 +67,13 @@ typedef struct sd_journal sd_journal; /* Open flags */ enum { - SD_JOURNAL_LOCAL_ONLY = 1, - SD_JOURNAL_RUNTIME_ONLY = 2, - SD_JOURNAL_SYSTEM = 4, - SD_JOURNAL_CURRENT_USER = 8, + SD_JOURNAL_LOCAL_ONLY = 1 << 0, + SD_JOURNAL_RUNTIME_ONLY = 1 << 1, + SD_JOURNAL_SYSTEM = 1 << 2, + SD_JOURNAL_CURRENT_USER = 1 << 3, + SD_JOURNAL_OS_ROOT = 1 << 4, - SD_JOURNAL_SYSTEM_ONLY = SD_JOURNAL_SYSTEM, /* deprecated name */ + SD_JOURNAL_SYSTEM_ONLY = SD_JOURNAL_SYSTEM /* deprecated name */ }; /* Wakeup event types */ @@ -84,8 +85,10 @@ enum { int sd_journal_open(sd_journal **ret, int flags); int sd_journal_open_directory(sd_journal **ret, const char *path, int flags); +int sd_journal_open_directory_fd(sd_journal **ret, int fd, int flags); int sd_journal_open_files(sd_journal **ret, const char **paths, int flags); -int sd_journal_open_container(sd_journal **ret, const char *machine, int flags); +int sd_journal_open_files_fd(sd_journal **ret, int fds[], unsigned n_fds, int flags); +int sd_journal_open_container(sd_journal **ret, const char *machine, int flags); /* deprecated */ void sd_journal_close(sd_journal *j); int sd_journal_previous(sd_journal *j); diff --git a/src/systemd/sd-lldp.h b/src/systemd/sd-lldp.h index ea952ef187..5772d5794a 100644 --- a/src/systemd/sd-lldp.h +++ b/src/systemd/sd-lldp.h @@ -30,57 +30,147 @@ _SD_BEGIN_DECLARATIONS; +typedef struct sd_lldp sd_lldp; +typedef struct sd_lldp_neighbor sd_lldp_neighbor; + +/* IEEE 802.3AB Clause 9: TLV Types */ enum { - SD_LLDP_EVENT_UPDATE_INFO = 0, + SD_LLDP_TYPE_END = 0, + SD_LLDP_TYPE_CHASSIS_ID = 1, + SD_LLDP_TYPE_PORT_ID = 2, + SD_LLDP_TYPE_TTL = 3, + SD_LLDP_TYPE_PORT_DESCRIPTION = 4, + SD_LLDP_TYPE_SYSTEM_NAME = 5, + SD_LLDP_TYPE_SYSTEM_DESCRIPTION = 6, + SD_LLDP_TYPE_SYSTEM_CAPABILITIES = 7, + SD_LLDP_TYPE_MGMT_ADDRESS = 8, + SD_LLDP_TYPE_PRIVATE = 127, }; +/* IEEE 802.3AB Clause 9.5.2: Chassis subtypes */ enum { - SD_LLDP_DESTINATION_TYPE_NEAREST_BRIDGE, - SD_LLDP_DESTINATION_TYPE_NEAREST_NON_TPMR_BRIDGE, - SD_LLDP_DESTINATION_TYPE_NEAREST_CUSTOMER_BRIDGE, + SD_LLDP_CHASSIS_SUBTYPE_RESERVED = 0, + SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT = 1, + SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS = 2, + SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT = 3, + SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS = 4, + SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS = 5, + SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME = 6, + SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED = 7, }; -typedef struct sd_lldp sd_lldp; -typedef struct sd_lldp_packet sd_lldp_packet; +/* IEEE 802.3AB Clause 9.5.3: Port subtype */ +enum { + SD_LLDP_PORT_SUBTYPE_RESERVED = 0, + SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS = 1, + SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT = 2, + SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS = 3, + SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS = 4, + SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME = 5, + SD_LLDP_PORT_SUBTYPE_AGENT_CIRCUIT_ID = 6, + SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED = 7, +}; -typedef void (*sd_lldp_cb_t)(sd_lldp *lldp, int event, void *userdata); +enum { + SD_LLDP_SYSTEM_CAPABILITIES_OTHER = 1 << 0, + SD_LLDP_SYSTEM_CAPABILITIES_REPEATER = 1 << 1, + SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE = 1 << 2, + SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP = 1 << 3, + SD_LLDP_SYSTEM_CAPABILITIES_ROUTER = 1 << 4, + SD_LLDP_SYSTEM_CAPABILITIES_PHONE = 1 << 5, + SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS = 1 << 6, + SD_LLDP_SYSTEM_CAPABILITIES_STATION = 1 << 7, + SD_LLDP_SYSTEM_CAPABILITIES_CVLAN = 1 << 8, + SD_LLDP_SYSTEM_CAPABILITIES_SVLAN = 1 << 9, + SD_LLDP_SYSTEM_CAPABILITIES_TPMR = 1 << 10, +}; -int sd_lldp_new(int ifindex, const char *ifname, const struct ether_addr *mac, sd_lldp **ret); -sd_lldp* sd_lldp_unref(sd_lldp *lldp); +#define SD_LLDP_SYSTEM_CAPABILITIES_ALL ((uint16_t) -1) -int sd_lldp_start(sd_lldp *lldp); -int sd_lldp_stop(sd_lldp *lldp); +#define SD_LLDP_SYSTEM_CAPABILITIES_ALL_ROUTERS \ + ((uint16_t) \ + (SD_LLDP_SYSTEM_CAPABILITIES_REPEATER| \ + SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE| \ + SD_LLDP_SYSTEM_CAPABILITIES_WLAN_AP| \ + SD_LLDP_SYSTEM_CAPABILITIES_ROUTER| \ + SD_LLDP_SYSTEM_CAPABILITIES_DOCSIS| \ + SD_LLDP_SYSTEM_CAPABILITIES_CVLAN| \ + SD_LLDP_SYSTEM_CAPABILITIES_SVLAN| \ + SD_LLDP_SYSTEM_CAPABILITIES_TPMR)) -int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int priority); -int sd_lldp_detach_event(sd_lldp *lldp); +#define SD_LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 } +#define SD_LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f } -int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_cb_t cb, void *userdata); -int sd_lldp_save(sd_lldp *lldp, const char *file); +enum { + SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID = 1, + SD_LLDP_OUI_802_1_SUBTYPE_PORT_PROTOCOL_VLAN_ID = 2, + SD_LLDP_OUI_802_1_SUBTYPE_VLAN_NAME = 3, + SD_LLDP_OUI_802_1_SUBTYPE_PROTOCOL_IDENTITY = 4, + SD_LLDP_OUI_802_1_SUBTYPE_VID_USAGE_DIGEST = 5, + SD_LLDP_OUI_802_1_SUBTYPE_MANAGEMENT_VID = 6, + SD_LLDP_OUI_802_1_SUBTYPE_LINK_AGGREGATION = 7, +}; -int sd_lldp_packet_read_chassis_id(sd_lldp_packet *tlv, uint8_t *type, uint8_t **data, uint16_t *length); -int sd_lldp_packet_read_port_id(sd_lldp_packet *tlv, uint8_t *type, uint8_t **data, uint16_t *length); -int sd_lldp_packet_read_ttl(sd_lldp_packet *tlv, uint16_t *ttl); -int sd_lldp_packet_read_system_name(sd_lldp_packet *tlv, char **data, uint16_t *length); -int sd_lldp_packet_read_system_description(sd_lldp_packet *tlv, char **data, uint16_t *length); -int sd_lldp_packet_read_system_capability(sd_lldp_packet *tlv, uint16_t *data); -int sd_lldp_packet_read_port_description(sd_lldp_packet *tlv, char **data, uint16_t *length); +typedef enum sd_lldp_event { + SD_LLDP_EVENT_ADDED = 'a', + SD_LLDP_EVENT_REMOVED = 'r', + SD_LLDP_EVENT_UPDATED = 'u', + SD_LLDP_EVENT_REFRESHED = 'f', +} sd_lldp_event; -/* IEEE 802.1 organizationally specific TLVs */ -int sd_lldp_packet_read_port_vlan_id(sd_lldp_packet *tlv, uint16_t *id); -int sd_lldp_packet_read_port_protocol_vlan_id(sd_lldp_packet *tlv, uint8_t *flags, uint16_t *id); -int sd_lldp_packet_read_vlan_name(sd_lldp_packet *tlv, uint16_t *vlan_id, char **name, uint16_t *length); -int sd_lldp_packet_read_management_vid(sd_lldp_packet *tlv, uint16_t *id); -int sd_lldp_packet_read_link_aggregation(sd_lldp_packet *tlv, uint8_t *status, uint32_t *id); +typedef void (*sd_lldp_callback_t)(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n, void *userdata); -sd_lldp_packet *sd_lldp_packet_ref(sd_lldp_packet *tlv); -sd_lldp_packet *sd_lldp_packet_unref(sd_lldp_packet *tlv); +int sd_lldp_new(sd_lldp **ret, int ifindex); +sd_lldp* sd_lldp_unref(sd_lldp *lldp); -int sd_lldp_packet_get_destination_type(sd_lldp_packet *tlv, int *dest); +int sd_lldp_start(sd_lldp *lldp); +int sd_lldp_stop(sd_lldp *lldp); + +int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority); +int sd_lldp_detach_event(sd_lldp *lldp); -int sd_lldp_get_packets(sd_lldp *lldp, sd_lldp_packet ***tlvs); +int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata); + +/* Controls how much and what to store in the neighbors database */ +int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t n); +int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask); +int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *address); + +int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***neighbors); + +int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size); +sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n); +sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n); + +/* Access to LLDP frame metadata */ +int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address); +int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address); +int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size); + +/* High-level, direct, parsed out field access. These fields exist at most once, hence may be queried directly. */ +int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size); +int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size); +int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret); +int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret); +int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret); +int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret); + +/* Low-level, iterative TLV access. This is for evertyhing else, it iteratively goes through all available TLVs + * (including the ones covered with the calls above), and allows multiple TLVs for the same fields. */ +int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n); +int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n); +int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type); +int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type); +int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], uint8_t *subtype); +int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[3], uint8_t subtype); +int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp, sd_lldp_unref); -_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_packet, sd_lldp_packet_unref); +_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_lldp_neighbor, sd_lldp_neighbor_unref); _SD_END_DECLARATIONS; diff --git a/src/systemd/sd-login.h b/src/systemd/sd-login.h index 3c10ff032f..e3ecbd8378 100644 --- a/src/systemd/sd-login.h +++ b/src/systemd/sd-login.h @@ -108,7 +108,7 @@ int sd_peer_get_slice(int fd, char **slice); int sd_peer_get_user_slice(int fd, char **slice); /* Similar to sd_pid_get_machine_name(), but retrieves data about the - * peer of a a connected AF_UNIX socket */ + * peer of a connected AF_UNIX socket */ int sd_peer_get_machine_name(int fd, char **machine); /* Similar to sd_pid_get_cgroup(), but retrieves data about the peer diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h index 8a72576ec8..3c44d63021 100644 --- a/src/systemd/sd-messages.h +++ b/src/systemd/sd-messages.h @@ -82,8 +82,6 @@ _SD_BEGIN_DECLARATIONS; #define SD_MESSAGE_INVALID_CONFIGURATION SD_ID128_MAKE(c7,72,d2,4e,9a,88,4c,be,b9,ea,12,62,5c,30,6c,01) -#define SD_MESSAGE_BOOTCHART SD_ID128_MAKE(9f,26,aa,56,2c,f4,40,c2,b1,6c,77,3d,04,79,b5,18) - #define SD_MESSAGE_DNSSEC_FAILURE SD_ID128_MAKE(16,75,d7,f1,72,17,40,98,b1,10,8b,f8,c7,dc,8f,5d) #define SD_MESSAGE_DNSSEC_TRUST_ANCHOR_REVOKED SD_ID128_MAKE(4d,44,08,cf,d0,d1,44,85,91,84,d1,e6,5d,7c,8a,65) #define SD_MESSAGE_DNSSEC_DOWNGRADE SD_ID128_MAKE(36,db,2d,fa,5a,90,45,e1,bd,4a,f5,f9,3e,1c,f0,57) diff --git a/src/systemd/sd-ndisc.h b/src/systemd/sd-ndisc.h index 762947531d..29bcbe8e3e 100644 --- a/src/systemd/sd-ndisc.h +++ b/src/systemd/sd-ndisc.h @@ -52,7 +52,7 @@ int sd_ndisc_set_callback(sd_ndisc *nd, int sd_ndisc_set_index(sd_ndisc *nd, int interface_index); int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr); -int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int priority); +int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority); int sd_ndisc_detach_event(sd_ndisc *nd); sd_event *sd_ndisc_get_event(sd_ndisc *nd); diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h index b4798d2476..3ae110c080 100644 --- a/src/systemd/sd-netlink.h +++ b/src/systemd/sd-netlink.h @@ -64,7 +64,7 @@ int sd_netlink_wait(sd_netlink *nl, uint64_t timeout); int sd_netlink_add_match(sd_netlink *nl, uint16_t match, sd_netlink_message_handler_t c, void *userdata); int sd_netlink_remove_match(sd_netlink *nl, uint16_t match, sd_netlink_message_handler_t c, void *userdata); -int sd_netlink_attach_event(sd_netlink *nl, sd_event *e, int priority); +int sd_netlink_attach_event(sd_netlink *nl, sd_event *e, int64_t priority); int sd_netlink_detach_event(sd_netlink *nl); int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, const char *data); @@ -131,14 +131,16 @@ int sd_rtnl_message_link_set_type(sd_netlink_message *m, unsigned type); int sd_rtnl_message_link_set_family(sd_netlink_message *m, unsigned family); int sd_rtnl_message_link_get_ifindex(sd_netlink_message *m, int *ifindex); int sd_rtnl_message_link_get_flags(sd_netlink_message *m, unsigned *flags); -int sd_rtnl_message_link_get_type(sd_netlink_message *m, unsigned *type); +int sd_rtnl_message_link_get_type(sd_netlink_message *m, unsigned short *type); int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char prefixlen); int sd_rtnl_message_route_set_src_prefixlen(sd_netlink_message *m, unsigned char prefixlen); int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope); int sd_rtnl_message_route_set_flags(sd_netlink_message *m, unsigned flags); +int sd_rtnl_message_route_set_table(sd_netlink_message *m, unsigned char table); int sd_rtnl_message_route_get_flags(sd_netlink_message *m, unsigned *flags); int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family); +int sd_rtnl_message_route_set_family(sd_netlink_message *m, int family); int sd_rtnl_message_route_get_protocol(sd_netlink_message *m, unsigned char *protocol); int sd_rtnl_message_route_get_scope(sd_netlink_message *m, unsigned char *scope); int sd_rtnl_message_route_get_tos(sd_netlink_message *m, unsigned char *tos); diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h index e20d12c44d..0f13e2bae7 100644 --- a/src/systemd/sd-network.h +++ b/src/systemd/sd-network.h @@ -99,11 +99,11 @@ int sd_network_link_get_network_file(int ifindex, char **filename); /* Get DNS entries for a given link. These are string representations of * IP addresses */ -int sd_network_link_get_dns(int ifindex, char ***addr); +int sd_network_link_get_dns(int ifindex, char ***ret); /* Get NTP entries for a given link. These are domain names or string * representations of IP addresses */ -int sd_network_link_get_ntp(int ifindex, char ***addr); +int sd_network_link_get_ntp(int ifindex, char ***ret); /* Indicates whether or not LLMNR should be enabled for the link * Possible levels of support: yes, no, resolve @@ -133,19 +133,17 @@ int sd_network_link_get_dnssec(int ifindex, char **dnssec); */ int sd_network_link_get_dnssec_negative_trust_anchors(int ifindex, char ***nta); -int sd_network_link_get_lldp(int ifindex, char **lldp); - /* Get the search DNS domain names for a given link. */ int sd_network_link_get_search_domains(int ifindex, char ***domains); /* Get the route DNS domain names for a given link. */ int sd_network_link_get_route_domains(int ifindex, char ***domains); -/* Get the CARRIERS to which current link is bound to. */ -int sd_network_link_get_carrier_bound_to(int ifindex, char ***carriers); +/* Get the carrier interface indexes to which current link is bound to. */ +int sd_network_link_get_carrier_bound_to(int ifindex, int **ifindexes); /* Get the CARRIERS that are bound to current link. */ -int sd_network_link_get_carrier_bound_by(int ifindex, char ***carriers); +int sd_network_link_get_carrier_bound_by(int ifindex, int **ifindexes); /* Get the timezone that was learnt on a specific link. */ int sd_network_link_get_timezone(int ifindex, char **timezone); diff --git a/src/systemd/sd-resolve.h b/src/systemd/sd-resolve.h index 903b917f70..1c792dab39 100644 --- a/src/systemd/sd-resolve.h +++ b/src/systemd/sd-resolve.h @@ -79,7 +79,7 @@ int sd_resolve_wait(sd_resolve *resolve, uint64_t timeout_usec); int sd_resolve_get_tid(sd_resolve *resolve, pid_t *tid); -int sd_resolve_attach_event(sd_resolve *resolve, sd_event *e, int priority); +int sd_resolve_attach_event(sd_resolve *resolve, sd_event *e, int64_t priority); int sd_resolve_detach_event(sd_resolve *resolve); sd_event *sd_resolve_get_event(sd_resolve *resolve); diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 863c628323..4377f1b910 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -1820,7 +1820,7 @@ int main(int argc, char *argv[]) { umask(0022); - r = mac_selinux_init(NULL); + r = mac_selinux_init(); if (r < 0) { log_error_errno(r, "SELinux setup failed: %m"); goto finish; diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c index 5a6818a79d..fe4bbeeb75 100644 --- a/src/sysv-generator/sysv-generator.c +++ b/src/sysv-generator/sysv-generator.c @@ -70,7 +70,7 @@ static const struct { UP must be read before DOWN */ }; -const char *arg_dest = "/tmp"; +static const char *arg_dest = "/tmp"; typedef struct SysvStub { char *name; @@ -729,14 +729,50 @@ static int fix_order(SysvStub *s, Hashmap *all_services) { return 0; } +static int acquire_search_path(const char *def, const char *envvar, char ***ret) { + _cleanup_strv_free_ char **l = NULL; + const char *e; + int r; + + assert(def); + assert(envvar); + + e = getenv(envvar); + if (e) { + r = path_split_and_make_absolute(e, &l); + if (r < 0) + return log_error_errno(r, "Failed to make $%s search path absolute: %m", envvar); + } + + if (strv_isempty(l)) { + strv_free(l); + + l = strv_new(def, NULL); + if (!l) + return log_oom(); + } + + if (!path_strv_resolve_uniq(l, NULL)) + return log_oom(); + + *ret = l; + l = NULL; + + return 0; +} + static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { + _cleanup_strv_free_ char **sysvinit_path = NULL; char **path; int r; assert(lp); - assert(all_services); - STRV_FOREACH(path, lp->sysvinit_path) { + r = acquire_search_path(SYSTEM_SYSVINIT_PATH, "SYSTEMD_SYSVINIT_PATH", &sysvinit_path); + if (r < 0) + return r; + + STRV_FOREACH(path, sysvinit_path) { _cleanup_closedir_ DIR *d = NULL; struct dirent *de; @@ -770,11 +806,11 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { if (hashmap_contains(all_services, name)) continue; - r = unit_file_lookup_state(UNIT_FILE_SYSTEM, NULL, lp, name, NULL); - if (r < 0 && r != -ENOENT) { + r = unit_file_exists(UNIT_FILE_SYSTEM, lp, name); + if (r < 0 && !IN_SET(r, -ELOOP, -ERFKILL, -EADDRNOTAVAIL)) { log_debug_errno(r, "Failed to detect whether %s exists, skipping: %m", name); continue; - } else if (r >= 0) { + } else if (r != 0) { log_debug("Native unit for %s already exists, skipping.", name); continue; } @@ -806,6 +842,7 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_services) { Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {}; _cleanup_set_free_ Set *shutdown_services = NULL; + _cleanup_strv_free_ char **sysvrcnd_path = NULL; SysvStub *service; unsigned i; Iterator j; @@ -814,7 +851,11 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic assert(lp); - STRV_FOREACH(p, lp->sysvrcnd_path) { + r = acquire_search_path(SYSTEM_SYSVRCND_PATH, "SYSTEMD_SYSVRCND_PATH", &sysvrcnd_path); + if (r < 0) + return r; + + STRV_FOREACH(p, sysvrcnd_path) { for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) { _cleanup_closedir_ DIR *d = NULL; @@ -864,7 +905,7 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic } service = hashmap_get(all_services, name); - if (!service){ + if (!service) { log_debug("Ignoring %s symlink in %s, not generating %s.", de->d_name, rcnd_table[i].path, name); continue; } @@ -963,7 +1004,7 @@ int main(int argc, char *argv[]) { umask(0022); - r = lookup_paths_init(&lp, MANAGER_SYSTEM, true, NULL, NULL, NULL, NULL); + r = lookup_paths_init(&lp, UNIT_FILE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, NULL); if (r < 0) { log_error_errno(r, "Failed to find lookup paths: %m"); goto finish; diff --git a/src/test/test-alloc-util.c b/src/test/test-alloc-util.c new file mode 100644 index 0000000000..cc4821eaf5 --- /dev/null +++ b/src/test/test-alloc-util.c @@ -0,0 +1,55 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "alloc-util.h" +#include "macro.h" +#include "util.h" + +static void test_alloca(void) { + static const uint8_t zero[997] = { }; + char *t; + + t = alloca_align(17, 512); + assert_se(!((uintptr_t)t & 0xff)); + memzero(t, 17); + + t = alloca0_align(997, 1024); + assert_se(!((uintptr_t)t & 0x1ff)); + assert_se(!memcmp(t, zero, 997)); +} + +static void test_memdup_multiply(void) { + int org[] = {1, 2, 3}; + int *dup; + + dup = (int*)memdup_multiply(org, sizeof(int), 3); + + assert_se(dup); + assert_se(dup[0] == 1); + assert_se(dup[1] == 2); + assert_se(dup[2] == 3); + free(dup); +} + +int main(int argc, char *argv[]) { + test_alloca(); + test_memdup_multiply(); + + return 0; +} diff --git a/src/test/test-boot-timestamps.c b/src/test/test-boot-timestamps.c index d2add5880c..8e68d6510d 100644 --- a/src/test/test-boot-timestamps.c +++ b/src/test/test-boot-timestamps.c @@ -34,17 +34,18 @@ static int test_acpi_fpdt(void) { r = acpi_get_boot_usec(&loader_start, &loader_exit); if (r < 0) { - if (r != -ENOENT) - log_error_errno(r, "Failed to read ACPI FPDT: %m"); - return r; + bool ok = r == -ENOENT || (getuid() != 0 && r == -EACCES) || r == -ENODATA; + + log_full_errno(ok ? LOG_DEBUG : LOG_ERR, + r, "Failed to read ACPI FPDT: %m"); + return ok ? 0 : r; } log_info("ACPI FPDT: loader start=%s exit=%s duration=%s", format_timespan(ts_start, sizeof(ts_start), loader_start, USEC_PER_MSEC), format_timespan(ts_exit, sizeof(ts_exit), loader_exit, USEC_PER_MSEC), format_timespan(ts_span, sizeof(ts_span), loader_exit - loader_start, USEC_PER_MSEC)); - - return 0; + return 1; } static int test_efi_loader(void) { @@ -57,33 +58,34 @@ static int test_efi_loader(void) { r = efi_loader_get_boot_usec(&loader_start, &loader_exit); if (r < 0) { - if (r != -ENOENT) - log_error_errno(r, "Failed to read EFI loader data: %m"); - return r; + bool ok = r == -ENOENT || (getuid() != 0 && r == -EACCES); + + log_full_errno(ok ? LOG_DEBUG : LOG_ERR, + r, "Failed to read EFI loader data: %m"); + return ok ? 0 : r; } log_info("EFI Loader: start=%s exit=%s duration=%s", format_timespan(ts_start, sizeof(ts_start), loader_start, USEC_PER_MSEC), format_timespan(ts_exit, sizeof(ts_exit), loader_exit, USEC_PER_MSEC), format_timespan(ts_span, sizeof(ts_span), loader_exit - loader_start, USEC_PER_MSEC)); - - return 0; + return 1; } -int main(int argc, char* argv[]) { +static int test_boot_timestamps(void) { char s[MAX(FORMAT_TIMESPAN_MAX, FORMAT_TIMESTAMP_MAX)]; int r; dual_timestamp fw, l, k; - test_acpi_fpdt(); - test_efi_loader(); - dual_timestamp_from_monotonic(&k, 0); r = boot_timestamps(NULL, &fw, &l); if (r < 0) { - log_error_errno(r, "Failed to read variables: %m"); - return 1; + bool ok = r == -ENOENT || (getuid() != 0 && r == -EACCES); + + log_full_errno(ok ? LOG_DEBUG : LOG_ERR, + r, "Failed to read variables: %m"); + return ok ? 0 : r; } log_info("Firmware began %s before kernel.", format_timespan(s, sizeof(s), fw.monotonic, 0)); @@ -91,6 +93,21 @@ int main(int argc, char* argv[]) { log_info("Firmware began %s.", format_timestamp(s, sizeof(s), fw.realtime)); log_info("Loader began %s.", format_timestamp(s, sizeof(s), l.realtime)); log_info("Kernel began %s.", format_timestamp(s, sizeof(s), k.realtime)); + return 1; +} + +int main(int argc, char* argv[]) { + int p, q, r; + + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + + p = test_acpi_fpdt(); + assert(p >= 0); + q = test_efi_loader(); + assert(q >= 0); + r = test_boot_timestamps(); + assert(r >= 0); - return 0; + return (p > 0 || q > 0 || r >> 0) ? EXIT_SUCCESS : EXIT_TEST_SKIP; } diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c index 8754cb3381..5a8c6cbfb6 100644 --- a/src/test/test-calendarspec.c +++ b/src/test/test-calendarspec.c @@ -137,6 +137,7 @@ int main(int argc, char* argv[]) { test_next("2015-11-13 09:11:23.42", "EET", 12345, 1447398683420000); test_next("2015-11-13 09:11:23.42/1.77", "EET", 1447398683420000, 1447398685190000); test_next("2015-11-13 09:11:23.42/1.77", "EET", 1447398683419999, 1447398683420000); + test_next("Sun 16:00:00", "CET", 1456041600123456, 1456066800000000); assert_se(calendar_spec_from_string("test", &c) < 0); assert_se(calendar_spec_from_string("", &c) < 0); diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c index ad15075a5b..a027eb0fd2 100644 --- a/src/test/test-cgroup-mask.c +++ b/src/test/test-cgroup-mask.c @@ -21,7 +21,9 @@ #include "macro.h" #include "manager.h" +#include "rm-rf.h" #include "test-helper.h" +#include "tests.h" #include "unit.h" static int test_cgroup_mask(void) { @@ -33,7 +35,7 @@ static int test_cgroup_mask(void) { /* Prepare the manager. */ assert_se(set_unit_path(TEST_DIR) >= 0); - r = manager_new(MANAGER_USER, true, &m); + r = manager_new(UNIT_FILE_USER, true, &m); if (r == -EPERM || r == -EACCES) { puts("manager_new: Permission denied. Skipping test."); return EXIT_TEST_SKIP; @@ -46,6 +48,7 @@ static int test_cgroup_mask(void) { m->default_cpu_accounting = m->default_memory_accounting = m->default_blockio_accounting = + m->default_io_accounting = m->default_tasks_accounting = false; m->default_tasks_max = (uint64_t) -1; @@ -74,7 +77,7 @@ static int test_cgroup_mask(void) { assert_se(unit_get_own_mask(daughter) == 0); assert_se(unit_get_own_mask(grandchild) == 0); assert_se(unit_get_own_mask(parent_deep) == CGROUP_MASK_MEMORY); - assert_se(unit_get_own_mask(parent) == CGROUP_MASK_BLKIO); + assert_se(unit_get_own_mask(parent) == (CGROUP_MASK_IO | CGROUP_MASK_BLKIO)); assert_se(unit_get_own_mask(root) == 0); /* Verify aggregation of member masks */ @@ -83,23 +86,23 @@ static int test_cgroup_mask(void) { assert_se(unit_get_members_mask(grandchild) == 0); assert_se(unit_get_members_mask(parent_deep) == 0); assert_se(unit_get_members_mask(parent) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY)); - assert_se(unit_get_members_mask(root) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY)); + assert_se(unit_get_members_mask(root) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY)); /* Verify aggregation of sibling masks. */ assert_se(unit_get_siblings_mask(son) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY)); assert_se(unit_get_siblings_mask(daughter) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY)); assert_se(unit_get_siblings_mask(grandchild) == 0); assert_se(unit_get_siblings_mask(parent_deep) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY)); - assert_se(unit_get_siblings_mask(parent) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY)); - assert_se(unit_get_siblings_mask(root) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY)); + assert_se(unit_get_siblings_mask(parent) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY)); + assert_se(unit_get_siblings_mask(root) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY)); /* Verify aggregation of target masks. */ assert_se(unit_get_target_mask(son) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY) & m->cgroup_supported)); assert_se(unit_get_target_mask(daughter) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY) & m->cgroup_supported)); assert_se(unit_get_target_mask(grandchild) == 0); assert_se(unit_get_target_mask(parent_deep) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY) & m->cgroup_supported)); - assert_se(unit_get_target_mask(parent) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY) & m->cgroup_supported)); - assert_se(unit_get_target_mask(root) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY) & m->cgroup_supported)); + assert_se(unit_get_target_mask(parent) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY) & m->cgroup_supported)); + assert_se(unit_get_target_mask(root) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY) & m->cgroup_supported)); manager_free(m); @@ -107,7 +110,11 @@ static int test_cgroup_mask(void) { } int main(int argc, char* argv[]) { + _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; int rc = 0; + + assert_se(runtime_dir = setup_fake_runtime_dir()); TEST_REQ_RUNNING_SYSTEMD(rc = test_cgroup_mask()); + return rc; } diff --git a/src/test/test-clock.c b/src/test/test-clock.c new file mode 100644 index 0000000000..84f775e5bc --- /dev/null +++ b/src/test/test-clock.c @@ -0,0 +1,96 @@ +/*** + This file is part of systemd. + + Copyright (C) 2016 Canonical Ltd. + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <unistd.h> +#include <fcntl.h> + +#include "clock-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "log.h" +#include "macro.h" + +static void test_clock_is_localtime(void) { + char adjtime[] = "/tmp/test-adjtime.XXXXXX"; + int fd = -1; + _cleanup_fclose_ FILE* f = NULL; + + static const struct scenario { + const char* contents; + int expected_result; + } scenarios[] = { + /* adjtime configures UTC */ + {"0.0 0 0\n0\nUTC\n", 0}, + /* adjtime configures local time */ + {"0.0 0 0\n0\nLOCAL\n", 1}, + /* no final EOL */ + {"0.0 0 0\n0\nUTC", 0}, + {"0.0 0 0\n0\nLOCAL", 1}, + /* empty value -> defaults to UTC */ + {"0.0 0 0\n0\n", 0}, + /* unknown value -> defaults to UTC */ + {"0.0 0 0\n0\nFOO\n", 0}, + /* no third line */ + {"0.0 0 0", 0}, + {"0.0 0 0\n", 0}, + {"0.0 0 0\n0", 0}, + }; + + /* without an adjtime file we default to UTC */ + assert_se(clock_is_localtime("/nonexisting/adjtime") == 0); + + fd = mkostemp_safe(adjtime, O_WRONLY|O_CLOEXEC); + assert_se(fd >= 0); + log_info("adjtime test file: %s", adjtime); + f = fdopen(fd, "w"); + assert_se(f); + + for (size_t i = 0; i < ELEMENTSOF(scenarios); ++i) { + log_info("scenario #%zu:, expected result %i", i, scenarios[i].expected_result); + log_info("%s", scenarios[i].contents); + rewind(f); + ftruncate(fd, 0); + assert_se(write_string_stream(f, scenarios[i].contents, false) == 0); + assert_se(clock_is_localtime(adjtime) == scenarios[i].expected_result); + } + + unlink(adjtime); +} + +/* Test with the real /etc/adjtime */ +static void test_clock_is_localtime_system(void) { + int r; + r = clock_is_localtime(NULL); + + if (access("/etc/adjtime", F_OK) == 0) { + log_info("/etc/adjtime exists, clock_is_localtime() == %i", r); + /* if /etc/adjtime exists we expect some answer, no error or + * crash */ + assert_se(r == 0 || r == 1); + } else + /* default is UTC if there is no /etc/adjtime */ + assert_se(r == 0); +} + +int main(int argc, char *argv[]) { + test_clock_is_localtime(); + test_clock_is_localtime_system(); + + return 0; +} diff --git a/src/test/test-conf-parser.c b/src/test/test-conf-parser.c index b3a4c40339..be5d2611f8 100644 --- a/src/test/test-conf-parser.c +++ b/src/test/test-conf-parser.c @@ -215,6 +215,14 @@ static void test_config_parse_nsec(void) { test_config_parse_nsec_one("garbage", 0); } +static void test_config_parse_iec_uint64(void) { + uint64_t offset = 0; + assert_se(config_parse_iec_uint64(NULL, "/this/file", 11, "Section", 22, "Size", 0, "4M", &offset, NULL) == 0); + assert_se(offset == 4 * 1024 * 1024); + + assert_se(config_parse_iec_uint64(NULL, "/this/file", 11, "Section", 22, "Size", 0, "4.5M", &offset, NULL) == 0); +} + int main(int argc, char **argv) { log_parse_environment(); log_open(); @@ -230,6 +238,7 @@ int main(int argc, char **argv) { test_config_parse_mode(); test_config_parse_sec(); test_config_parse_nsec(); + test_config_parse_iec_uint64(); return 0; } diff --git a/src/test/test-copy.c b/src/test/test-copy.c index ad57cb0202..68154fc4e8 100644 --- a/src/test/test-copy.c +++ b/src/test/test-copy.c @@ -24,6 +24,7 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "log.h" #include "macro.h" #include "mkdir.h" #include "path-util.h" @@ -39,6 +40,8 @@ static void test_copy_file(void) { size_t sz = 0; int fd; + log_info("%s", __func__); + fd = mkostemp_safe(fn, O_RDWR|O_CLOEXEC); assert_se(fd >= 0); close(fd); @@ -66,6 +69,8 @@ static void test_copy_file_fd(void) { char text[] = "boohoo\nfoo\n\tbar\n"; char buf[64] = {0}; + log_info("%s", __func__); + in_fd = mkostemp_safe(in_fn, O_RDWR); assert_se(in_fd >= 0); out_fd = mkostemp_safe(out_fn, O_RDWR); @@ -90,31 +95,43 @@ static void test_copy_tree(void) { char **links = STRV_MAKE("link", "file", "link2", "dir1/file"); char **p, **link; + const char *unixsockp; + struct stat st; + + log_info("%s", __func__); (void) rm_rf(copy_dir, REMOVE_ROOT|REMOVE_PHYSICAL); (void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL); STRV_FOREACH(p, files) { - char *f = strjoina(original_dir, *p); + _cleanup_free_ char *f; + + assert_se(f = strappend(original_dir, *p)); assert_se(mkdir_parents(f, 0755) >= 0); assert_se(write_string_file(f, "file", WRITE_STRING_FILE_CREATE) == 0); } STRV_FOREACH_PAIR(link, p, links) { - char *f = strjoina(original_dir, *p); - char *l = strjoina(original_dir, *link); + _cleanup_free_ char *f, *l; + + assert_se(f = strappend(original_dir, *p)); + assert_se(l = strappend(original_dir, *link)); assert_se(mkdir_parents(l, 0755) >= 0); assert_se(symlink(f, l) == 0); } + unixsockp = strjoina(original_dir, "unixsock"); + assert_se(mknod(unixsockp, S_IFSOCK|0644, 0) >= 0); + assert_se(copy_tree(original_dir, copy_dir, true) == 0); STRV_FOREACH(p, files) { - _cleanup_free_ char *buf = NULL; + _cleanup_free_ char *buf = NULL, *f; size_t sz = 0; - char *f = strjoina(copy_dir, *p); + + assert_se(f = strappend(copy_dir, *p)); assert_se(access(f, F_OK) == 0); assert_se(read_full_file(f, &buf, &sz) == 0); @@ -122,14 +139,19 @@ static void test_copy_tree(void) { } STRV_FOREACH_PAIR(link, p, links) { - _cleanup_free_ char *target = NULL; - char *f = strjoina(original_dir, *p); - char *l = strjoina(copy_dir, *link); + _cleanup_free_ char *target = NULL, *f, *l; + + assert_se(f = strjoin(original_dir, *p, NULL)); + assert_se(l = strjoin(copy_dir, *link, NULL)); assert_se(readlink_and_canonicalize(l, &target) == 0); assert_se(path_equal(f, target)); } + unixsockp = strjoina(copy_dir, "unixsock"); + assert_se(stat(unixsockp, &st) >= 0); + assert_se(S_ISSOCK(st.st_mode)); + assert_se(copy_tree(original_dir, copy_dir, false) < 0); assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, false) < 0); @@ -173,11 +195,65 @@ static void test_copy_bytes(void) { assert_se(r == -EBADF); } +static void test_copy_bytes_regular_file(const char *src, bool try_reflink, uint64_t max_bytes) { + char fn2[] = "/tmp/test-copy-file-XXXXXX"; + char fn3[] = "/tmp/test-copy-file-XXXXXX"; + _cleanup_close_ int fd = -1, fd2 = -1, fd3 = -1; + int r; + struct stat buf, buf2, buf3; + + log_info("%s try_reflink=%s max_bytes=%" PRIu64, __func__, yes_no(try_reflink), max_bytes); + + fd = open(src, O_RDONLY | O_CLOEXEC | O_NOCTTY); + assert_se(fd >= 0); + + fd2 = mkostemp_safe(fn2, O_RDWR); + assert_se(fd2 >= 0); + + fd3 = mkostemp_safe(fn3, O_WRONLY); + assert_se(fd3 >= 0); + + r = copy_bytes(fd, fd2, max_bytes, try_reflink); + if (max_bytes == (uint64_t) -1) + assert_se(r == 0); + else + assert_se(IN_SET(r, 0, 1)); + + assert_se(lseek(fd2, 0, SEEK_SET) == 0); + + r = copy_bytes(fd2, fd3, max_bytes, try_reflink); + if (max_bytes == (uint64_t) -1) + assert_se(r == 0); + else + /* We cannot distinguish between the input being exactly max_bytes + * or longer than max_bytes (without trying to read one more byte, + * or calling stat, or FION_READ, etc, and we don't want to do any + * of that). So we expect "truncation" since we know that file we + * are copying is exactly max_bytes bytes. */ + assert_se(r == 1); + + assert_se(fstat(fd, &buf) == 0); + assert_se(fstat(fd2, &buf2) == 0); + assert_se(fstat(fd3, &buf3) == 0); + + assert_se((uint64_t) buf2.st_size == MIN((uint64_t) buf.st_size, max_bytes)); + assert_se(buf3.st_size == buf2.st_size); + + unlink(fn2); + unlink(fn3); +} + int main(int argc, char *argv[]) { test_copy_file(); test_copy_file_fd(); test_copy_tree(); test_copy_bytes(); + test_copy_bytes_regular_file(argv[0], false, (uint64_t) -1); + test_copy_bytes_regular_file(argv[0], true, (uint64_t) -1); + test_copy_bytes_regular_file(argv[0], false, 1000); /* smaller than copy buffer size */ + test_copy_bytes_regular_file(argv[0], true, 1000); + test_copy_bytes_regular_file(argv[0], false, 32000); /* larger than copy buffer size */ + test_copy_bytes_regular_file(argv[0], true, 32000); return 0; } diff --git a/src/test/test-cpu-set-util.c b/src/test/test-cpu-set-util.c new file mode 100644 index 0000000000..8818d1ffb7 --- /dev/null +++ b/src/test/test-cpu-set-util.c @@ -0,0 +1,143 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "alloc-util.h" +#include "cpu-set-util.h" +#include "macro.h" + +static void test_parse_cpu_set(void) { + cpu_set_t *c = NULL; + int ncpus; + int cpu; + + /* Simple range (from CPUAffinity example) */ + ncpus = parse_cpu_set_and_warn("1 2", &c, NULL, "fake", 1, "CPUAffinity"); + assert_se(ncpus >= 1024); + assert_se(CPU_ISSET_S(1, CPU_ALLOC_SIZE(ncpus), c)); + assert_se(CPU_ISSET_S(2, CPU_ALLOC_SIZE(ncpus), c)); + assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 2); + c = mfree(c); + + /* A more interesting range */ + ncpus = parse_cpu_set_and_warn("0 1 2 3 8 9 10 11", &c, NULL, "fake", 1, "CPUAffinity"); + assert_se(ncpus >= 1024); + assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8); + for (cpu = 0; cpu < 4; cpu++) + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); + for (cpu = 8; cpu < 12; cpu++) + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); + c = mfree(c); + + /* Quoted strings */ + ncpus = parse_cpu_set_and_warn("8 '9' 10 \"11\"", &c, NULL, "fake", 1, "CPUAffinity"); + assert_se(ncpus >= 1024); + assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 4); + for (cpu = 8; cpu < 12; cpu++) + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); + c = mfree(c); + + /* Use commas as separators */ + ncpus = parse_cpu_set_and_warn("0,1,2,3 8,9,10,11", &c, NULL, "fake", 1, "CPUAffinity"); + assert_se(ncpus >= 1024); + assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8); + for (cpu = 0; cpu < 4; cpu++) + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); + for (cpu = 8; cpu < 12; cpu++) + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); + c = mfree(c); + + /* Commas with spaces (and trailing comma, space) */ + ncpus = parse_cpu_set_and_warn("0, 1, 2, 3, 4, 5, 6, 7, ", &c, NULL, "fake", 1, "CPUAffinity"); + assert_se(ncpus >= 1024); + assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8); + for (cpu = 0; cpu < 8; cpu++) + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); + c = mfree(c); + + /* Ranges */ + ncpus = parse_cpu_set_and_warn("0-3,8-11", &c, NULL, "fake", 1, "CPUAffinity"); + assert_se(ncpus >= 1024); + assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8); + for (cpu = 0; cpu < 4; cpu++) + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); + for (cpu = 8; cpu < 12; cpu++) + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); + c = mfree(c); + + /* Ranges with trailing comma, space */ + ncpus = parse_cpu_set_and_warn("0-3 8-11, ", &c, NULL, "fake", 1, "CPUAffinity"); + assert_se(ncpus >= 1024); + assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8); + for (cpu = 0; cpu < 4; cpu++) + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); + for (cpu = 8; cpu < 12; cpu++) + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); + c = mfree(c); + + /* Negative range (returns empty cpu_set) */ + ncpus = parse_cpu_set_and_warn("3-0", &c, NULL, "fake", 1, "CPUAffinity"); + assert_se(ncpus >= 1024); + assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 0); + c = mfree(c); + + /* Overlapping ranges */ + ncpus = parse_cpu_set_and_warn("0-7 4-11", &c, NULL, "fake", 1, "CPUAffinity"); + assert_se(ncpus >= 1024); + assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 12); + for (cpu = 0; cpu < 12; cpu++) + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); + c = mfree(c); + + /* Mix ranges and individual CPUs */ + ncpus = parse_cpu_set_and_warn("0,1 4-11", &c, NULL, "fake", 1, "CPUAffinity"); + assert_se(ncpus >= 1024); + assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 10); + assert_se(CPU_ISSET_S(0, CPU_ALLOC_SIZE(ncpus), c)); + assert_se(CPU_ISSET_S(1, CPU_ALLOC_SIZE(ncpus), c)); + for (cpu = 4; cpu < 12; cpu++) + assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); + c = mfree(c); + + /* Garbage */ + ncpus = parse_cpu_set_and_warn("0 1 2 3 garbage", &c, NULL, "fake", 1, "CPUAffinity"); + assert_se(ncpus < 0); + assert_se(!c); + + /* Range with garbage */ + ncpus = parse_cpu_set_and_warn("0-3 8-garbage", &c, NULL, "fake", 1, "CPUAffinity"); + assert_se(ncpus < 0); + assert_se(!c); + + /* Empty string */ + c = NULL; + ncpus = parse_cpu_set_and_warn("", &c, NULL, "fake", 1, "CPUAffinity"); + assert_se(ncpus == 0); /* empty string returns 0 */ + assert_se(!c); + + /* Runnaway quoted string */ + ncpus = parse_cpu_set_and_warn("0 1 2 3 \"4 5 6 7 ", &c, NULL, "fake", 1, "CPUAffinity"); + assert_se(ncpus < 0); + assert_se(!c); +} + +int main(int argc, char *argv[]) { + test_parse_cpu_set(); + + return 0; +} diff --git a/src/test/test-daemon.c b/src/test/test-daemon.c index 4ce00f4b1f..a7cb426282 100644 --- a/src/test/test-daemon.c +++ b/src/test/test-daemon.c @@ -38,27 +38,27 @@ int main(int argc, char*argv[]) { sd_notify(0, "STATUS=Starting up"); - sleep(5); + sleep(1); sd_notify(0, "STATUS=Running\n" "READY=1"); - sleep(5); + sleep(1); sd_notify(0, "STATUS=Reloading\n" "RELOADING=1"); - sleep(5); + sleep(1); sd_notify(0, "STATUS=Running\n" "READY=1"); - sleep(5); + sleep(1); sd_notify(0, "STATUS=Quitting\n" "STOPPING=1"); - sleep(5); + sleep(1); return EXIT_SUCCESS; } diff --git a/src/test/test-engine.c b/src/test/test-engine.c index ca66f5b684..23da10fa1a 100644 --- a/src/test/test-engine.c +++ b/src/test/test-engine.c @@ -23,9 +23,12 @@ #include "bus-util.h" #include "manager.h" +#include "rm-rf.h" #include "test-helper.h" +#include "tests.h" int main(int argc, char *argv[]) { + _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; Manager *m = NULL; Unit *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = NULL, *h = NULL; @@ -34,9 +37,11 @@ int main(int argc, char *argv[]) { Job *j; int r; + assert_se(runtime_dir = setup_fake_runtime_dir()); + /* prepare the test */ assert_se(set_unit_path(TEST_DIR) >= 0); - r = manager_new(MANAGER_USER, true, &m); + r = manager_new(UNIT_FILE_USER, true, &m); if (MANAGER_SKIP_TEST(r)) { printf("Skipping test: manager_new: %s\n", strerror(-r)); return EXIT_TEST_SKIP; diff --git a/src/test/test-env-replace.c b/src/test/test-env-util.c index 264acc6ea6..35bb62906e 100644 --- a/src/test/test-env-replace.c +++ b/src/test/test-env-util.c @@ -2,6 +2,7 @@ This file is part of systemd. Copyright 2010 Lennart Poettering + 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 @@ -177,10 +178,37 @@ static void test_env_name_is_valid(void) { assert_se(!env_name_is_valid(NULL)); assert_se(!env_name_is_valid("")); + assert_se(!env_name_is_valid("xxx\a")); + assert_se(!env_name_is_valid("xxx\007b")); + assert_se(!env_name_is_valid("\007\009")); assert_se(!env_name_is_valid("5_starting_with_a_number_is_wrong")); assert_se(!env_name_is_valid("#¤%&?_only_numbers_letters_and_underscore_allowed")); } +static void test_env_value_is_valid(void) { + assert_se(env_value_is_valid("")); + assert_se(env_value_is_valid("głąb kapuściany")); + assert_se(env_value_is_valid("printf \"\\x1b]0;<mock-chroot>\\x07<mock-chroot>\"")); +} + +static void test_env_assignment_is_valid(void) { + assert_se(env_assignment_is_valid("a=")); + assert_se(env_assignment_is_valid("b=głąb kapuściany")); + assert_se(env_assignment_is_valid("c=\\007\\009\\011")); + assert_se(env_assignment_is_valid("e=printf \"\\x1b]0;<mock-chroot>\\x07<mock-chroot>\"")); + + assert_se(!env_assignment_is_valid("=")); + assert_se(!env_assignment_is_valid("a b=")); + assert_se(!env_assignment_is_valid("a =")); + assert_se(!env_assignment_is_valid(" b=")); + /* no dots or dashes: http://tldp.org/LDP/abs/html/gotchas.html */ + assert_se(!env_assignment_is_valid("a.b=")); + assert_se(!env_assignment_is_valid("a-b=")); + assert_se(!env_assignment_is_valid("\007=głąb kapuściany")); + assert_se(!env_assignment_is_valid("c\009=\007\009\011")); + assert_se(!env_assignment_is_valid("głąb=printf \"\x1b]0;<mock-chroot>\x07<mock-chroot>\"")); +} + int main(int argc, char *argv[]) { test_strv_env_delete(); test_strv_env_unset(); @@ -189,6 +217,8 @@ int main(int argc, char *argv[]) { test_replace_env_arg(); test_env_clean(); test_env_name_is_valid(); + test_env_value_is_valid(); + test_env_assignment_is_valid(); return 0; } diff --git a/src/test/test-escape.c b/src/test/test-escape.c new file mode 100644 index 0000000000..6cbb8443fe --- /dev/null +++ b/src/test/test-escape.c @@ -0,0 +1,114 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "alloc-util.h" +#include "escape.h" +#include "macro.h" + +static void test_cescape(void) { + _cleanup_free_ char *escaped; + + assert_se(escaped = cescape("abc\\\"\b\f\n\r\t\v\a\003\177\234\313")); + assert_se(streq(escaped, "abc\\\\\\\"\\b\\f\\n\\r\\t\\v\\a\\003\\177\\234\\313")); +} + +static void test_cunescape(void) { + _cleanup_free_ char *unescaped; + + assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", 0, &unescaped) < 0); + assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", UNESCAPE_RELAX, &unescaped) >= 0); + assert_se(streq_ptr(unescaped, "abc\\\"\b\f\a\n\r\t\v\003\177\234\313\\000\\x00")); + unescaped = mfree(unescaped); + + /* incomplete sequences */ + assert_se(cunescape("\\x0", 0, &unescaped) < 0); + assert_se(cunescape("\\x0", UNESCAPE_RELAX, &unescaped) >= 0); + assert_se(streq_ptr(unescaped, "\\x0")); + unescaped = mfree(unescaped); + + assert_se(cunescape("\\x", 0, &unescaped) < 0); + assert_se(cunescape("\\x", UNESCAPE_RELAX, &unescaped) >= 0); + assert_se(streq_ptr(unescaped, "\\x")); + unescaped = mfree(unescaped); + + assert_se(cunescape("\\", 0, &unescaped) < 0); + assert_se(cunescape("\\", UNESCAPE_RELAX, &unescaped) >= 0); + assert_se(streq_ptr(unescaped, "\\")); + unescaped = mfree(unescaped); + + assert_se(cunescape("\\11", 0, &unescaped) < 0); + assert_se(cunescape("\\11", UNESCAPE_RELAX, &unescaped) >= 0); + assert_se(streq_ptr(unescaped, "\\11")); + unescaped = mfree(unescaped); + + assert_se(cunescape("\\1", 0, &unescaped) < 0); + assert_se(cunescape("\\1", UNESCAPE_RELAX, &unescaped) >= 0); + assert_se(streq_ptr(unescaped, "\\1")); + unescaped = mfree(unescaped); + + assert_se(cunescape("\\u0000", 0, &unescaped) < 0); + assert_se(cunescape("\\u00DF\\U000000df\\u03a0\\U00000041", UNESCAPE_RELAX, &unescaped) >= 0); + assert_se(streq_ptr(unescaped, "ßßΠA")); + unescaped = mfree(unescaped); + + assert_se(cunescape("\\073", 0, &unescaped) >= 0); + assert_se(streq_ptr(unescaped, ";")); +} + +static void test_shell_escape_one(const char *s, const char *bad, const char *expected) { + _cleanup_free_ char *r; + + assert_se(r = shell_escape(s, bad)); + assert_se(streq_ptr(r, expected)); +} + +static void test_shell_escape(void) { + test_shell_escape_one("", "", ""); + test_shell_escape_one("\\", "", "\\\\"); + test_shell_escape_one("foobar", "", "foobar"); + test_shell_escape_one("foobar", "o", "f\\o\\obar"); + 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; + + assert_se(r = shell_maybe_quote(s)); + assert_se(streq(r, 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\""); +} + +int main(int argc, char *argv[]) { + test_cescape(); + test_cunescape(); + test_shell_escape(); + test_shell_maybe_quote(); + + return 0; +} diff --git a/src/test/test-execute.c b/src/test/test-execute.c index 92857cb5e2..77ef4e8b2a 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -91,7 +91,7 @@ static void test_exec_personality(Manager *m) { #elif defined(__s390__) test(m, "exec-personality-s390.service", 0, CLD_EXITED); -#else +#elif defined(__i386__) test(m, "exec-personality-x86.service", 0, CLD_EXITED); #endif } @@ -130,18 +130,33 @@ static void test_exec_systemcallerrornumber(Manager *m) { #endif } +static void test_exec_systemcall_system_mode_with_user(Manager *m) { +#ifdef HAVE_SECCOMP + if (getpwnam("nobody")) + test(m, "exec-systemcallfilter-system-user.service", 0, CLD_EXITED); + else if (getpwnam("nfsnobody")) + test(m, "exec-systemcallfilter-system-user-nfsnobody.service", 0, CLD_EXITED); + else + log_error_errno(errno, "Skipping test_exec_systemcall_system_mode_with_user, could not find nobody/nfsnobody user: %m"); +#endif +} + static void test_exec_user(Manager *m) { if (getpwnam("nobody")) test(m, "exec-user.service", 0, CLD_EXITED); + else if (getpwnam("nfsnobody")) + test(m, "exec-user-nfsnobody.service", 0, CLD_EXITED); else - log_error_errno(errno, "Skipping test_exec_user, could not find nobody user: %m"); + log_error_errno(errno, "Skipping test_exec_user, could not find nobody/nfsnobody user: %m"); } static void test_exec_group(Manager *m) { if (getgrnam("nobody")) test(m, "exec-group.service", 0, CLD_EXITED); + else if (getgrnam("nfsnobody")) + test(m, "exec-group-nfsnobody.service", 0, CLD_EXITED); else - log_error_errno(errno, "Skipping test_exec_group, could not find nobody group: %m"); + log_error_errno(errno, "Skipping test_exec_group, could not find nobody/nfsnobody group: %m"); } static void test_exec_environment(Manager *m) { @@ -204,8 +219,10 @@ static void test_exec_runtimedirectory(Manager *m) { test(m, "exec-runtimedirectory-mode.service", 0, CLD_EXITED); if (getgrnam("nobody")) test(m, "exec-runtimedirectory-owner.service", 0, CLD_EXITED); + else if (getgrnam("nfsnobody")) + test(m, "exec-runtimedirectory-owner-nfsnobody.service", 0, CLD_EXITED); else - log_error_errno(errno, "Skipping test_exec_runtimedirectory-owner, could not find nobody group: %m"); + log_error_errno(errno, "Skipping test_exec_runtimedirectory-owner, could not find nobody/nfsnobody group: %m"); } static void test_exec_capabilityboundingset(Manager *m) { @@ -234,9 +251,16 @@ static void test_exec_capabilityambientset(Manager *m) { * in the first place for the tests. */ r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0); if (r >= 0 || errno != EINVAL) { - test(m, "exec-capabilityambientset.service", 0, CLD_EXITED); - test(m, "exec-capabilityambientset-merge.service", 0, CLD_EXITED); - } + if (getpwnam("nobody")) { + test(m, "exec-capabilityambientset.service", 0, CLD_EXITED); + test(m, "exec-capabilityambientset-merge.service", 0, CLD_EXITED); + } else if (getpwnam("nfsnobody")) { + test(m, "exec-capabilityambientset-nfsnobody.service", 0, CLD_EXITED); + test(m, "exec-capabilityambientset-merge-nfsnobody.service", 0, CLD_EXITED); + } else + log_error_errno(errno, "Skipping test_exec_capabilityambientset, could not find nobody/nfsnobody user: %m"); + } else + log_error_errno(errno, "Skipping test_exec_capabilityambientset, the kernel does not support ambient capabilities: %m"); } static void test_exec_privatenetwork(Manager *m) { @@ -263,8 +287,35 @@ static void test_exec_ioschedulingclass(Manager *m) { test(m, "exec-ioschedulingclass-best-effort.service", 0, CLD_EXITED); } +static void test_exec_spec_interpolation(Manager *m) { + test(m, "exec-spec-interpolation.service", 0, CLD_EXITED); +} + +static int run_tests(UnitFileScope scope, test_function_t *tests) { + test_function_t *test = NULL; + Manager *m = NULL; + int r; + + assert_se(tests); + + r = manager_new(scope, true, &m); + if (MANAGER_SKIP_TEST(r)) { + printf("Skipping test: manager_new: %s\n", strerror(-r)); + return EXIT_TEST_SKIP; + } + assert_se(r >= 0); + assert_se(manager_startup(m, NULL, NULL) >= 0); + + for (test = tests; test && *test; test++) + (*test)(m); + + manager_free(m); + + return 0; +} + int main(int argc, char *argv[]) { - test_function_t tests[] = { + test_function_t user_tests[] = { test_exec_workingdirectory, test_exec_personality, test_exec_ignoresigpipe, @@ -284,10 +335,13 @@ int main(int argc, char *argv[]) { test_exec_capabilityambientset, test_exec_oomscoreadjust, test_exec_ioschedulingclass, + test_exec_spec_interpolation, + NULL, + }; + test_function_t system_tests[] = { + test_exec_systemcall_system_mode_with_user, NULL, }; - test_function_t *test = NULL; - Manager *m = NULL; int r; log_parse_environment(); @@ -312,18 +366,9 @@ int main(int argc, char *argv[]) { assert_se(unsetenv("VAR2") == 0); assert_se(unsetenv("VAR3") == 0); - r = manager_new(MANAGER_USER, true, &m); - if (MANAGER_SKIP_TEST(r)) { - printf("Skipping test: manager_new: %s\n", strerror(-r)); - return EXIT_TEST_SKIP; - } - assert_se(r >= 0); - assert_se(manager_startup(m, NULL, NULL) >= 0); - - for (test = tests; test && *test; test++) - (*test)(m); + r = run_tests(UNIT_FILE_USER, user_tests); + if (r != 0) + return r; - manager_free(m); - - return 0; + return run_tests(UNIT_FILE_SYSTEM, system_tests); } diff --git a/src/test/test-fd-util.c b/src/test/test-fd-util.c new file mode 100644 index 0000000000..421d3bdeb3 --- /dev/null +++ b/src/test/test-fd-util.c @@ -0,0 +1,103 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <fcntl.h> +#include <unistd.h> + +#include "alloc-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "macro.h" + +static void test_close_many(void) { + int fds[3]; + char name0[] = "/tmp/test-close-many.XXXXXX"; + char name1[] = "/tmp/test-close-many.XXXXXX"; + char name2[] = "/tmp/test-close-many.XXXXXX"; + + fds[0] = mkostemp_safe(name0, O_RDWR|O_CLOEXEC); + fds[1] = mkostemp_safe(name1, O_RDWR|O_CLOEXEC); + fds[2] = mkostemp_safe(name2, O_RDWR|O_CLOEXEC); + + close_many(fds, 2); + + assert_se(fcntl(fds[0], F_GETFD) == -1); + assert_se(fcntl(fds[1], F_GETFD) == -1); + assert_se(fcntl(fds[2], F_GETFD) >= 0); + + safe_close(fds[2]); + + unlink(name0); + unlink(name1); + unlink(name2); +} + +static void test_close_nointr(void) { + char name[] = "/tmp/test-test-close_nointr.XXXXXX"; + int fd; + + fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + assert_se(fd >= 0); + assert_se(close_nointr(fd) >= 0); + assert_se(close_nointr(fd) < 0); + + unlink(name); +} + +static void test_same_fd(void) { + _cleanup_close_pair_ int p[2] = { -1, -1 }; + _cleanup_close_ int a = -1, b = -1, c = -1; + + assert_se(pipe2(p, O_CLOEXEC) >= 0); + assert_se((a = dup(p[0])) >= 0); + assert_se((b = open("/dev/null", O_RDONLY|O_CLOEXEC)) >= 0); + assert_se((c = dup(a)) >= 0); + + assert_se(same_fd(p[0], p[0]) > 0); + assert_se(same_fd(p[1], p[1]) > 0); + assert_se(same_fd(a, a) > 0); + assert_se(same_fd(b, b) > 0); + + assert_se(same_fd(a, p[0]) > 0); + assert_se(same_fd(p[0], a) > 0); + assert_se(same_fd(c, p[0]) > 0); + assert_se(same_fd(p[0], c) > 0); + assert_se(same_fd(a, c) > 0); + assert_se(same_fd(c, a) > 0); + + assert_se(same_fd(p[0], p[1]) == 0); + assert_se(same_fd(p[1], p[0]) == 0); + assert_se(same_fd(p[0], b) == 0); + assert_se(same_fd(b, p[0]) == 0); + assert_se(same_fd(p[1], a) == 0); + assert_se(same_fd(a, p[1]) == 0); + assert_se(same_fd(p[1], b) == 0); + assert_se(same_fd(b, p[1]) == 0); + + assert_se(same_fd(a, b) == 0); + assert_se(same_fd(b, a) == 0); +} + +int main(int argc, char *argv[]) { + test_close_many(); + test_close_nointr(); + test_same_fd(); + + return 0; +} diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index 5586a2d6c1..79609765e0 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -27,6 +27,7 @@ #include "env-util.h" #include "fd-util.h" #include "fileio.h" +#include "io-util.h" #include "parse-util.h" #include "process-util.h" #include "string-util.h" @@ -288,7 +289,7 @@ static void test_capeff(void) { assert_se(r == 0); assert_se(*capeff); - p = capeff[strspn(capeff, DIGITS "abcdefABCDEF")]; + p = capeff[strspn(capeff, HEXDIGITS)]; assert_se(!p || isspace(p)); } } @@ -425,6 +426,134 @@ static void test_load_env_file_pairs(void) { unlink(fn); } +static void test_search_and_fopen(void) { + const char *dirs[] = {"/tmp/foo/bar", "/tmp", NULL}; + char name[] = "/tmp/test-search_and_fopen.XXXXXX"; + int fd = -1; + int r; + FILE *f; + + fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + assert_se(fd >= 0); + close(fd); + + r = search_and_fopen(basename(name), "r", NULL, dirs, &f); + assert_se(r >= 0); + fclose(f); + + r = search_and_fopen(name, "r", NULL, dirs, &f); + assert_se(r >= 0); + fclose(f); + + r = search_and_fopen(basename(name), "r", "/", dirs, &f); + assert_se(r >= 0); + fclose(f); + + r = search_and_fopen("/a/file/which/does/not/exist/i/guess", "r", NULL, dirs, &f); + assert_se(r < 0); + r = search_and_fopen("afilewhichdoesnotexistiguess", "r", NULL, dirs, &f); + assert_se(r < 0); + + r = unlink(name); + assert_se(r == 0); + + r = search_and_fopen(basename(name), "r", NULL, dirs, &f); + assert_se(r < 0); +} + + +static void test_search_and_fopen_nulstr(void) { + const char dirs[] = "/tmp/foo/bar\0/tmp\0"; + char name[] = "/tmp/test-search_and_fopen.XXXXXX"; + int fd = -1; + int r; + FILE *f; + + fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + assert_se(fd >= 0); + close(fd); + + r = search_and_fopen_nulstr(basename(name), "r", NULL, dirs, &f); + assert_se(r >= 0); + fclose(f); + + r = search_and_fopen_nulstr(name, "r", NULL, dirs, &f); + assert_se(r >= 0); + fclose(f); + + r = search_and_fopen_nulstr("/a/file/which/does/not/exist/i/guess", "r", NULL, dirs, &f); + assert_se(r < 0); + r = search_and_fopen_nulstr("afilewhichdoesnotexistiguess", "r", NULL, dirs, &f); + assert_se(r < 0); + + r = unlink(name); + assert_se(r == 0); + + r = search_and_fopen_nulstr(basename(name), "r", NULL, dirs, &f); + assert_se(r < 0); +} + +static void test_writing_tmpfile(void) { + char name[] = "/tmp/test-systemd_writing_tmpfile.XXXXXX"; + _cleanup_free_ char *contents = NULL; + size_t size; + int fd, r; + struct iovec iov[3]; + + IOVEC_SET_STRING(iov[0], "abc\n"); + IOVEC_SET_STRING(iov[1], ALPHANUMERICAL "\n"); + IOVEC_SET_STRING(iov[2], ""); + + fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + printf("tmpfile: %s", name); + + r = writev(fd, iov, 3); + assert_se(r >= 0); + + r = read_full_file(name, &contents, &size); + assert_se(r == 0); + printf("contents: %s", contents); + assert_se(streq(contents, "abc\n" ALPHANUMERICAL "\n")); + + unlink(name); +} + +static void test_tempfn(void) { + char *ret = NULL, *p; + + assert_se(tempfn_xxxxxx("/foo/bar/waldo", NULL, &ret) >= 0); + assert_se(streq_ptr(ret, "/foo/bar/.#waldoXXXXXX")); + free(ret); + + assert_se(tempfn_xxxxxx("/foo/bar/waldo", "[miau]", &ret) >= 0); + assert_se(streq_ptr(ret, "/foo/bar/.#[miau]waldoXXXXXX")); + free(ret); + + assert_se(tempfn_random("/foo/bar/waldo", NULL, &ret) >= 0); + assert_se(p = startswith(ret, "/foo/bar/.#waldo")); + assert_se(strlen(p) == 16); + assert_se(in_charset(p, "0123456789abcdef")); + free(ret); + + assert_se(tempfn_random("/foo/bar/waldo", "[wuff]", &ret) >= 0); + assert_se(p = startswith(ret, "/foo/bar/.#[wuff]waldo")); + assert_se(strlen(p) == 16); + assert_se(in_charset(p, "0123456789abcdef")); + free(ret); + + assert_se(tempfn_random_child("/foo/bar/waldo", NULL, &ret) >= 0); + assert_se(p = startswith(ret, "/foo/bar/waldo/.#")); + assert_se(strlen(p) == 16); + assert_se(in_charset(p, "0123456789abcdef")); + free(ret); + + assert_se(tempfn_random_child("/foo/bar/waldo", "[kikiriki]", &ret) >= 0); + assert_se(p = startswith(ret, "/foo/bar/waldo/.#[kikiriki]")); + assert_se(strlen(p) == 16); + assert_se(in_charset(p, "0123456789abcdef")); + free(ret); +} + int main(int argc, char *argv[]) { log_parse_environment(); log_open(); @@ -439,6 +568,10 @@ int main(int argc, char *argv[]) { test_write_string_file_no_create(); test_write_string_file_verify(); test_load_env_file_pairs(); + test_search_and_fopen(); + test_search_and_fopen_nulstr(); + test_writing_tmpfile(); + test_tempfn(); return 0; } diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c new file mode 100644 index 0000000000..6db2c2b6f1 --- /dev/null +++ b/src/test/test-fs-util.c @@ -0,0 +1,91 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <unistd.h> + +#include "alloc-util.h" +#include "fileio.h" +#include "fd-util.h" +#include "fs-util.h" +#include "macro.h" +#include "mkdir.h" +#include "rm-rf.h" +#include "string-util.h" +#include "strv.h" +#include "util.h" + +static void test_unlink_noerrno(void) { + char name[] = "/tmp/test-close_nointr.XXXXXX"; + int fd; + + fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + assert_se(fd >= 0); + assert_se(close_nointr(fd) >= 0); + + { + PROTECT_ERRNO; + errno = -42; + assert_se(unlink_noerrno(name) >= 0); + assert_se(errno == -42); + assert_se(unlink_noerrno(name) < 0); + assert_se(errno == -42); + } +} + +static void test_readlink_and_make_absolute(void) { + char tempdir[] = "/tmp/test-readlink_and_make_absolute"; + char name[] = "/tmp/test-readlink_and_make_absolute/original"; + char name2[] = "test-readlink_and_make_absolute/original"; + char name_alias[] = "/tmp/test-readlink_and_make_absolute-alias"; + char *r = NULL; + + assert_se(mkdir_safe(tempdir, 0755, getuid(), getgid()) >= 0); + assert_se(touch(name) >= 0); + + assert_se(symlink(name, name_alias) >= 0); + assert_se(readlink_and_make_absolute(name_alias, &r) >= 0); + assert_se(streq(r, name)); + free(r); + assert_se(unlink(name_alias) >= 0); + + assert_se(chdir(tempdir) >= 0); + assert_se(symlink(name2, name_alias) >= 0); + assert_se(readlink_and_make_absolute(name_alias, &r) >= 0); + assert_se(streq(r, name)); + free(r); + assert_se(unlink(name_alias) >= 0); + + assert_se(rm_rf(tempdir, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); +} + +static void test_get_files_in_directory(void) { + _cleanup_strv_free_ char **l = NULL, **t = NULL; + + assert_se(get_files_in_directory("/tmp", &l) >= 0); + assert_se(get_files_in_directory(".", &t) >= 0); + assert_se(get_files_in_directory(".", NULL) >= 0); +} + +int main(int argc, char *argv[]) { + test_unlink_noerrno(); + test_readlink_and_make_absolute(); + test_get_files_in_directory(); + + return 0; +} diff --git a/src/test/test-fstab-util.c b/src/test/test-fstab-util.c index ea3d1a6909..63a4b8c243 100644 --- a/src/test/test-fstab-util.c +++ b/src/test/test-fstab-util.c @@ -131,8 +131,45 @@ static void test_fstab_yes_no_option(void) { assert_se(fstab_test_yes_no_option("nofail,nofail=0,fail=0", "nofail\0fail\0") == false); } +static void test_fstab_node_to_udev_node(void) { + char *n; + + n = fstab_node_to_udev_node("LABEL=applé/jack"); + puts(n); + assert_se(streq(n, "/dev/disk/by-label/applé\\x2fjack")); + free(n); + + n = fstab_node_to_udev_node("PARTLABEL=pinkié pie"); + puts(n); + assert_se(streq(n, "/dev/disk/by-partlabel/pinkié\\x20pie")); + free(n); + + n = fstab_node_to_udev_node("UUID=037b9d94-148e-4ee4-8d38-67bfe15bb535"); + puts(n); + assert_se(streq(n, "/dev/disk/by-uuid/037b9d94-148e-4ee4-8d38-67bfe15bb535")); + free(n); + + n = fstab_node_to_udev_node("PARTUUID=037b9d94-148e-4ee4-8d38-67bfe15bb535"); + puts(n); + assert_se(streq(n, "/dev/disk/by-partuuid/037b9d94-148e-4ee4-8d38-67bfe15bb535")); + free(n); + + n = fstab_node_to_udev_node("PONIES=awesome"); + puts(n); + assert_se(streq(n, "PONIES=awesome")); + free(n); + + n = fstab_node_to_udev_node("/dev/xda1"); + puts(n); + assert_se(streq(n, "/dev/xda1")); + free(n); +} + int main(void) { test_fstab_filter_options(); test_fstab_find_pri(); test_fstab_yes_no_option(); + test_fstab_node_to_udev_node(); + + return 0; } diff --git a/src/test/test-glob-util.c b/src/test/test-glob-util.c new file mode 100644 index 0000000000..227d4290f0 --- /dev/null +++ b/src/test/test-glob-util.c @@ -0,0 +1,50 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <fcntl.h> +#include <unistd.h> + +#include "alloc-util.h" +#include "fileio.h" +#include "glob-util.h" +#include "macro.h" + +static void test_glob_exists(void) { + char name[] = "/tmp/test-glob_exists.XXXXXX"; + int fd = -1; + int r; + + fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + assert_se(fd >= 0); + close(fd); + + r = glob_exists("/tmp/test-glob_exists*"); + assert_se(r == 1); + + r = unlink(name); + assert_se(r == 0); + r = glob_exists("/tmp/test-glob_exists*"); + assert_se(r == 0); +} + +int main(void) { + test_glob_exists(); + + return 0; +} diff --git a/src/test/test-hashmap-plain.c b/src/test/test-hashmap-plain.c index 6bf33306a9..1bd5c02f87 100644 --- a/src/test/test-hashmap-plain.c +++ b/src/test/test-hashmap-plain.c @@ -323,26 +323,29 @@ static void test_hashmap_remove_value(void) { _cleanup_hashmap_free_ Hashmap *m = NULL; char *r; - r = hashmap_remove_value(NULL, "key 1", (void*) "val 1"); + char val1[] = "val 1"; + char val2[] = "val 2"; + + r = hashmap_remove_value(NULL, "key 1", val1); assert_se(r == NULL); m = hashmap_new(&string_hash_ops); assert_se(m); - r = hashmap_remove_value(m, "key 1", (void*) "val 1"); + r = hashmap_remove_value(m, "key 1", val1); assert_se(r == NULL); - hashmap_put(m, "key 1", (void*) "val 1"); - hashmap_put(m, "key 2", (void*) "val 2"); + hashmap_put(m, "key 1", val1); + hashmap_put(m, "key 2", val2); - r = hashmap_remove_value(m, "key 1", (void*) "val 1"); + r = hashmap_remove_value(m, "key 1", val1); assert_se(streq(r, "val 1")); r = hashmap_get(m, "key 2"); assert_se(streq(r, "val 2")); assert_se(!hashmap_get(m, "key 1")); - r = hashmap_remove_value(m, "key 2", (void*) "val 1"); + r = hashmap_remove_value(m, "key 2", val1); assert_se(r == NULL); r = hashmap_get(m, "key 2"); diff --git a/src/test/test-hexdecoct.c b/src/test/test-hexdecoct.c new file mode 100644 index 0000000000..276f25d091 --- /dev/null +++ b/src/test/test-hexdecoct.c @@ -0,0 +1,387 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "alloc-util.h" +#include "hexdecoct.h" +#include "macro.h" +#include "string-util.h" + +static void test_hexchar(void) { + assert_se(hexchar(0xa) == 'a'); + assert_se(hexchar(0x0) == '0'); +} + +static void test_unhexchar(void) { + assert_se(unhexchar('a') == 0xA); + assert_se(unhexchar('A') == 0xA); + assert_se(unhexchar('0') == 0x0); +} + +static void test_base32hexchar(void) { + assert_se(base32hexchar(0) == '0'); + assert_se(base32hexchar(9) == '9'); + assert_se(base32hexchar(10) == 'A'); + assert_se(base32hexchar(31) == 'V'); +} + +static void test_unbase32hexchar(void) { + assert_se(unbase32hexchar('0') == 0); + assert_se(unbase32hexchar('9') == 9); + assert_se(unbase32hexchar('A') == 10); + assert_se(unbase32hexchar('V') == 31); + assert_se(unbase32hexchar('=') == -EINVAL); +} + +static void test_base64char(void) { + assert_se(base64char(0) == 'A'); + assert_se(base64char(26) == 'a'); + assert_se(base64char(63) == '/'); +} + +static void test_unbase64char(void) { + assert_se(unbase64char('A') == 0); + assert_se(unbase64char('Z') == 25); + assert_se(unbase64char('a') == 26); + assert_se(unbase64char('z') == 51); + assert_se(unbase64char('0') == 52); + assert_se(unbase64char('9') == 61); + assert_se(unbase64char('+') == 62); + assert_se(unbase64char('/') == 63); + assert_se(unbase64char('=') == -EINVAL); +} + +static void test_octchar(void) { + assert_se(octchar(00) == '0'); + assert_se(octchar(07) == '7'); +} + +static void test_unoctchar(void) { + assert_se(unoctchar('0') == 00); + assert_se(unoctchar('7') == 07); +} + +static void test_decchar(void) { + assert_se(decchar(0) == '0'); + assert_se(decchar(9) == '9'); +} + +static void test_undecchar(void) { + assert_se(undecchar('0') == 0); + assert_se(undecchar('9') == 9); +} + +static void test_unhexmem(void) { + const char *hex = "efa214921"; + const char *hex_invalid = "efa214921o"; + _cleanup_free_ char *hex2 = NULL; + _cleanup_free_ void *mem = NULL; + size_t len; + + assert_se(unhexmem(hex, strlen(hex), &mem, &len) == 0); + assert_se(unhexmem(hex, strlen(hex) + 1, &mem, &len) == -EINVAL); + assert_se(unhexmem(hex_invalid, strlen(hex_invalid), &mem, &len) == -EINVAL); + + assert_se((hex2 = hexmem(mem, len))); + + free(mem); + + assert_se(memcmp(hex, hex2, strlen(hex)) == 0); + + free(hex2); + + assert_se(unhexmem(hex, strlen(hex) - 1, &mem, &len) == 0); + assert_se((hex2 = hexmem(mem, len))); + assert_se(memcmp(hex, hex2, strlen(hex) - 1) == 0); +} + +/* https://tools.ietf.org/html/rfc4648#section-10 */ +static void test_base32hexmem(void) { + char *b32; + + b32 = base32hexmem("", strlen(""), true); + assert_se(b32); + assert_se(streq(b32, "")); + free(b32); + + b32 = base32hexmem("f", strlen("f"), true); + assert_se(b32); + assert_se(streq(b32, "CO======")); + free(b32); + + b32 = base32hexmem("fo", strlen("fo"), true); + assert_se(b32); + assert_se(streq(b32, "CPNG====")); + free(b32); + + b32 = base32hexmem("foo", strlen("foo"), true); + assert_se(b32); + assert_se(streq(b32, "CPNMU===")); + free(b32); + + b32 = base32hexmem("foob", strlen("foob"), true); + assert_se(b32); + assert_se(streq(b32, "CPNMUOG=")); + free(b32); + + b32 = base32hexmem("fooba", strlen("fooba"), true); + assert_se(b32); + assert_se(streq(b32, "CPNMUOJ1")); + free(b32); + + b32 = base32hexmem("foobar", strlen("foobar"), true); + assert_se(b32); + assert_se(streq(b32, "CPNMUOJ1E8======")); + free(b32); + + b32 = base32hexmem("", strlen(""), false); + assert_se(b32); + assert_se(streq(b32, "")); + free(b32); + + b32 = base32hexmem("f", strlen("f"), false); + assert_se(b32); + assert_se(streq(b32, "CO")); + free(b32); + + b32 = base32hexmem("fo", strlen("fo"), false); + assert_se(b32); + assert_se(streq(b32, "CPNG")); + free(b32); + + b32 = base32hexmem("foo", strlen("foo"), false); + assert_se(b32); + assert_se(streq(b32, "CPNMU")); + free(b32); + + b32 = base32hexmem("foob", strlen("foob"), false); + assert_se(b32); + assert_se(streq(b32, "CPNMUOG")); + free(b32); + + b32 = base32hexmem("fooba", strlen("fooba"), false); + assert_se(b32); + assert_se(streq(b32, "CPNMUOJ1")); + free(b32); + + b32 = base32hexmem("foobar", strlen("foobar"), false); + assert_se(b32); + assert_se(streq(b32, "CPNMUOJ1E8")); + free(b32); +} + +static void test_unbase32hexmem(void) { + void *mem; + size_t len; + + assert_se(unbase32hexmem("", strlen(""), true, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "")); + free(mem); + + assert_se(unbase32hexmem("CO======", strlen("CO======"), true, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "f")); + free(mem); + + assert_se(unbase32hexmem("CPNG====", strlen("CPNG===="), true, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "fo")); + free(mem); + + assert_se(unbase32hexmem("CPNMU===", strlen("CPNMU==="), true, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "foo")); + free(mem); + + assert_se(unbase32hexmem("CPNMUOG=", strlen("CPNMUOG="), true, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "foob")); + free(mem); + + assert_se(unbase32hexmem("CPNMUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "fooba")); + free(mem); + + assert_se(unbase32hexmem("CPNMUOJ1E8======", strlen("CPNMUOJ1E8======"), true, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "foobar")); + free(mem); + + assert_se(unbase32hexmem("A", strlen("A"), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("A=======", strlen("A======="), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AAA=====", strlen("AAA====="), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AAAAAA==", strlen("AAAAAA=="), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AB======", strlen("AB======"), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AAAB====", strlen("AAAB===="), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AAAAB===", strlen("AAAAB==="), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AAAAAAB=", strlen("AAAAAAB="), true, &mem, &len) == -EINVAL); + + assert_se(unbase32hexmem("XPNMUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("CXNMUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("CPXMUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("CPNXUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("CPNMXOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("CPNMUXJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("CPNMUOX1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("CPNMUOJX", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL); + + assert_se(unbase32hexmem("", strlen(""), false, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "")); + free(mem); + + assert_se(unbase32hexmem("CO", strlen("CO"), false, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "f")); + free(mem); + + assert_se(unbase32hexmem("CPNG", strlen("CPNG"), false, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "fo")); + free(mem); + + assert_se(unbase32hexmem("CPNMU", strlen("CPNMU"), false, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "foo")); + free(mem); + + assert_se(unbase32hexmem("CPNMUOG", strlen("CPNMUOG"), false, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "foob")); + free(mem); + + assert_se(unbase32hexmem("CPNMUOJ1", strlen("CPNMUOJ1"), false, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "fooba")); + free(mem); + + assert_se(unbase32hexmem("CPNMUOJ1E8", strlen("CPNMUOJ1E8"), false, &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "foobar")); + free(mem); + + assert_se(unbase32hexmem("CPNMUOG=", strlen("CPNMUOG="), false, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("CPNMUOJ1E8======", strlen("CPNMUOJ1E8======"), false, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("A", strlen("A"), false, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("A", strlen("A"), false, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AAA", strlen("AAA"), false, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AAAAAA", strlen("AAAAAA"), false, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AB", strlen("AB"), false, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AAAB", strlen("AAAB"), false, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AAAAB", strlen("AAAAB"), false, &mem, &len) == -EINVAL); + assert_se(unbase32hexmem("AAAAAAB", strlen("AAAAAAB"), false, &mem, &len) == -EINVAL); +} + +/* https://tools.ietf.org/html/rfc4648#section-10 */ +static void test_base64mem(void) { + char *b64; + + assert_se(base64mem("", strlen(""), &b64) == 0); + assert_se(streq(b64, "")); + free(b64); + + assert_se(base64mem("f", strlen("f"), &b64) == 4); + assert_se(streq(b64, "Zg==")); + free(b64); + + assert_se(base64mem("fo", strlen("fo"), &b64) == 4); + assert_se(streq(b64, "Zm8=")); + free(b64); + + assert_se(base64mem("foo", strlen("foo"), &b64) == 4); + assert_se(streq(b64, "Zm9v")); + free(b64); + + assert_se(base64mem("foob", strlen("foob"), &b64) == 8); + assert_se(streq(b64, "Zm9vYg==")); + free(b64); + + assert_se(base64mem("fooba", strlen("fooba"), &b64) == 8); + assert_se(streq(b64, "Zm9vYmE=")); + free(b64); + + assert_se(base64mem("foobar", strlen("foobar"), &b64) == 8); + assert_se(streq(b64, "Zm9vYmFy")); + free(b64); +} + +static void test_unbase64mem(void) { + void *mem; + size_t len; + + assert_se(unbase64mem("", strlen(""), &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "")); + free(mem); + + assert_se(unbase64mem("Zg==", strlen("Zg=="), &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "f")); + free(mem); + + assert_se(unbase64mem("Zm8=", strlen("Zm8="), &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "fo")); + free(mem); + + assert_se(unbase64mem("Zm9v", strlen("Zm9v"), &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "foo")); + free(mem); + + assert_se(unbase64mem("Zm9vYg==", strlen("Zm9vYg=="), &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "foob")); + free(mem); + + assert_se(unbase64mem("Zm9vYmE=", strlen("Zm9vYmE="), &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "fooba")); + free(mem); + + assert_se(unbase64mem("Zm9vYmFy", strlen("Zm9vYmFy"), &mem, &len) == 0); + assert_se(streq(strndupa(mem, len), "foobar")); + free(mem); + + assert_se(unbase64mem("A", strlen("A"), &mem, &len) == -EINVAL); + assert_se(unbase64mem("A====", strlen("A===="), &mem, &len) == -EINVAL); + assert_se(unbase64mem("AAB==", strlen("AAB=="), &mem, &len) == -EINVAL); + assert_se(unbase64mem("AAAB=", strlen("AAAB="), &mem, &len) == -EINVAL); +} + +static void test_hexdump(void) { + uint8_t data[146]; + unsigned i; + + hexdump(stdout, NULL, 0); + hexdump(stdout, "", 0); + hexdump(stdout, "", 1); + hexdump(stdout, "x", 1); + hexdump(stdout, "x", 2); + hexdump(stdout, "foobar", 7); + hexdump(stdout, "f\nobar", 7); + hexdump(stdout, "xxxxxxxxxxxxxxxxxxxxyz", 23); + + for (i = 0; i < ELEMENTSOF(data); i++) + data[i] = i*2; + + hexdump(stdout, data, sizeof(data)); +} + +int main(int argc, char *argv[]) { + test_hexchar(); + test_unhexchar(); + test_base32hexchar(); + test_unbase32hexchar(); + test_base64char(); + test_unbase64char(); + test_octchar(); + test_unoctchar(); + test_decchar(); + test_undecchar(); + test_unhexmem(); + test_base32hexmem(); + test_unbase32hexmem(); + test_base64mem(); + test_unbase64mem(); + test_hexdump(); + + return 0; +} diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c index cd250ca7b8..4b9a74fca4 100644 --- a/src/test/test-install-root.c +++ b/src/test/test-install-root.c @@ -30,6 +30,8 @@ static void test_basic_mask_and_enable(const char *root) { UnitFileChange *changes = NULL; unsigned n_changes = 0; + log_set_max_level(LOG_DEBUG); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", NULL) == -ENOENT); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) == -ENOENT); assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) == -ENOENT); @@ -78,7 +80,7 @@ static void test_basic_mask_and_enable(const char *root) { assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_MASKED); /* Enabling a masked unit should fail! */ - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == -ESHUTDOWN); + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == -ERFKILL); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; @@ -105,7 +107,7 @@ static void test_basic_mask_and_enable(const char *root) { assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ENABLED); /* Enabling it again should succeed but be a NOP */ - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) == 1); + assert_se(unit_file_enable(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("a.service"), false, &changes, &n_changes) >= 0); assert_se(n_changes == 0); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; @@ -604,7 +606,7 @@ static void test_preset_and_list(const char *root) { assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(h = hashmap_new(&string_hash_ops)); - assert_se(unit_file_get_list(UNIT_FILE_SYSTEM, root, h) >= 0); + assert_se(unit_file_get_list(UNIT_FILE_SYSTEM, root, h, NULL, NULL) >= 0); p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service"); q = strjoina(root, "/usr/lib/systemd/system/preset-no.service"); @@ -628,6 +630,104 @@ static void test_preset_and_list(const char *root) { assert_se(got_yes && got_no); } +static void test_revert(const char *root) { + const char *p; + UnitFileState state; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + + assert(root); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "yy.service", NULL) == -ENOENT); + + p = strjoina(root, "/usr/lib/systemd/system/xx.service"); + assert_se(write_string_file(p, "# Empty\n", WRITE_STRING_FILE_CREATE) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", NULL) >= 0); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", &state) >= 0 && state == UNIT_FILE_STATIC); + + /* Initially there's nothing to revert */ + assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 0); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service"); + assert_se(write_string_file(p, "# Empty override\n", WRITE_STRING_FILE_CREATE) >= 0); + + /* Revert the override file */ + assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_UNLINK); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service.d/dropin.conf"); + assert_se(mkdir_parents(p, 0755) >= 0); + assert_se(write_string_file(p, "# Empty dropin\n", WRITE_STRING_FILE_CREATE) >= 0); + + /* Revert the dropin file */ + assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); + assert_se(n_changes == 2); + assert_se(changes[0].type == UNIT_FILE_UNLINK); + assert_se(streq(changes[0].path, p)); + + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/xx.service.d"); + assert_se(changes[1].type == UNIT_FILE_UNLINK); + assert_se(streq(changes[1].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; +} + +static void test_preset_order(const char *root) { + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + const char *p; + UnitFileState state; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-1.service", &state) == -ENOENT); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) == -ENOENT); + + p = strjoina(root, "/usr/lib/systemd/system/prefix-1.service"); + assert_se(write_string_file(p, + "[Install]\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system/prefix-2.service"); + assert_se(write_string_file(p, + "[Install]\n" + "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); + + p = strjoina(root, "/usr/lib/systemd/system-preset/test.preset"); + assert_se(write_string_file(p, + "enable prefix-1.service\n" + "disable prefix-*.service\n" + "enable prefix-2.service\n", WRITE_STRING_FILE_CREATE) >= 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_preset(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("prefix-1.service"), UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0); + assert_se(n_changes == 1); + assert_se(changes[0].type == UNIT_FILE_SYMLINK); + assert_se(streq(changes[0].source, "/usr/lib/systemd/system/prefix-1.service")); + p = strjoina(root, SYSTEM_CONFIG_UNIT_PATH"/multi-user.target.wants/prefix-1.service"); + assert_se(streq(changes[0].path, p)); + unit_file_changes_free(changes, n_changes); + changes = NULL; n_changes = 0; + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + + assert_se(unit_file_preset(UNIT_FILE_SYSTEM, false, root, STRV_MAKE("prefix-2.service"), UNIT_FILE_PRESET_FULL, false, &changes, &n_changes) >= 0); + assert_se(n_changes == 0); + + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); +} + int main(int argc, char *argv[]) { char root[] = "/tmp/rootXXXXXX"; const char *p; @@ -656,6 +756,8 @@ int main(int argc, char *argv[]) { test_template_enable(root); test_indirect(root); test_preset_and_list(root); + test_preset_order(root); + test_revert(root); assert_se(rm_rf(root, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); diff --git a/src/test/test-install.c b/src/test/test-install.c index 874d617621..0ac85f040a 100644 --- a/src/test/test-install.c +++ b/src/test/test-install.c @@ -46,8 +46,11 @@ int main(int argc, char* argv[]) { unsigned n_changes = 0; UnitFileState state = 0; + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + h = hashmap_new(&string_hash_ops); - r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h); + r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h, NULL, NULL); assert_se(r == 0); HASHMAP_FOREACH(p, h, i) { @@ -65,12 +68,12 @@ int main(int argc, char* argv[]) { unit_file_list_free(h); - log_error("enable"); + log_info("/*** enable **/"); r = unit_file_enable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes); assert_se(r >= 0); - log_error("enable2"); + log_info("/*** enable2 **/"); r = unit_file_enable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes); assert_se(r >= 0); @@ -82,8 +85,7 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_ENABLED); - log_error("disable"); - + log_info("/*** disable ***/"); changes = NULL; n_changes = 0; @@ -97,13 +99,13 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_DISABLED); - log_error("mask"); + log_info("/*** mask ***/"); changes = NULL; n_changes = 0; r = unit_file_mask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes); assert_se(r >= 0); - log_error("mask2"); + log_info("/*** mask2 ***/"); r = unit_file_mask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes); assert_se(r >= 0); @@ -114,13 +116,13 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_MASKED); - log_error("unmask"); + log_info("/*** unmask ***/"); changes = NULL; n_changes = 0; r = unit_file_unmask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); - log_error("unmask2"); + log_info("/*** unmask2 ***/"); r = unit_file_unmask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); @@ -131,7 +133,7 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_DISABLED); - log_error("mask"); + log_info("/*** mask ***/"); changes = NULL; n_changes = 0; @@ -145,13 +147,13 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_MASKED); - log_error("disable"); + log_info("/*** disable ***/"); changes = NULL; n_changes = 0; r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); - log_error("disable2"); + log_info("/*** disable2 ***/"); r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); @@ -162,7 +164,7 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_MASKED); - log_error("umask"); + log_info("/*** umask ***/"); changes = NULL; n_changes = 0; @@ -176,7 +178,7 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_DISABLED); - log_error("enable files2"); + log_info("/*** enable files2 ***/"); changes = NULL; n_changes = 0; @@ -190,7 +192,7 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_ENABLED); - log_error("disable files2"); + log_info("/*** disable files2 ***/"); changes = NULL; n_changes = 0; @@ -203,7 +205,7 @@ int main(int argc, char* argv[]) { r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); assert_se(r < 0); - log_error("link files2"); + log_info("/*** link files2 ***/"); changes = NULL; n_changes = 0; @@ -217,7 +219,7 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_LINKED); - log_error("disable files2"); + log_info("/*** disable files2 ***/"); changes = NULL; n_changes = 0; @@ -230,7 +232,7 @@ int main(int argc, char* argv[]) { r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); assert_se(r < 0); - log_error("link files2"); + log_info("/*** link files2 ***/"); changes = NULL; n_changes = 0; @@ -244,7 +246,7 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_LINKED); - log_error("reenable files2"); + log_info("/*** reenable files2 ***/"); changes = NULL; n_changes = 0; @@ -258,7 +260,7 @@ int main(int argc, char* argv[]) { assert_se(r >= 0); assert_se(state == UNIT_FILE_ENABLED); - log_error("disable files2"); + log_info("/*** disable files2 ***/"); changes = NULL; n_changes = 0; @@ -270,7 +272,7 @@ int main(int argc, char* argv[]) { r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); assert_se(r < 0); - log_error("preset files"); + log_info("/*** preset files ***/"); changes = NULL; n_changes = 0; diff --git a/src/test/test-io-util.c b/src/test/test-io-util.c new file mode 100644 index 0000000000..10bd3833bc --- /dev/null +++ b/src/test/test-io-util.c @@ -0,0 +1,69 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> + +#include "alloc-util.h" +#include "fd-util.h" +#include "io-util.h" +#include "macro.h" + +static void test_sparse_write_one(int fd, const char *buffer, size_t n) { + char check[n]; + + assert_se(lseek(fd, 0, SEEK_SET) == 0); + assert_se(ftruncate(fd, 0) >= 0); + assert_se(sparse_write(fd, buffer, n, 4) == (ssize_t) n); + + assert_se(lseek(fd, 0, SEEK_CUR) == (off_t) n); + assert_se(ftruncate(fd, n) >= 0); + + assert_se(lseek(fd, 0, SEEK_SET) == 0); + assert_se(read(fd, check, n) == (ssize_t) n); + + assert_se(memcmp(buffer, check, n) == 0); +} + +static void test_sparse_write(void) { + const char test_a[] = "test"; + const char test_b[] = "\0\0\0\0test\0\0\0\0"; + const char test_c[] = "\0\0test\0\0\0\0"; + const char test_d[] = "\0\0test\0\0\0test\0\0\0\0test\0\0\0\0\0test\0\0\0test\0\0\0\0test\0\0\0\0\0\0\0\0"; + const char test_e[] = "test\0\0\0\0test"; + _cleanup_close_ int fd = -1; + char fn[] = "/tmp/sparseXXXXXX"; + + fd = mkostemp(fn, O_CLOEXEC); + assert_se(fd >= 0); + unlink(fn); + + test_sparse_write_one(fd, test_a, sizeof(test_a)); + test_sparse_write_one(fd, test_b, sizeof(test_b)); + test_sparse_write_one(fd, test_c, sizeof(test_c)); + test_sparse_write_one(fd, test_d, sizeof(test_d)); + test_sparse_write_one(fd, test_e, sizeof(test_e)); +} + +int main(void) { + test_sparse_write(); + + return 0; +} diff --git a/src/test/test-ipcrm.c b/src/test/test-ipcrm.c index 2464d32458..c5bcaf47bb 100644 --- a/src/test/test-ipcrm.c +++ b/src/test/test-ipcrm.c @@ -23,9 +23,14 @@ int main(int argc, char *argv[]) { uid_t uid; - - assert_se(argc == 2); - assert_se(parse_uid(argv[1], &uid) >= 0); + int r; + const char* name = argv[1] ?: "nfsnobody"; + + r = get_user_creds(&name, &uid, NULL, NULL, NULL); + if (r < 0) { + log_error_errno(r, "Failed to resolve \"%s\": %m", name); + return EXIT_FAILURE; + } return clean_ipc(uid) < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/test/test-json.c b/src/test/test-json.c deleted file mode 100644 index 3fe2f58d04..0000000000 --- a/src/test/test-json.c +++ /dev/null @@ -1,202 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2014 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <math.h> - -#include "alloc-util.h" -#include "json.h" -#include "string-util.h" -#include "util.h" - -static void test_one(const char *data, ...) { - void *state = NULL; - va_list ap; - - va_start(ap, data); - - for (;;) { - _cleanup_free_ char *str = NULL; - union json_value v = {}; - int t, tt; - - t = json_tokenize(&data, &str, &v, &state, NULL); - tt = va_arg(ap, int); - - assert_se(t == tt); - - if (t == JSON_END || t < 0) - break; - - else if (t == JSON_STRING) { - const char *nn; - - nn = va_arg(ap, const char *); - assert_se(streq_ptr(nn, str)); - - } else if (t == JSON_REAL) { - double d; - - d = va_arg(ap, double); - assert_se(fabs(d - v.real) < 0.001); - - } else if (t == JSON_INTEGER) { - intmax_t i; - - i = va_arg(ap, intmax_t); - assert_se(i == v.integer); - - } else if (t == JSON_BOOLEAN) { - bool b; - - b = va_arg(ap, int); - assert_se(b == v.boolean); - } - } - - va_end(ap); -} - -typedef void (*Test)(JsonVariant *); - -static void test_file(const char *data, Test test) { - _cleanup_json_variant_unref_ JsonVariant *v = NULL; - int r; - - r = json_parse(data, &v); - assert_se(r == 0); - assert_se(v != NULL); - assert_se(v->type == JSON_VARIANT_OBJECT); - - if (test) - test(v); -} - -static void test_1(JsonVariant *v) { - JsonVariant *p, *q; - unsigned i; - - /* 3 keys + 3 values */ - assert_se(v->size == 6); - - /* has k */ - p = json_variant_value(v, "k"); - assert_se(p && p->type == JSON_VARIANT_STRING); - - /* k equals v */ - assert_se(streq(json_variant_string(p), "v")); - - /* has foo */ - p = json_variant_value(v, "foo"); - assert_se(p && p->type == JSON_VARIANT_ARRAY && p->size == 3); - - /* check foo[0] = 1, foo[1] = 2, foo[2] = 3 */ - for (i = 0; i < 3; ++i) { - q = json_variant_element(p, i); - assert_se(q && q->type == JSON_VARIANT_INTEGER && json_variant_integer(q) == (i+1)); - } - - /* has bar */ - p = json_variant_value(v, "bar"); - assert_se(p && p->type == JSON_VARIANT_OBJECT && p->size == 2); - - /* zap is null */ - q = json_variant_value(p, "zap"); - assert_se(q && q->type == JSON_VARIANT_NULL); -} - -static void test_2(JsonVariant *v) { - JsonVariant *p, *q; - - /* 2 keys + 2 values */ - assert_se(v->size == 4); - - /* has mutant */ - p = json_variant_value(v, "mutant"); - assert_se(p && p->type == JSON_VARIANT_ARRAY && p->size == 4); - - /* mutant[0] == 1 */ - q = json_variant_element(p, 0); - assert_se(q && q->type == JSON_VARIANT_INTEGER && json_variant_integer(q) == 1); - - /* mutant[1] == null */ - q = json_variant_element(p, 1); - assert_se(q && q->type == JSON_VARIANT_NULL); - - /* mutant[2] == "1" */ - q = json_variant_element(p, 2); - assert_se(q && q->type == JSON_VARIANT_STRING && streq(json_variant_string(q), "1")); - - /* mutant[3] == JSON_VARIANT_OBJECT */ - q = json_variant_element(p, 3); - assert_se(q && q->type == JSON_VARIANT_OBJECT && q->size == 2); - - /* has 1 */ - p = json_variant_value(q, "1"); - assert_se(p && p->type == JSON_VARIANT_ARRAY && p->size == 2); - - /* "1"[0] == 1 */ - q = json_variant_element(p, 0); - assert_se(q && q->type == JSON_VARIANT_INTEGER && json_variant_integer(q) == 1); - - /* "1"[1] == "1" */ - q = json_variant_element(p, 1); - assert_se(q && q->type == JSON_VARIANT_STRING && streq(json_variant_string(q), "1")); - - /* has blah */ - p = json_variant_value(v, "blah"); - assert_se(p && p->type == JSON_VARIANT_REAL && fabs(json_variant_real(p) - 1.27) < 0.001); -} - -int main(int argc, char *argv[]) { - - test_one("x", -EINVAL); - test_one("", JSON_END); - test_one(" ", JSON_END); - test_one("0", JSON_INTEGER, (intmax_t) 0, JSON_END); - test_one("1234", JSON_INTEGER, (intmax_t) 1234, JSON_END); - test_one("3.141", JSON_REAL, 3.141, JSON_END); - test_one("0.0", JSON_REAL, 0.0, JSON_END); - test_one("7e3", JSON_REAL, 7e3, JSON_END); - test_one("-7e-3", JSON_REAL, -7e-3, JSON_END); - test_one("true", JSON_BOOLEAN, true, JSON_END); - test_one("false", JSON_BOOLEAN, false, JSON_END); - test_one("null", JSON_NULL, JSON_END); - test_one("{}", JSON_OBJECT_OPEN, JSON_OBJECT_CLOSE, JSON_END); - test_one("\t {\n} \n", JSON_OBJECT_OPEN, JSON_OBJECT_CLOSE, JSON_END); - test_one("[]", JSON_ARRAY_OPEN, JSON_ARRAY_CLOSE, JSON_END); - test_one("\t [] \n\n", JSON_ARRAY_OPEN, JSON_ARRAY_CLOSE, JSON_END); - test_one("\"\"", JSON_STRING, "", JSON_END); - test_one("\"foo\"", JSON_STRING, "foo", JSON_END); - test_one("\"foo\\nfoo\"", JSON_STRING, "foo\nfoo", JSON_END); - test_one("{\"foo\" : \"bar\"}", JSON_OBJECT_OPEN, JSON_STRING, "foo", JSON_COLON, JSON_STRING, "bar", JSON_OBJECT_CLOSE, JSON_END); - test_one("{\"foo\" : [true, false]}", JSON_OBJECT_OPEN, JSON_STRING, "foo", JSON_COLON, JSON_ARRAY_OPEN, JSON_BOOLEAN, true, JSON_COMMA, JSON_BOOLEAN, false, JSON_ARRAY_CLOSE, JSON_OBJECT_CLOSE, JSON_END); - test_one("\"\xef\xbf\xbd\"", JSON_STRING, "\xef\xbf\xbd", JSON_END); - test_one("\"\\ufffd\"", JSON_STRING, "\xef\xbf\xbd", JSON_END); - test_one("\"\\uf\"", -EINVAL); - test_one("\"\\ud800a\"", -EINVAL); - test_one("\"\\udc00\\udc00\"", -EINVAL); - test_one("\"\\ud801\\udc37\"", JSON_STRING, "\xf0\x90\x90\xb7", JSON_END); - - test_one("[1, 2]", JSON_ARRAY_OPEN, JSON_INTEGER, (intmax_t) 1, JSON_COMMA, JSON_INTEGER, (intmax_t) 2, JSON_ARRAY_CLOSE, JSON_END); - - test_file("{\"k\": \"v\", \"foo\": [1, 2, 3], \"bar\": {\"zap\": null}}", test_1); - test_file("{\"mutant\": [1, null, \"1\", {\"1\": [1, \"1\"]}], \"blah\": 1.27}", test_2); - - return 0; -} diff --git a/src/test/test-libudev.c b/src/test/test-libudev.c index a7eb60e8cf..e28de9b37b 100644 --- a/src/test/test-libudev.c +++ b/src/test/test-libudev.c @@ -24,170 +24,140 @@ #include "libudev.h" +#include "fd-util.h" +#include "log.h" #include "stdio-util.h" #include "string-util.h" #include "udev-util.h" #include "util.h" -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) - static void print_device(struct udev_device *device) { const char *str; dev_t devnum; int count; struct udev_list_entry *list_entry; - printf("*** device: %p ***\n", device); + log_info("*** device: %p ***", device); str = udev_device_get_action(device); if (str != NULL) - printf("action: '%s'\n", str); + log_info("action: '%s'", str); str = udev_device_get_syspath(device); - printf("syspath: '%s'\n", str); + log_info("syspath: '%s'", str); str = udev_device_get_sysname(device); - printf("sysname: '%s'\n", str); + log_info("sysname: '%s'", str); str = udev_device_get_sysnum(device); if (str != NULL) - printf("sysnum: '%s'\n", str); + log_info("sysnum: '%s'", str); str = udev_device_get_devpath(device); - printf("devpath: '%s'\n", str); + log_info("devpath: '%s'", str); str = udev_device_get_subsystem(device); if (str != NULL) - printf("subsystem: '%s'\n", str); + log_info("subsystem: '%s'", str); str = udev_device_get_devtype(device); if (str != NULL) - printf("devtype: '%s'\n", str); + log_info("devtype: '%s'", str); str = udev_device_get_driver(device); if (str != NULL) - printf("driver: '%s'\n", str); + log_info("driver: '%s'", str); str = udev_device_get_devnode(device); if (str != NULL) - printf("devname: '%s'\n", str); + log_info("devname: '%s'", str); devnum = udev_device_get_devnum(device); if (major(devnum) > 0) - printf("devnum: %u:%u\n", major(devnum), minor(devnum)); + log_info("devnum: %u:%u", major(devnum), minor(devnum)); count = 0; udev_list_entry_foreach(list_entry, udev_device_get_devlinks_list_entry(device)) { - printf("link: '%s'\n", udev_list_entry_get_name(list_entry)); + log_info("link: '%s'", udev_list_entry_get_name(list_entry)); count++; } if (count > 0) - printf("found %i links\n", count); + log_info("found %i links", count); count = 0; udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(device)) { - printf("property: '%s=%s'\n", + log_info("property: '%s=%s'", udev_list_entry_get_name(list_entry), udev_list_entry_get_value(list_entry)); count++; } if (count > 0) - printf("found %i properties\n", count); + log_info("found %i properties", count); str = udev_device_get_property_value(device, "MAJOR"); if (str != NULL) - printf("MAJOR: '%s'\n", str); + log_info("MAJOR: '%s'", str); str = udev_device_get_sysattr_value(device, "dev"); if (str != NULL) - printf("attr{dev}: '%s'\n", str); - - printf("\n"); + log_info("attr{dev}: '%s'", str); } -static int test_device(struct udev *udev, const char *syspath) { +static void test_device(struct udev *udev, const char *syspath) { _cleanup_udev_device_unref_ struct udev_device *device; - printf("looking at device: %s\n", syspath); + log_info("looking at device: %s", syspath); device = udev_device_new_from_syspath(udev, syspath); - if (device == NULL) { - printf("no device found\n"); - return -1; - } - print_device(device); - - return 0; + if (device == NULL) + log_warning_errno(errno, "udev_device_new_from_syspath: %m"); + else + print_device(device); } -static int test_device_parents(struct udev *udev, const char *syspath) { +static void test_device_parents(struct udev *udev, const char *syspath) { _cleanup_udev_device_unref_ struct udev_device *device; struct udev_device *device_parent; - printf("looking at device: %s\n", syspath); + log_info("looking at device: %s", syspath); device = udev_device_new_from_syspath(udev, syspath); if (device == NULL) - return -1; + return; - printf("looking at parents\n"); + log_info("looking at parents"); device_parent = device; do { print_device(device_parent); device_parent = udev_device_get_parent(device_parent); } while (device_parent != NULL); - printf("looking at parents again\n"); + log_info("looking at parents again"); device_parent = device; do { print_device(device_parent); device_parent = udev_device_get_parent(device_parent); } while (device_parent != NULL); - - return 0; } -static int test_device_devnum(struct udev *udev) { +static void test_device_devnum(struct udev *udev) { dev_t devnum = makedev(1, 3); - struct udev_device *device; + _cleanup_udev_device_unref_ struct udev_device *device; - printf("looking up device: %u:%u\n", major(devnum), minor(devnum)); + log_info("looking up device: %u:%u", major(devnum), minor(devnum)); device = udev_device_new_from_devnum(udev, 'c', devnum); if (device == NULL) - return -1; - print_device(device); - udev_device_unref(device); - return 0; + log_warning_errno(errno, "udev_device_new_from_devnum: %m"); + else + print_device(device); } -static int test_device_subsys_name(struct udev *udev) { - struct udev_device *device; - - printf("looking up device: 'block':'sda'\n"); - device = udev_device_new_from_subsystem_sysname(udev, "block", "sda"); - if (device == NULL) - return -1; - print_device(device); - udev_device_unref(device); - - printf("looking up device: 'subsystem':'pci'\n"); - device = udev_device_new_from_subsystem_sysname(udev, "subsystem", "pci"); - if (device == NULL) - return -1; - print_device(device); - udev_device_unref(device); - - printf("looking up device: 'drivers':'scsi:sd'\n"); - device = udev_device_new_from_subsystem_sysname(udev, "drivers", "scsi:sd"); - if (device == NULL) - return -1; - print_device(device); - udev_device_unref(device); +static void test_device_subsys_name(struct udev *udev, const char *subsys, const char *dev) { + _cleanup_udev_device_unref_ struct udev_device *device; - printf("looking up device: 'module':'printk'\n"); - device = udev_device_new_from_subsystem_sysname(udev, "module", "printk"); + log_info("looking up device: '%s:%s'", subsys, dev); + device = udev_device_new_from_subsystem_sysname(udev, subsys, dev); if (device == NULL) - return -1; - print_device(device); - udev_device_unref(device); - return 0; + log_warning_errno(errno, "udev_device_new_from_subsystem_sysname: %m"); + else + print_device(device); } static int test_enumerate_print_list(struct udev_enumerate *enumerate) { @@ -200,63 +170,45 @@ static int test_enumerate_print_list(struct udev_enumerate *enumerate) { device = udev_device_new_from_syspath(udev_enumerate_get_udev(enumerate), udev_list_entry_get_name(list_entry)); if (device != NULL) { - printf("device: '%s' (%s)\n", - udev_device_get_syspath(device), - udev_device_get_subsystem(device)); + log_info("device: '%s' (%s)", + udev_device_get_syspath(device), + udev_device_get_subsystem(device)); udev_device_unref(device); count++; } } - printf("found %i devices\n\n", count); + log_info("found %i devices", count); return count; } -static int test_monitor(struct udev *udev) { - struct udev_monitor *udev_monitor = NULL; - int fd_ep; - int fd_udev = -1; - struct epoll_event ep_udev, ep_stdin; +static void test_monitor(struct udev *udev) { + _cleanup_udev_monitor_unref_ struct udev_monitor *udev_monitor; + _cleanup_close_ int fd_ep; + int fd_udev; + struct epoll_event ep_udev = { + .events = EPOLLIN, + }, ep_stdin = { + .events = EPOLLIN, + .data.fd = STDIN_FILENO, + }; fd_ep = epoll_create1(EPOLL_CLOEXEC); - if (fd_ep < 0) { - printf("error creating epoll fd: %m\n"); - goto out; - } + assert_se(fd_ep >= 0); udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); - if (udev_monitor == NULL) { - printf("no socket\n"); - goto out; - } + assert_se(udev_monitor != NULL); + fd_udev = udev_monitor_get_fd(udev_monitor); + ep_udev.data.fd = fd_udev; - if (udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "block", NULL) < 0 || - udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "tty", NULL) < 0 || - udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "usb", "usb_device") < 0) { - printf("filter failed\n"); - goto out; - } + assert_se(udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "block", NULL) >= 0); + assert_se(udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "tty", NULL) >= 0); + assert_se(udev_monitor_filter_add_match_subsystem_devtype(udev_monitor, "usb", "usb_device") >= 0); - if (udev_monitor_enable_receiving(udev_monitor) < 0) { - printf("bind failed\n"); - goto out; - } + assert_se(udev_monitor_enable_receiving(udev_monitor) >= 0); - memzero(&ep_udev, sizeof(struct epoll_event)); - ep_udev.events = EPOLLIN; - ep_udev.data.fd = fd_udev; - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) { - printf("fail to add fd to epoll: %m\n"); - goto out; - } - - memzero(&ep_stdin, sizeof(struct epoll_event)); - ep_stdin.events = EPOLLIN; - ep_stdin.data.fd = STDIN_FILENO; - if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, STDIN_FILENO, &ep_stdin) < 0) { - printf("fail to add fd to epoll: %m\n"); - goto out; - } + assert_se(epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) >= 0); + assert_se(epoll_ctl(fd_ep, EPOLL_CTL_ADD, STDIN_FILENO, &ep_stdin) >= 0); for (;;) { int fdcount; @@ -265,7 +217,7 @@ static int test_monitor(struct udev *udev) { int i; printf("waiting for events from udev, press ENTER to exit\n"); - fdcount = epoll_wait(fd_ep, ev, ARRAY_SIZE(ev), -1); + fdcount = epoll_wait(fd_ep, ev, ELEMENTSOF(ev), -1); printf("epoll fd count: %i\n", fdcount); for (i = 0; i < fdcount; i++) { @@ -279,36 +231,29 @@ static int test_monitor(struct udev *udev) { udev_device_unref(device); } else if (ev[i].data.fd == STDIN_FILENO && ev[i].events & EPOLLIN) { printf("exiting loop\n"); - goto out; + return; } } } -out: - if (fd_ep >= 0) - close(fd_ep); - udev_monitor_unref(udev_monitor); - return 0; } -static int test_queue(struct udev *udev) { +static void test_queue(struct udev *udev) { struct udev_queue *udev_queue; + bool empty; udev_queue = udev_queue_new(udev); - if (udev_queue == NULL) - return -1; - - if (udev_queue_get_queue_is_empty(udev_queue)) - printf("queue is empty\n"); + assert_se(udev_queue); + empty = udev_queue_get_queue_is_empty(udev_queue); + log_info("queue is %s", empty ? "empty" : "not empty"); udev_queue_unref(udev_queue); - return 0; } static int test_enumerate(struct udev *udev, const char *subsystem) { struct udev_enumerate *udev_enumerate; int r; - printf("enumerate '%s'\n", subsystem == NULL ? "<all>" : subsystem); + log_info("enumerate '%s'", subsystem == NULL ? "<all>" : subsystem); udev_enumerate = udev_enumerate_new(udev); if (udev_enumerate == NULL) return -1; @@ -317,7 +262,7 @@ static int test_enumerate(struct udev *udev, const char *subsystem) { test_enumerate_print_list(udev_enumerate); udev_enumerate_unref(udev_enumerate); - printf("enumerate 'net' + duplicated scan + null + zero\n"); + log_info("enumerate 'net' + duplicated scan + null + zero"); udev_enumerate = udev_enumerate_new(udev); if (udev_enumerate == NULL) return -1; @@ -337,7 +282,7 @@ static int test_enumerate(struct udev *udev, const char *subsystem) { test_enumerate_print_list(udev_enumerate); udev_enumerate_unref(udev_enumerate); - printf("enumerate 'block'\n"); + log_info("enumerate 'block'"); udev_enumerate = udev_enumerate_new(udev); if (udev_enumerate == NULL) return -1; @@ -351,7 +296,7 @@ static int test_enumerate(struct udev *udev, const char *subsystem) { test_enumerate_print_list(udev_enumerate); udev_enumerate_unref(udev_enumerate); - printf("enumerate 'not block'\n"); + log_info("enumerate 'not block'"); udev_enumerate = udev_enumerate_new(udev); if (udev_enumerate == NULL) return -1; @@ -360,7 +305,7 @@ static int test_enumerate(struct udev *udev, const char *subsystem) { test_enumerate_print_list(udev_enumerate); udev_enumerate_unref(udev_enumerate); - printf("enumerate 'pci, mem, vc'\n"); + log_info("enumerate 'pci, mem, vc'"); udev_enumerate = udev_enumerate_new(udev); if (udev_enumerate == NULL) return -1; @@ -371,7 +316,7 @@ static int test_enumerate(struct udev *udev, const char *subsystem) { test_enumerate_print_list(udev_enumerate); udev_enumerate_unref(udev_enumerate); - printf("enumerate 'subsystem'\n"); + log_info("enumerate 'subsystem'"); udev_enumerate = udev_enumerate_new(udev); if (udev_enumerate == NULL) return -1; @@ -379,7 +324,7 @@ static int test_enumerate(struct udev *udev, const char *subsystem) { test_enumerate_print_list(udev_enumerate); udev_enumerate_unref(udev_enumerate); - printf("enumerate 'property IF_FS_*=filesystem'\n"); + log_info("enumerate 'property IF_FS_*=filesystem'"); udev_enumerate = udev_enumerate_new(udev); if (udev_enumerate == NULL) return -1; @@ -397,32 +342,32 @@ static void test_hwdb(struct udev *udev, const char *modalias) { hwdb = udev_hwdb_new(udev); udev_list_entry_foreach(entry, udev_hwdb_get_properties_list_entry(hwdb, modalias, 0)) - printf("'%s'='%s'\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry)); - printf("\n"); + log_info("'%s'='%s'", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry)); hwdb = udev_hwdb_unref(hwdb); assert_se(hwdb == NULL); } int main(int argc, char *argv[]) { - struct udev *udev = NULL; + _cleanup_udev_unref_ struct udev *udev = NULL; + bool arg_monitor = false; static const struct option options[] = { - { "syspath", required_argument, NULL, 'p' }, + { "syspath", required_argument, NULL, 'p' }, { "subsystem", required_argument, NULL, 's' }, - { "debug", no_argument, NULL, 'd' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'V' }, + { "debug", no_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { "monitor", no_argument, NULL, 'm' }, {} }; const char *syspath = "/devices/virtual/mem/null"; const char *subsystem = NULL; - char path[1024]; int c; udev = udev_new(); - printf("context: %p\n", udev); + log_info("context: %p", udev); if (udev == NULL) { - printf("no context\n"); + log_info("no context"); return 1; } @@ -444,14 +389,18 @@ int main(int argc, char *argv[]) { case 'h': printf("--debug --syspath= --subsystem= --help\n"); - goto out; + return EXIT_SUCCESS; case 'V': printf("%s\n", VERSION); - goto out; + return EXIT_SUCCESS; + + case 'm': + arg_monitor = true; + break; case '?': - goto out; + return EXIT_FAILURE; default: assert_not_reached("Unhandled option code."); @@ -459,14 +408,16 @@ int main(int argc, char *argv[]) { /* add sys path if needed */ - if (!startswith(syspath, "/sys")) { - xsprintf(path, "/sys/%s", syspath); - syspath = path; - } + if (!startswith(syspath, "/sys")) + syspath = strjoina("/sys/", syspath); test_device(udev, syspath); test_device_devnum(udev); - test_device_subsys_name(udev); + test_device_subsys_name(udev, "block", "sda"); + test_device_subsys_name(udev, "subsystem", "pci"); + test_device_subsys_name(udev, "drivers", "scsi:sd"); + test_device_subsys_name(udev, "module", "printk"); + test_device_parents(udev, syspath); test_enumerate(udev, subsystem); @@ -475,8 +426,8 @@ int main(int argc, char *argv[]) { test_hwdb(udev, "usb:v0D50p0011*"); - test_monitor(udev); -out: - udev_unref(udev); - return 0; + if (arg_monitor) + test_monitor(udev); + + return EXIT_SUCCESS; } diff --git a/src/test/test-loopback.c b/src/test/test-loopback.c index 2748395ade..7b67337331 100644 --- a/src/test/test-loopback.c +++ b/src/test/test-loopback.c @@ -31,7 +31,7 @@ int main(int argc, char* argv[]) { r = loopback_setup(); if (r < 0) - fprintf(stderr, "loopback: %s\n", strerror(-r)); + log_error("loopback: %m"); - return 0; + return r >= 0 ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/test/test-namespace.c b/src/test/test-namespace.c index 0b2f9e9173..ff9f35cecd 100644 --- a/src/test/test-namespace.c +++ b/src/test/test-namespace.c @@ -69,8 +69,10 @@ static void test_netns(void) { int r, n = 0; siginfo_t si; - if (geteuid() > 0) - return; + if (geteuid() > 0) { + log_info("Skipping test: not root"); + exit(EXIT_TEST_SKIP); + } assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, s) >= 0); @@ -124,6 +126,9 @@ int main(int argc, char *argv[]) { char boot_id[SD_ID128_STRING_MAX]; _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *zz = NULL; + log_parse_environment(); + log_open(); + assert_se(sd_id128_get_boot(&bid) >= 0); sd_id128_to_string(bid, boot_id); diff --git a/src/test/test-netlink-manual.c b/src/test/test-netlink-manual.c index 79ff6ae74d..bc6dd0926c 100644 --- a/src/test/test-netlink-manual.c +++ b/src/test/test-netlink-manual.c @@ -68,10 +68,10 @@ static int test_tunnel_configure(sd_netlink *rtnl) { /* skip test if module cannot be loaded */ r = load_module("ipip"); - if(r < 0) + if (r < 0) return EXIT_TEST_SKIP; - if(getuid() != 0) + if (getuid() != 0) return EXIT_TEST_SKIP; /* IPIP tunnel */ @@ -99,7 +99,7 @@ static int test_tunnel_configure(sd_netlink *rtnl) { assert_se((m = sd_netlink_message_unref(m)) == NULL); r = load_module("sit"); - if(r < 0) + if (r < 0) return EXIT_TEST_SKIP; /* sit */ diff --git a/src/test/test-ns.c b/src/test/test-ns.c index cf627be6c5..9248f2987c 100644 --- a/src/test/test-ns.c +++ b/src/test/test-ns.c @@ -68,7 +68,6 @@ int main(int argc, char *argv[]) { (char **) inaccessible, tmp_dir, var_tmp_dir, - NULL, true, PROTECT_HOME_NO, PROTECT_SYSTEM_NO, diff --git a/src/test/test-nss.c b/src/test/test-nss.c new file mode 100644 index 0000000000..55af592287 --- /dev/null +++ b/src/test/test-nss.c @@ -0,0 +1,454 @@ +/*** + 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 <net/if.h> + +#include "log.h" +#include "nss-util.h" +#include "path-util.h" +#include "string-util.h" +#include "alloc-util.h" +#include "in-addr-util.h" +#include "hexdecoct.h" +#include "af-list.h" +#include "stdio-util.h" +#include "strv.h" +#include "errno-list.h" +#include "hostname-util.h" +#include "local-addresses.h" + +static const char* nss_status_to_string(enum nss_status status, char *buf, size_t buf_len) { + switch (status) { + case NSS_STATUS_TRYAGAIN: + return "NSS_STATUS_TRYAGAIN"; + case NSS_STATUS_UNAVAIL: + return "NSS_STATUS_UNAVAIL"; + case NSS_STATUS_NOTFOUND: + return "NSS_STATUS_NOTFOUND"; + case NSS_STATUS_SUCCESS: + return "NSS_STATUS_SUCCESS"; + case NSS_STATUS_RETURN: + return "NSS_STATUS_RETURN"; + default: + snprintf(buf, buf_len, "%i", status); + return buf; + } +}; + +static const char* af_to_string(int family, char *buf, size_t buf_len) { + const char *name; + + if (family == AF_UNSPEC) + return "*"; + + name = af_to_name(family); + if (name) + return name; + + snprintf(buf, buf_len, "%i", family); + return buf; +} + +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 + path = strjoina("libnss_", module, ".so.2"); + + handle = dlopen(path, flags); + assert_se(handle); + return handle; +} + +static int print_gaih_addrtuples(const struct gaih_addrtuple *tuples) { + const struct gaih_addrtuple *it; + int n = 0; + + for (it = tuples; it; it = it->next) { + _cleanup_free_ char *a = NULL; + union in_addr_union u; + int r; + char family_name[DECIMAL_STR_MAX(int)]; + char ifname[IF_NAMESIZE]; + + memcpy(&u, it->addr, 16); + r = in_addr_to_string(it->family, &u, &a); + assert_se(r == 0 || r == -EAFNOSUPPORT); + if (r == -EAFNOSUPPORT) + assert_se((a = hexmem(it->addr, 16))); + + if (it->scopeid == 0) + goto numerical_index; + + if (if_indextoname(it->scopeid, ifname) == NULL) { + log_warning("if_indextoname(%d) failed: %m", it->scopeid); + numerical_index: + xsprintf(ifname, "%i", it->scopeid); + }; + + log_info(" \"%s\" %s %s %%%s", + it->name, + af_to_string(it->family, family_name, sizeof family_name), + a, + ifname); + n ++; + } + return n; +} + +static void print_struct_hostent(struct hostent *host, const char *canon) { + char **s; + + log_info(" \"%s\"", host->h_name); + STRV_FOREACH(s, host->h_aliases) + log_info(" alias \"%s\"", *s); + STRV_FOREACH(s, host->h_addr_list) { + union in_addr_union u; + _cleanup_free_ char *a = NULL; + char family_name[DECIMAL_STR_MAX(int)]; + int r; + + assert_se((unsigned) host->h_length == FAMILY_ADDRESS_SIZE(host->h_addrtype)); + memcpy(&u, *s, host->h_length); + r = in_addr_to_string(host->h_addrtype, &u, &a); + assert_se(r == 0); + log_info(" %s %s", + af_to_string(host->h_addrtype, family_name, sizeof family_name), + a); + } + if (canon) + log_info(" canonical: \"%s\"", canon); +} + +static void test_gethostbyname4_r(void *handle, const char *module, const char *name) { + const char *fname; + _nss_gethostbyname4_r_t f; + char buffer[2000]; + struct gaih_addrtuple *pat = NULL; + int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */ + int32_t ttl = INT32_MAX; /* nss-dns wants to return the lowest ttl, + and will access this variable through *ttlp, + so we need to set it to something. + I'm not sure if this is a bug in nss-dns + or not. */ + enum nss_status status; + char pretty_status[DECIMAL_STR_MAX(enum nss_status)]; + int n; + + fname = strjoina("_nss_", module, "_gethostbyname4_r"); + f = dlsym(handle, fname); + log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f); + assert_se(f); + + status = f(name, &pat, buffer, sizeof buffer, &errno1, &errno2, &ttl); + if (status == NSS_STATUS_SUCCESS) { + log_info("%s(\"%s\") → status=%s%-20spat=buffer+0x%tx errno=%d/%s h_errno=%d/%s ttl=%"PRIi32, + fname, name, + nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n", + pat ? (char*) pat - buffer : 0, + errno1, errno_to_name(errno1) ?: "---", + errno2, hstrerror(errno2), + ttl); + n = print_gaih_addrtuples(pat); + } else { + log_info("%s(\"%s\") → status=%s%-20spat=0x%p errno=%d/%s h_errno=%d/%s", + fname, name, + nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n", + pat, + errno1, errno_to_name(errno1) ?: "---", + errno2, hstrerror(errno2)); + n = 0; + } + + if (STR_IN_SET(module, "resolve", "mymachines") && status == NSS_STATUS_UNAVAIL) + return; + + if (STR_IN_SET(module, "myhostname", "resolve") && streq(name, "localhost")) { + assert_se(status == NSS_STATUS_SUCCESS); + assert_se(n == 2); + } +} + + +static void test_gethostbyname3_r(void *handle, const char *module, const char *name, int af) { + const char *fname; + _nss_gethostbyname3_r_t f; + char buffer[2000]; + int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */ + int32_t ttl = INT32_MAX; /* nss-dns wants to return the lowest ttl, + and will access this variable through *ttlp, + so we need to set it to something. + I'm not sure if this is a bug in nss-dns + or not. */ + enum nss_status status; + char pretty_status[DECIMAL_STR_MAX(enum nss_status)]; + struct hostent host; + char *canon; + char family_name[DECIMAL_STR_MAX(int)]; + + fname = strjoina("_nss_", module, "_gethostbyname3_r"); + f = dlsym(handle, fname); + log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f); + assert_se(f); + + status = f(name, af, &host, buffer, sizeof buffer, &errno1, &errno2, &ttl, &canon); + log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32, + fname, name, af_to_string(af, family_name, sizeof family_name), + nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n", + errno1, errno_to_name(errno1) ?: "---", + errno2, hstrerror(errno2), + ttl); + if (status == NSS_STATUS_SUCCESS) + print_struct_hostent(&host, canon); +} + +static void test_gethostbyname2_r(void *handle, const char *module, const char *name, int af) { + const char *fname; + _nss_gethostbyname2_r_t f; + char buffer[2000]; + int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */ + enum nss_status status; + char pretty_status[DECIMAL_STR_MAX(enum nss_status)]; + struct hostent host; + char family_name[DECIMAL_STR_MAX(int)]; + + fname = strjoina("_nss_", module, "_gethostbyname2_r"); + f = dlsym(handle, fname); + log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f); + assert_se(f); + + status = f(name, af, &host, buffer, sizeof buffer, &errno1, &errno2); + log_info("%s(\"%s\", %s) → status=%s%-20serrno=%d/%s h_errno=%d/%s", + fname, name, af_to_string(af, family_name, sizeof family_name), + nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n", + errno1, errno_to_name(errno1) ?: "---", + errno2, hstrerror(errno2)); + if (status == NSS_STATUS_SUCCESS) + print_struct_hostent(&host, NULL); +} + +static void test_gethostbyname_r(void *handle, const char *module, const char *name) { + const char *fname; + _nss_gethostbyname_r_t f; + char buffer[2000]; + int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */ + enum nss_status status; + char pretty_status[DECIMAL_STR_MAX(enum nss_status)]; + struct hostent host; + + fname = strjoina("_nss_", module, "_gethostbyname_r"); + f = dlsym(handle, fname); + log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f); + assert_se(f); + + status = f(name, &host, buffer, sizeof buffer, &errno1, &errno2); + log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s", + fname, name, + nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n", + errno1, errno_to_name(errno1) ?: "---", + errno2, hstrerror(errno2)); + if (status == NSS_STATUS_SUCCESS) + print_struct_hostent(&host, NULL); +} + +static void test_gethostbyaddr2_r(void *handle, + const char *module, + const void* addr, socklen_t len, + int af) { + + const char *fname; + _nss_gethostbyaddr2_r_t f; + char buffer[2000]; + int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */ + enum nss_status status; + char pretty_status[DECIMAL_STR_MAX(enum nss_status)]; + struct hostent host; + int32_t ttl = INT32_MAX; + _cleanup_free_ char *addr_pretty = NULL; + + fname = strjoina("_nss_", module, "_gethostbyaddr2_r"); + f = dlsym(handle, fname); + + log_full_errno(f ? LOG_DEBUG : LOG_INFO, errno, + "dlsym(0x%p, %s) → 0x%p: %m", handle, fname, f); + if (!f) + return; + + assert_se(in_addr_to_string(af, addr, &addr_pretty) >= 0); + + status = f(addr, len, af, &host, buffer, sizeof buffer, &errno1, &errno2, &ttl); + log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s ttl=%"PRIi32, + fname, addr_pretty, + nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n", + errno1, errno_to_name(errno1) ?: "---", + errno2, hstrerror(errno2), + ttl); + if (status == NSS_STATUS_SUCCESS) + print_struct_hostent(&host, NULL); +} + +static void test_gethostbyaddr_r(void *handle, + const char *module, + const void* addr, socklen_t len, + int af) { + + const char *fname; + _nss_gethostbyaddr_r_t f; + char buffer[2000]; + int errno1 = 999, errno2 = 999; /* nss-dns doesn't set those */ + enum nss_status status; + char pretty_status[DECIMAL_STR_MAX(enum nss_status)]; + struct hostent host; + _cleanup_free_ char *addr_pretty = NULL; + + fname = strjoina("_nss_", module, "_gethostbyaddr_r"); + f = dlsym(handle, fname); + + log_full_errno(f ? LOG_DEBUG : LOG_INFO, errno, + "dlsym(0x%p, %s) → 0x%p: %m", handle, fname, f); + if (!f) + return; + + assert_se(in_addr_to_string(af, addr, &addr_pretty) >= 0); + + status = f(addr, len, af, &host, buffer, sizeof buffer, &errno1, &errno2); + log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s h_errno=%d/%s", + fname, addr_pretty, + nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n", + errno1, errno_to_name(errno1) ?: "---", + errno2, hstrerror(errno2)); + if (status == NSS_STATUS_SUCCESS) + print_struct_hostent(&host, NULL); +} + +static void test_byname(void *handle, const char *module, const char *name) { + test_gethostbyname4_r(handle, module, name); + puts(""); + + test_gethostbyname3_r(handle, module, name, AF_INET); + puts(""); + test_gethostbyname3_r(handle, module, name, AF_INET6); + puts(""); + test_gethostbyname3_r(handle, module, name, AF_UNSPEC); + puts(""); + test_gethostbyname3_r(handle, module, name, AF_LOCAL); + puts(""); + + test_gethostbyname2_r(handle, module, name, AF_INET); + puts(""); + test_gethostbyname2_r(handle, module, name, AF_INET6); + puts(""); + test_gethostbyname2_r(handle, module, name, AF_UNSPEC); + puts(""); + test_gethostbyname2_r(handle, module, name, AF_LOCAL); + puts(""); + + test_gethostbyname_r(handle, module, name); + puts(""); +} + +static void test_byaddr(void *handle, + const char *module, + const void* addr, socklen_t len, + int af) { + test_gethostbyaddr2_r(handle, module, addr, len, af); + puts(""); + + test_gethostbyaddr_r(handle, module, addr, len, af); + puts(""); +} + +#ifdef HAVE_MYHOSTNAME +# define MODULE1 "myhostname\0" +#else +# define MODULE1 +#endif +#ifdef HAVE_RESOLVED +# define MODULE2 "resolve\0" +#else +# define MODULE2 +#endif +#ifdef HAVE_MACHINED +# define MODULE3 "mymachines\0" +#else +# define MODULE3 +#endif +#define MODULE4 "dns\0" + +int main(int argc, char **argv) { + _cleanup_free_ char *dir = NULL, *hostname = NULL; + const char *module; + + const uint32_t local_address_ipv4 = htonl(0x7F000001); + const uint32_t local_address_ipv4_2 = htonl(0x7F000002); + _cleanup_free_ struct local_address *addresses = NULL; + int n_addresses; + + log_set_max_level(LOG_INFO); + log_parse_environment(); + + dir = dirname_malloc(argv[0]); + assert_se(dir); + + hostname = gethostname_malloc(); + assert_se(hostname); + + n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses); + if (n_addresses < 0) { + log_info_errno(n_addresses, "Failed to query local addresses: %m"); + n_addresses = 0; + } + + NULSTR_FOREACH(module, MODULE1 MODULE2 MODULE3 MODULE4) { + void *handle; + const char *name; + int i; + + log_info("======== %s ========", module); + + handle = open_handle(streq(module, "dns") ? NULL : dir, + module, + RTLD_LAZY|RTLD_NODELETE); + NULSTR_FOREACH(name, "localhost\0" "gateway\0" "foo_no_such_host\0") + test_byname(handle, module, name); + + test_byname(handle, module, hostname); + + test_byaddr(handle, module, &local_address_ipv4, sizeof local_address_ipv4, AF_INET); + test_byaddr(handle, module, &local_address_ipv4_2, sizeof local_address_ipv4_2, AF_INET); + test_byaddr(handle, module, &in6addr_loopback, sizeof in6addr_loopback, AF_INET6); + + for (i = 0; i < n_addresses; i++) + test_byaddr(handle, module, + &addresses[i].address, + FAMILY_ADDRESS_SIZE(addresses[i].family), + addresses[i].family); + + dlclose(handle); + + log_info(" "); + } + + return EXIT_SUCCESS; +} diff --git a/src/test/test-path-lookup.c b/src/test/test-path-lookup.c index 268da002a9..096326d176 100644 --- a/src/test/test-path-lookup.c +++ b/src/test/test-path-lookup.c @@ -26,41 +26,38 @@ #include "string-util.h" #include "strv.h" -static void test_paths(ManagerRunningAs running_as, bool personal) { +static void test_paths(UnitFileScope scope) { char template[] = "/tmp/test-path-lookup.XXXXXXX"; _cleanup_lookup_paths_free_ LookupPaths lp_without_env = {}; _cleanup_lookup_paths_free_ LookupPaths lp_with_env = {}; - char *exists, *not, *systemd_unit_path; + char *systemd_unit_path; assert_se(mkdtemp(template)); - exists = strjoina(template, "/exists"); - assert_se(mkdir(exists, 0755) == 0); - not = strjoina(template, "/not"); assert_se(unsetenv("SYSTEMD_UNIT_PATH") == 0); - assert_se(lookup_paths_init(&lp_without_env, running_as, personal, NULL, exists, not, not) == 0); - - assert_se(!strv_isempty(lp_without_env.unit_path)); - assert_se(strv_contains(lp_without_env.unit_path, exists)); - assert_se(strv_contains(lp_without_env.unit_path, not)); + assert_se(lookup_paths_init(&lp_without_env, scope, 0, NULL) >= 0); + assert_se(!strv_isempty(lp_without_env.search_path)); + assert_se(lookup_paths_reduce(&lp_without_env) >= 0); systemd_unit_path = strjoina(template, "/systemd-unit-path"); assert_se(setenv("SYSTEMD_UNIT_PATH", systemd_unit_path, 1) == 0); - assert_se(lookup_paths_init(&lp_with_env, running_as, personal, NULL, exists, not, not) == 0); - assert_se(strv_length(lp_with_env.unit_path) == 1); - assert_se(streq(lp_with_env.unit_path[0], systemd_unit_path)); + assert_se(lookup_paths_init(&lp_with_env, scope, 0, NULL) == 0); + assert_se(strv_length(lp_with_env.search_path) == 1); + assert_se(streq(lp_with_env.search_path[0], systemd_unit_path)); + assert_se(lookup_paths_reduce(&lp_with_env) >= 0); + assert_se(strv_length(lp_with_env.search_path) == 0); assert_se(rm_rf(template, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); } -static void print_generator_paths(ManagerRunningAs running_as) { +static void print_generator_binary_paths(UnitFileScope scope) { _cleanup_strv_free_ char **paths; char **dir; - log_info("Generators dirs (%s):", running_as == MANAGER_SYSTEM ? "system" : "user"); + log_info("Generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user"); - paths = generator_paths(running_as); + paths = generator_binary_paths(scope); STRV_FOREACH(dir, paths) log_info(" %s", *dir); } @@ -70,13 +67,12 @@ int main(int argc, char **argv) { log_parse_environment(); log_open(); - test_paths(MANAGER_SYSTEM, false); - test_paths(MANAGER_SYSTEM, true); - test_paths(MANAGER_USER, false); - test_paths(MANAGER_USER, true); + test_paths(UNIT_FILE_SYSTEM); + test_paths(UNIT_FILE_USER); + test_paths(UNIT_FILE_GLOBAL); - print_generator_paths(MANAGER_SYSTEM); - print_generator_paths(MANAGER_USER); + print_generator_binary_paths(UNIT_FILE_SYSTEM); + print_generator_binary_paths(UNIT_FILE_USER); return EXIT_SUCCESS; } diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index 53a585290a..b53324b5e6 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -90,6 +90,18 @@ static void test_path(void) { assert_se(path_equal(path_kill_slashes(p2), "/aaa/./ccc")); assert_se(path_equal(path_kill_slashes(p3), "/./")); } + + assert_se(PATH_IN_SET("/bin", "/", "/bin", "/foo")); + assert_se(PATH_IN_SET("/bin", "/bin")); + assert_se(PATH_IN_SET("/bin", "/foo/bar", "/bin")); + assert_se(PATH_IN_SET("/", "/", "/", "/foo/bar")); + assert_se(!PATH_IN_SET("/", "/abc", "/def")); + + assert_se(path_equal_ptr(NULL, NULL)); + assert_se(path_equal_ptr("/a", "/a")); + assert_se(!path_equal_ptr("/a", "/b")); + assert_se(!path_equal_ptr("/a", NULL)); + assert_se(!path_equal_ptr(NULL, "/a")); } static void test_find_binary(const char *self) { @@ -433,6 +445,71 @@ static void test_path_is_mount_point(void) { assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0); } +static void test_file_in_same_dir(void) { + char *t; + + t = file_in_same_dir("/", "a"); + assert_se(streq(t, "/a")); + free(t); + + t = file_in_same_dir("/", "/a"); + assert_se(streq(t, "/a")); + free(t); + + t = file_in_same_dir("", "a"); + assert_se(streq(t, "a")); + free(t); + + t = file_in_same_dir("a/", "a"); + assert_se(streq(t, "a/a")); + free(t); + + t = file_in_same_dir("bar/foo", "bar"); + assert_se(streq(t, "bar/bar")); + free(t); +} + +static void test_filename_is_valid(void) { + char foo[FILENAME_MAX+2]; + int i; + + assert_se(!filename_is_valid("")); + assert_se(!filename_is_valid("/bar/foo")); + assert_se(!filename_is_valid("/")); + assert_se(!filename_is_valid(".")); + assert_se(!filename_is_valid("..")); + + for (i=0; i<FILENAME_MAX+1; i++) + foo[i] = 'a'; + foo[FILENAME_MAX+1] = '\0'; + + assert_se(!filename_is_valid(foo)); + + assert_se(filename_is_valid("foo_bar-333")); + assert_se(filename_is_valid("o.o")); +} + +static void test_hidden_or_backup_file(void) { + assert_se(hidden_or_backup_file(".hidden")); + assert_se(hidden_or_backup_file("..hidden")); + assert_se(!hidden_or_backup_file("hidden.")); + + assert_se(hidden_or_backup_file("backup~")); + assert_se(hidden_or_backup_file(".backup~")); + + assert_se(hidden_or_backup_file("lost+found")); + assert_se(hidden_or_backup_file("aquota.user")); + assert_se(hidden_or_backup_file("aquota.group")); + + assert_se(hidden_or_backup_file("test.rpmnew")); + assert_se(hidden_or_backup_file("test.dpkg-old")); + assert_se(hidden_or_backup_file("test.dpkg-remove")); + assert_se(hidden_or_backup_file("test.swp")); + + assert_se(!hidden_or_backup_file("test.rpmnew.")); + assert_se(!hidden_or_backup_file("test.dpkg-old.foo")); +} + int main(int argc, char **argv) { test_path(); test_find_binary(argv[0]); @@ -444,6 +521,9 @@ int main(int argc, char **argv) { test_path_startswith(); test_prefix_root(); test_path_is_mount_point(); + test_file_in_same_dir(); + test_filename_is_valid(); + test_hidden_or_backup_file(); return 0; } diff --git a/src/test/test-path.c b/src/test/test-path.c index 7a3b145414..62181e22a0 100644 --- a/src/test/test-path.c +++ b/src/test/test-path.c @@ -30,6 +30,7 @@ #include "string-util.h" #include "strv.h" #include "test-helper.h" +#include "tests.h" #include "unit.h" #include "util.h" @@ -44,7 +45,7 @@ static int setup_test(Manager **m) { assert_se(m); - r = manager_new(MANAGER_USER, true, &tmp); + r = manager_new(UNIT_FILE_USER, true, &tmp); if (MANAGER_SKIP_TEST(r)) { printf("Skipping test: manager_new: %s\n", strerror(-r)); return -EXIT_TEST_SKIP; @@ -93,7 +94,7 @@ static void check_stop_unlink(Manager *m, Unit *unit, const char *test_path, con ts = now(CLOCK_MONOTONIC); /* We process events until the service related to the path has been successfully started */ - while(service->result != SERVICE_SUCCESS || service->state != SERVICE_START) { + while (service->result != SERVICE_SUCCESS || service->state != SERVICE_START) { usec_t n; int r; @@ -243,7 +244,7 @@ static void test_path_makedirectory_directorymode(Manager *m) { } int main(int argc, char *argv[]) { - test_function_t tests[] = { + static const test_function_t tests[] = { test_path_exists, test_path_existsglob, test_path_changed, @@ -253,12 +254,15 @@ int main(int argc, char *argv[]) { test_path_makedirectory_directorymode, NULL, }; - test_function_t *test = NULL; + + _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; + const test_function_t *test = NULL; Manager *m = NULL; log_parse_environment(); log_open(); + assert_se(runtime_dir = setup_fake_runtime_dir()); assert_se(set_unit_path(TEST_DIR "/test-path/") >= 0); for (test = tests; test && *test; test++) { diff --git a/src/test/test-proc-cmdline.c b/src/test/test-proc-cmdline.c new file mode 100644 index 0000000000..a7a8f621a2 --- /dev/null +++ b/src/test/test-proc-cmdline.c @@ -0,0 +1,52 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "alloc-util.h" +#include "log.h" +#include "macro.h" +#include "proc-cmdline.h" +#include "special.h" +#include "string-util.h" + +static int parse_item(const char *key, const char *value) { + assert_se(key); + + log_info("kernel cmdline option <%s> = <%s>", key, strna(value)); + return 0; +} + +static void test_parse_proc_cmdline(void) { + assert_se(parse_proc_cmdline(parse_item) >= 0); +} + +static void test_runlevel_to_target(void) { + assert_se(streq_ptr(runlevel_to_target(NULL), NULL)); + assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL)); + assert_se(streq_ptr(runlevel_to_target("3"), SPECIAL_MULTI_USER_TARGET)); +} + +int main(void) { + log_parse_environment(); + log_open(); + + test_parse_proc_cmdline(); + test_runlevel_to_target(); + + return 0; +} diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c index 48be5a3a87..4616314200 100644 --- a/src/test/test-process-util.c +++ b/src/test/test-process-util.c @@ -18,12 +18,14 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <sys/personality.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include "alloc-util.h" +#include "architecture.h" #include "log.h" #include "macro.h" #include "process-util.h" @@ -128,6 +130,29 @@ static void test_pid_is_alive(void) { assert_se(!pid_is_alive(-1)); } +static void test_personality(void) { + + assert_se(personality_to_string(PER_LINUX)); + assert_se(!personality_to_string(PERSONALITY_INVALID)); + + assert_se(streq(personality_to_string(PER_LINUX), architecture_to_string(native_architecture()))); + + assert_se(personality_from_string(personality_to_string(PER_LINUX)) == PER_LINUX); + assert_se(personality_from_string(architecture_to_string(native_architecture())) == PER_LINUX); + +#ifdef __x86_64__ + assert_se(streq_ptr(personality_to_string(PER_LINUX), "x86-64")); + assert_se(streq_ptr(personality_to_string(PER_LINUX32), "x86")); + + assert_se(personality_from_string("x86-64") == PER_LINUX); + assert_se(personality_from_string("x86") == PER_LINUX32); + assert_se(personality_from_string("ia64") == PERSONALITY_INVALID); + assert_se(personality_from_string(NULL) == PERSONALITY_INVALID); + + assert_se(personality_from_string(personality_to_string(PER_LINUX32)) == PER_LINUX32); +#endif +} + int main(int argc, char *argv[]) { log_parse_environment(); log_open(); @@ -135,6 +160,7 @@ int main(int argc, char *argv[]) { test_get_process_comm(); test_pid_is_unwaited(); test_pid_is_alive(); + test_personality(); return 0; } diff --git a/src/test/test-rbtree.c b/src/test/test-rbtree.c deleted file mode 100644 index 8ae416c557..0000000000 --- a/src/test/test-rbtree.c +++ /dev/null @@ -1,362 +0,0 @@ -/*** - This file is part of systemd. See COPYING for details. - - 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/>. -***/ - -/* - * Tests for RB-Tree - */ - -#undef NDEBUG -#include <assert.h> -#include <stddef.h> -#include <stdlib.h> -#include "c-rbtree.h" - -/* verify that all API calls are exported */ -static void test_api(void) { - CRBTree t = {}; - CRBNode n = C_RBNODE_INIT(n); - - assert(!c_rbnode_is_linked(&n)); - - /* init, is_linked, add, remove, remove_init */ - - c_rbtree_add(&t, NULL, &t.root, &n); - assert(c_rbnode_is_linked(&n)); - - c_rbtree_remove_init(&t, &n); - assert(!c_rbnode_is_linked(&n)); - - c_rbtree_add(&t, NULL, &t.root, &n); - assert(c_rbnode_is_linked(&n)); - - c_rbtree_remove(&t, &n); - assert(c_rbnode_is_linked(&n)); /* @n wasn't touched */ - - c_rbnode_init(&n); - assert(!c_rbnode_is_linked(&n)); - - /* first, last, leftmost, rightmost, next, prev */ - - assert(!c_rbtree_first(&t)); - assert(!c_rbtree_last(&t)); - assert(&n == c_rbnode_leftmost(&n)); - assert(&n == c_rbnode_rightmost(&n)); - assert(!c_rbnode_next(&n)); - assert(!c_rbnode_prev(&n)); -} - -/* copied from c-rbtree.c, relies on internal representation */ -static inline _Bool c_rbnode_is_red(CRBNode *n) { - return !((unsigned long)n->__parent_and_color & 1UL); -} - -/* copied from c-rbtree.c, relies on internal representation */ -static inline _Bool c_rbnode_is_black(CRBNode *n) { - return !!((unsigned long)n->__parent_and_color & 1UL); -} - -static size_t validate(CRBTree *t) { - unsigned int i_black, n_black; - CRBNode *n, *p, *o; - size_t count = 0; - - assert(t); - assert(!t->root || c_rbnode_is_black(t->root)); - - /* traverse to left-most child, count black nodes */ - i_black = 0; - n = t->root; - while (n && n->left) { - if (c_rbnode_is_black(n)) - ++i_black; - n = n->left; - } - n_black = i_black; - - /* - * Traverse tree and verify correctness: - * 1) A node is either red or black - * 2) The root is black - * 3) All leaves are black - * 4) Every red node must have two black child nodes - * 5) Every path to a leaf contains the same number of black nodes - * - * Note that NULL nodes are considered black, which is why we don't - * check for 3). - */ - o = NULL; - while (n) { - ++count; - - /* verify natural order */ - assert(n > o); - o = n; - - /* verify consistency */ - assert(!n->right || c_rbnode_parent(n->right) == n); - assert(!n->left || c_rbnode_parent(n->left) == n); - - /* verify 2) */ - if (!c_rbnode_parent(n)) - assert(c_rbnode_is_black(n)); - - if (c_rbnode_is_red(n)) { - /* verify 4) */ - assert(!n->left || c_rbnode_is_black(n->left)); - assert(!n->right || c_rbnode_is_black(n->right)); - } else { - /* verify 1) */ - assert(c_rbnode_is_black(n)); - } - - /* verify 5) */ - if (!n->left && !n->right) - assert(i_black == n_black); - - /* get next node */ - if (n->right) { - n = n->right; - if (c_rbnode_is_black(n)) - ++i_black; - - while (n->left) { - n = n->left; - if (c_rbnode_is_black(n)) - ++i_black; - } - } else { - while ((p = c_rbnode_parent(n)) && n == p->right) { - n = p; - if (c_rbnode_is_black(p->right)) - --i_black; - } - - n = p; - if (p && c_rbnode_is_black(p->left)) - --i_black; - } - } - - return count; -} - -static void insert(CRBTree *t, CRBNode *n) { - CRBNode **i, *p; - - assert(t); - assert(n); - assert(!c_rbnode_is_linked(n)); - - i = &t->root; - p = NULL; - while (*i) { - p = *i; - if (n < *i) { - i = &(*i)->left; - } else { - assert(n > *i); - i = &(*i)->right; - } - } - - c_rbtree_add(t, p, i, n); -} - -static void shuffle(void **nodes, size_t n_memb) { - unsigned int i, j; - void *t; - - for (i = 0; i < n_memb; ++i) { - j = rand() % n_memb; - t = nodes[j]; - nodes[j] = nodes[i]; - nodes[i] = t; - } -} - -/* run some pseudo-random tests on the tree */ -static void test_shuffle(void) { - CRBNode *nodes[256]; - CRBTree t = {}; - unsigned int i, j; - size_t n; - - /* allocate and initialize all nodes */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { - nodes[i] = malloc(sizeof(*nodes[i])); - assert(nodes[i]); - c_rbnode_init(nodes[i]); - } - - /* shuffle nodes and validate *empty* tree */ - shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); - n = validate(&t); - assert(n == 0); - - /* add all nodes and validate after each insertion */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { - insert(&t, nodes[i]); - n = validate(&t); - assert(n == i + 1); - } - - /* shuffle nodes again */ - shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); - - /* remove all nodes (in different order) and validate on each round */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { - c_rbtree_remove(&t, nodes[i]); - n = validate(&t); - assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1); - c_rbnode_init(nodes[i]); - } - - /* shuffle nodes and validate *empty* tree again */ - shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); - n = validate(&t); - assert(n == 0); - - /* add all nodes again */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { - insert(&t, nodes[i]); - n = validate(&t); - assert(n == i + 1); - } - - /* 4 times, remove half of the nodes and add them again */ - for (j = 0; j < 4; ++j) { - /* shuffle nodes again */ - shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); - - /* remove half of the nodes */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes) / 2; ++i) { - c_rbtree_remove(&t, nodes[i]); - n = validate(&t); - assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1); - c_rbnode_init(nodes[i]); - } - - /* shuffle the removed half */ - shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes) / 2); - - /* add the removed half again */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes) / 2; ++i) { - insert(&t, nodes[i]); - n = validate(&t); - assert(n == sizeof(nodes) / sizeof(*nodes) / 2 + i + 1); - } - } - - /* shuffle nodes again */ - shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); - - /* remove all */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { - c_rbtree_remove(&t, nodes[i]); - n = validate(&t); - assert(n == sizeof(nodes) / sizeof(*nodes) - i - 1); - c_rbnode_init(nodes[i]); - } - - /* free nodes again */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) - free(nodes[i]); -} - -typedef struct { - unsigned long key; - CRBNode rb; -} Node; - -#define node_from_rb(_rb) ((Node *)((char *)(_rb) - offsetof(Node, rb))) - -static int compare(CRBTree *t, void *k, CRBNode *n) { - unsigned long key = (unsigned long)k; - Node *node = node_from_rb(n); - - return (key < node->key) ? -1 : (key > node->key) ? 1 : 0; -} - -/* run tests against the c_rbtree_find*() helpers */ -static void test_map(void) { - CRBNode **slot, *p; - CRBTree t = {}; - Node *nodes[2048]; - unsigned long i; - - /* allocate and initialize all nodes */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { - nodes[i] = malloc(sizeof(*nodes[i])); - assert(nodes[i]); - nodes[i]->key = i; - c_rbnode_init(&nodes[i]->rb); - } - - /* shuffle nodes */ - shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); - - /* add all nodes, and verify that each node is linked */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { - assert(!c_rbnode_is_linked(&nodes[i]->rb)); - assert(!c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb)); - - slot = c_rbtree_find_slot(&t, compare, (void *)nodes[i]->key, &p); - assert(slot); - c_rbtree_add(&t, p, slot, &nodes[i]->rb); - - assert(c_rbnode_is_linked(&nodes[i]->rb)); - assert(nodes[i] == c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb)); - } - - /* shuffle nodes again */ - shuffle((void **)nodes, sizeof(nodes) / sizeof(*nodes)); - - /* remove all nodes (in different order) */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) { - assert(c_rbnode_is_linked(&nodes[i]->rb)); - assert(nodes[i] == c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb)); - - c_rbtree_remove_init(&t, &nodes[i]->rb); - - assert(!c_rbnode_is_linked(&nodes[i]->rb)); - assert(!c_rbtree_find_entry(&t, compare, (void *)nodes[i]->key, Node, rb)); - } - - /* free nodes again */ - for (i = 0; i < sizeof(nodes) / sizeof(*nodes); ++i) - free(nodes[i]); -} - -int main(int argc, char **argv) { - unsigned int i; - - /* we want stable tests, so use fixed seed */ - srand(0xdeadbeef); - - test_api(); - - /* - * The tests are pseudo random; run them multiple times, each run will - * have different orders and thus different results. - */ - for (i = 0; i < 4; ++i) { - test_shuffle(); - test_map(); - } - - return 0; -} diff --git a/src/test/test-rlimit-util.c b/src/test/test-rlimit-util.c index d9ac9368cd..62afd2de5e 100644 --- a/src/test/test-rlimit-util.c +++ b/src/test/test-rlimit-util.c @@ -99,6 +99,18 @@ int main(int argc, char *argv[]) { test_rlimit_parse_format(RLIMIT_NOFILE, "", 0, 0, -EINVAL, NULL); test_rlimit_parse_format(RLIMIT_NOFILE, "5:4", 0, 0, -EILSEQ, NULL); test_rlimit_parse_format(RLIMIT_NOFILE, "5:4:3", 0, 0, -EINVAL, NULL); + test_rlimit_parse_format(RLIMIT_NICE, "20", 20, 20, 0, "20"); + test_rlimit_parse_format(RLIMIT_NICE, "40", 40, 40, 0, "40"); + test_rlimit_parse_format(RLIMIT_NICE, "41", 41, 41, -ERANGE, "41"); + test_rlimit_parse_format(RLIMIT_NICE, "0", 0, 0, 0, "0"); + test_rlimit_parse_format(RLIMIT_NICE, "-7", 27, 27, 0, "27"); + test_rlimit_parse_format(RLIMIT_NICE, "-20", 40, 40, 0, "40"); + test_rlimit_parse_format(RLIMIT_NICE, "-21", 41, 41, -ERANGE, "41"); + test_rlimit_parse_format(RLIMIT_NICE, "-0", 20, 20, 0, "20"); + test_rlimit_parse_format(RLIMIT_NICE, "+7", 13, 13, 0, "13"); + test_rlimit_parse_format(RLIMIT_NICE, "+19", 1, 1, 0, "1"); + test_rlimit_parse_format(RLIMIT_NICE, "+20", 0, 0, -ERANGE, "0"); + test_rlimit_parse_format(RLIMIT_NICE, "+0", 20, 20, 0, "20"); return 0; } diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c index 7f515b53d8..c068f5c39e 100644 --- a/src/test/test-sched-prio.c +++ b/src/test/test-sched-prio.c @@ -21,9 +21,12 @@ #include "macro.h" #include "manager.h" +#include "rm-rf.h" #include "test-helper.h" +#include "tests.h" int main(int argc, char *argv[]) { + _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; Manager *m = NULL; Unit *idle_ok, *idle_bad, *rr_ok, *rr_bad, *rr_sched; Service *ser; @@ -31,9 +34,11 @@ int main(int argc, char *argv[]) { FDSet *fdset = NULL; int r; + assert_se(runtime_dir = setup_fake_runtime_dir()); + /* prepare the test */ assert_se(set_unit_path(TEST_DIR) >= 0); - r = manager_new(MANAGER_USER, true, &m); + r = manager_new(UNIT_FILE_USER, true, &m); if (MANAGER_SKIP_TEST(r)) { printf("Skipping test: manager_new: %s\n", strerror(-r)); return EXIT_TEST_SKIP; diff --git a/src/test/test-selinux.c b/src/test/test-selinux.c new file mode 100644 index 0000000000..7545ad3764 --- /dev/null +++ b/src/test/test-selinux.c @@ -0,0 +1,122 @@ +/*** + This file is part of systemd. + + Copyright 2016 Zbigniew Jędrzejewski-Szmek + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <sys/stat.h> + +#include "alloc-util.h" +#include "fd-util.h" +#include "log.h" +#include "selinux-util.h" +#include "string-util.h" +#include "time-util.h" +#include "util.h" + +static void test_testing(void) { + bool b; + + log_info("============ %s ==========", __func__); + + 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)); + + 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)); +} + +static void test_loading(void) { + usec_t n1, n2; + int r; + + log_info("============ %s ==========", __func__); + + n1 = now(CLOCK_MONOTONIC); + r = mac_selinux_init(); + n2 = now(CLOCK_MONOTONIC); + log_info_errno(r, "mac_selinux_init → %d (%m) %.2fs", r, (n2 - n1)/1e6); +} + +static void test_cleanup(void) { + usec_t n1, n2; + + log_info("============ %s ==========", __func__); + + n1 = now(CLOCK_MONOTONIC); + mac_selinux_finish(); + n2 = now(CLOCK_MONOTONIC); + log_info("mac_selinux_finish → %.2fs", (n2 - n1)/1e6); +} + +static void test_misc(const char* fname) { + _cleanup_(mac_selinux_freep) char *label = NULL, *label2 = NULL, *label3 = NULL; + int r; + _cleanup_close_ int fd = -1; + + log_info("============ %s ==========", __func__); + + r = mac_selinux_get_our_label(&label); + log_info_errno(r, "mac_selinux_get_our_label → %d (%m), \"%s\"", + r, strnull(label)); + + r = mac_selinux_get_create_label_from_exe(fname, &label2); + log_info_errno(r, "mac_selinux_create_label_from_exe → %d (%m), \"%s\"", + r, strnull(label2)); + + fd = socket(AF_INET, SOCK_DGRAM, 0); + assert_se(fd >= 0); + + r = mac_selinux_get_child_mls_label(fd, fname, label2, &label3); + log_info_errno(r, "mac_selinux_get_child_mls_label → %d (%m), \"%s\"", + r, strnull(label3)); +} + +static void test_create_file_prepare(const char* fname) { + int r; + + log_info("============ %s ==========", __func__); + + r = mac_selinux_create_file_prepare(fname, S_IRWXU); + log_info_errno(r, "mac_selinux_create_file_prepare → %d (%m)", r); + + mac_selinux_create_file_clear(); +} + +int main(int argc, char **argv) { + const char *path = SYSTEMD_BINARY_PATH; + if (argc >= 2) + path = argv[1]; + + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + + test_testing(); + test_loading(); + test_misc(path); + test_create_file_prepare(path); + test_cleanup(); + + return 0; +} diff --git a/src/test/test-signal-util.c b/src/test/test-signal-util.c index 3083501ce9..671eb869cb 100644 --- a/src/test/test-signal-util.c +++ b/src/test/test-signal-util.c @@ -17,6 +17,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <signal.h> +#include <unistd.h> + +#include "macro.h" #include "signal-util.h" static void test_block_signals(void) { @@ -44,6 +48,20 @@ static void test_block_signals(void) { assert_se(sigismember(&ss, SIGVTALRM) == 0); } +static void test_ignore_signals(void) { + assert_se(ignore_signals(SIGINT, -1) >= 0); + assert_se(kill(getpid(), SIGINT) >= 0); + assert_se(ignore_signals(SIGUSR1, SIGUSR2, SIGTERM, SIGPIPE, -1) >= 0); + assert_se(kill(getpid(), SIGUSR1) >= 0); + assert_se(kill(getpid(), SIGUSR2) >= 0); + assert_se(kill(getpid(), SIGTERM) >= 0); + assert_se(kill(getpid(), SIGPIPE) >= 0); + assert_se(default_signals(SIGINT, SIGUSR1, SIGUSR2, SIGTERM, SIGPIPE, -1) >= 0); +} + int main(int argc, char *argv[]) { test_block_signals(); + test_ignore_signals(); + + return 0; } diff --git a/src/test/test-siphash24.c b/src/test/test-siphash24.c index caae911f30..b74b7ad2dd 100644 --- a/src/test/test-siphash24.c +++ b/src/test/test-siphash24.c @@ -22,9 +22,9 @@ #define ITERATIONS 10000000ULL -static int do_test(const uint8_t *in, size_t len, const uint8_t *key) { +static void do_test(const uint8_t *in, size_t len, const uint8_t *key) { struct siphash state = {}; - uint64_t out = 0; + uint64_t out; unsigned i, j; out = siphash24(in, len, key); @@ -60,7 +60,46 @@ static int do_test(const uint8_t *in, size_t len, const uint8_t *key) { assert_se(out == 0xa129ca6149be45e5); } } - return 0; +} + +static void test_short_hashes(void) { + const uint8_t one[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16 }; + const uint8_t key[16] = { 0x22, 0x24, 0x41, 0x22, 0x55, 0x77, 0x88, 0x07, + 0x23, 0x09, 0x23, 0x14, 0x0c, 0x33, 0x0e, 0x0f}; + uint8_t two[sizeof one] = {}; + + struct siphash state1 = {}, state2 = {}; + unsigned i, j; + + siphash24_init(&state1, key); + siphash24_init(&state2, key); + + /* hashing 1, 2, 3, 4, 5, ..., 16 bytes, with the byte after the buffer different */ + for (i = 1; i <= sizeof one; i++) { + siphash24_compress(one, i, &state1); + + two[i-1] = one[i-1]; + siphash24_compress(two, i, &state2); + + assert_se(memcmp(&state1, &state2, sizeof state1) == 0); + } + + /* hashing n and 1, n and 2, n and 3, ..., n-1 and 1, n-2 and 2, ... */ + for (i = sizeof one; i > 0; i--) { + zero(two); + + for (j = 1; j <= sizeof one; j++) { + siphash24_compress(one, i, &state1); + siphash24_compress(one, j, &state1); + + siphash24_compress(one, i, &state2); + two[j-1] = one[j-1]; + siphash24_compress(two, j, &state2); + + assert_se(memcmp(&state1, &state2, sizeof state1) == 0); + } + } } /* see https://131002.net/siphash/siphash.pdf, Appendix A */ @@ -80,4 +119,6 @@ int main(int argc, char *argv[]) { do_test(in_buf + 2, sizeof(in), key); memcpy(in_buf + 4, in, sizeof(in)); do_test(in_buf + 4, sizeof(in), key); + + test_short_hashes(); } diff --git a/src/test/test-sizeof.c b/src/test/test-sizeof.c new file mode 100644 index 0000000000..8f99a13772 --- /dev/null +++ b/src/test/test-sizeof.c @@ -0,0 +1,53 @@ +/*** + 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 "log.h" +#include "time-util.h" + +/* Print information about various types. Useful when diagnosing + * gcc diagnostics on an unfamiliar architecture. */ + +#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")); + +int main(void) { + info(char); + info(signed char); + info(unsigned char); + info(short unsigned); + info(unsigned); + info(long unsigned); + info(long long unsigned); + + info(float); + info(double); + info(long double); + + info(size_t); + info(ssize_t); + info(time_t); + info(usec_t); + + return 0; +} diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c index 33ff3755bc..b480fdaa9c 100644 --- a/src/test/test-socket-util.c +++ b/src/test/test-socket-util.c @@ -27,6 +27,29 @@ #include "string-util.h" #include "util.h" +static void test_ifname_valid(void) { + assert(ifname_valid("foo")); + assert(ifname_valid("eth0")); + + assert(!ifname_valid("0")); + assert(!ifname_valid("99")); + assert(ifname_valid("a99")); + assert(ifname_valid("99a")); + + assert(!ifname_valid(NULL)); + assert(!ifname_valid("")); + assert(!ifname_valid(" ")); + assert(!ifname_valid(" foo")); + assert(!ifname_valid("bar\n")); + assert(!ifname_valid(".")); + assert(!ifname_valid("..")); + assert(ifname_valid("foo.bar")); + assert(!ifname_valid("x:y")); + + assert(ifname_valid("xxxxxxxxxxxxxxx")); + assert(!ifname_valid("xxxxxxxxxxxxxxxx")); +} + static void test_socket_address_parse(void) { SocketAddress a; @@ -343,10 +366,27 @@ static void test_sockaddr_equal(void) { assert_se(!sockaddr_equal(&b, &c)); } +static void test_sockaddr_un_len(void) { + static const struct sockaddr_un fs = { + .sun_family = AF_UNIX, + .sun_path = "/foo/bar/waldo", + }; + + static const struct sockaddr_un abstract = { + .sun_family = AF_UNIX, + .sun_path = "\0foobar", + }; + + assert_se(SOCKADDR_UN_LEN(fs) == offsetof(struct sockaddr_un, sun_path) + strlen(fs.sun_path)); + assert_se(SOCKADDR_UN_LEN(abstract) == offsetof(struct sockaddr_un, sun_path) + 1 + strlen(abstract.sun_path + 1)); +} + int main(int argc, char *argv[]) { log_set_max_level(LOG_DEBUG); + test_ifname_valid(); + test_socket_address_parse(); test_socket_address_parse_netlink(); test_socket_address_equal(); @@ -363,5 +403,7 @@ int main(int argc, char *argv[]) { test_sockaddr_equal(); + test_sockaddr_un_len(); + return 0; } diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c new file mode 100644 index 0000000000..a10227f823 --- /dev/null +++ b/src/test/test-stat-util.c @@ -0,0 +1,68 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <fcntl.h> +#include <unistd.h> + +#include "alloc-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "macro.h" +#include "stat-util.h" + +static void test_files_same(void) { + _cleanup_close_ int fd = -1; + char name[] = "/tmp/test-files_same.XXXXXX"; + char name_alias[] = "/tmp/test-files_same.alias"; + + fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + assert_se(fd >= 0); + assert_se(symlink(name, name_alias) >= 0); + + assert_se(files_same(name, name)); + assert_se(files_same(name, name_alias)); + + unlink(name); + unlink(name_alias); +} + +static void test_is_symlink(void) { + char name[] = "/tmp/test-is_symlink.XXXXXX"; + char name_link[] = "/tmp/test-is_symlink.link"; + _cleanup_close_ int fd = -1; + + fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + assert_se(fd >= 0); + assert_se(symlink(name, name_link) >= 0); + + assert_se(is_symlink(name) == 0); + assert_se(is_symlink(name_link) == 1); + assert_se(is_symlink("/a/file/which/does/not/exist/i/guess") < 0); + + + unlink(name); + unlink(name_link); +} + +int main(int argc, char *argv[]) { + test_files_same(); + test_is_symlink(); + + return 0; +} diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c index 9b48e95998..d0f84d70bc 100644 --- a/src/test/test-string-util.c +++ b/src/test/test-string-util.c @@ -17,7 +17,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "alloc-util.h" +#include "macro.h" #include "string-util.h" +#include "strv.h" static void test_string_erase(void) { char *x; @@ -97,9 +100,271 @@ static void test_ascii_strcasecmp_nn(void) { assert_se(ascii_strcasecmp_nn("BBbb", 4, "aaaa", 4) > 0); } +static void test_streq_ptr(void) { + assert_se(streq_ptr(NULL, NULL)); + assert_se(!streq_ptr("abc", "cdef")); +} + +static void test_strstrip(void) { + char *r; + char input[] = " hello, waldo. "; + + r = strstrip(input); + assert_se(streq(r, "hello, waldo.")); +} + +static void test_strextend(void) { + _cleanup_free_ char *str = strdup("0123"); + strextend(&str, "456", "78", "9", NULL); + assert_se(streq(str, "0123456789")); +} + +static void test_strrep(void) { + _cleanup_free_ char *one, *three, *zero; + one = strrep("waldo", 1); + three = strrep("waldo", 3); + zero = strrep("waldo", 0); + + assert_se(streq(one, "waldo")); + assert_se(streq(three, "waldowaldowaldo")); + assert_se(streq(zero, "")); +} + + +static void test_strappend(void) { + _cleanup_free_ char *t1, *t2, *t3, *t4; + + t1 = strappend(NULL, NULL); + assert_se(streq(t1, "")); + + t2 = strappend(NULL, "suf"); + assert_se(streq(t2, "suf")); + + t3 = strappend("pre", NULL); + assert_se(streq(t3, "pre")); + + t4 = strappend("pre", "suf"); + assert_se(streq(t4, "presuf")); +} + +static void test_string_has_cc(void) { + assert_se(string_has_cc("abc\1", NULL)); + assert_se(string_has_cc("abc\x7f", NULL)); + assert_se(string_has_cc("abc\x7f", NULL)); + assert_se(string_has_cc("abc\t\x7f", "\t")); + assert_se(string_has_cc("abc\t\x7f", "\t")); + assert_se(string_has_cc("\x7f", "\t")); + assert_se(string_has_cc("\x7f", "\t\a")); + + assert_se(!string_has_cc("abc\t\t", "\t")); + assert_se(!string_has_cc("abc\t\t\a", "\t\a")); + assert_se(!string_has_cc("a\ab\tc", "\t\a")); +} + +static void test_ascii_strlower(void) { + char a[] = "AabBcC Jk Ii Od LKJJJ kkd LK"; + assert_se(streq(ascii_strlower(a), "aabbcc jk ii od lkjjj kkd lk")); +} + +static void test_strshorten(void) { + char s[] = "foobar"; + + assert_se(strlen(strshorten(s, 6)) == 6); + assert_se(strlen(strshorten(s, 12)) == 6); + assert_se(strlen(strshorten(s, 2)) == 2); + assert_se(strlen(strshorten(s, 0)) == 0); +} + +static void test_strjoina(void) { + char *actual; + + actual = strjoina("", "foo", "bar"); + assert_se(streq(actual, "foobar")); + + actual = strjoina("foo", "bar", "baz"); + assert_se(streq(actual, "foobarbaz")); + + actual = strjoina("foo", "", "bar", "baz"); + assert_se(streq(actual, "foobarbaz")); + + actual = strjoina("foo"); + assert_se(streq(actual, "foo")); + + actual = strjoina(NULL); + assert_se(streq(actual, "")); + + actual = strjoina(NULL, "foo"); + assert_se(streq(actual, "")); + + actual = strjoina("foo", NULL, "bar"); + assert_se(streq(actual, "foo")); +} + +static void test_strcmp_ptr(void) { + assert_se(strcmp_ptr(NULL, NULL) == 0); + assert_se(strcmp_ptr("", NULL) > 0); + assert_se(strcmp_ptr("foo", NULL) > 0); + assert_se(strcmp_ptr(NULL, "") < 0); + assert_se(strcmp_ptr(NULL, "bar") < 0); + assert_se(strcmp_ptr("foo", "bar") > 0); + assert_se(strcmp_ptr("bar", "baz") < 0); + assert_se(strcmp_ptr("foo", "foo") == 0); + assert_se(strcmp_ptr("", "") == 0); +} + +static void test_foreach_word(void) { + const char *word, *state; + size_t l; + int i = 0; + const char test[] = "test abc d\te f "; + const char * const expected[] = { + "test", + "abc", + "d", + "e", + "f", + "", + NULL + }; + + FOREACH_WORD(word, l, test, state) + assert_se(strneq(expected[i++], word, l)); +} + +static void check(const char *test, char** expected, bool trailing) { + const char *word, *state; + size_t l; + int i = 0; + + printf("<<<%s>>>\n", test); + FOREACH_WORD_QUOTED(word, l, test, state) { + _cleanup_free_ char *t = NULL; + + assert_se(t = strndup(word, l)); + assert_se(strneq(expected[i++], word, l)); + printf("<%s>\n", t); + } + printf("<<<%s>>>\n", state); + assert_se(expected[i] == NULL); + assert_se(isempty(state) == !trailing); +} + +static void test_foreach_word_quoted(void) { + check("test a b c 'd' e '' '' hhh '' '' \"a b c\"", + STRV_MAKE("test", + "a", + "b", + "c", + "d", + "e", + "", + "", + "hhh", + "", + "", + "a b c"), + false); + + check("test \"xxx", + STRV_MAKE("test"), + true); + + check("test\\", + STRV_MAKE_EMPTY, + true); +} + +static void test_endswith(void) { + assert_se(endswith("foobar", "bar")); + assert_se(endswith("foobar", "")); + assert_se(endswith("foobar", "foobar")); + assert_se(endswith("", "")); + + assert_se(!endswith("foobar", "foo")); + assert_se(!endswith("foobar", "foobarfoofoo")); +} + +static void test_endswith_no_case(void) { + assert_se(endswith_no_case("fooBAR", "bar")); + assert_se(endswith_no_case("foobar", "")); + assert_se(endswith_no_case("foobar", "FOOBAR")); + assert_se(endswith_no_case("", "")); + + assert_se(!endswith_no_case("foobar", "FOO")); + assert_se(!endswith_no_case("foobar", "FOOBARFOOFOO")); +} + +static void test_delete_chars(void) { + char *r; + char input[] = " hello, waldo. abc"; + + r = delete_chars(input, WHITESPACE); + assert_se(streq(r, "hello,waldo.abc")); +} + +static void test_in_charset(void) { + assert_se(in_charset("dddaaabbbcccc", "abcd")); + assert_se(!in_charset("dddaaabbbcccc", "abc f")); +} + +static void test_split_pair(void) { + _cleanup_free_ char *a = NULL, *b = NULL; + + assert_se(split_pair("", "", &a, &b) == -EINVAL); + assert_se(split_pair("foo=bar", "", &a, &b) == -EINVAL); + assert_se(split_pair("", "=", &a, &b) == -EINVAL); + assert_se(split_pair("foo=bar", "=", &a, &b) >= 0); + assert_se(streq(a, "foo")); + assert_se(streq(b, "bar")); + free(a); + free(b); + assert_se(split_pair("==", "==", &a, &b) >= 0); + assert_se(streq(a, "")); + assert_se(streq(b, "")); + free(a); + free(b); + + assert_se(split_pair("===", "==", &a, &b) >= 0); + assert_se(streq(a, "")); + assert_se(streq(b, "=")); +} + +static void test_first_word(void) { + assert_se(first_word("Hello", "")); + assert_se(first_word("Hello", "Hello")); + assert_se(first_word("Hello world", "Hello")); + assert_se(first_word("Hello\tworld", "Hello")); + assert_se(first_word("Hello\nworld", "Hello")); + assert_se(first_word("Hello\rworld", "Hello")); + assert_se(first_word("Hello ", "Hello")); + + assert_se(!first_word("Hello", "Hellooo")); + assert_se(!first_word("Hello", "xxxxx")); + assert_se(!first_word("Hellooo", "Hello")); +} + int main(int argc, char *argv[]) { test_string_erase(); test_ascii_strcasecmp_n(); test_ascii_strcasecmp_nn(); + test_streq_ptr(); + test_strstrip(); + test_strextend(); + test_strrep(); + test_strappend(); + test_string_has_cc(); + test_ascii_strlower(); + test_strshorten(); + test_strjoina(); + test_strcmp_ptr(); + test_foreach_word(); + test_foreach_word_quoted(); + test_endswith(); + test_endswith_no_case(); + test_delete_chars(); + test_in_charset(); + test_split_pair(); + test_first_word(); + return 0; } diff --git a/src/test/test-strv.c b/src/test/test-strv.c index 2b2f76cc7f..fc01dcfaf1 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -70,6 +70,18 @@ static const char* const input_table_none[] = { NULL, }; +static const char* const input_table_two_empties[] = { + "", + "", + NULL, +}; + +static const char* const input_table_one_empty[] = { + "", + NULL, +}; + + static const char* const input_table_quotes[] = { "\"", "'", @@ -130,7 +142,7 @@ static void test_strv_find_startswith(void) { } static void test_strv_join(void) { - _cleanup_free_ char *p = NULL, *q = NULL, *r = NULL, *s = NULL, *t = NULL; + _cleanup_free_ char *p = NULL, *q = NULL, *r = NULL, *s = NULL, *t = NULL, *v = NULL, *w = NULL; p = strv_join((char **)input_table_multiple, ", "); assert_se(p); @@ -151,6 +163,14 @@ static void test_strv_join(void) { t = strv_join((char **)input_table_none, ", "); assert_se(t); assert_se(streq(t, "")); + + v = strv_join((char **)input_table_two_empties, ", "); + assert_se(v); + assert_se(streq(v, ", ")); + + w = strv_join((char **)input_table_one_empty, ", "); + assert_se(w); + assert_se(streq(w, "")); } static void test_strv_quote_unquote(const char* const *split, const char *quoted) { @@ -338,7 +358,7 @@ static void test_strv_extend_strv_concat(void) { } static void test_strv_extend_strv(void) { - _cleanup_strv_free_ char **a = NULL, **b = NULL; + _cleanup_strv_free_ char **a = NULL, **b = NULL, **n = NULL; a = strv_new("abc", "def", "ghi", NULL); b = strv_new("jkl", "mno", "abc", "pqr", NULL); @@ -353,8 +373,14 @@ static void test_strv_extend_strv(void) { assert_se(streq(a[3], "jkl")); assert_se(streq(a[4], "mno")); assert_se(streq(a[5], "pqr")); - assert_se(strv_length(a) == 6); + + assert_se(strv_extend_strv(&n, b, false) >= 0); + assert_se(streq(n[0], "jkl")); + assert_se(streq(n[1], "mno")); + assert_se(streq(n[2], "abc")); + assert_se(streq(n[3], "pqr")); + assert_se(strv_length(n) == 4); } static void test_strv_extend(void) { @@ -640,6 +666,25 @@ static void test_strv_make_nulstr(void) { test_strv_make_nulstr_one(STRV_MAKE("foo", "bar", "quuux")); } +static void test_foreach_string(void) { + const char * const t[] = { + "foo", + "bar", + "waldo", + NULL + }; + const char *x; + unsigned i = 0; + + FOREACH_STRING(x, "foo", "bar", "waldo") + assert_se(streq_ptr(t[i++], x)); + + assert_se(i == 3); + + FOREACH_STRING(x, "zzz") + assert_se(streq(x, "zzz")); +} + int main(int argc, char *argv[]) { test_specifier_printf(); test_strv_foreach(); @@ -653,6 +698,8 @@ int main(int argc, char *argv[]) { test_strv_quote_unquote(input_table_multiple, "\"one\" \"two\" \"three\""); test_strv_quote_unquote(input_table_one, "\"one\""); test_strv_quote_unquote(input_table_none, ""); + test_strv_quote_unquote(input_table_one_empty, "\"\""); + test_strv_quote_unquote(input_table_two_empties, "\"\" \"\""); test_strv_quote_unquote(input_table_quotes, QUOTES_STRING); test_strv_quote_unquote(input_table_spaces, SPACES_STRING); @@ -702,5 +749,7 @@ int main(int argc, char *argv[]) { test_strv_extend_n(); test_strv_make_nulstr(); + test_foreach_string(); + return 0; } diff --git a/src/test/test-tables.c b/src/test/test-tables.c index aef992ee3c..0be74921fc 100644 --- a/src/test/test-tables.c +++ b/src/test/test-tables.c @@ -19,7 +19,6 @@ #include "architecture.h" #include "automount.h" -#include "bus-xml-policy.h" #include "busname.h" #include "cgroup.h" #include "compress.h" @@ -83,8 +82,6 @@ int main(int argc, char **argv) { test_table(path_result, PATH_RESULT); test_table(path_state, PATH_STATE); test_table(path_type, PATH_TYPE); - test_table(policy_item_class, POLICY_ITEM_CLASS); - test_table(policy_item_type, POLICY_ITEM_TYPE); test_table(protect_home, PROTECT_HOME); test_table(protect_system, PROTECT_SYSTEM); test_table(rlimit, RLIMIT); diff --git a/src/test/test-time.c b/src/test/test-time.c index 9062c3f3c1..ee7d55c5ab 100644 --- a/src/test/test-time.c +++ b/src/test/test-time.c @@ -220,7 +220,7 @@ int main(int argc, char *argv[]) { /* Ensure TIME_T_MAX works correctly */ x = (uintmax_t) TIME_T_MAX; - x ++; + x++; assert((time_t) x < 0); return 0; diff --git a/src/test/test-tmpfiles.c b/src/test/test-tmpfiles.c index d7223dd2bf..b34ebeefb2 100644 --- a/src/test/test-tmpfiles.c +++ b/src/test/test-tmpfiles.c @@ -32,15 +32,17 @@ #include "util.h" int main(int argc, char** argv) { + _cleanup_free_ char *cmd = NULL, *cmd2 = NULL, *ans = NULL, *ans2 = NULL, *d = NULL, *tmp = NULL, *line = NULL; + _cleanup_close_ int fd = -1, fd2 = -1; const char *p = argv[1] ?: "/tmp"; - char *pattern = strjoina(p, "/systemd-test-XXXXXX"); - _cleanup_close_ int fd, fd2; - _cleanup_free_ char *cmd, *cmd2, *ans, *ans2; + char *pattern; log_set_max_level(LOG_DEBUG); log_parse_environment(); - fd = open_tmpfile(p, O_RDWR|O_CLOEXEC); + pattern = strjoina(p, "/systemd-test-XXXXXX"); + + fd = open_tmpfile_unlinkable(p, O_RDWR|O_CLOEXEC); assert_se(fd >= 0); assert_se(asprintf(&cmd, "ls -l /proc/"PID_FMT"/fd/%d", getpid(), fd) > 0); @@ -59,5 +61,21 @@ int main(int argc, char** argv) { log_debug("link2: %s", ans2); assert_se(endswith(ans2, " (deleted)")); + pattern = strjoina(p, "/tmpfiles-test"); + assert_se(tempfn_random(pattern, NULL, &d) >= 0); + + fd = open_tmpfile_linkable(d, O_RDWR|O_CLOEXEC, &tmp); + assert_se(fd >= 0); + assert_se(write(fd, "foobar\n", 7) == 7); + + assert_se(touch(d) >= 0); + assert_se(link_tmpfile(fd, tmp, d) == -EEXIST); + assert_se(unlink(d) >= 0); + assert_se(link_tmpfile(fd, tmp, d) >= 0); + + assert_se(read_one_line_file(d, &line) >= 0); + assert_se(streq(line, "foobar")); + assert_se(unlink(d) >= 0); + return 0; } diff --git a/src/test/test-udev.c b/src/test/test-udev.c index 9cc64f7c68..e965b4494a 100644 --- a/src/test/test-udev.c +++ b/src/test/test-udev.c @@ -27,6 +27,7 @@ #include <unistd.h> #include "fs-util.h" +#include "log.h" #include "missing.h" #include "selinux-util.h" #include "signal-util.h" @@ -39,39 +40,31 @@ static int fake_filesystems(void) { const char *src; const char *target; const char *error; + bool ignore_mount_error; } fakefss[] = { - { "test/sys", "/sys", "failed to mount test /sys" }, - { "test/dev", "/dev", "failed to mount test /dev" }, - { "test/run", "/run", "failed to mount test /run" }, - { "test/run", "/etc/udev/rules.d", "failed to mount empty /etc/udev/rules.d" }, - { "test/run", UDEVLIBEXECDIR "/rules.d","failed to mount empty " UDEVLIBEXECDIR "/rules.d" }, + { "test/tmpfs/sys", "/sys", "failed to mount test /sys", false }, + { "test/tmpfs/dev", "/dev", "failed to mount test /dev", false }, + { "test/run", "/run", "failed to mount test /run", false }, + { "test/run", "/etc/udev/rules.d", "failed to mount empty /etc/udev/rules.d", true }, + { "test/run", UDEVLIBEXECDIR "/rules.d", "failed to mount empty " UDEVLIBEXECDIR "/rules.d", true }, }; unsigned int i; - int err; - err = unshare(CLONE_NEWNS); - if (err < 0) { - err = -errno; - fprintf(stderr, "failed to call unshare(): %m\n"); - goto out; - } + if (unshare(CLONE_NEWNS) < 0) + return log_error_errno(errno, "failed to call unshare(): %m"); - if (mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) < 0) { - err = -errno; - fprintf(stderr, "failed to mount / as private: %m\n"); - goto out; - } + if (mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) < 0) + return log_error_errno(errno, "failed to mount / as private: %m"); for (i = 0; i < ELEMENTSOF(fakefss); i++) { - err = mount(fakefss[i].src, fakefss[i].target, NULL, MS_BIND, NULL); - if (err < 0) { - err = -errno; - fprintf(stderr, "%s %m\n", fakefss[i].error); - return err; + if (mount(fakefss[i].src, fakefss[i].target, NULL, MS_BIND, NULL) < 0) { + log_full_errno(fakefss[i].ignore_mount_error ? LOG_DEBUG : LOG_ERR, errno, "%s: %m", fakefss[i].error); + if (!fakefss[i].ignore_mount_error) + return -errno; } } -out: - return err; + + return 0; } int main(int argc, char *argv[]) { @@ -84,6 +77,9 @@ int main(int argc, char *argv[]) { const char *action; int err; + log_parse_environment(); + log_open(); + err = fake_filesystems(); if (err < 0) return EXIT_FAILURE; @@ -93,7 +89,7 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; log_debug("version %s", VERSION); - mac_selinux_init("/dev"); + mac_selinux_init(); action = argv[1]; if (action == NULL) { diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c index b0c343590d..ade0ff2a63 100644 --- a/src/test/test-unit-file.c +++ b/src/test/test-unit-file.c @@ -35,10 +35,12 @@ #include "install.h" #include "load-fragment.h" #include "macro.h" +#include "rm-rf.h" #include "specifier.h" #include "string-util.h" #include "strv.h" #include "test-helper.h" +#include "tests.h" #include "user-util.h" #include "util.h" @@ -51,7 +53,7 @@ static int test_unit_file_get_set(void) { h = hashmap_new(&string_hash_ops); assert_se(h); - r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h); + r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h, NULL, NULL); if (r == -EPERM || r == -EACCES) { printf("Skipping test: unit_file_get_list: %s", strerror(-r)); @@ -113,7 +115,7 @@ static void test_config_parse_exec(void) { Manager *m = NULL; Unit *u = NULL; - r = manager_new(MANAGER_USER, true, &m); + r = manager_new(UNIT_FILE_USER, true, &m); if (MANAGER_SKIP_TEST(r)) { printf("Skipping test: manager_new: %s\n", strerror(-r)); return; @@ -606,7 +608,7 @@ static void test_install_printf(void) { } else assert_se(t == NULL); \ strcpy(i.name, d1); \ strcpy(i.path, d2); \ - } while(false) + } while (false) expect(i, "%n", "name.service"); expect(i, "%N", "name"); @@ -840,11 +842,14 @@ static void test_config_parse_pass_environ(void) { } int main(int argc, char *argv[]) { + _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; int r; log_parse_environment(); log_open(); + assert_se(runtime_dir = setup_fake_runtime_dir()); + r = test_unit_file_get_set(); test_config_parse_exec(); test_config_parse_capability_set(); diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c index 3de94ef425..2fd83f321c 100644 --- a/src/test/test-unit-name.c +++ b/src/test/test-unit-name.c @@ -209,7 +209,7 @@ static int test_unit_printf(void) { assert_se(get_home_dir(&home) >= 0); assert_se(get_shell(&shell) >= 0); - r = manager_new(MANAGER_USER, true, &m); + r = manager_new(UNIT_FILE_USER, true, &m); if (r == -EPERM || r == -EACCES || r == -EADDRINUSE) { puts("manager_new: Permission denied. Skipping test."); return EXIT_TEST_SKIP; diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c index 42c6a8d5e2..8d1ec19f17 100644 --- a/src/test/test-user-util.c +++ b/src/test/test-user-util.c @@ -37,6 +37,30 @@ static void test_gid_to_name_one(gid_t gid, const char *name) { assert_se(streq_ptr(t, name)); } +static void test_parse_uid(void) { + int r; + uid_t uid; + + r = parse_uid("100", &uid); + assert_se(r == 0); + assert_se(uid == 100); + + r = parse_uid("65535", &uid); + assert_se(r == -ENXIO); + + r = parse_uid("asdsdas", &uid); + assert_se(r == -EINVAL); +} + +static void test_uid_ptr(void) { + + assert_se(UID_TO_PTR(0) != NULL); + assert_se(UID_TO_PTR(1000) != NULL); + + assert_se(PTR_TO_UID(UID_TO_PTR(0)) == 0); + assert_se(PTR_TO_UID(UID_TO_PTR(1000)) == 1000); +} + int main(int argc, char*argv[]) { test_uid_to_name_one(0, "root"); @@ -48,5 +72,8 @@ int main(int argc, char*argv[]) { test_gid_to_name_one(0xFFFF, "65535"); test_gid_to_name_one(0xFFFFFFFF, "4294967295"); + test_parse_uid(); + test_uid_ptr(); + return 0; } diff --git a/src/test/test-util.c b/src/test/test-util.c index 9a8a265790..05cb1eae76 100644 --- a/src/test/test-util.c +++ b/src/test/test-util.c @@ -19,47 +19,16 @@ ***/ #include <errno.h> -#include <fcntl.h> -#include <signal.h> #include <string.h> -#include <sys/types.h> #include <sys/wait.h> -#include <sys/xattr.h> #include <unistd.h> -#include "alloc-util.h" -#include "conf-parser.h" -#include "cpu-set-util.h" #include "def.h" -#include "escape.h" -#include "fd-util.h" #include "fileio.h" #include "fs-util.h" -#include "fstab-util.h" -#include "glob-util.h" -#include "hexdecoct.h" -#include "io-util.h" -#include "mkdir.h" -#include "parse-util.h" -#include "path-util.h" -#include "proc-cmdline.h" -#include "process-util.h" #include "rm-rf.h" -#include "signal-util.h" -#include "special.h" -#include "stat-util.h" #include "string-util.h" -#include "strv.h" -#include "user-util.h" #include "util.h" -#include "virt.h" -#include "web-util.h" -#include "xattr-util.h" - -static void test_streq_ptr(void) { - assert_se(streq_ptr(NULL, NULL)); - assert_se(!streq_ptr("abc", "cdef")); -} static void test_align_power2(void) { unsigned long i, p2; @@ -151,19 +120,6 @@ static void test_container_of(void) { v1) == &myval); } -static void test_alloca(void) { - static const uint8_t zero[997] = { }; - char *t; - - t = alloca_align(17, 512); - assert_se(!((uintptr_t)t & 0xff)); - memzero(t, 17); - - t = alloca0_align(997, 1024); - assert_se(!((uintptr_t)t & 0x1ff)); - assert_se(!memcmp(t, zero, 997)); -} - static void test_div_round_up(void) { int div; @@ -197,544 +153,6 @@ static void test_div_round_up(void) { assert_se(0xfffffffdU / 10U + !!(0xfffffffdU % 10U) == 429496730U); } -static void test_first_word(void) { - assert_se(first_word("Hello", "")); - assert_se(first_word("Hello", "Hello")); - assert_se(first_word("Hello world", "Hello")); - assert_se(first_word("Hello\tworld", "Hello")); - assert_se(first_word("Hello\nworld", "Hello")); - assert_se(first_word("Hello\rworld", "Hello")); - assert_se(first_word("Hello ", "Hello")); - - assert_se(!first_word("Hello", "Hellooo")); - assert_se(!first_word("Hello", "xxxxx")); - assert_se(!first_word("Hellooo", "Hello")); -} - -static void test_close_many(void) { - int fds[3]; - char name0[] = "/tmp/test-close-many.XXXXXX"; - char name1[] = "/tmp/test-close-many.XXXXXX"; - char name2[] = "/tmp/test-close-many.XXXXXX"; - - fds[0] = mkostemp_safe(name0, O_RDWR|O_CLOEXEC); - fds[1] = mkostemp_safe(name1, O_RDWR|O_CLOEXEC); - fds[2] = mkostemp_safe(name2, O_RDWR|O_CLOEXEC); - - close_many(fds, 2); - - assert_se(fcntl(fds[0], F_GETFD) == -1); - assert_se(fcntl(fds[1], F_GETFD) == -1); - assert_se(fcntl(fds[2], F_GETFD) >= 0); - - safe_close(fds[2]); - - unlink(name0); - unlink(name1); - unlink(name2); -} - -static void test_parse_uid(void) { - int r; - uid_t uid; - - r = parse_uid("100", &uid); - assert_se(r == 0); - assert_se(uid == 100); - - r = parse_uid("65535", &uid); - assert_se(r == -ENXIO); - - r = parse_uid("asdsdas", &uid); - assert_se(r == -EINVAL); -} - -static void test_strappend(void) { - _cleanup_free_ char *t1, *t2, *t3, *t4; - - t1 = strappend(NULL, NULL); - assert_se(streq(t1, "")); - - t2 = strappend(NULL, "suf"); - assert_se(streq(t2, "suf")); - - t3 = strappend("pre", NULL); - assert_se(streq(t3, "pre")); - - t4 = strappend("pre", "suf"); - assert_se(streq(t4, "presuf")); -} - -static void test_strstrip(void) { - char *r; - char input[] = " hello, waldo. "; - - r = strstrip(input); - assert_se(streq(r, "hello, waldo.")); -} - -static void test_delete_chars(void) { - char *r; - char input[] = " hello, waldo. abc"; - - r = delete_chars(input, WHITESPACE); - assert_se(streq(r, "hello,waldo.abc")); -} - -static void test_in_charset(void) { - assert_se(in_charset("dddaaabbbcccc", "abcd")); - assert_se(!in_charset("dddaaabbbcccc", "abc f")); -} - -static void test_hexchar(void) { - assert_se(hexchar(0xa) == 'a'); - assert_se(hexchar(0x0) == '0'); -} - -static void test_unhexchar(void) { - assert_se(unhexchar('a') == 0xA); - assert_se(unhexchar('A') == 0xA); - assert_se(unhexchar('0') == 0x0); -} - -static void test_base32hexchar(void) { - assert_se(base32hexchar(0) == '0'); - assert_se(base32hexchar(9) == '9'); - assert_se(base32hexchar(10) == 'A'); - assert_se(base32hexchar(31) == 'V'); -} - -static void test_unbase32hexchar(void) { - assert_se(unbase32hexchar('0') == 0); - assert_se(unbase32hexchar('9') == 9); - assert_se(unbase32hexchar('A') == 10); - assert_se(unbase32hexchar('V') == 31); - assert_se(unbase32hexchar('=') == -EINVAL); -} - -static void test_base64char(void) { - assert_se(base64char(0) == 'A'); - assert_se(base64char(26) == 'a'); - assert_se(base64char(63) == '/'); -} - -static void test_unbase64char(void) { - assert_se(unbase64char('A') == 0); - assert_se(unbase64char('Z') == 25); - assert_se(unbase64char('a') == 26); - assert_se(unbase64char('z') == 51); - assert_se(unbase64char('0') == 52); - assert_se(unbase64char('9') == 61); - assert_se(unbase64char('+') == 62); - assert_se(unbase64char('/') == 63); - assert_se(unbase64char('=') == -EINVAL); -} - -static void test_octchar(void) { - assert_se(octchar(00) == '0'); - assert_se(octchar(07) == '7'); -} - -static void test_unoctchar(void) { - assert_se(unoctchar('0') == 00); - assert_se(unoctchar('7') == 07); -} - -static void test_decchar(void) { - assert_se(decchar(0) == '0'); - assert_se(decchar(9) == '9'); -} - -static void test_undecchar(void) { - assert_se(undecchar('0') == 0); - assert_se(undecchar('9') == 9); -} - -static void test_unhexmem(void) { - const char *hex = "efa214921"; - const char *hex_invalid = "efa214921o"; - _cleanup_free_ char *hex2 = NULL; - _cleanup_free_ void *mem = NULL; - size_t len; - - assert_se(unhexmem(hex, strlen(hex), &mem, &len) == 0); - assert_se(unhexmem(hex, strlen(hex) + 1, &mem, &len) == -EINVAL); - assert_se(unhexmem(hex_invalid, strlen(hex_invalid), &mem, &len) == -EINVAL); - - assert_se((hex2 = hexmem(mem, len))); - - free(mem); - - assert_se(memcmp(hex, hex2, strlen(hex)) == 0); - - free(hex2); - - assert_se(unhexmem(hex, strlen(hex) - 1, &mem, &len) == 0); - assert_se((hex2 = hexmem(mem, len))); - assert_se(memcmp(hex, hex2, strlen(hex) - 1) == 0); -} - -/* https://tools.ietf.org/html/rfc4648#section-10 */ -static void test_base32hexmem(void) { - char *b32; - - b32 = base32hexmem("", strlen(""), true); - assert_se(b32); - assert_se(streq(b32, "")); - free(b32); - - b32 = base32hexmem("f", strlen("f"), true); - assert_se(b32); - assert_se(streq(b32, "CO======")); - free(b32); - - b32 = base32hexmem("fo", strlen("fo"), true); - assert_se(b32); - assert_se(streq(b32, "CPNG====")); - free(b32); - - b32 = base32hexmem("foo", strlen("foo"), true); - assert_se(b32); - assert_se(streq(b32, "CPNMU===")); - free(b32); - - b32 = base32hexmem("foob", strlen("foob"), true); - assert_se(b32); - assert_se(streq(b32, "CPNMUOG=")); - free(b32); - - b32 = base32hexmem("fooba", strlen("fooba"), true); - assert_se(b32); - assert_se(streq(b32, "CPNMUOJ1")); - free(b32); - - b32 = base32hexmem("foobar", strlen("foobar"), true); - assert_se(b32); - assert_se(streq(b32, "CPNMUOJ1E8======")); - free(b32); - - b32 = base32hexmem("", strlen(""), false); - assert_se(b32); - assert_se(streq(b32, "")); - free(b32); - - b32 = base32hexmem("f", strlen("f"), false); - assert_se(b32); - assert_se(streq(b32, "CO")); - free(b32); - - b32 = base32hexmem("fo", strlen("fo"), false); - assert_se(b32); - assert_se(streq(b32, "CPNG")); - free(b32); - - b32 = base32hexmem("foo", strlen("foo"), false); - assert_se(b32); - assert_se(streq(b32, "CPNMU")); - free(b32); - - b32 = base32hexmem("foob", strlen("foob"), false); - assert_se(b32); - assert_se(streq(b32, "CPNMUOG")); - free(b32); - - b32 = base32hexmem("fooba", strlen("fooba"), false); - assert_se(b32); - assert_se(streq(b32, "CPNMUOJ1")); - free(b32); - - b32 = base32hexmem("foobar", strlen("foobar"), false); - assert_se(b32); - assert_se(streq(b32, "CPNMUOJ1E8")); - free(b32); -} - -static void test_unbase32hexmem(void) { - void *mem; - size_t len; - - assert_se(unbase32hexmem("", strlen(""), true, &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "")); - free(mem); - - assert_se(unbase32hexmem("CO======", strlen("CO======"), true, &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "f")); - free(mem); - - assert_se(unbase32hexmem("CPNG====", strlen("CPNG===="), true, &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "fo")); - free(mem); - - assert_se(unbase32hexmem("CPNMU===", strlen("CPNMU==="), true, &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "foo")); - free(mem); - - assert_se(unbase32hexmem("CPNMUOG=", strlen("CPNMUOG="), true, &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "foob")); - free(mem); - - assert_se(unbase32hexmem("CPNMUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "fooba")); - free(mem); - - assert_se(unbase32hexmem("CPNMUOJ1E8======", strlen("CPNMUOJ1E8======"), true, &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "foobar")); - free(mem); - - assert_se(unbase32hexmem("A", strlen("A"), true, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("A=======", strlen("A======="), true, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("AAA=====", strlen("AAA====="), true, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("AAAAAA==", strlen("AAAAAA=="), true, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("AB======", strlen("AB======"), true, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("AAAB====", strlen("AAAB===="), true, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("AAAAB===", strlen("AAAAB==="), true, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("AAAAAAB=", strlen("AAAAAAB="), true, &mem, &len) == -EINVAL); - - assert_se(unbase32hexmem("XPNMUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("CXNMUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("CPXMUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("CPNXUOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("CPNMXOJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("CPNMUXJ1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("CPNMUOX1", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("CPNMUOJX", strlen("CPNMUOJ1"), true, &mem, &len) == -EINVAL); - - assert_se(unbase32hexmem("", strlen(""), false, &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "")); - free(mem); - - assert_se(unbase32hexmem("CO", strlen("CO"), false, &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "f")); - free(mem); - - assert_se(unbase32hexmem("CPNG", strlen("CPNG"), false, &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "fo")); - free(mem); - - assert_se(unbase32hexmem("CPNMU", strlen("CPNMU"), false, &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "foo")); - free(mem); - - assert_se(unbase32hexmem("CPNMUOG", strlen("CPNMUOG"), false, &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "foob")); - free(mem); - - assert_se(unbase32hexmem("CPNMUOJ1", strlen("CPNMUOJ1"), false, &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "fooba")); - free(mem); - - assert_se(unbase32hexmem("CPNMUOJ1E8", strlen("CPNMUOJ1E8"), false, &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "foobar")); - free(mem); - - assert_se(unbase32hexmem("CPNMUOG=", strlen("CPNMUOG="), false, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("CPNMUOJ1E8======", strlen("CPNMUOJ1E8======"), false, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("A", strlen("A"), false, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("A", strlen("A"), false, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("AAA", strlen("AAA"), false, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("AAAAAA", strlen("AAAAAA"), false, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("AB", strlen("AB"), false, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("AAAB", strlen("AAAB"), false, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("AAAAB", strlen("AAAAB"), false, &mem, &len) == -EINVAL); - assert_se(unbase32hexmem("AAAAAAB", strlen("AAAAAAB"), false, &mem, &len) == -EINVAL); -} - -/* https://tools.ietf.org/html/rfc4648#section-10 */ -static void test_base64mem(void) { - char *b64; - - assert_se(base64mem("", strlen(""), &b64) == 0); - assert_se(streq(b64, "")); - free(b64); - - assert_se(base64mem("f", strlen("f"), &b64) == 4); - assert_se(streq(b64, "Zg==")); - free(b64); - - assert_se(base64mem("fo", strlen("fo"), &b64) == 4); - assert_se(streq(b64, "Zm8=")); - free(b64); - - assert_se(base64mem("foo", strlen("foo"), &b64) == 4); - assert_se(streq(b64, "Zm9v")); - free(b64); - - assert_se(base64mem("foob", strlen("foob"), &b64) == 8); - assert_se(streq(b64, "Zm9vYg==")); - free(b64); - - assert_se(base64mem("fooba", strlen("fooba"), &b64) == 8); - assert_se(streq(b64, "Zm9vYmE=")); - free(b64); - - assert_se(base64mem("foobar", strlen("foobar"), &b64) == 8); - assert_se(streq(b64, "Zm9vYmFy")); - free(b64); -} - -static void test_unbase64mem(void) { - void *mem; - size_t len; - - assert_se(unbase64mem("", strlen(""), &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "")); - free(mem); - - assert_se(unbase64mem("Zg==", strlen("Zg=="), &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "f")); - free(mem); - - assert_se(unbase64mem("Zm8=", strlen("Zm8="), &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "fo")); - free(mem); - - assert_se(unbase64mem("Zm9v", strlen("Zm9v"), &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "foo")); - free(mem); - - assert_se(unbase64mem("Zm9vYg==", strlen("Zm9vYg=="), &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "foob")); - free(mem); - - assert_se(unbase64mem("Zm9vYmE=", strlen("Zm9vYmE="), &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "fooba")); - free(mem); - - assert_se(unbase64mem("Zm9vYmFy", strlen("Zm9vYmFy"), &mem, &len) == 0); - assert_se(streq(strndupa(mem, len), "foobar")); - free(mem); - - assert_se(unbase64mem("A", strlen("A"), &mem, &len) == -EINVAL); - assert_se(unbase64mem("A====", strlen("A===="), &mem, &len) == -EINVAL); - assert_se(unbase64mem("AAB==", strlen("AAB=="), &mem, &len) == -EINVAL); - assert_se(unbase64mem("AAAB=", strlen("AAAB="), &mem, &len) == -EINVAL); -} - -static void test_cescape(void) { - _cleanup_free_ char *escaped; - - assert_se(escaped = cescape("abc\\\"\b\f\n\r\t\v\a\003\177\234\313")); - assert_se(streq(escaped, "abc\\\\\\\"\\b\\f\\n\\r\\t\\v\\a\\003\\177\\234\\313")); -} - -static void test_cunescape(void) { - _cleanup_free_ char *unescaped; - - assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", 0, &unescaped) < 0); - assert_se(cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00", UNESCAPE_RELAX, &unescaped) >= 0); - assert_se(streq_ptr(unescaped, "abc\\\"\b\f\a\n\r\t\v\003\177\234\313\\000\\x00")); - unescaped = mfree(unescaped); - - /* incomplete sequences */ - assert_se(cunescape("\\x0", 0, &unescaped) < 0); - assert_se(cunescape("\\x0", UNESCAPE_RELAX, &unescaped) >= 0); - assert_se(streq_ptr(unescaped, "\\x0")); - unescaped = mfree(unescaped); - - assert_se(cunescape("\\x", 0, &unescaped) < 0); - assert_se(cunescape("\\x", UNESCAPE_RELAX, &unescaped) >= 0); - assert_se(streq_ptr(unescaped, "\\x")); - unescaped = mfree(unescaped); - - assert_se(cunescape("\\", 0, &unescaped) < 0); - assert_se(cunescape("\\", UNESCAPE_RELAX, &unescaped) >= 0); - assert_se(streq_ptr(unescaped, "\\")); - unescaped = mfree(unescaped); - - assert_se(cunescape("\\11", 0, &unescaped) < 0); - assert_se(cunescape("\\11", UNESCAPE_RELAX, &unescaped) >= 0); - assert_se(streq_ptr(unescaped, "\\11")); - unescaped = mfree(unescaped); - - assert_se(cunescape("\\1", 0, &unescaped) < 0); - assert_se(cunescape("\\1", UNESCAPE_RELAX, &unescaped) >= 0); - assert_se(streq_ptr(unescaped, "\\1")); - unescaped = mfree(unescaped); - - assert_se(cunescape("\\u0000", 0, &unescaped) < 0); - assert_se(cunescape("\\u00DF\\U000000df\\u03a0\\U00000041", UNESCAPE_RELAX, &unescaped) >= 0); - assert_se(streq_ptr(unescaped, "ßßΠA")); - unescaped = mfree(unescaped); - - assert_se(cunescape("\\073", 0, &unescaped) >= 0); - assert_se(streq_ptr(unescaped, ";")); -} - -static void test_foreach_word(void) { - const char *word, *state; - size_t l; - int i = 0; - const char test[] = "test abc d\te f "; - const char * const expected[] = { - "test", - "abc", - "d", - "e", - "f", - "", - NULL - }; - - FOREACH_WORD(word, l, test, state) - assert_se(strneq(expected[i++], word, l)); -} - -static void check(const char *test, char** expected, bool trailing) { - const char *word, *state; - size_t l; - int i = 0; - - printf("<<<%s>>>\n", test); - FOREACH_WORD_QUOTED(word, l, test, state) { - _cleanup_free_ char *t = NULL; - - assert_se(t = strndup(word, l)); - assert_se(strneq(expected[i++], word, l)); - printf("<%s>\n", t); - } - printf("<<<%s>>>\n", state); - assert_se(expected[i] == NULL); - assert_se(isempty(state) == !trailing); -} - -static void test_foreach_word_quoted(void) { - check("test a b c 'd' e '' '' hhh '' '' \"a b c\"", - STRV_MAKE("test", - "a", - "b", - "c", - "d", - "e", - "", - "", - "hhh", - "", - "", - "a b c"), - false); - - check("test \"xxx", - STRV_MAKE("test"), - true); - - check("test\\", - STRV_MAKE_EMPTY, - true); -} - -static void test_memdup_multiply(void) { - int org[] = {1, 2, 3}; - int *dup; - - dup = (int*)memdup_multiply(org, sizeof(int), 3); - - assert_se(dup); - assert_se(dup[0] == 1); - assert_se(dup[1] == 2); - assert_se(dup[2] == 3); - free(dup); -} - static void test_u64log2(void) { assert_se(u64log2(0) == 0); assert_se(u64log2(8) == 3); @@ -754,210 +172,6 @@ static void test_protect_errno(void) { assert_se(errno == 12); } -static void test_parse_cpu_set(void) { - cpu_set_t *c = NULL; - int ncpus; - int cpu; - - /* Simple range (from CPUAffinity example) */ - ncpus = parse_cpu_set_and_warn("1 2", &c, NULL, "fake", 1, "CPUAffinity"); - assert_se(ncpus >= 1024); - assert_se(CPU_ISSET_S(1, CPU_ALLOC_SIZE(ncpus), c)); - assert_se(CPU_ISSET_S(2, CPU_ALLOC_SIZE(ncpus), c)); - assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 2); - c = mfree(c); - - /* A more interesting range */ - ncpus = parse_cpu_set_and_warn("0 1 2 3 8 9 10 11", &c, NULL, "fake", 1, "CPUAffinity"); - assert_se(ncpus >= 1024); - assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8); - for (cpu = 0; cpu < 4; cpu++) - assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); - for (cpu = 8; cpu < 12; cpu++) - assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); - c = mfree(c); - - /* Quoted strings */ - ncpus = parse_cpu_set_and_warn("8 '9' 10 \"11\"", &c, NULL, "fake", 1, "CPUAffinity"); - assert_se(ncpus >= 1024); - assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 4); - for (cpu = 8; cpu < 12; cpu++) - assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); - c = mfree(c); - - /* Use commas as separators */ - ncpus = parse_cpu_set_and_warn("0,1,2,3 8,9,10,11", &c, NULL, "fake", 1, "CPUAffinity"); - assert_se(ncpus >= 1024); - assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8); - for (cpu = 0; cpu < 4; cpu++) - assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); - for (cpu = 8; cpu < 12; cpu++) - assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); - c = mfree(c); - - /* Commas with spaces (and trailing comma, space) */ - ncpus = parse_cpu_set_and_warn("0, 1, 2, 3, 4, 5, 6, 7, ", &c, NULL, "fake", 1, "CPUAffinity"); - assert_se(ncpus >= 1024); - assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8); - for (cpu = 0; cpu < 8; cpu++) - assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); - c = mfree(c); - - /* Ranges */ - ncpus = parse_cpu_set_and_warn("0-3,8-11", &c, NULL, "fake", 1, "CPUAffinity"); - assert_se(ncpus >= 1024); - assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8); - for (cpu = 0; cpu < 4; cpu++) - assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); - for (cpu = 8; cpu < 12; cpu++) - assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); - c = mfree(c); - - /* Ranges with trailing comma, space */ - ncpus = parse_cpu_set_and_warn("0-3 8-11, ", &c, NULL, "fake", 1, "CPUAffinity"); - assert_se(ncpus >= 1024); - assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8); - for (cpu = 0; cpu < 4; cpu++) - assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); - for (cpu = 8; cpu < 12; cpu++) - assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); - c = mfree(c); - - /* Negative range (returns empty cpu_set) */ - ncpus = parse_cpu_set_and_warn("3-0", &c, NULL, "fake", 1, "CPUAffinity"); - assert_se(ncpus >= 1024); - assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 0); - c = mfree(c); - - /* Overlapping ranges */ - ncpus = parse_cpu_set_and_warn("0-7 4-11", &c, NULL, "fake", 1, "CPUAffinity"); - assert_se(ncpus >= 1024); - assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 12); - for (cpu = 0; cpu < 12; cpu++) - assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); - c = mfree(c); - - /* Mix ranges and individual CPUs */ - ncpus = parse_cpu_set_and_warn("0,1 4-11", &c, NULL, "fake", 1, "CPUAffinity"); - assert_se(ncpus >= 1024); - assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 10); - assert_se(CPU_ISSET_S(0, CPU_ALLOC_SIZE(ncpus), c)); - assert_se(CPU_ISSET_S(1, CPU_ALLOC_SIZE(ncpus), c)); - for (cpu = 4; cpu < 12; cpu++) - assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c)); - c = mfree(c); - - /* Garbage */ - ncpus = parse_cpu_set_and_warn("0 1 2 3 garbage", &c, NULL, "fake", 1, "CPUAffinity"); - assert_se(ncpus < 0); - assert_se(!c); - - /* Range with garbage */ - ncpus = parse_cpu_set_and_warn("0-3 8-garbage", &c, NULL, "fake", 1, "CPUAffinity"); - assert_se(ncpus < 0); - assert_se(!c); - - /* Empty string */ - c = NULL; - ncpus = parse_cpu_set_and_warn("", &c, NULL, "fake", 1, "CPUAffinity"); - assert_se(ncpus == 0); /* empty string returns 0 */ - assert_se(!c); - - /* Runnaway quoted string */ - ncpus = parse_cpu_set_and_warn("0 1 2 3 \"4 5 6 7 ", &c, NULL, "fake", 1, "CPUAffinity"); - assert_se(ncpus < 0); - assert_se(!c); -} - -static void test_config_parse_iec_uint64(void) { - uint64_t offset = 0; - assert_se(config_parse_iec_uint64(NULL, "/this/file", 11, "Section", 22, "Size", 0, "4M", &offset, NULL) == 0); - assert_se(offset == 4 * 1024 * 1024); - - assert_se(config_parse_iec_uint64(NULL, "/this/file", 11, "Section", 22, "Size", 0, "4.5M", &offset, NULL) == 0); -} - -static void test_strextend(void) { - _cleanup_free_ char *str = strdup("0123"); - strextend(&str, "456", "78", "9", NULL); - assert_se(streq(str, "0123456789")); -} - -static void test_strrep(void) { - _cleanup_free_ char *one, *three, *zero; - one = strrep("waldo", 1); - three = strrep("waldo", 3); - zero = strrep("waldo", 0); - - assert_se(streq(one, "waldo")); - assert_se(streq(three, "waldowaldowaldo")); - assert_se(streq(zero, "")); -} - -static void test_split_pair(void) { - _cleanup_free_ char *a = NULL, *b = NULL; - - assert_se(split_pair("", "", &a, &b) == -EINVAL); - assert_se(split_pair("foo=bar", "", &a, &b) == -EINVAL); - assert_se(split_pair("", "=", &a, &b) == -EINVAL); - assert_se(split_pair("foo=bar", "=", &a, &b) >= 0); - assert_se(streq(a, "foo")); - assert_se(streq(b, "bar")); - free(a); - free(b); - assert_se(split_pair("==", "==", &a, &b) >= 0); - assert_se(streq(a, "")); - assert_se(streq(b, "")); - free(a); - free(b); - - assert_se(split_pair("===", "==", &a, &b) >= 0); - assert_se(streq(a, "")); - assert_se(streq(b, "=")); -} - -static void test_fstab_node_to_udev_node(void) { - char *n; - - n = fstab_node_to_udev_node("LABEL=applé/jack"); - puts(n); - assert_se(streq(n, "/dev/disk/by-label/applé\\x2fjack")); - free(n); - - n = fstab_node_to_udev_node("PARTLABEL=pinkié pie"); - puts(n); - assert_se(streq(n, "/dev/disk/by-partlabel/pinkié\\x20pie")); - free(n); - - n = fstab_node_to_udev_node("UUID=037b9d94-148e-4ee4-8d38-67bfe15bb535"); - puts(n); - assert_se(streq(n, "/dev/disk/by-uuid/037b9d94-148e-4ee4-8d38-67bfe15bb535")); - free(n); - - n = fstab_node_to_udev_node("PARTUUID=037b9d94-148e-4ee4-8d38-67bfe15bb535"); - puts(n); - assert_se(streq(n, "/dev/disk/by-partuuid/037b9d94-148e-4ee4-8d38-67bfe15bb535")); - free(n); - - n = fstab_node_to_udev_node("PONIES=awesome"); - puts(n); - assert_se(streq(n, "PONIES=awesome")); - free(n); - - n = fstab_node_to_udev_node("/dev/xda1"); - puts(n); - assert_se(streq(n, "/dev/xda1")); - free(n); -} - -static void test_get_files_in_directory(void) { - _cleanup_strv_free_ char **l = NULL, **t = NULL; - - assert_se(get_files_in_directory("/tmp", &l) >= 0); - assert_se(get_files_in_directory(".", &t) >= 0); - assert_se(get_files_in_directory(".", NULL) >= 0); -} - static void test_in_set(void) { assert_se(IN_SET(1, 1)); assert_se(IN_SET(1, 1, 2, 3, 4)); @@ -968,50 +182,6 @@ static void test_in_set(void) { assert_se(!IN_SET(0, 1, 2, 3, 4)); } -static void test_writing_tmpfile(void) { - char name[] = "/tmp/test-systemd_writing_tmpfile.XXXXXX"; - _cleanup_free_ char *contents = NULL; - size_t size; - int fd, r; - struct iovec iov[3]; - - IOVEC_SET_STRING(iov[0], "abc\n"); - IOVEC_SET_STRING(iov[1], ALPHANUMERICAL "\n"); - IOVEC_SET_STRING(iov[2], ""); - - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); - printf("tmpfile: %s", name); - - r = writev(fd, iov, 3); - assert_se(r >= 0); - - r = read_full_file(name, &contents, &size); - assert_se(r == 0); - printf("contents: %s", contents); - assert_se(streq(contents, "abc\n" ALPHANUMERICAL "\n")); - - unlink(name); -} - -static void test_hexdump(void) { - uint8_t data[146]; - unsigned i; - - hexdump(stdout, NULL, 0); - hexdump(stdout, "", 0); - hexdump(stdout, "", 1); - hexdump(stdout, "x", 1); - hexdump(stdout, "x", 2); - hexdump(stdout, "foobar", 7); - hexdump(stdout, "f\nobar", 7); - hexdump(stdout, "xxxxxxxxxxxxxxxxxxxxyz", 23); - - for (i = 0; i < ELEMENTSOF(data); i++) - data[i] = i*2; - - hexdump(stdout, data, sizeof(data)); -} - static void test_log2i(void) { assert_se(log2i(1) == 0); assert_se(log2i(2) == 1); @@ -1023,341 +193,6 @@ static void test_log2i(void) { assert_se(log2i(INT_MAX) == sizeof(int)*8-2); } -static void test_foreach_string(void) { - const char * const t[] = { - "foo", - "bar", - "waldo", - NULL - }; - const char *x; - unsigned i = 0; - - FOREACH_STRING(x, "foo", "bar", "waldo") - assert_se(streq_ptr(t[i++], x)); - - assert_se(i == 3); - - FOREACH_STRING(x, "zzz") - assert_se(streq(x, "zzz")); -} - -static void test_filename_is_valid(void) { - char foo[FILENAME_MAX+2]; - int i; - - assert_se(!filename_is_valid("")); - assert_se(!filename_is_valid("/bar/foo")); - assert_se(!filename_is_valid("/")); - assert_se(!filename_is_valid(".")); - assert_se(!filename_is_valid("..")); - - for (i=0; i<FILENAME_MAX+1; i++) - foo[i] = 'a'; - foo[FILENAME_MAX+1] = '\0'; - - assert_se(!filename_is_valid(foo)); - - assert_se(filename_is_valid("foo_bar-333")); - assert_se(filename_is_valid("o.o")); -} - -static void test_string_has_cc(void) { - assert_se(string_has_cc("abc\1", NULL)); - assert_se(string_has_cc("abc\x7f", NULL)); - assert_se(string_has_cc("abc\x7f", NULL)); - assert_se(string_has_cc("abc\t\x7f", "\t")); - assert_se(string_has_cc("abc\t\x7f", "\t")); - assert_se(string_has_cc("\x7f", "\t")); - assert_se(string_has_cc("\x7f", "\t\a")); - - assert_se(!string_has_cc("abc\t\t", "\t")); - assert_se(!string_has_cc("abc\t\t\a", "\t\a")); - assert_se(!string_has_cc("a\ab\tc", "\t\a")); -} - -static void test_ascii_strlower(void) { - char a[] = "AabBcC Jk Ii Od LKJJJ kkd LK"; - assert_se(streq(ascii_strlower(a), "aabbcc jk ii od lkjjj kkd lk")); -} - -static void test_files_same(void) { - _cleanup_close_ int fd = -1; - char name[] = "/tmp/test-files_same.XXXXXX"; - char name_alias[] = "/tmp/test-files_same.alias"; - - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); - assert_se(fd >= 0); - assert_se(symlink(name, name_alias) >= 0); - - assert_se(files_same(name, name)); - assert_se(files_same(name, name_alias)); - - unlink(name); - unlink(name_alias); -} - -static void test_is_valid_documentation_url(void) { - assert_se(documentation_url_is_valid("http://www.freedesktop.org/wiki/Software/systemd")); - assert_se(documentation_url_is_valid("https://www.kernel.org/doc/Documentation/binfmt_misc.txt")); - assert_se(documentation_url_is_valid("file:/foo/foo")); - assert_se(documentation_url_is_valid("man:systemd.special(7)")); - assert_se(documentation_url_is_valid("info:bar")); - - assert_se(!documentation_url_is_valid("foo:")); - assert_se(!documentation_url_is_valid("info:")); - assert_se(!documentation_url_is_valid("")); -} - -static void test_file_in_same_dir(void) { - char *t; - - t = file_in_same_dir("/", "a"); - assert_se(streq(t, "/a")); - free(t); - - t = file_in_same_dir("/", "/a"); - assert_se(streq(t, "/a")); - free(t); - - t = file_in_same_dir("", "a"); - assert_se(streq(t, "a")); - free(t); - - t = file_in_same_dir("a/", "a"); - assert_se(streq(t, "a/a")); - free(t); - - t = file_in_same_dir("bar/foo", "bar"); - assert_se(streq(t, "bar/bar")); - free(t); -} - -static void test_endswith(void) { - assert_se(endswith("foobar", "bar")); - assert_se(endswith("foobar", "")); - assert_se(endswith("foobar", "foobar")); - assert_se(endswith("", "")); - - assert_se(!endswith("foobar", "foo")); - assert_se(!endswith("foobar", "foobarfoofoo")); -} - -static void test_endswith_no_case(void) { - assert_se(endswith_no_case("fooBAR", "bar")); - assert_se(endswith_no_case("foobar", "")); - assert_se(endswith_no_case("foobar", "FOOBAR")); - assert_se(endswith_no_case("", "")); - - assert_se(!endswith_no_case("foobar", "FOO")); - assert_se(!endswith_no_case("foobar", "FOOBARFOOFOO")); -} - -static void test_close_nointr(void) { - char name[] = "/tmp/test-test-close_nointr.XXXXXX"; - int fd; - - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); - assert_se(fd >= 0); - assert_se(close_nointr(fd) >= 0); - assert_se(close_nointr(fd) < 0); - - unlink(name); -} - - -static void test_unlink_noerrno(void) { - char name[] = "/tmp/test-close_nointr.XXXXXX"; - int fd; - - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); - assert_se(fd >= 0); - assert_se(close_nointr(fd) >= 0); - - { - PROTECT_ERRNO; - errno = -42; - assert_se(unlink_noerrno(name) >= 0); - assert_se(errno == -42); - assert_se(unlink_noerrno(name) < 0); - assert_se(errno == -42); - } -} - -static void test_readlink_and_make_absolute(void) { - char tempdir[] = "/tmp/test-readlink_and_make_absolute"; - char name[] = "/tmp/test-readlink_and_make_absolute/original"; - char name2[] = "test-readlink_and_make_absolute/original"; - char name_alias[] = "/tmp/test-readlink_and_make_absolute-alias"; - char *r = NULL; - - assert_se(mkdir_safe(tempdir, 0755, getuid(), getgid()) >= 0); - assert_se(touch(name) >= 0); - - assert_se(symlink(name, name_alias) >= 0); - assert_se(readlink_and_make_absolute(name_alias, &r) >= 0); - assert_se(streq(r, name)); - free(r); - assert_se(unlink(name_alias) >= 0); - - assert_se(chdir(tempdir) >= 0); - assert_se(symlink(name2, name_alias) >= 0); - assert_se(readlink_and_make_absolute(name_alias, &r) >= 0); - assert_se(streq(r, name)); - free(r); - assert_se(unlink(name_alias) >= 0); - - assert_se(rm_rf(tempdir, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); -} - -static void test_ignore_signals(void) { - assert_se(ignore_signals(SIGINT, -1) >= 0); - assert_se(kill(getpid(), SIGINT) >= 0); - assert_se(ignore_signals(SIGUSR1, SIGUSR2, SIGTERM, SIGPIPE, -1) >= 0); - assert_se(kill(getpid(), SIGUSR1) >= 0); - assert_se(kill(getpid(), SIGUSR2) >= 0); - assert_se(kill(getpid(), SIGTERM) >= 0); - assert_se(kill(getpid(), SIGPIPE) >= 0); - assert_se(default_signals(SIGINT, SIGUSR1, SIGUSR2, SIGTERM, SIGPIPE, -1) >= 0); -} - -static void test_strshorten(void) { - char s[] = "foobar"; - - assert_se(strlen(strshorten(s, 6)) == 6); - assert_se(strlen(strshorten(s, 12)) == 6); - assert_se(strlen(strshorten(s, 2)) == 2); - assert_se(strlen(strshorten(s, 0)) == 0); -} - -static void test_strjoina(void) { - char *actual; - - actual = strjoina("", "foo", "bar"); - assert_se(streq(actual, "foobar")); - - actual = strjoina("foo", "bar", "baz"); - assert_se(streq(actual, "foobarbaz")); - - actual = strjoina("foo", "", "bar", "baz"); - assert_se(streq(actual, "foobarbaz")); - - actual = strjoina("foo"); - assert_se(streq(actual, "foo")); - - actual = strjoina(NULL); - assert_se(streq(actual, "")); - - actual = strjoina(NULL, "foo"); - assert_se(streq(actual, "")); - - actual = strjoina("foo", NULL, "bar"); - assert_se(streq(actual, "foo")); -} - -static void test_is_symlink(void) { - char name[] = "/tmp/test-is_symlink.XXXXXX"; - char name_link[] = "/tmp/test-is_symlink.link"; - _cleanup_close_ int fd = -1; - - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); - assert_se(fd >= 0); - assert_se(symlink(name, name_link) >= 0); - - assert_se(is_symlink(name) == 0); - assert_se(is_symlink(name_link) == 1); - assert_se(is_symlink("/a/file/which/does/not/exist/i/guess") < 0); - - - unlink(name); - unlink(name_link); -} - -static void test_search_and_fopen(void) { - const char *dirs[] = {"/tmp/foo/bar", "/tmp", NULL}; - char name[] = "/tmp/test-search_and_fopen.XXXXXX"; - int fd = -1; - int r; - FILE *f; - - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); - assert_se(fd >= 0); - close(fd); - - r = search_and_fopen(basename(name), "r", NULL, dirs, &f); - assert_se(r >= 0); - fclose(f); - - r = search_and_fopen(name, "r", NULL, dirs, &f); - assert_se(r >= 0); - fclose(f); - - r = search_and_fopen(basename(name), "r", "/", dirs, &f); - assert_se(r >= 0); - fclose(f); - - r = search_and_fopen("/a/file/which/does/not/exist/i/guess", "r", NULL, dirs, &f); - assert_se(r < 0); - r = search_and_fopen("afilewhichdoesnotexistiguess", "r", NULL, dirs, &f); - assert_se(r < 0); - - r = unlink(name); - assert_se(r == 0); - - r = search_and_fopen(basename(name), "r", NULL, dirs, &f); - assert_se(r < 0); -} - - -static void test_search_and_fopen_nulstr(void) { - const char dirs[] = "/tmp/foo/bar\0/tmp\0"; - char name[] = "/tmp/test-search_and_fopen.XXXXXX"; - int fd = -1; - int r; - FILE *f; - - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); - assert_se(fd >= 0); - close(fd); - - r = search_and_fopen_nulstr(basename(name), "r", NULL, dirs, &f); - assert_se(r >= 0); - fclose(f); - - r = search_and_fopen_nulstr(name, "r", NULL, dirs, &f); - assert_se(r >= 0); - fclose(f); - - r = search_and_fopen_nulstr("/a/file/which/does/not/exist/i/guess", "r", NULL, dirs, &f); - assert_se(r < 0); - r = search_and_fopen_nulstr("afilewhichdoesnotexistiguess", "r", NULL, dirs, &f); - assert_se(r < 0); - - r = unlink(name); - assert_se(r == 0); - - r = search_and_fopen_nulstr(basename(name), "r", NULL, dirs, &f); - assert_se(r < 0); -} - -static void test_glob_exists(void) { - char name[] = "/tmp/test-glob_exists.XXXXXX"; - int fd = -1; - int r; - - fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); - assert_se(fd >= 0); - close(fd); - - r = glob_exists("/tmp/test-glob_exists*"); - assert_se(r == 1); - - r = unlink(name); - assert_se(r == 0); - r = glob_exists("/tmp/test-glob_exists*"); - assert_se(r == 0); -} - static void test_execute_directory(void) { char template_lo[] = "/tmp/test-readlink_and_make_absolute-lo.XXXXXXX"; char template_hi[] = "/tmp/test-readlink_and_make_absolute-hi.XXXXXXX"; @@ -1402,17 +237,6 @@ static void test_execute_directory(void) { (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL); } -static int parse_item(const char *key, const char *value) { - assert_se(key); - - log_info("kernel cmdline option <%s> = <%s>", key, strna(value)); - return 0; -} - -static void test_parse_proc_cmdline(void) { - assert_se(parse_proc_cmdline(parse_item) >= 0); -} - static void test_raw_clone(void) { pid_t parent, pid, pid2; @@ -1438,285 +262,20 @@ static void test_raw_clone(void) { } } -static void test_same_fd(void) { - _cleanup_close_pair_ int p[2] = { -1, -1 }; - _cleanup_close_ int a = -1, b = -1, c = -1; - - assert_se(pipe2(p, O_CLOEXEC) >= 0); - assert_se((a = dup(p[0])) >= 0); - assert_se((b = open("/dev/null", O_RDONLY|O_CLOEXEC)) >= 0); - assert_se((c = dup(a)) >= 0); - - assert_se(same_fd(p[0], p[0]) > 0); - assert_se(same_fd(p[1], p[1]) > 0); - assert_se(same_fd(a, a) > 0); - assert_se(same_fd(b, b) > 0); - - assert_se(same_fd(a, p[0]) > 0); - assert_se(same_fd(p[0], a) > 0); - assert_se(same_fd(c, p[0]) > 0); - assert_se(same_fd(p[0], c) > 0); - assert_se(same_fd(a, c) > 0); - assert_se(same_fd(c, a) > 0); - - assert_se(same_fd(p[0], p[1]) == 0); - assert_se(same_fd(p[1], p[0]) == 0); - assert_se(same_fd(p[0], b) == 0); - assert_se(same_fd(b, p[0]) == 0); - assert_se(same_fd(p[1], a) == 0); - assert_se(same_fd(a, p[1]) == 0); - assert_se(same_fd(p[1], b) == 0); - assert_se(same_fd(b, p[1]) == 0); - - assert_se(same_fd(a, b) == 0); - assert_se(same_fd(b, a) == 0); -} - -static void test_uid_ptr(void) { - - assert_se(UID_TO_PTR(0) != NULL); - assert_se(UID_TO_PTR(1000) != NULL); - - assert_se(PTR_TO_UID(UID_TO_PTR(0)) == 0); - assert_se(PTR_TO_UID(UID_TO_PTR(1000)) == 1000); -} - -static void test_sparse_write_one(int fd, const char *buffer, size_t n) { - char check[n]; - - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(ftruncate(fd, 0) >= 0); - assert_se(sparse_write(fd, buffer, n, 4) == (ssize_t) n); - - assert_se(lseek(fd, 0, SEEK_CUR) == (off_t) n); - assert_se(ftruncate(fd, n) >= 0); - - assert_se(lseek(fd, 0, SEEK_SET) == 0); - assert_se(read(fd, check, n) == (ssize_t) n); - - assert_se(memcmp(buffer, check, n) == 0); -} - -static void test_sparse_write(void) { - const char test_a[] = "test"; - const char test_b[] = "\0\0\0\0test\0\0\0\0"; - const char test_c[] = "\0\0test\0\0\0\0"; - const char test_d[] = "\0\0test\0\0\0test\0\0\0\0test\0\0\0\0\0test\0\0\0test\0\0\0\0test\0\0\0\0\0\0\0\0"; - const char test_e[] = "test\0\0\0\0test"; - _cleanup_close_ int fd = -1; - char fn[] = "/tmp/sparseXXXXXX"; - - fd = mkostemp(fn, O_CLOEXEC); - assert_se(fd >= 0); - unlink(fn); - - test_sparse_write_one(fd, test_a, sizeof(test_a)); - test_sparse_write_one(fd, test_b, sizeof(test_b)); - test_sparse_write_one(fd, test_c, sizeof(test_c)); - test_sparse_write_one(fd, test_d, sizeof(test_d)); - test_sparse_write_one(fd, test_e, sizeof(test_e)); -} - -static void test_shell_escape_one(const char *s, const char *bad, const char *expected) { - _cleanup_free_ char *r; - - assert_se(r = shell_escape(s, bad)); - assert_se(streq_ptr(r, expected)); -} - -static void test_shell_escape(void) { - test_shell_escape_one("", "", ""); - test_shell_escape_one("\\", "", "\\\\"); - test_shell_escape_one("foobar", "", "foobar"); - test_shell_escape_one("foobar", "o", "f\\o\\obar"); - 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; - - assert_se(r = shell_maybe_quote(s)); - assert_se(streq(r, 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\""); -} - -static void test_tempfn(void) { - char *ret = NULL, *p; - - assert_se(tempfn_xxxxxx("/foo/bar/waldo", NULL, &ret) >= 0); - assert_se(streq_ptr(ret, "/foo/bar/.#waldoXXXXXX")); - free(ret); - - assert_se(tempfn_xxxxxx("/foo/bar/waldo", "[miau]", &ret) >= 0); - assert_se(streq_ptr(ret, "/foo/bar/.#[miau]waldoXXXXXX")); - free(ret); - - assert_se(tempfn_random("/foo/bar/waldo", NULL, &ret) >= 0); - assert_se(p = startswith(ret, "/foo/bar/.#waldo")); - assert_se(strlen(p) == 16); - assert_se(in_charset(p, "0123456789abcdef")); - free(ret); - - assert_se(tempfn_random("/foo/bar/waldo", "[wuff]", &ret) >= 0); - assert_se(p = startswith(ret, "/foo/bar/.#[wuff]waldo")); - assert_se(strlen(p) == 16); - assert_se(in_charset(p, "0123456789abcdef")); - free(ret); - - assert_se(tempfn_random_child("/foo/bar/waldo", NULL, &ret) >= 0); - assert_se(p = startswith(ret, "/foo/bar/waldo/.#")); - assert_se(strlen(p) == 16); - assert_se(in_charset(p, "0123456789abcdef")); - free(ret); - - assert_se(tempfn_random_child("/foo/bar/waldo", "[kikiriki]", &ret) >= 0); - assert_se(p = startswith(ret, "/foo/bar/waldo/.#[kikiriki]")); - assert_se(strlen(p) == 16); - assert_se(in_charset(p, "0123456789abcdef")); - free(ret); -} - -static void test_strcmp_ptr(void) { - assert_se(strcmp_ptr(NULL, NULL) == 0); - assert_se(strcmp_ptr("", NULL) > 0); - assert_se(strcmp_ptr("foo", NULL) > 0); - assert_se(strcmp_ptr(NULL, "") < 0); - assert_se(strcmp_ptr(NULL, "bar") < 0); - assert_se(strcmp_ptr("foo", "bar") > 0); - assert_se(strcmp_ptr("bar", "baz") < 0); - assert_se(strcmp_ptr("foo", "foo") == 0); - assert_se(strcmp_ptr("", "") == 0); -} - -static void test_fgetxattrat_fake(void) { - char t[] = "/var/tmp/xattrtestXXXXXX"; - _cleanup_close_ int fd = -1; - const char *x; - char v[3] = {}; - int r; - - assert_se(mkdtemp(t)); - x = strjoina(t, "/test"); - assert_se(touch(x) >= 0); - - r = setxattr(x, "user.foo", "bar", 3, 0); - if (r < 0 && errno == EOPNOTSUPP) /* no xattrs supported on /var/tmp... */ - goto cleanup; - assert_se(r >= 0); - - fd = open(t, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY); - assert_se(fd >= 0); - - assert_se(fgetxattrat_fake(fd, "test", "user.foo", v, 3, 0) >= 0); - assert_se(memcmp(v, "bar", 3) == 0); - - safe_close(fd); - fd = open("/", O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY); - assert_se(fd >= 0); - assert_se(fgetxattrat_fake(fd, "usr", "user.idontexist", v, 3, 0) == -ENODATA); - -cleanup: - assert_se(unlink(x) >= 0); - assert_se(rmdir(t) >= 0); -} - -static void test_runlevel_to_target(void) { - assert_se(streq_ptr(runlevel_to_target(NULL), NULL)); - assert_se(streq_ptr(runlevel_to_target("unknown-runlevel"), NULL)); - assert_se(streq_ptr(runlevel_to_target("3"), SPECIAL_MULTI_USER_TARGET)); -} - int main(int argc, char *argv[]) { log_parse_environment(); log_open(); - test_streq_ptr(); test_align_power2(); test_max(); test_container_of(); - test_alloca(); test_div_round_up(); - test_first_word(); - test_close_many(); - test_parse_uid(); - test_strappend(); - test_strstrip(); - test_delete_chars(); - test_in_charset(); - test_hexchar(); - test_unhexchar(); - test_base32hexchar(); - test_unbase32hexchar(); - test_base64char(); - test_unbase64char(); - test_octchar(); - test_unoctchar(); - test_decchar(); - test_undecchar(); - test_unhexmem(); - test_base32hexmem(); - test_unbase32hexmem(); - test_base64mem(); - test_unbase64mem(); - test_cescape(); - test_cunescape(); - test_foreach_word(); - test_foreach_word_quoted(); - test_memdup_multiply(); test_u64log2(); test_protect_errno(); - test_parse_cpu_set(); - test_config_parse_iec_uint64(); - test_strextend(); - test_strrep(); - test_split_pair(); - test_fstab_node_to_udev_node(); - test_get_files_in_directory(); test_in_set(); - test_writing_tmpfile(); - test_hexdump(); test_log2i(); - test_foreach_string(); - test_filename_is_valid(); - test_string_has_cc(); - test_ascii_strlower(); - test_files_same(); - test_is_valid_documentation_url(); - test_file_in_same_dir(); - test_endswith(); - test_endswith_no_case(); - test_close_nointr(); - test_unlink_noerrno(); - test_readlink_and_make_absolute(); - test_ignore_signals(); - test_strshorten(); - test_strjoina(); - test_is_symlink(); - test_search_and_fopen(); - test_search_and_fopen_nulstr(); - test_glob_exists(); test_execute_directory(); - test_parse_proc_cmdline(); test_raw_clone(); - test_same_fd(); - test_uid_ptr(); - test_sparse_write(); - test_shell_escape(); - test_shell_maybe_quote(); - test_tempfn(); - test_strcmp_ptr(); - test_fgetxattrat_fake(); - test_runlevel_to_target(); return 0; } diff --git a/src/test/test-web-util.c b/src/test/test-web-util.c new file mode 100644 index 0000000000..79a3a13af6 --- /dev/null +++ b/src/test/test-web-util.c @@ -0,0 +1,39 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "macro.h" +#include "web-util.h" + +static void test_is_valid_documentation_url(void) { + assert_se(documentation_url_is_valid("http://www.freedesktop.org/wiki/Software/systemd")); + assert_se(documentation_url_is_valid("https://www.kernel.org/doc/Documentation/binfmt_misc.txt")); + assert_se(documentation_url_is_valid("file:/foo/foo")); + assert_se(documentation_url_is_valid("man:systemd.special(7)")); + assert_se(documentation_url_is_valid("info:bar")); + + assert_se(!documentation_url_is_valid("foo:")); + assert_se(!documentation_url_is_valid("info:")); + assert_se(!documentation_url_is_valid("")); +} + +int main(int argc, char *argv[]) { + test_is_valid_documentation_url(); + + return 0; +} diff --git a/src/test/test-xattr-util.c b/src/test/test-xattr-util.c new file mode 100644 index 0000000000..267f29426c --- /dev/null +++ b/src/test/test-xattr-util.c @@ -0,0 +1,69 @@ +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/xattr.h> +#include <unistd.h> + +#include "alloc-util.h" +#include "fd-util.h" +#include "fs-util.h" +#include "macro.h" +#include "string-util.h" +#include "xattr-util.h" + +static void test_fgetxattrat_fake(void) { + char t[] = "/var/tmp/xattrtestXXXXXX"; + _cleanup_close_ int fd = -1; + const char *x; + char v[3] = {}; + int r; + + assert_se(mkdtemp(t)); + x = strjoina(t, "/test"); + assert_se(touch(x) >= 0); + + r = setxattr(x, "user.foo", "bar", 3, 0); + if (r < 0 && errno == EOPNOTSUPP) /* no xattrs supported on /var/tmp... */ + goto cleanup; + assert_se(r >= 0); + + fd = open(t, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY); + assert_se(fd >= 0); + + assert_se(fgetxattrat_fake(fd, "test", "user.foo", v, 3, 0) >= 0); + assert_se(memcmp(v, "bar", 3) == 0); + + safe_close(fd); + fd = open("/", O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY); + assert_se(fd >= 0); + assert_se(fgetxattrat_fake(fd, "usr", "user.idontexist", v, 3, 0) == -ENODATA); + +cleanup: + assert_se(unlink(x) >= 0); + assert_se(rmdir(t) >= 0); +} + +int main(void) { + test_fgetxattrat_fake(); + + return 0; +} diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c index 097963b41b..a2270aff46 100644 --- a/src/timedate/timedatectl.c +++ b/src/timedate/timedatectl.c @@ -40,14 +40,6 @@ static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; static char *arg_host = NULL; static bool arg_adjust_system_clock = false; -static void pager_open_if_enabled(void) { - - if (arg_no_pager) - return; - - pager_open(false); -} - static void polkit_agent_open_if_enabled(void) { /* Open the polkit agent as a child process if necessary */ @@ -313,7 +305,7 @@ static int list_timezones(sd_bus *bus, char **args, unsigned n) { if (r < 0) return log_error_errno(r, "Failed to read list of time zones: %m"); - pager_open_if_enabled(); + pager_open(arg_no_pager, false); strv_print(zones); return 0; diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c index 2a10135fba..ffec609c69 100644 --- a/src/timedate/timedated.c +++ b/src/timedate/timedated.c @@ -78,7 +78,7 @@ static int context_read_data(Context *c) { c->zone = t; t = NULL; - c->local_rtc = clock_is_localtime() > 0; + c->local_rtc = clock_is_localtime(NULL) > 0; return 0; } @@ -125,30 +125,44 @@ static int context_write_data_local_rtc(Context *c) { if (!w) return -ENOMEM; } else { - char *p, *e; + char *p; + const char *e = "\n"; /* default if there is less than 3 lines */ + const char *prepend = ""; size_t a, b; - p = strchr(s, '\n'); - if (!p) - return -EIO; - - p = strchr(p+1, '\n'); - if (!p) - return -EIO; - - p++; - e = strchr(p, '\n'); - if (!e) - return -EIO; + p = strchrnul(s, '\n'); + if (*p == '\0') + /* only one line, no \n terminator */ + prepend = "\n0\n"; + else if (p[1] == '\0') { + /* only one line, with \n terminator */ + ++p; + prepend = "0\n"; + } else { + p = strchr(p+1, '\n'); + if (!p) { + /* only two lines, no \n terminator */ + prepend = "\n"; + p = s + strlen(s); + } else { + char *end; + /* third line might have a \n terminator or not */ + p++; + end = strchr(p, '\n'); + /* if we actually have a fourth line, use that as suffix "e", otherwise the default \n */ + if (end) + e = end; + } + } a = p - s; b = strlen(e); - w = new(char, a + (c->local_rtc ? 5 : 3) + b + 1); + w = new(char, a + (c->local_rtc ? 5 : 3) + strlen(prepend) + b + 1); if (!w) return -ENOMEM; - *(char*) mempcpy(stpcpy(mempcpy(w, s, a), c->local_rtc ? "LOCAL" : "UTC"), e, b) = 0; + *(char*) mempcpy(stpcpy(stpcpy(mempcpy(w, s, a), prepend), c->local_rtc ? "LOCAL" : "UTC"), e, b) = 0; if (streq(w, NULL_ADJTIME_UTC)) { if (unlink("/etc/adjtime") < 0) @@ -159,7 +173,7 @@ static int context_write_data_local_rtc(Context *c) { } } - mac_selinux_init("/etc"); + mac_selinux_init(); return write_string_file_atomic_label("/etc/adjtime", w); } diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c index 23e19159e0..b67d672a6a 100644 --- a/src/timesync/timesyncd.c +++ b/src/timesync/timesyncd.c @@ -122,7 +122,7 @@ int main(int argc, char *argv[]) { goto finish; } - if (clock_is_localtime() > 0) { + if (clock_is_localtime(NULL) > 0) { log_info("The system is configured to read the RTC time in the local time zone. " "This mode can not be fully supported. All system time to RTC updates are disabled."); m->rtc_local_time = true; diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 7b105a6bd4..2053d35a67 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -94,6 +94,7 @@ typedef enum ItemType { /* These ones take globs */ WRITE_FILE = 'w', + EMPTY_DIRECTORY = 'e', SET_XATTR = 't', RECURSIVE_SET_XATTR = 'T', SET_ACL = 'a', @@ -179,6 +180,7 @@ static bool needs_glob(ItemType t) { IGNORE_DIRECTORY_PATH, REMOVE_PATH, RECURSIVE_REMOVE_PATH, + EMPTY_DIRECTORY, ADJUST_MODE, RELABEL_PATH, RECURSIVE_RELABEL_PATH, @@ -195,6 +197,7 @@ static bool takes_ownership(ItemType t) { CREATE_FILE, TRUNCATE_FILE, CREATE_DIRECTORY, + EMPTY_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, @@ -613,7 +616,7 @@ static int path_set_perms(Item *i, const char *path) { * with AT_SYMLINK_NOFOLLOW, hence we emulate it here via * O_PATH. */ - fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_NOATIME); + fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); if (fd < 0) return log_error_errno(errno, "Adjusting owner and mode for %s failed: %m", path); @@ -804,7 +807,7 @@ static int path_set_acls(Item *item, const char *path) { assert(item); assert(path); - fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_NOATIME); + fd = open(path, O_NOFOLLOW|O_CLOEXEC|O_PATH); if (fd < 0) return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path); @@ -917,10 +920,7 @@ static int parse_attribute_from_arg(Item *item) { v = attributes[i].value; - if (mode == MODE_ADD || mode == MODE_SET) - value |= v; - else - value &= ~v; + SET_FLAG(value, v, (mode == MODE_ADD || mode == MODE_SET)); mask |= v; } @@ -1220,7 +1220,6 @@ static int create_item(Item *i) { case CREATE_SUBVOLUME: case CREATE_SUBVOLUME_INHERIT_QUOTA: case CREATE_SUBVOLUME_NEW_QUOTA: - RUN_WITH_UMASK(0000) mkdir_parents_label(i->path, 0755); @@ -1279,11 +1278,11 @@ static int create_item(Item *i) { if (IN_SET(i->type, CREATE_SUBVOLUME_NEW_QUOTA, CREATE_SUBVOLUME_INHERIT_QUOTA)) { r = btrfs_subvol_auto_qgroup(i->path, 0, i->type == CREATE_SUBVOLUME_NEW_QUOTA); if (r == -ENOTTY) - log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because of unsupported file system or because directory is not a subvolume: %m", i->path); + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (unsupported fs or dir not a subvolume): %m", i->path); else if (r == -EROFS) - log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because of read-only file system: %m", i->path); + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (fs is read-only).", i->path); else if (r == -ENOPROTOOPT) - log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" because quota support is disabled: %m", i->path); + log_debug_errno(r, "Couldn't adjust quota for subvolume \"%s\" (quota support is disabled).", i->path); else if (r < 0) q = log_error_errno(r, "Failed to adjust quota for subvolume \"%s\": %m", i->path); else if (r > 0) @@ -1292,6 +1291,9 @@ static int create_item(Item *i) { log_debug("Quota for subvolume \"%s\" already in place, no change made.", i->path); } + /* fall through */ + + case EMPTY_DIRECTORY: r = path_set_perms(i, i->path); if (q < 0) return q; @@ -1301,7 +1303,6 @@ static int create_item(Item *i) { break; case CREATE_FIFO: - RUN_WITH_UMASK(0000) { mac_selinux_create_file_prepare(i->path, S_IFIFO); r = mkfifo(i->path, i->mode); @@ -1538,47 +1539,20 @@ static int remove_item_instance(Item *i, const char *instance) { } static int remove_item(Item *i) { - int r = 0; - assert(i); log_debug("Running remove action for entry %c %s", (char) i->type, i->path); switch (i->type) { - case CREATE_FILE: - case TRUNCATE_FILE: - case CREATE_DIRECTORY: - case CREATE_SUBVOLUME: - case CREATE_SUBVOLUME_INHERIT_QUOTA: - case CREATE_SUBVOLUME_NEW_QUOTA: - case CREATE_FIFO: - case CREATE_SYMLINK: - case CREATE_CHAR_DEVICE: - case CREATE_BLOCK_DEVICE: - case IGNORE_PATH: - case IGNORE_DIRECTORY_PATH: - case ADJUST_MODE: - case RELABEL_PATH: - case RECURSIVE_RELABEL_PATH: - case WRITE_FILE: - case COPY_FILES: - case SET_XATTR: - case RECURSIVE_SET_XATTR: - case SET_ACL: - case RECURSIVE_SET_ACL: - case SET_ATTRIBUTE: - case RECURSIVE_SET_ATTRIBUTE: - break; - case REMOVE_PATH: case TRUNCATE_DIRECTORY: case RECURSIVE_REMOVE_PATH: - r = glob_item(i, remove_item_instance, false); - break; - } + return glob_item(i, remove_item_instance, false); - return r; + default: + return 0; + } } static int clean_item_instance(Item *i, const char* instance) { @@ -1633,8 +1607,6 @@ static int clean_item_instance(Item *i, const char* instance) { } static int clean_item(Item *i) { - int r = 0; - assert(i); log_debug("Running clean action for entry %c %s", (char) i->type, i->path); @@ -1644,19 +1616,17 @@ static int clean_item(Item *i) { case CREATE_SUBVOLUME: case CREATE_SUBVOLUME_INHERIT_QUOTA: case CREATE_SUBVOLUME_NEW_QUOTA: + case EMPTY_DIRECTORY: case TRUNCATE_DIRECTORY: case IGNORE_PATH: case COPY_FILES: clean_item_instance(i, i->path); - break; + return 0; case IGNORE_DIRECTORY_PATH: - r = glob_item(i, clean_item_instance, false); - break; + return glob_item(i, clean_item_instance, false); default: - break; + return 0; } - - return r; } static int process_item_array(ItemArray *array); @@ -1882,6 +1852,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { case CREATE_SUBVOLUME: case CREATE_SUBVOLUME_INHERIT_QUOTA: case CREATE_SUBVOLUME_NEW_QUOTA: + case EMPTY_DIRECTORY: case TRUNCATE_DIRECTORY: case CREATE_FIFO: case IGNORE_PATH: @@ -2201,7 +2172,8 @@ static int parse_argv(int argc, char *argv[]) { } static int read_config_file(const char *fn, bool ignore_enoent) { - _cleanup_fclose_ FILE *f = NULL; + _cleanup_fclose_ FILE *_f = NULL; + FILE *f; char line[LINE_MAX]; Iterator iterator; unsigned v = 0; @@ -2210,16 +2182,23 @@ static int read_config_file(const char *fn, bool ignore_enoent) { assert(fn); - r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f); - if (r < 0) { - if (ignore_enoent && r == -ENOENT) { - log_debug_errno(r, "Failed to open \"%s\": %m", fn); - return 0; - } + if (streq(fn, "-")) { + log_debug("Reading config from stdin."); + fn = "<stdin>"; + f = stdin; + } else { + r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &_f); + if (r < 0) { + if (ignore_enoent && r == -ENOENT) { + log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn); + return 0; + } - return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn); + return log_error_errno(r, "Failed to open '%s': %m", fn); + } + log_debug("Reading config file \"%s\".", fn); + f = _f; } - log_debug("Reading config file \"%s\".", fn); FOREACH_LINE(line, f, break) { char *l; @@ -2288,7 +2267,7 @@ int main(int argc, char *argv[]) { umask(0022); - mac_selinux_init(NULL); + mac_selinux_init(); items = ordered_hashmap_new(&string_hash_ops); globs = ordered_hashmap_new(&string_hash_ops); diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index 7b67831e54..ee879c7b89 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -65,8 +65,8 @@ static int ask_password_plymouth( const char *flag_file, char ***ret) { + static const union sockaddr_union sa = PLYMOUTH_SOCKET; _cleanup_close_ int fd = -1, notify = -1; - union sockaddr_union sa = PLYMOUTH_SOCKET; _cleanup_free_ char *packet = NULL; ssize_t k; int r, n; @@ -94,7 +94,7 @@ static int ask_password_plymouth( if (fd < 0) return -errno; - r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)); + r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) return -errno; @@ -269,8 +269,7 @@ static int send_passwords(const char *socket_name, char **passwords) { strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path)); - r = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, - offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)); + r = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) r = log_debug_errno(errno, "sendto(): %m"); @@ -481,7 +480,7 @@ static int show_passwords(void) { if (de->d_type != DT_REG) continue; - if (hidden_file(de->d_name)) + if (hidden_or_backup_file(de->d_name)) continue; if (!startswith(de->d_name, "ask.")) diff --git a/src/udev/mtd_probe/mtd_probe.h b/src/udev/mtd_probe/mtd_probe.h index caea5c2693..68e4954537 100644 --- a/src/udev/mtd_probe/mtd_probe.h +++ b/src/udev/mtd_probe/mtd_probe.h @@ -1,3 +1,5 @@ +#pragma once + /* * Copyright (C) 2010 - Maxim Levitsky * @@ -17,8 +19,6 @@ * Boston, MA 02110-1301 USA */ -#pragma once - #include <mtd/mtd-user.h> #include "macro.h" diff --git a/src/udev/mtd_probe/probe_smartmedia.c b/src/udev/mtd_probe/probe_smartmedia.c index 6a6c5522a7..2a7ba17637 100644 --- a/src/udev/mtd_probe/probe_smartmedia.c +++ b/src/udev/mtd_probe/probe_smartmedia.c @@ -73,7 +73,7 @@ void probe_smart_media(int mtd_fd, mtd_info_t* info) for (offset = 0 ; offset < block_size * spare_count ; offset += sector_size) { lseek(mtd_fd, SEEK_SET, offset); - if (read(mtd_fd, cis_buffer, SM_SECTOR_SIZE) == SM_SECTOR_SIZE){ + if (read(mtd_fd, cis_buffer, SM_SECTOR_SIZE) == SM_SECTOR_SIZE) { cis_found = 1; break; } diff --git a/src/udev/net/ethtool-util.h b/src/udev/net/ethtool-util.h index 2e6e1d7150..7716516e76 100644 --- a/src/udev/net/ethtool-util.h +++ b/src/udev/net/ethtool-util.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - #include <macro.h> /* we can't use DUPLEX_ prefix, as it diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 15145fc5eb..c66504102f 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -18,7 +18,6 @@ ***/ #include <netinet/ether.h> -#include <linux/netdevice.h> #include "sd-netlink.h" diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index f525fe2116..9df5529d05 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -1,3 +1,5 @@ +#pragma once + /*** This file is part of systemd. @@ -17,8 +19,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#pragma once - #include "libudev.h" #include "condition.h" diff --git a/src/udev/scsi_id/scsi.h b/src/udev/scsi_id/scsi.h index 3bf1a94200..a27a84a40a 100644 --- a/src/udev/scsi_id/scsi.h +++ b/src/udev/scsi_id/scsi.h @@ -1,3 +1,5 @@ +#pragma once + /* * scsi.h * @@ -10,8 +12,6 @@ * Free Software Foundation version 2 of the License. */ -#pragma once - #include <scsi/scsi.h> struct scsi_ioctl_command { diff --git a/src/udev/scsi_id/scsi_id.h b/src/udev/scsi_id/scsi_id.h index 141b116a88..5c2e1c28ee 100644 --- a/src/udev/scsi_id/scsi_id.h +++ b/src/udev/scsi_id/scsi_id.h @@ -1,3 +1,5 @@ +#pragma once + /* * Copyright (C) IBM Corp. 2003 * @@ -15,8 +17,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#pragma once - #define MAX_PATH_LEN 512 /* diff --git a/src/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c index 3a3d8a1770..51a55cdbc4 100644 --- a/src/udev/udev-builtin-input_id.c +++ b/src/udev/udev-builtin-input_id.c @@ -177,7 +177,7 @@ static bool test_pointers(struct udev_device *dev, has_mt_coordinates = test_bit(ABS_MT_POSITION_X, bitmask_abs) && test_bit(ABS_MT_POSITION_Y, bitmask_abs); /* unset has_mt_coordinates if devices claims to have all abs axis */ - if(has_mt_coordinates && test_bit(ABS_MT_SLOT, bitmask_abs) && test_bit(ABS_MT_SLOT - 1, bitmask_abs)) + if (has_mt_coordinates && test_bit(ABS_MT_SLOT, bitmask_abs) && test_bit(ABS_MT_SLOT - 1, bitmask_abs)) has_mt_coordinates = false; is_direct = test_bit(INPUT_PROP_DIRECT, bitmask_props); has_touch = test_bit(BTN_TOUCH, bitmask_key); diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c index 8b1bcefe2d..a7be2a4eed 100644 --- a/src/udev/udev-builtin-net_id.c +++ b/src/udev/udev-builtin-net_id.c @@ -27,21 +27,21 @@ * http://www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames * * Two character prefixes based on the type of interface: - * en -- Ethernet - * sl -- serial line IP (slip) - * wl -- wlan - * ww -- wwan + * en — Ethernet + * sl — serial line IP (slip) + * wl — wlan + * ww — wwan * * Type of names: - * b<number> -- BCMA bus core number - * c<bus_id> -- CCW bus group name, without leading zeros [s390] - * o<index>[d<dev_port>] -- on-board device index number - * s<slot>[f<function>][d<dev_port>] -- hotplug slot index number - * x<MAC> -- MAC address + * b<number> — BCMA bus core number + * c<bus_id> — CCW bus group name, without leading zeros [s390] + * o<index>[d<dev_port>] — on-board device index number + * s<slot>[f<function>][d<dev_port>] — hotplug slot index number + * x<MAC> — MAC address * [P<domain>]p<bus>s<slot>[f<function>][d<dev_port>] - * -- PCI geographical location + * — PCI geographical location * [P<domain>]p<bus>s<slot>[f<function>][u<port>][..][c<config>][i<interface>] - * -- USB port number chain + * — USB port number chain * * All multi-function PCI devices will carry the [f<function>] number in the * device name, including the function 0 device. @@ -140,9 +140,9 @@ static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) { const char *attr; int idx; - /* ACPI _DSM -- device specific method for naming a PCI or PCI Express device */ + /* ACPI _DSM — device specific method for naming a PCI or PCI Express device */ attr = udev_device_get_sysattr_value(names->pcidev, "acpi_index"); - /* SMBIOS type 41 -- Onboard Devices Extended Information */ + /* SMBIOS type 41 — Onboard Devices Extended Information */ if (!attr) attr = udev_device_get_sysattr_value(names->pcidev, "index"); if (!attr) @@ -230,7 +230,7 @@ static int dev_pci_slot(struct udev_device *dev, struct netnames *names) { if (l == 0) names->pci_path[0] = '\0'; - /* ACPI _SUN -- slot user number */ + /* ACPI _SUN — slot user number */ pci = udev_device_new_from_subsystem_sysname(udev, "subsystem", "pci"); if (!pci) { err = -ENOENT; diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c index b6ed45d8ba..6e9adc6e96 100644 --- a/src/udev/udev-builtin-path_id.c +++ b/src/udev/udev-builtin-path_id.c @@ -712,7 +712,7 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool * devices do not expose their buses and do not provide a unique * and predictable name that way. */ - if (streq(udev_device_get_subsystem(dev), "block") && !supported_transport) + if (streq_ptr(udev_device_get_subsystem(dev), "block") && !supported_transport) path = mfree(path); if (path != NULL) { diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c index 962de22f43..f68a09d7a8 100644 --- a/src/udev/udev-ctrl.c +++ b/src/udev/udev-ctrl.c @@ -105,7 +105,7 @@ struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd) { uctrl->saddr.un.sun_family = AF_LOCAL; strscpy(uctrl->saddr.un.sun_path, sizeof(uctrl->saddr.un.sun_path), "/run/udev/control"); - uctrl->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(uctrl->saddr.un.sun_path); + uctrl->addrlen = SOCKADDR_UN_LEN(uctrl->saddr.un); return uctrl; } diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index c06ace09cf..26fa52cf6c 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -33,9 +33,11 @@ #include "conf-files.h" #include "escape.h" #include "fd-util.h" +#include "fs-util.h" #include "glob-util.h" #include "path-util.h" #include "stat-util.h" +#include "stdio-util.h" #include "strbuf.h" #include "string-util.h" #include "strv.h" @@ -327,8 +329,8 @@ static void dump_token(struct udev_rules *rules, struct token *token) { enum token_type type = token->type; enum operation_type op = token->key.op; enum string_glob_type glob = token->key.glob; - const char *value = str(rules, token->key.value_off); - const char *attr = &rules->buf[token->key.attr_off]; + const char *value = rules_str(rules, token->key.value_off); + const char *attr = &rules->strbuf->buf[token->key.attr_off]; switch (type) { case TK_RULE: @@ -338,9 +340,9 @@ static void dump_token(struct udev_rules *rules, struct token *token) { unsigned int idx = (tk_ptr - tks_ptr) / sizeof(struct token); log_debug("* RULE %s:%u, token: %u, count: %u, label: '%s'", - &rules->buf[token->rule.filename_off], token->rule.filename_line, + &rules->strbuf->buf[token->rule.filename_off], token->rule.filename_line, idx, token->rule.token_count, - &rules->buf[token->rule.label_off]); + &rules->strbuf->buf[token->rule.label_off]); break; } case TK_M_ACTION: @@ -437,11 +439,11 @@ static void dump_token(struct udev_rules *rules, struct token *token) { static void dump_rules(struct udev_rules *rules) { unsigned int i; - log_debug("dumping %u (%zu bytes) tokens, %u (%zu bytes) strings", + log_debug("dumping %u (%zu bytes) tokens, %zu (%zu bytes) strings", rules->token_cur, rules->token_cur * sizeof(struct token), - rules->buf_count, - rules->buf_cur); + rules->strbuf->nodes_count, + rules->strbuf->len); for (i = 0; i < rules->token_cur; i++) dump_token(rules, &rules->tokens[i]); } @@ -686,41 +688,31 @@ static int import_parent_into_properties(struct udev_device *dev, const char *fi return 0; } -static int attr_subst_subdir(char *attr, size_t len) { - bool found = false; +static void attr_subst_subdir(char *attr, size_t len) { + const char *pos, *tail, *path; + _cleanup_closedir_ DIR *dir = NULL; + struct dirent *dent; - if (strstr(attr, "/*/")) { - char *pos; - char dirname[UTIL_PATH_SIZE]; - const char *tail; - DIR *dir; + pos = strstr(attr, "/*/"); + if (!pos) + return; - strscpy(dirname, sizeof(dirname), attr); - pos = strstr(dirname, "/*/"); - if (pos == NULL) - return -1; - pos[0] = '\0'; - tail = &pos[2]; - dir = opendir(dirname); - if (dir != NULL) { - struct dirent *dent; + tail = pos + 2; + path = strndupa(attr, pos - attr + 1); /* include slash at end */ + dir = opendir(path); + if (dir == NULL) + return; - for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { - struct stat stats; + for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) + if (dent->d_name[0] != '.') { + char n[strlen(dent->d_name) + strlen(tail) + 1]; - if (dent->d_name[0] == '.') - continue; - strscpyl(attr, len, dirname, "/", dent->d_name, tail, NULL); - if (stat(attr, &stats) == 0) { - found = true; - break; - } + strscpyl(n, sizeof n, dent->d_name, tail, NULL); + if (faccessat(dirfd(dir), n, F_OK, 0) == 0) { + strscpyl(attr, len, path, n, NULL); + break; } - closedir(dir); } - } - - return found; } static int get_key(struct udev *udev, char **line, char **key, enum operation_type *op, char **value) { @@ -831,12 +823,13 @@ static const char *get_key_attribute(struct udev *udev, char *str) { return NULL; } -static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, - enum operation_type op, - const char *value, const void *data) { - struct token *token = &rule_tmp->token[rule_tmp->token_cur]; +static void rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, + enum operation_type op, + const char *value, const void *data) { + struct token *token = rule_tmp->token + rule_tmp->token_cur; const char *attr = NULL; + assert(rule_tmp->token_cur < ELEMENTSOF(rule_tmp->token)); memzero(token, sizeof(struct token)); switch (type) { @@ -919,8 +912,7 @@ static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, case TK_M_MAX: case TK_END: case TK_UNSET: - log_error("wrong type %u", type); - return -1; + assert_not_reached("wrong type"); } if (value != NULL && type < TK_M_MAX) { @@ -969,11 +961,6 @@ static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, token->key.type = type; token->key.op = op; rule_tmp->token_cur++; - if (rule_tmp->token_cur >= ELEMENTSOF(rule_tmp->token)) { - log_error("temporary rule array too small"); - return -1; - } - return 0; } static int sort_token(struct udev_rules *rules, struct rule_tmp *rule_tmp) { @@ -1010,8 +997,13 @@ static int sort_token(struct udev_rules *rules, struct rule_tmp *rule_tmp) { return 0; } -static int add_rule(struct udev_rules *rules, char *line, - const char *filename, unsigned int filename_off, unsigned int lineno) { +#define LOG_RULE_ERROR(fmt, ...) log_error("Invalid rule %s:%u: " fmt, filename, lineno, ##__VA_ARGS__) +#define LOG_RULE_WARNING(fmt, ...) log_warning("%s:%u: " fmt, filename, lineno, ##__VA_ARGS__) +#define LOG_RULE_DEBUG(fmt, ...) log_debug("%s:%u: " fmt, filename, lineno, ##__VA_ARGS__) +#define LOG_AND_RETURN(fmt, ...) { LOG_RULE_ERROR(fmt, __VA_ARGS__); return; } + +static void add_rule(struct udev_rules *rules, char *line, + const char *filename, unsigned int filename_off, unsigned int lineno) { char *linepos; const char *attr; struct rule_tmp rule_tmp = { @@ -1052,427 +1044,322 @@ static int add_rule(struct udev_rules *rules, char *line, break; } + if (rule_tmp.token_cur >= ELEMENTSOF(rule_tmp.token)) + LOG_AND_RETURN("temporary rule array too small, aborting event processing with %u items", rule_tmp.token_cur); + if (streq(key, "ACTION")) { - if (op > OP_MATCH_MAX) { - log_error("invalid ACTION operation"); - goto invalid; - } + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", key); + rule_add_key(&rule_tmp, TK_M_ACTION, op, value, NULL); - continue; - } - if (streq(key, "DEVPATH")) { - if (op > OP_MATCH_MAX) { - log_error("invalid DEVPATH operation"); - goto invalid; - } + } else if (streq(key, "DEVPATH")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", key); + rule_add_key(&rule_tmp, TK_M_DEVPATH, op, value, NULL); - continue; - } - if (streq(key, "KERNEL")) { - if (op > OP_MATCH_MAX) { - log_error("invalid KERNEL operation"); - goto invalid; - } + } else if (streq(key, "KERNEL")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", key); + rule_add_key(&rule_tmp, TK_M_KERNEL, op, value, NULL); - continue; - } - if (streq(key, "SUBSYSTEM")) { - if (op > OP_MATCH_MAX) { - log_error("invalid SUBSYSTEM operation"); - goto invalid; - } + } else if (streq(key, "SUBSYSTEM")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", key); + /* bus, class, subsystem events should all be the same */ - if (streq(value, "subsystem") || - streq(value, "bus") || - streq(value, "class")) { - if (streq(value, "bus") || streq(value, "class")) - log_error("'%s' must be specified as 'subsystem' " - "please fix it in %s:%u", value, filename, lineno); + if (STR_IN_SET(value, "subsystem", "bus", "class")) { + if (!streq(value, "subsystem")) + LOG_RULE_WARNING("'%s' must be specified as 'subsystem'; please fix", value); + rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, "subsystem|class|bus", NULL); } else rule_add_key(&rule_tmp, TK_M_SUBSYSTEM, op, value, NULL); - continue; - } - if (streq(key, "DRIVER")) { - if (op > OP_MATCH_MAX) { - log_error("invalid DRIVER operation"); - goto invalid; - } + } else if (streq(key, "DRIVER")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", key); + rule_add_key(&rule_tmp, TK_M_DRIVER, op, value, NULL); - continue; - } - if (startswith(key, "ATTR{")) { + } else if (startswith(key, "ATTR{")) { attr = get_key_attribute(rules->udev, key + strlen("ATTR")); - if (attr == NULL) { - log_error("error parsing ATTR attribute"); - goto invalid; - } - if (op == OP_REMOVE) { - log_error("invalid ATTR operation"); - goto invalid; - } + if (attr == NULL) + LOG_AND_RETURN("error parsing %s attribute", "ATTR"); + + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", "ATTR"); + if (op < OP_MATCH_MAX) rule_add_key(&rule_tmp, TK_M_ATTR, op, value, attr); else rule_add_key(&rule_tmp, TK_A_ATTR, op, value, attr); - continue; - } - if (startswith(key, "SYSCTL{")) { + } else if (startswith(key, "SYSCTL{")) { attr = get_key_attribute(rules->udev, key + strlen("SYSCTL")); - if (attr == NULL) { - log_error("error parsing SYSCTL attribute"); - goto invalid; - } - if (op == OP_REMOVE) { - log_error("invalid SYSCTL operation"); - goto invalid; - } + if (attr == NULL) + LOG_AND_RETURN("error parsing %s attribute", "ATTR"); + + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", "ATTR"); + if (op < OP_MATCH_MAX) rule_add_key(&rule_tmp, TK_M_SYSCTL, op, value, attr); else rule_add_key(&rule_tmp, TK_A_SYSCTL, op, value, attr); - continue; - } - if (startswith(key, "SECLABEL{")) { + } else if (startswith(key, "SECLABEL{")) { attr = get_key_attribute(rules->udev, key + strlen("SECLABEL")); - if (!attr) { - log_error("error parsing SECLABEL attribute"); - goto invalid; - } - if (op == OP_REMOVE) { - log_error("invalid SECLABEL operation"); - goto invalid; - } + if (attr == NULL) + LOG_AND_RETURN("error parsing %s attribute", "SECLABEL"); + + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", "SECLABEL"); rule_add_key(&rule_tmp, TK_A_SECLABEL, op, value, attr); - continue; - } - if (streq(key, "KERNELS")) { - if (op > OP_MATCH_MAX) { - log_error("invalid KERNELS operation"); - goto invalid; - } + } else if (streq(key, "KERNELS")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", key); + rule_add_key(&rule_tmp, TK_M_KERNELS, op, value, NULL); - continue; - } - if (streq(key, "SUBSYSTEMS")) { - if (op > OP_MATCH_MAX) { - log_error("invalid SUBSYSTEMS operation"); - goto invalid; - } + } else if (streq(key, "SUBSYSTEMS")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", key); + rule_add_key(&rule_tmp, TK_M_SUBSYSTEMS, op, value, NULL); - continue; - } - if (streq(key, "DRIVERS")) { - if (op > OP_MATCH_MAX) { - log_error("invalid DRIVERS operation"); - goto invalid; - } + } else if (streq(key, "DRIVERS")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", key); + rule_add_key(&rule_tmp, TK_M_DRIVERS, op, value, NULL); - continue; - } - if (startswith(key, "ATTRS{")) { - if (op > OP_MATCH_MAX) { - log_error("invalid ATTRS operation"); - goto invalid; - } + } else if (startswith(key, "ATTRS{")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", "ATTRS"); + attr = get_key_attribute(rules->udev, key + strlen("ATTRS")); - if (attr == NULL) { - log_error("error parsing ATTRS attribute"); - goto invalid; - } + if (attr == NULL) + LOG_AND_RETURN("error parsing %s attribute", "ATTRS"); + if (startswith(attr, "device/")) - log_error("the 'device' link may not be available in a future kernel, " - "please fix it in %s:%u", filename, lineno); - else if (strstr(attr, "../") != NULL) - log_error("do not reference parent sysfs directories directly, " - "it may break with a future kernel, please fix it in %s:%u", filename, lineno); + LOG_RULE_WARNING("'device' link may not be available in future kernels; please fix"); + if (strstr(attr, "../") != NULL) + LOG_RULE_WARNING("direct reference to parent sysfs directory, may break in future kernels; please fix"); rule_add_key(&rule_tmp, TK_M_ATTRS, op, value, attr); - continue; - } - if (streq(key, "TAGS")) { - if (op > OP_MATCH_MAX) { - log_error("invalid TAGS operation"); - goto invalid; - } + } else if (streq(key, "TAGS")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", key); + rule_add_key(&rule_tmp, TK_M_TAGS, op, value, NULL); - continue; - } - if (startswith(key, "ENV{")) { + } else if (startswith(key, "ENV{")) { attr = get_key_attribute(rules->udev, key + strlen("ENV")); - if (attr == NULL) { - log_error("error parsing ENV attribute"); - goto invalid; - } - if (op == OP_REMOVE) { - log_error("invalid ENV operation"); - goto invalid; - } - if (op < OP_MATCH_MAX) { - if (rule_add_key(&rule_tmp, TK_M_ENV, op, value, attr) != 0) - goto invalid; - } else { - static const char *blacklist[] = { - "ACTION", - "SUBSYSTEM", - "DEVTYPE", - "MAJOR", - "MINOR", - "DRIVER", - "IFINDEX", - "DEVNAME", - "DEVLINKS", - "DEVPATH", - "TAGS", - }; - unsigned int i; - - for (i = 0; i < ELEMENTSOF(blacklist); i++) { - if (!streq(attr, blacklist[i])) - continue; - log_error("invalid ENV attribute, '%s' can not be set %s:%u", attr, filename, lineno); - goto invalid; - } - if (rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr) != 0) - goto invalid; - } - continue; - } + if (attr == NULL) + LOG_AND_RETURN("error parsing %s attribute", "ENV"); + + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", "ENV"); - if (streq(key, "TAG")) { + if (op < OP_MATCH_MAX) + rule_add_key(&rule_tmp, TK_M_ENV, op, value, attr); + else { + if (STR_IN_SET(attr, + "ACTION", + "SUBSYSTEM", + "DEVTYPE", + "MAJOR", + "MINOR", + "DRIVER", + "IFINDEX", + "DEVNAME", + "DEVLINKS", + "DEVPATH", + "TAGS")) + LOG_AND_RETURN("invalid ENV attribute, '%s' cannot be set", attr); + + rule_add_key(&rule_tmp, TK_A_ENV, op, value, attr); + } + + } else if (streq(key, "TAG")) { if (op < OP_MATCH_MAX) rule_add_key(&rule_tmp, TK_M_TAG, op, value, NULL); else rule_add_key(&rule_tmp, TK_A_TAG, op, value, NULL); - continue; - } - if (streq(key, "PROGRAM")) { - if (op == OP_REMOVE) { - log_error("invalid PROGRAM operation"); - goto invalid; - } + } else if (streq(key, "PROGRAM")) { + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", key); + rule_add_key(&rule_tmp, TK_M_PROGRAM, op, value, NULL); - continue; - } - if (streq(key, "RESULT")) { - if (op > OP_MATCH_MAX) { - log_error("invalid RESULT operation"); - goto invalid; - } + } else if (streq(key, "RESULT")) { + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", key); + rule_add_key(&rule_tmp, TK_M_RESULT, op, value, NULL); - continue; - } - if (startswith(key, "IMPORT")) { + } else if (startswith(key, "IMPORT")) { attr = get_key_attribute(rules->udev, key + strlen("IMPORT")); if (attr == NULL) { - log_error("IMPORT{} type missing, ignoring IMPORT %s:%u", filename, lineno); + LOG_RULE_WARNING("ignoring IMPORT{} with missing type"); continue; } - if (op == OP_REMOVE) { - log_error("invalid IMPORT operation"); - goto invalid; - } + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", "IMPORT"); + if (streq(attr, "program")) { /* find known built-in command */ if (value[0] != '/') { - enum udev_builtin_cmd cmd; + const enum udev_builtin_cmd cmd = udev_builtin_lookup(value); - cmd = udev_builtin_lookup(value); if (cmd < UDEV_BUILTIN_MAX) { - log_debug("IMPORT found builtin '%s', replacing %s:%u", - value, filename, lineno); + LOG_RULE_DEBUG("IMPORT found builtin '%s', replacing", value); rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd); continue; } } rule_add_key(&rule_tmp, TK_M_IMPORT_PROG, op, value, NULL); } else if (streq(attr, "builtin")) { - enum udev_builtin_cmd cmd = udev_builtin_lookup(value); + const enum udev_builtin_cmd cmd = udev_builtin_lookup(value); - if (cmd < UDEV_BUILTIN_MAX) - rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd); + if (cmd >= UDEV_BUILTIN_MAX) + LOG_RULE_WARNING("IMPORT{builtin} '%s' unknown", value); else - log_error("IMPORT{builtin}: '%s' unknown %s:%u", value, filename, lineno); - } else if (streq(attr, "file")) { + rule_add_key(&rule_tmp, TK_M_IMPORT_BUILTIN, op, value, &cmd); + } else if (streq(attr, "file")) rule_add_key(&rule_tmp, TK_M_IMPORT_FILE, op, value, NULL); - } else if (streq(attr, "db")) { + else if (streq(attr, "db")) rule_add_key(&rule_tmp, TK_M_IMPORT_DB, op, value, NULL); - } else if (streq(attr, "cmdline")) { + else if (streq(attr, "cmdline")) rule_add_key(&rule_tmp, TK_M_IMPORT_CMDLINE, op, value, NULL); - } else if (streq(attr, "parent")) { + else if (streq(attr, "parent")) rule_add_key(&rule_tmp, TK_M_IMPORT_PARENT, op, value, NULL); - } else - log_error("IMPORT{} unknown type, ignoring IMPORT %s:%u", filename, lineno); - continue; - } + else + LOG_RULE_ERROR("ignoring unknown %s{} type '%s'", "IMPORT", attr); - if (startswith(key, "TEST")) { + } else if (startswith(key, "TEST")) { mode_t mode = 0; - if (op > OP_MATCH_MAX) { - log_error("invalid TEST operation"); - goto invalid; - } + if (op > OP_MATCH_MAX) + LOG_AND_RETURN("invalid %s operation", "TEST"); + attr = get_key_attribute(rules->udev, key + strlen("TEST")); if (attr != NULL) { mode = strtol(attr, NULL, 8); rule_add_key(&rule_tmp, TK_M_TEST, op, value, &mode); - } else { + } else rule_add_key(&rule_tmp, TK_M_TEST, op, value, NULL); - } - continue; - } - if (startswith(key, "RUN")) { + } else if (startswith(key, "RUN")) { attr = get_key_attribute(rules->udev, key + strlen("RUN")); if (attr == NULL) attr = "program"; - if (op == OP_REMOVE) { - log_error("invalid RUN operation"); - goto invalid; - } + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", "RUN"); if (streq(attr, "builtin")) { - enum udev_builtin_cmd cmd = udev_builtin_lookup(value); + const enum udev_builtin_cmd cmd = udev_builtin_lookup(value); if (cmd < UDEV_BUILTIN_MAX) rule_add_key(&rule_tmp, TK_A_RUN_BUILTIN, op, value, &cmd); else - log_error("RUN{builtin}: '%s' unknown %s:%u", value, filename, lineno); + LOG_RULE_ERROR("RUN{builtin}: '%s' unknown", value); } else if (streq(attr, "program")) { - enum udev_builtin_cmd cmd = UDEV_BUILTIN_MAX; + const enum udev_builtin_cmd cmd = UDEV_BUILTIN_MAX; rule_add_key(&rule_tmp, TK_A_RUN_PROGRAM, op, value, &cmd); - } else { - log_error("RUN{} unknown type, ignoring RUN %s:%u", filename, lineno); - } + } else + LOG_RULE_ERROR("ignoring unknown %s{} type '%s'", "RUN", attr); - continue; - } + } else if (streq(key, "LABEL")) { + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", key); - if (streq(key, "LABEL")) { - if (op == OP_REMOVE) { - log_error("invalid LABEL operation"); - goto invalid; - } rule_tmp.rule.rule.label_off = rules_add_string(rules, value); - continue; - } - if (streq(key, "GOTO")) { - if (op == OP_REMOVE) { - log_error("invalid GOTO operation"); - goto invalid; - } + } else if (streq(key, "GOTO")) { + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", key); + rule_add_key(&rule_tmp, TK_A_GOTO, 0, value, NULL); - continue; - } - if (startswith(key, "NAME")) { - if (op == OP_REMOVE) { - log_error("invalid NAME operation"); - goto invalid; - } - if (op < OP_MATCH_MAX) { + } else if (startswith(key, "NAME")) { + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", key); + + if (op < OP_MATCH_MAX) rule_add_key(&rule_tmp, TK_M_NAME, op, value, NULL); - } else { + else { if (streq(value, "%k")) { - log_error("NAME=\"%%k\" is ignored, because it breaks kernel supplied names, " - "please remove it from %s:%u\n", filename, lineno); + LOG_RULE_WARNING("NAME=\"%%k\" is ignored, because it breaks kernel supplied names; please remove"); continue; } - if (value[0] == '\0') { - log_debug("NAME=\"\" is ignored, because udev will not delete any device nodes, " - "please remove it from %s:%u\n", filename, lineno); + if (isempty(value)) { + LOG_RULE_DEBUG("NAME=\"\" is ignored, because udev will not delete any device nodes; please remove"); continue; } rule_add_key(&rule_tmp, TK_A_NAME, op, value, NULL); } rule_tmp.rule.rule.can_set_name = true; - continue; - } - if (streq(key, "SYMLINK")) { - if (op == OP_REMOVE) { - log_error("invalid SYMLINK operation"); - goto invalid; - } + } else if (streq(key, "SYMLINK")) { + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", key); + if (op < OP_MATCH_MAX) rule_add_key(&rule_tmp, TK_M_DEVLINK, op, value, NULL); else rule_add_key(&rule_tmp, TK_A_DEVLINK, op, value, NULL); rule_tmp.rule.rule.can_set_name = true; - continue; - } - if (streq(key, "OWNER")) { + } else if (streq(key, "OWNER")) { uid_t uid; char *endptr; - if (op == OP_REMOVE) { - log_error("invalid OWNER operation"); - goto invalid; - } + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", key); uid = strtoul(value, &endptr, 10); - if (endptr[0] == '\0') { + if (endptr[0] == '\0') rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); - } else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) { + else if (rules->resolve_names > 0 && strchr("$%", value[0]) == NULL) { uid = add_uid(rules, value); rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); } else if (rules->resolve_names >= 0) rule_add_key(&rule_tmp, TK_A_OWNER, op, value, NULL); rule_tmp.rule.rule.can_set_name = true; - continue; - } - if (streq(key, "GROUP")) { + } else if (streq(key, "GROUP")) { gid_t gid; char *endptr; - if (op == OP_REMOVE) { - log_error("invalid GROUP operation"); - goto invalid; - } + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", key); gid = strtoul(value, &endptr, 10); - if (endptr[0] == '\0') { + if (endptr[0] == '\0') rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); - } else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) { + else if ((rules->resolve_names > 0) && strchr("$%", value[0]) == NULL) { gid = add_gid(rules, value); rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); } else if (rules->resolve_names >= 0) rule_add_key(&rule_tmp, TK_A_GROUP, op, value, NULL); rule_tmp.rule.rule.can_set_name = true; - continue; - } - if (streq(key, "MODE")) { + } else if (streq(key, "MODE")) { mode_t mode; char *endptr; - if (op == OP_REMOVE) { - log_error("invalid MODE operation"); - goto invalid; - } + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", key); mode = strtol(value, &endptr, 8); if (endptr[0] == '\0') @@ -1480,27 +1367,23 @@ static int add_rule(struct udev_rules *rules, char *line, else rule_add_key(&rule_tmp, TK_A_MODE, op, value, NULL); rule_tmp.rule.rule.can_set_name = true; - continue; - } - if (streq(key, "OPTIONS")) { + } else if (streq(key, "OPTIONS")) { const char *pos; - if (op == OP_REMOVE) { - log_error("invalid OPTIONS operation"); - goto invalid; - } + if (op == OP_REMOVE) + LOG_AND_RETURN("invalid %s operation", key); pos = strstr(value, "link_priority="); if (pos != NULL) { - int prio = atoi(&pos[strlen("link_priority=")]); + int prio = atoi(pos + strlen("link_priority=")); rule_add_key(&rule_tmp, TK_A_DEVLINK_PRIO, op, NULL, &prio); } pos = strstr(value, "string_escape="); if (pos != NULL) { - pos = &pos[strlen("string_escape=")]; + pos += strlen("string_escape="); if (startswith(pos, "none")) rule_add_key(&rule_tmp, TK_A_STRING_ESCAPE_NONE, op, NULL, NULL); else if (startswith(pos, "replace")) @@ -1527,30 +1410,19 @@ static int add_rule(struct udev_rules *rules, char *line, pos = strstr(value, "static_node="); if (pos != NULL) { - rule_add_key(&rule_tmp, TK_A_STATIC_NODE, op, &pos[strlen("static_node=")], NULL); + pos += strlen("static_node="); + rule_add_key(&rule_tmp, TK_A_STATIC_NODE, op, pos, NULL); rule_tmp.rule.rule.has_static_node = true; } - continue; - } - - log_error("unknown key '%s' in %s:%u", key, filename, lineno); - goto invalid; + } else + LOG_AND_RETURN("unknown key '%s'", key); } - /* add rule token */ + /* add rule token and sort tokens */ rule_tmp.rule.rule.token_count = 1 + rule_tmp.token_cur; - if (add_token(rules, &rule_tmp.rule) != 0) - goto invalid; - - /* add tokens to list, sorted by type */ - if (sort_token(rules, &rule_tmp) != 0) - goto invalid; - - return 0; -invalid: - log_error("invalid rule '%s:%u'", filename, lineno); - return -1; + if (add_token(rules, &rule_tmp.rule) != 0 || sort_token(rules, &rule_tmp) != 0) + LOG_RULE_ERROR("failed to add rule token"); } static int parse_file(struct udev_rules *rules, const char *filename) { @@ -1849,18 +1721,18 @@ enum escape_type { ESCAPE_REPLACE, }; -int udev_rules_apply_to_event(struct udev_rules *rules, - struct udev_event *event, - usec_t timeout_usec, - usec_t timeout_warn_usec, - struct udev_list *properties_list) { +void udev_rules_apply_to_event(struct udev_rules *rules, + struct udev_event *event, + usec_t timeout_usec, + usec_t timeout_warn_usec, + struct udev_list *properties_list) { struct token *cur; struct token *rule; enum escape_type esc = ESCAPE_UNSET; bool can_set_name; if (rules->tokens == NULL) - return -1; + return; can_set_name = ((!streq(udev_device_get_action(event->dev), "remove")) && (major(udev_device_get_devnum(event->dev)) > 0 || @@ -2168,7 +2040,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, break; } case TK_M_IMPORT_CMDLINE: { - FILE *f; + _cleanup_fclose_ FILE *f = NULL; bool imported = false; f = fopen("/proc/cmdline", "re"); @@ -2181,12 +2053,12 @@ int udev_rules_apply_to_event(struct udev_rules *rules, pos = strstr(cmdline, key); if (pos != NULL) { + imported = true; pos += strlen(key); - if (pos[0] == '\0' || isspace(pos[0])) { + if (pos[0] == '\0' || isspace(pos[0])) /* we import simple flags as 'FLAG=1' */ udev_device_add_property(event->dev, key, "1"); - imported = true; - } else if (pos[0] == '=') { + else if (pos[0] == '=') { const char *value; pos++; @@ -2195,11 +2067,9 @@ int udev_rules_apply_to_event(struct udev_rules *rules, pos++; pos[0] = '\0'; udev_device_add_property(event->dev, key, value); - imported = true; } } } - fclose(f); } if (!imported && cur->key.op != OP_NOMATCH) goto nomatch; @@ -2428,13 +2298,17 @@ int udev_rules_apply_to_event(struct udev_rules *rules, log_debug("%i character(s) replaced", count); } if (major(udev_device_get_devnum(event->dev)) && - (!streq(name_str, udev_device_get_devnode(event->dev) + strlen("/dev/")))) { - log_error("NAME=\"%s\" ignored, kernel device nodes " - "can not be renamed; please fix it in %s:%u\n", name, - rules_str(rules, rule->rule.filename_off), rule->rule.filename_line); + !streq(name_str, udev_device_get_devnode(event->dev) + strlen("/dev/"))) { + log_error("NAME=\"%s\" ignored, kernel device nodes cannot be renamed; please fix it in %s:%u\n", + name, + rules_str(rules, rule->rule.filename_off), + rule->rule.filename_line); break; } - free_and_strdup(&event->name, name_str); + if (free_and_strdup(&event->name, name_str) < 0) { + log_oom(); + return; + } log_debug("NAME '%s' %s:%u", event->name, rules_str(rules, rule->rule.filename_off), @@ -2491,7 +2365,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, const char *key_name = rules_str(rules, cur->key.attr_off); char attr[UTIL_PATH_SIZE]; char value[UTIL_NAME_SIZE]; - FILE *f; + _cleanup_fclose_ FILE *f = NULL; if (util_resolve_subsys_kernel(event->udev, key_name, attr, sizeof(attr), 0) != 0) strscpyl(attr, sizeof(attr), udev_device_get_syspath(event->dev), "/", key_name, NULL); @@ -2502,13 +2376,10 @@ int udev_rules_apply_to_event(struct udev_rules *rules, rules_str(rules, rule->rule.filename_off), rule->rule.filename_line); f = fopen(attr, "we"); - if (f != NULL) { - if (fprintf(f, "%s", value) <= 0) - log_error_errno(errno, "error writing ATTR{%s}: %m", attr); - fclose(f); - } else { + if (f == NULL) log_error_errno(errno, "error opening ATTR{%s} for writing: %m", attr); - } + else if (fprintf(f, "%s", value) <= 0) + log_error_errno(errno, "error writing ATTR{%s}: %m", attr); break; } case TK_A_SYSCTL: { @@ -2546,7 +2417,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, cur = &rules->tokens[cur->key.rule_goto]; continue; case TK_END: - return 0; + return; case TK_M_PARENTS_MIN: case TK_M_PARENTS_MAX: @@ -2574,7 +2445,7 @@ int udev_rules_apply_static_dev_perms(struct udev_rules *rules) { char **t; FILE *f = NULL; _cleanup_free_ char *path = NULL; - int r = 0; + int r; if (rules->tokens == NULL) return 0; @@ -2645,8 +2516,6 @@ int udev_rules_apply_static_dev_perms(struct udev_rules *rules) { if (r < 0 && errno != EEXIST) return log_error_errno(errno, "failed to create symlink %s -> %s: %m", tag_symlink, device_node); - else - r = 0; } } @@ -2698,12 +2567,11 @@ finish: fflush(f); fchmod(fileno(f), 0644); if (ferror(f) || rename(path, "/run/udev/static_node-tags") < 0) { - r = -errno; - unlink("/run/udev/static_node-tags"); - unlink(path); + unlink_noerrno("/run/udev/static_node-tags"); + unlink_noerrno(path); + return -errno; } - fclose(f); } - return r; + return 0; } diff --git a/src/udev/udev.h b/src/udev/udev.h index 1f9c8120c0..8433e8d9f2 100644 --- a/src/udev/udev.h +++ b/src/udev/udev.h @@ -1,3 +1,5 @@ +#pragma once + /* * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com> * Copyright (C) 2003-2010 Kay Sievers <kay@vrfy.org> @@ -16,9 +18,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#pragma once - #include <sys/param.h> +#include <sys/sysmacros.h> #include <sys/types.h> #include "libudev.h" @@ -71,9 +72,9 @@ struct udev_rules; struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names); struct udev_rules *udev_rules_unref(struct udev_rules *rules); bool udev_rules_check_timestamp(struct udev_rules *rules); -int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, - usec_t timeout_usec, usec_t timeout_warn_usec, - struct udev_list *properties_list); +void udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, + usec_t timeout_usec, usec_t timeout_warn_usec, + struct udev_list *properties_list); int udev_rules_apply_static_dev_perms(struct udev_rules *rules); /* udev-event.c */ diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c index f9cb5e63a2..c0ef073476 100644 --- a/src/udev/udevadm-monitor.c +++ b/src/udev/udevadm-monitor.c @@ -40,7 +40,7 @@ static void sig_handler(int signum) { static void print_device(struct udev_device *device, const char *source, int prop) { struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); + assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); printf("%-6s[%"PRI_TIME".%06ld] %-8s %s (%s)\n", source, ts.tv_sec, ts.tv_nsec/1000, @@ -100,7 +100,7 @@ static int adm_monitor(struct udev *udev, int argc, char *argv[]) { udev_list_init(udev, &subsystem_match_list, true); udev_list_init(udev, &tag_match_list, true); - while((c = getopt_long(argc, argv, "pekus:t:h", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "pekus:t:h", options, NULL)) >= 0) switch (c) { case 'p': case 'e': diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c index ff427cf292..702dbe5282 100644 --- a/src/udev/udevadm-test.c +++ b/src/udev/udevadm-test.c @@ -61,7 +61,7 @@ static int adm_test(struct udev *udev, int argc, char *argv[]) { log_debug("version %s", VERSION); - while((c = getopt_long(argc, argv, "a:N:h", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "a:N:h", options, NULL)) >= 0) switch (c) { case 'a': action = optarg; diff --git a/src/udev/udevadm-util.h b/src/udev/udevadm-util.h index 37e4fe8369..dc712b0d93 100644 --- a/src/udev/udevadm-util.h +++ b/src/udev/udevadm-util.h @@ -1,3 +1,5 @@ +#pragma once + /* * Copyright (C) 2014 Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> * @@ -15,8 +17,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#pragma once - #include "udev.h" struct udev_device *find_device(struct udev *udev, diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c index 7bd2c1ea42..a6a873e5de 100644 --- a/src/udev/udevadm.c +++ b/src/udev/udevadm.c @@ -93,7 +93,7 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); - mac_selinux_init("/dev"); + mac_selinux_init(); 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 2c1c4a967b..e9dd2f47c7 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -400,10 +400,11 @@ static void worker_spawn(Manager *manager, struct event *event) { goto out; } - /* request TERM signal if parent exits */ - prctl(PR_SET_PDEATHSIG, SIGTERM); + /* Request TERM signal if parent exits. + Ignore error, not much we can do in that case. */ + (void) prctl(PR_SET_PDEATHSIG, SIGTERM); - /* reset OOM score, we only protect the main daemon */ + /* Reset OOM score, we only protect the main daemon. */ write_string_file("/proc/self/oom_score_adj", "0", 0); for (;;) { @@ -1695,7 +1696,7 @@ int main(int argc, char *argv[]) { umask(022); - r = mac_selinux_init("/dev"); + r = mac_selinux_init(); if (r < 0) { log_error_errno(r, "could not initialize labelling: %m"); goto exit; @@ -1715,7 +1716,7 @@ int main(int argc, char *argv[]) { by PID1. otherwise we are not guaranteed to have a dedicated cgroup */ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup); if (r < 0) { - if (r == -ENOENT || r == -ENOEXEC) + if (r == -ENOENT || r == -ENOMEDIUM) log_debug_errno(r, "did not find dedicated cgroup: %m"); else log_warning_errno(r, "failed to get cgroup: %m"); diff --git a/src/update-done/update-done.c b/src/update-done/update-done.c index 931e583785..da306a4444 100644 --- a/src/update-done/update-done.c +++ b/src/update-done/update-done.c @@ -101,7 +101,7 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - r = mac_selinux_init(NULL); + r = mac_selinux_init(); if (r < 0) { log_error_errno(r, "SELinux setup failed: %m"); goto finish; diff --git a/src/user-sessions/user-sessions.c b/src/user-sessions/user-sessions.c index 8bf44e2100..9b29b5ba1d 100644 --- a/src/user-sessions/user-sessions.c +++ b/src/user-sessions/user-sessions.c @@ -40,7 +40,7 @@ int main(int argc, char*argv[]) { umask(0022); - mac_selinux_init(NULL); + mac_selinux_init(); if (streq(argv[1], "start")) { int r = 0; diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index 8a1b824e65..1118118450 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -195,9 +195,15 @@ static void font_copy_to_all_vcs(int fd) { unsigned char map8[E_TABSZ]; unsigned short map16[E_TABSZ]; struct unimapdesc unimapd; - struct unipair unipairs[USHRT_MAX]; + _cleanup_free_ struct unipair* unipairs = NULL; int i, r; + unipairs = new(struct unipair, USHRT_MAX); + if (!unipairs) { + log_oom(); + return; + } + /* get active, and 16 bit mask of used VT numbers */ r = ioctl(fd, VT_GETSTATE, &vcs); if (r < 0) { |