diff options
author | Michael Biebl <biebl@debian.org> | 2018-01-28 22:49:17 +0100 |
---|---|---|
committer | Michael Biebl <biebl@debian.org> | 2018-01-28 22:49:17 +0100 |
commit | 1d42b86df9052528a8f56b2f52d8bc2faf87b2da (patch) | |
tree | 0d80f37a1ad6f02067261ee3e7ee62e1869fcd56 /src | |
parent | 52ad194e0b816b8273dd8d0fea3e6d467f6ca34e (diff) | |
download | systemd-1d42b86df9052528a8f56b2f52d8bc2faf87b2da.tar.gz |
New upstream version 237
Diffstat (limited to 'src')
427 files changed, 15587 insertions, 7702 deletions
diff --git a/src/activate/activate.c b/src/activate/activate.c index 83807efdcf..c07dcb8626 100644 --- a/src/activate/activate.c +++ b/src/activate/activate.c @@ -267,38 +267,23 @@ static int exec_process(const char* name, char **argv, char **env, int start_fd, static int fork_and_exec_process(const char* child, char** argv, char **env, int fd) { _cleanup_free_ char *joined = NULL; - pid_t parent_pid, child_pid; + pid_t child_pid; + int r; joined = strv_join(argv, " "); if (!joined) return log_oom(); - parent_pid = getpid_cached(); - - child_pid = fork(); - if (child_pid < 0) - return log_error_errno(errno, "Failed to fork: %m"); - - /* In the child */ - if (child_pid == 0) { - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - /* Make sure the child goes away when the parent dies */ - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) - _exit(EXIT_FAILURE); - - /* Check whether our parent died before we were able - * to set the death signal */ - if (getppid() != parent_pid) - _exit(EXIT_SUCCESS); - + r = safe_fork("(activate)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &child_pid); + if (r < 0) + return r; + if (r == 0) { + /* In the child */ exec_process(child, argv, env, fd, 1); _exit(EXIT_FAILURE); } - log_info("Spawned %s (%s) as PID %d", child, joined, child_pid); + log_info("Spawned %s (%s) as PID " PID_FMT ".", child, joined, child_pid); return 0; } diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c index 461e7852ac..f475b6598c 100644 --- a/src/analyze/analyze-verify.c +++ b/src/analyze/analyze-verify.c @@ -220,7 +220,7 @@ static int verify_unit(Unit *u, bool check_man) { assert(u); - if (log_get_max_level() >= LOG_DEBUG) + if (DEBUG_LOGGING) unit_dump(u, stdout, "\t"); log_unit_debug(u, "Creating %s/start job", u->id); diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index d45c1dc496..834620a4cd 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -47,6 +47,7 @@ #include "terminal-util.h" #include "unit-name.h" #include "util.h" +#include "verbs.h" #define SCALE_X (0.1 / 1000.0) /* pixels per us */ #define SCALE_Y (20.0) @@ -78,7 +79,7 @@ static char** arg_dot_to_patterns = NULL; static usec_t arg_fuzz = 0; static bool arg_no_pager = false; static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; -static char *arg_host = NULL; +static const char *arg_host = NULL; static bool arg_user = false; static bool arg_man = true; static bool arg_generators = false; @@ -130,6 +131,13 @@ struct host_info { char *architecture; }; +static int acquire_bus(bool need_full_bus, sd_bus **bus) { + if (need_full_bus) + return bus_connect_transport(arg_transport, arg_host, arg_user, bus); + else + return bus_connect_transport_systemd(arg_transport, arg_host, arg_user, bus); +} + 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; @@ -521,7 +529,7 @@ static int pretty_boot_time(sd_bus *bus, char **_buf) { "ActiveEnterTimestampMonotonic", &activated_time); if (r < 0) { - log_info_errno(r, "default.target seems not to be started. Continuing..."); + log_info_errno(r, "Could not get time to reach default.target. Continuing..."); activated_time = USEC_INFINITY; } @@ -541,8 +549,15 @@ static int pretty_boot_time(sd_bus *bus, char **_buf) { size = strpcpyf(&ptr, size, "%s (userspace) ", format_timespan(ts, sizeof(ts), t->finish_time - t->userspace_time, USEC_PER_MSEC)); strpcpyf(&ptr, size, "= %s", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC)); - if (unit_id && activated_time != USEC_INFINITY) + if (unit_id && (activated_time > 0 && activated_time != USEC_INFINITY)) size = strpcpyf(&ptr, size, "\n%s reached after %s in userspace", unit_id, format_timespan(ts, sizeof(ts), activated_time - t->userspace_time, USEC_PER_MSEC)); + else if (unit_id && activated_time == 0) + size = strpcpyf(&ptr, size, "\n%s was never reached", unit_id); + else if (unit_id && activated_time == USEC_INFINITY) + size = strpcpyf(&ptr, size, "\nCould not get time to reach %s.",unit_id); + else if (!unit_id) + size = strpcpyf(&ptr, size, "\ncould not find default.target"); + ptr = strdup(buf); if (!ptr) @@ -575,15 +590,20 @@ static void svg_graph_box(double height, double begin, double end) { } } -static int analyze_plot(sd_bus *bus) { +static int analyze_plot(int argc, char *argv[], void *userdata) { _cleanup_(free_host_infop) struct host_info *host = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; struct unit_times *times; struct boot_times *boot; - int n, m = 1, y=0; + int n, m = 1, y = 0, r; double width; _cleanup_free_ char *pretty_times = NULL; struct unit_times *u; + r = acquire_bus(true, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); + n = acquire_boot_times(bus, &boot); if (n < 0) return n; @@ -977,24 +997,29 @@ static int list_dependencies(sd_bus *bus, const char *name) { return list_dependencies_one(bus, name, 0, &units, 0); } -static int analyze_critical_chain(sd_bus *bus, char *names[]) { +static int analyze_critical_chain(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; struct unit_times *times; unsigned int i; Hashmap *h; int n, r; + r = acquire_bus(false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); + n = acquire_time_data(bus, ×); if (n <= 0) return n; h = hashmap_new(&string_hash_ops); if (!h) - return -ENOMEM; + return log_oom(); - for (i = 0; i < (unsigned)n; i++) { + for (i = 0; i < (unsigned) n; i++) { r = hashmap_put(h, times[i].name, ×[i]); if (r < 0) - return r; + return log_error_errno(r, "Failed to add entry to hashmap: %m"); } unit_times_hashmap = h; @@ -1003,22 +1028,27 @@ static int analyze_critical_chain(sd_bus *bus, char *names[]) { 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"); - if (!strv_isempty(names)) { + if (argc > 1) { char **name; - STRV_FOREACH(name, names) + STRV_FOREACH(name, strv_skip(argv, 1)) list_dependencies(bus, *name); } else list_dependencies(bus, SPECIAL_DEFAULT_TARGET); - hashmap_free(h); + h = hashmap_free(h); free_unit_times(times, (unsigned) n); return 0; } -static int analyze_blame(sd_bus *bus) { +static int analyze_blame(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; struct unit_times *times; unsigned i; - int n; + int n, r; + + r = acquire_bus(false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); n = acquire_time_data(bus, ×); if (n <= 0) @@ -1039,10 +1069,15 @@ static int analyze_blame(sd_bus *bus) { return 0; } -static int analyze_time(sd_bus *bus) { +static int analyze_time(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_free_ char *buf = NULL; int r; + r = acquire_bus(false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); + r = pretty_boot_time(bus, &buf); if (r < 0) return r; @@ -1163,16 +1198,21 @@ static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) { return 0; } -static int dot(sd_bus *bus, char* patterns[]) { +static int dot(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; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_strv_free_ char **expanded_patterns = NULL; _cleanup_strv_free_ char **expanded_from_patterns = NULL; _cleanup_strv_free_ char **expanded_to_patterns = NULL; int r; UnitInfo u; - r = expand_patterns(bus, patterns, &expanded_patterns); + r = acquire_bus(false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); + + r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns); if (r < 0) return r; @@ -1228,28 +1268,28 @@ static int dot(sd_bus *bus, char* patterns[]) { return 0; } -static int dump(sd_bus *bus, char **args) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; +static int dump(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; const char *text = NULL; int r; - if (!strv_isempty(args)) { - log_error("Too many arguments."); - return -E2BIG; - } + r = acquire_bus(false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); pager_open(arg_no_pager, false); r = sd_bus_call_method( bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "Dump", - &error, - &reply, - ""); + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "Dump", + &error, + &reply, + NULL); if (r < 0) return log_error_errno(r, "Failed issue method call: %s", bus_error_message(&error, r)); @@ -1261,17 +1301,17 @@ static int dump(sd_bus *bus, char **args) { return 0; } -static int set_log_level(sd_bus *bus, char **args) { +static int set_log_level(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r; - assert(bus); - assert(args); + assert(argc == 2); + assert(argv); - if (strv_length(args) != 1) { - log_error("This command expects one argument only."); - return -E2BIG; - } + r = acquire_bus(false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); r = sd_bus_set_property( bus, @@ -1281,25 +1321,22 @@ static int set_log_level(sd_bus *bus, char **args) { "LogLevel", &error, "s", - args[0]); + argv[1]); if (r < 0) return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r)); return 0; } -static int get_log_level(sd_bus *bus, char **args) { +static int get_log_level(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_free_ char *level = NULL; + int r; - assert(bus); - assert(args); - - if (!strv_isempty(args)) { - log_error("Too many arguments."); - return -E2BIG; - } + r = acquire_bus(false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); r = sd_bus_get_property_string( bus, @@ -1316,17 +1353,21 @@ static int get_log_level(sd_bus *bus, char **args) { return 0; } -static int set_log_target(sd_bus *bus, char **args) { +static int get_or_set_log_level(int argc, char *argv[], void *userdata) { + return (argc == 1) ? get_log_level(argc, argv, userdata) : set_log_level(argc, argv, userdata); +} + +static int set_log_target(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r; - assert(bus); - assert(args); + assert(argc == 2); + assert(argv); - if (strv_length(args) != 1) { - log_error("This command expects one argument only."); - return -E2BIG; - } + r = acquire_bus(false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); r = sd_bus_set_property( bus, @@ -1336,25 +1377,22 @@ static int set_log_target(sd_bus *bus, char **args) { "LogTarget", &error, "s", - args[0]); + argv[1]); if (r < 0) return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r)); return 0; } -static int get_log_target(sd_bus *bus, char **args) { +static int get_log_target(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_free_ char *target = NULL; + int r; - assert(bus); - assert(args); - - if (!strv_isempty(args)) { - log_error("Too many arguments."); - return -E2BIG; - } + r = acquire_bus(false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); r = sd_bus_get_property_string( bus, @@ -1371,6 +1409,10 @@ static int get_log_target(sd_bus *bus, char **args) { return 0; } +static int get_or_set_log_target(int argc, char *argv[], void *userdata) { + return (argc == 1) ? get_log_target(argc, argv, userdata) : set_log_target(argc, argv, userdata); +} + #if HAVE_SECCOMP static void dump_syscall_filter(const SyscallFilterSet *set) { const char *syscall; @@ -1381,12 +1423,12 @@ static void dump_syscall_filter(const SyscallFilterSet *set) { printf(" %s\n", syscall); } -static int dump_syscall_filters(char** names) { +static int dump_syscall_filters(int argc, char *argv[], void *userdata) { bool first = true; pager_open(arg_no_pager, false); - if (strv_isempty(names)) { + if (strv_isempty(strv_skip(argv, 1))) { int i; for (i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) { @@ -1398,7 +1440,7 @@ static int dump_syscall_filters(char** names) { } else { char **name; - STRV_FOREACH(name, names) { + STRV_FOREACH(name, strv_skip(argv, 1)) { const SyscallFilterSet *set; if (!first) @@ -1422,25 +1464,20 @@ static int dump_syscall_filters(char** names) { } #else -static int dump_syscall_filters(char** names) { +static int dump_syscall_filters(int argc, char *argv[], void *userdata) { log_error("Not compiled with syscall filters, sorry."); return -EOPNOTSUPP; } #endif -static int test_calendar(char **args) { +static int test_calendar(int argc, char *argv[], void *userdata) { int ret = 0, r; char **p; usec_t n; - if (strv_isempty(args)) { - log_error("Expected at least one calendar specification string as argument."); - return -EINVAL; - } - n = now(CLOCK_REALTIME); - STRV_FOREACH(p, args) { + STRV_FOREACH(p, strv_skip(argv, 1)) { _cleanup_(calendar_spec_freep) CalendarSpec *spec = NULL; _cleanup_free_ char *t = NULL; usec_t next; @@ -1492,7 +1529,67 @@ static int test_calendar(char **args) { return ret; } -static void help(void) { +static int service_watchdogs(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int b, r; + + assert(IN_SET(argc, 1, 2)); + assert(argv); + + r = acquire_bus(false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); + + /* get ServiceWatchdogs */ + if (argc == 1) { + r = sd_bus_get_property_trivial( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ServiceWatchdogs", + &error, + 'b', + &b); + if (r < 0) + return log_error_errno(r, "Failed to get service-watchdog state: %s", bus_error_message(&error, r)); + + printf("%s\n", yes_no(!!b)); + + return 0; + } + + /* set ServiceWatchdogs */ + b = parse_boolean(argv[1]); + if (b < 0) { + log_error("Failed to parse service-watchdogs argument."); + return -EINVAL; + } + + r = sd_bus_set_property( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ServiceWatchdogs", + &error, + "b", + b); + if (r < 0) + return log_error_errno(r, "Failed to set service-watchdog state: %s", bus_error_message(&error, r)); + + return 0; +} + +static int do_verify(int argc, char *argv[], void *userdata) { + return verify_units(strv_skip(argv, 1), + arg_user ? UNIT_FILE_USER : UNIT_FILE_SYSTEM, + arg_man, + arg_generators); +} + +static int help(int argc, char *argv[], void *userdata) { pager_open(arg_no_pager, false); @@ -1516,22 +1613,23 @@ static void help(void) { "Commands:\n" " time Print time spent in the kernel\n" " blame Print list of running units ordered by time to init\n" - " critical-chain Print a tree of the time critical chain of units\n" + " critical-chain [UNIT...] Print a tree of the time critical chain of units\n" " plot Output SVG graphic showing service initialization\n" - " dot Output dependency graph in man:dot(1) format\n" - " set-log-level LEVEL Set logging threshold for manager\n" - " set-log-target TARGET Set logging target for manager\n" - " get-log-level Get logging threshold for manager\n" - " get-log-target Get logging target for manager\n" + " dot [UNIT...] Output dependency graph in man:dot(1) format\n" + " log-level [LEVEL] Get/set logging threshold for manager\n" + " log-target [TARGET] Get/set logging target for manager\n" " dump Output state serialization of service manager\n" " syscall-filter [NAME...] Print list of syscalls in seccomp filter\n" " verify FILE... Check unit files for correctness\n" " calendar SPEC... Validate repetitive calendar time events\n" + " service-watchdogs [BOOL] Get/set service watchdog state\n" , program_invocation_short_name); /* When updating this list, including descriptions, apply * changes to shell-completion/bash/systemd-analyze and * shell-completion/zsh/_systemd-analyze too. */ + + return 0; } static int parse_argv(int argc, char *argv[]) { @@ -1576,8 +1674,7 @@ static int parse_argv(int argc, char *argv[]) { switch (c) { case 'h': - help(); - return 0; + return help(0, NULL, NULL); case ARG_VERSION: return version(); @@ -1669,10 +1766,34 @@ static int parse_argv(int argc, char *argv[]) { } int main(int argc, char *argv[]) { + + static const Verb verbs[] = { + { "help", VERB_ANY, VERB_ANY, 0, help }, + { "time", VERB_ANY, 1, VERB_DEFAULT, analyze_time }, + { "blame", VERB_ANY, 1, 0, analyze_blame }, + { "critical-chain", VERB_ANY, VERB_ANY, 0, analyze_critical_chain }, + { "plot", VERB_ANY, 1, 0, analyze_plot }, + { "dot", VERB_ANY, VERB_ANY, 0, dot }, + { "log-level", VERB_ANY, 2, 0, get_or_set_log_level }, + { "log-target", VERB_ANY, 2, 0, get_or_set_log_target }, + /* The following four verbs are deprecated aliases */ + { "set-log-level", 2, 2, 0, set_log_level }, + { "get-log-level", VERB_ANY, 1, 0, get_log_level }, + { "set-log-target", 2, 2, 0, set_log_target }, + { "get-log-target", VERB_ANY, 1, 0, get_log_target }, + { "dump", VERB_ANY, 1, 0, dump }, + { "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters }, + { "verify", 2, VERB_ANY, 0, do_verify }, + { "calendar", 2, VERB_ANY, 0, test_calendar }, + { "service-watchdogs", VERB_ANY, 2, 0, service_watchdogs }, + {} + }; + int r; setlocale(LC_ALL, ""); setlocale(LC_NUMERIC, "C"); /* we want to format/parse floats in C style */ + log_parse_environment(); log_open(); @@ -1680,47 +1801,7 @@ int main(int argc, char *argv[]) { if (r <= 0) goto finish; - if (streq_ptr(argv[optind], "verify")) - r = verify_units(argv+optind+1, - arg_user ? UNIT_FILE_USER : UNIT_FILE_SYSTEM, - arg_man, - arg_generators); - else { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - - r = bus_connect_transport_systemd(arg_transport, arg_host, arg_user, &bus); - if (r < 0) { - log_error_errno(r, "Failed to create bus connection: %m"); - goto finish; - } - - if (!argv[optind] || streq(argv[optind], "time")) - r = analyze_time(bus); - else if (streq(argv[optind], "blame")) - r = analyze_blame(bus); - else if (streq(argv[optind], "critical-chain")) - r = analyze_critical_chain(bus, argv+optind+1); - else if (streq(argv[optind], "plot")) - r = analyze_plot(bus); - else if (streq(argv[optind], "dot")) - r = dot(bus, argv+optind+1); - else if (streq(argv[optind], "dump")) - r = dump(bus, argv+optind+1); - else if (streq(argv[optind], "set-log-level")) - r = set_log_level(bus, argv+optind+1); - else if (streq(argv[optind], "get-log-level")) - r = get_log_level(bus, argv+optind+1); - else if (streq(argv[optind], "set-log-target")) - r = set_log_target(bus, argv+optind+1); - else if (streq(argv[optind], "get-log-target")) - r = get_log_target(bus, argv+optind+1); - else if (streq(argv[optind], "syscall-filter")) - r = dump_syscall_filters(argv+optind+1); - else if (streq(argv[optind], "calendar")) - r = test_calendar(argv+optind+1); - else - log_error("Unknown operation '%s'.", argv[optind]); - } + r = dispatch_verb(argc, argv, verbs, NULL); finish: pager_close(); diff --git a/src/basic/af-list.h b/src/basic/af-list.h index 65656022cc..1684bc6a0f 100644 --- a/src/basic/af-list.h +++ b/src/basic/af-list.h @@ -20,6 +20,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <sys/socket.h> + #include "string-util.h" const char *af_to_name(int id); diff --git a/src/basic/async.c b/src/basic/async.c index d368b92522..b6c6d6a80b 100644 --- a/src/basic/async.c +++ b/src/basic/async.c @@ -27,49 +27,82 @@ #include "fd-util.h" #include "log.h" #include "macro.h" +#include "process-util.h" +#include "signal-util.h" #include "util.h" int asynchronous_job(void* (*func)(void *p), void *arg) { + sigset_t ss, saved_ss; pthread_attr_t a; pthread_t t; - int r; + int r, k; - /* It kinda sucks that we have to resort to threads to - * implement an asynchronous sync(), but well, such is - * life. - * - * Note that issuing this command right before exiting a - * process will cause the process to wait for the sync() to - * complete. This function hence is nicely asynchronous really - * only in long running processes. */ + /* It kinda sucks that we have to resort to threads to implement an asynchronous close(), but well, such is + * life. */ r = pthread_attr_init(&a); if (r > 0) return -r; r = pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED); - if (r > 0) + if (r > 0) { + r = -r; + goto finish; + } + + if (sigfillset(&ss) < 0) { + r = -errno; + goto finish; + } + + /* Block all signals before forking off the thread, so that the new thread is started with all signals + * blocked. This way the existence of the new thread won't affect signal handling in other threads. */ + + r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss); + if (r > 0) { + r = -r; goto finish; + } r = pthread_create(&t, &a, func, arg); + k = pthread_sigmask(SIG_SETMASK, &saved_ss, NULL); + + if (r > 0) + r = -r; + else if (k > 0) + r = -k; + else + r = 0; + finish: pthread_attr_destroy(&a); - return -r; + return r; } -static void *sync_thread(void *p) { - sync(); - return NULL; -} +int asynchronous_sync(pid_t *ret_pid) { + int r; -int asynchronous_sync(void) { - log_debug("Spawning new thread for sync"); + /* This forks off an invocation of fork() as a child process, in order to initiate synchronization to + * disk. Note that we implement this as helper process rather than thread as we don't want the sync() to hang our + * original process ever, and a thread would do that as the process can't exit with threads hanging in blocking + * syscalls. */ + + r = safe_fork("(sd-sync)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, ret_pid); + if (r < 0) + return r; + if (r == 0) { + /* Child process */ + (void) sync(); + _exit(EXIT_SUCCESS); + } - return asynchronous_job(sync_thread, NULL); + return 0; } static void *close_thread(void *p) { + (void) pthread_setname_np(pthread_self(), "close"); + assert_se(close_nointr(PTR_TO_FD(p)) != -EBADF); return NULL; } diff --git a/src/basic/async.h b/src/basic/async.h index 7eac54d8b2..01c975bb30 100644 --- a/src/basic/async.h +++ b/src/basic/async.h @@ -22,5 +22,5 @@ int asynchronous_job(void* (*func)(void *p), void *arg); -int asynchronous_sync(void); +int asynchronous_sync(pid_t *ret_pid); int asynchronous_close(int fd); diff --git a/src/basic/blockdev-util.c b/src/basic/blockdev-util.c new file mode 100644 index 0000000000..3a8f8d1c27 --- /dev/null +++ b/src/basic/blockdev-util.c @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/*** + 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 <sys/stat.h> +#include <sys/statfs.h> + +#include "alloc-util.h" +#include "blockdev-util.h" +#include "btrfs-util.h" +#include "dirent-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "missing.h" +#include "stat-util.h" + +int block_get_whole_disk(dev_t d, dev_t *ret) { + char p[SYS_BLOCK_PATH_MAX("/partition")]; + _cleanup_free_ char *s = NULL; + unsigned n, m; + int r; + + assert(ret); + + /* If it has a queue this is good enough for us */ + xsprintf_sys_block_path(p, "/queue", d); + if (access(p, F_OK) >= 0) { + *ret = d; + return 0; + } + + /* If it is a partition find the originating device */ + xsprintf_sys_block_path(p, "/partition", d); + if (access(p, F_OK) < 0) + return -ENOENT; + + /* Get parent dev_t */ + xsprintf_sys_block_path(p, "/../dev", d); + r = read_one_line_file(p, &s); + if (r < 0) + return r; + + r = sscanf(s, "%u:%u", &m, &n); + if (r != 2) + return -EINVAL; + + /* Only return this if it is really good enough for us. */ + xsprintf_sys_block_path(p, "/queue", makedev(m, n)); + if (access(p, F_OK) < 0) + return -ENOENT; + + *ret = makedev(m, n); + return 0; +} + +int get_block_device(const char *path, dev_t *dev) { + struct stat st; + struct statfs sfs; + + assert(path); + assert(dev); + + /* Get's the block device directly backing a file system. If + * the block device is encrypted, returns the device mapper + * block device. */ + + if (lstat(path, &st)) + return -errno; + + if (major(st.st_dev) != 0) { + *dev = st.st_dev; + return 1; + } + + if (statfs(path, &sfs) < 0) + return -errno; + + if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) + return btrfs_get_block_device(path, dev); + + return 0; +} + +int get_block_device_harder(const char *path, dev_t *dev) { + _cleanup_closedir_ DIR *d = NULL; + _cleanup_free_ char *t = NULL; + char p[SYS_BLOCK_PATH_MAX("/slaves")]; + struct dirent *de, *found = NULL; + const char *q; + unsigned maj, min; + dev_t dt; + int r; + + assert(path); + assert(dev); + + /* Gets the backing block device for a file system, and + * handles LUKS encrypted file systems, looking for its + * immediate parent, if there is one. */ + + r = get_block_device(path, &dt); + if (r <= 0) + return r; + + xsprintf_sys_block_path(p, "/slaves", dt); + d = opendir(p); + if (!d) { + if (errno == ENOENT) + goto fallback; + + return -errno; + } + + FOREACH_DIRENT_ALL(de, d, return -errno) { + + if (dot_or_dot_dot(de->d_name)) + continue; + + if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN)) + continue; + + if (found) { + _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL; + + /* We found a device backed by multiple other devices. We don't really support automatic + * discovery on such setups, with the exception of dm-verity partitions. In this case there are + * two backing devices: the data partition and the hash partition. We are fine with such + * setups, however, only if both partitions are on the same physical device. Hence, let's + * verify this. */ + + u = strjoin(p, "/", de->d_name, "/../dev"); + if (!u) + return -ENOMEM; + + v = strjoin(p, "/", found->d_name, "/../dev"); + if (!v) + return -ENOMEM; + + r = read_one_line_file(u, &a); + if (r < 0) { + log_debug_errno(r, "Failed to read %s: %m", u); + goto fallback; + } + + r = read_one_line_file(v, &b); + if (r < 0) { + log_debug_errno(r, "Failed to read %s: %m", v); + goto fallback; + } + + /* Check if the parent device is the same. If not, then the two backing devices are on + * different physical devices, and we don't support that. */ + if (!streq(a, b)) + goto fallback; + } + + found = de; + } + + if (!found) + goto fallback; + + q = strjoina(p, "/", found->d_name, "/dev"); + + r = read_one_line_file(q, &t); + if (r == -ENOENT) + goto fallback; + if (r < 0) + return r; + + if (sscanf(t, "%u:%u", &maj, &min) != 2) + return -EINVAL; + + if (maj == 0) + goto fallback; + + *dev = makedev(maj, min); + return 1; + +fallback: + *dev = dt; + return 1; +} diff --git a/src/libsystemd/sd-bus/bus-bloom.h b/src/basic/blockdev-util.h index 2650762145..642b7ce522 100644 --- a/src/libsystemd/sd-bus/bus-bloom.h +++ b/src/basic/blockdev-util.h @@ -4,7 +4,7 @@ /*** This file is part of systemd. - Copyright 2013 Lennart Poettering + 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 @@ -20,25 +20,19 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdbool.h> -#include <stddef.h> -#include <stdint.h> +#include <sys/types.h> -/* - * Our default bloom filter has the following parameters: - * - * m=512 (bits in the filter) - * k=8 (hash functions) - * - * We use SipHash24 as hash function with a number of (originally - * randomized) but fixed hash keys. - * - */ +#include "macro.h" +#include "stdio-util.h" +#include "string-util.h" -#define DEFAULT_BLOOM_SIZE (512/8) /* m: filter size */ -#define DEFAULT_BLOOM_N_HASH 8 /* k: number of hash functions */ +#define SYS_BLOCK_PATH_MAX(suffix) \ + (STRLEN("/sys/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t) + strlen_ptr(suffix)) +#define xsprintf_sys_block_path(buf, suffix, devno) \ + xsprintf(buf, "/sys/dev/block/%u:%u%s", major(devno), minor(devno), strempty(suffix)) -void bloom_add_pair(uint64_t filter[], size_t size, unsigned n_hash, const char *a, const char *b); -void bloom_add_prefixes(uint64_t filter[], size_t size, unsigned n_hash, const char *a, const char *b, char sep); +int block_get_whole_disk(dev_t d, dev_t *ret); -bool bloom_validate_parameters(size_t size, unsigned n_hash); +int get_block_device(const char *path, dev_t *dev); + +int get_block_device_harder(const char *path, dev_t *dev); diff --git a/src/basic/btrfs-ctree.h b/src/basic/btrfs-ctree.h index 66bdf9736e..c5a4244e37 100644 --- a/src/basic/btrfs-ctree.h +++ b/src/basic/btrfs-ctree.h @@ -1,6 +1,7 @@ #pragma once #include "macro.h" +#include "missing.h" #include "sparse-endian.h" /* Stolen from btrfs' ctree.h */ diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c index ac96e63531..19d385ab7c 100644 --- a/src/basic/btrfs-util.c +++ b/src/basic/btrfs-util.c @@ -38,6 +38,7 @@ #endif #include "alloc-util.h" +#include "blockdev-util.h" #include "btrfs-ctree.h" #include "btrfs-util.h" #include "chattr-util.h" @@ -50,7 +51,6 @@ #include "missing.h" #include "path-util.h" #include "rm-rf.h" -#include "selinux-util.h" #include "smack-util.h" #include "sparse-endian.h" #include "stat-util.h" @@ -174,24 +174,6 @@ int btrfs_subvol_make(const char *path) { return 0; } -int btrfs_subvol_make_label(const char *path) { - int r; - - assert(path); - - r = mac_selinux_create_file_prepare(path, S_IFDIR); - if (r < 0) - return r; - - r = btrfs_subvol_make(path); - mac_selinux_create_file_clear(); - - if (r < 0) - return r; - - return mac_smack_fix(path, false, false); -} - int btrfs_subvol_set_read_only_fd(int fd, bool b) { uint64_t flags, nflags; struct stat st; diff --git a/src/basic/btrfs-util.h b/src/basic/btrfs-util.h index 2c78daa37b..952b3c26da 100644 --- a/src/basic/btrfs-util.h +++ b/src/basic/btrfs-util.h @@ -84,7 +84,6 @@ int btrfs_resize_loopback_fd(int fd, uint64_t size, bool grow_only); int btrfs_resize_loopback(const char *path, uint64_t size, bool grow_only); int btrfs_subvol_make(const char *path); -int btrfs_subvol_make_label(const char *path); int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags); int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags); diff --git a/src/basic/build.h b/src/basic/build.h index ab2a838ce9..0d078efe6f 100644 --- a/src/basic/build.h +++ b/src/basic/build.h @@ -140,6 +140,12 @@ #define _IDN_FEATURE_ "-IDN" #endif +#if HAVE_PCRE2 +#define _PCRE2_FEATURE_ "+PCRE2" +#else +#define _PCRE2_FEATURE_ "-PCRE2" +#endif + #define _CGROUP_HIEARCHY_ "default-hierarchy=" DEFAULT_HIERARCHY_NAME #define SYSTEMD_FEATURES \ @@ -163,4 +169,5 @@ _KMOD_FEATURE_ " " \ _IDN2_FEATURE_ " " \ _IDN_FEATURE_ " " \ + _PCRE2_FEATURE_ " " \ _CGROUP_HIEARCHY_ diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c index e6add0c383..fd78022773 100644 --- a/src/basic/calendarspec.c +++ b/src/basic/calendarspec.c @@ -35,6 +35,7 @@ #include "fileio.h" #include "macro.h" #include "parse-util.h" +#include "process-util.h" #include "string-util.h" #include "time-util.h" @@ -156,6 +157,11 @@ static void fix_year(CalendarComponent *c) { int calendar_spec_normalize(CalendarSpec *c) { assert(c); + if (streq_ptr(c->timezone, "UTC")) { + c->utc = true; + c->timezone = mfree(c->timezone); + } + if (c->weekdays_bits <= 0 || c->weekdays_bits >= BITS_WEEKDAYS) c->weekdays_bits = -1; @@ -1348,9 +1354,7 @@ typedef struct SpecNextResult { } SpecNextResult; int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) { - pid_t pid; - SpecNextResult *shared; - SpecNextResult tmp; + SpecNextResult *shared, tmp; int r; if (isempty(spec->timezone)) @@ -1360,15 +1364,12 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) if (shared == MAP_FAILED) return negative_errno(); - pid = fork(); - - if (pid == -1) { - int fork_errno = errno; + r = safe_fork("(sd-calendar)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL); + if (r < 0) { (void) munmap(shared, sizeof *shared); - return -fork_errno; + return r; } - - if (pid == 0) { + if (r == 0) { if (setenv("TZ", spec->timezone, 1) != 0) { shared->return_value = negative_errno(); _exit(EXIT_FAILURE); @@ -1381,14 +1382,8 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) _exit(EXIT_SUCCESS); } - r = wait_for_terminate(pid, NULL); - if (r < 0) { - (void) munmap(shared, sizeof *shared); - return r; - } - tmp = *shared; - if (munmap(shared, sizeof *shared) != 0) + if (munmap(shared, sizeof *shared) < 0) return negative_errno(); if (tmp.return_value == 0) diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index f599d0f0f1..7c0ba92110 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -779,13 +779,11 @@ int cg_create(const char *controller, const char *path) { if (r < 0) return r; - if (mkdir(fs, 0755) < 0) { - - if (errno == EEXIST) - return 0; - - return -errno; - } + r = mkdir_errno_wrapper(fs, 0755); + if (r == -EEXIST) + return 0; + if (r < 0) + return r; r = cg_hybrid_unified(); if (r < 0) diff --git a/src/basic/def.h b/src/basic/def.h index 77ab735aed..43e7e17008 100644 --- a/src/basic/def.h +++ b/src/basic/def.h @@ -53,9 +53,10 @@ "/usr/lib/kbd/keymaps/\0" #endif -#define UNIX_SYSTEM_BUS_ADDRESS "unix:path=/var/run/dbus/system_bus_socket" -#define DEFAULT_SYSTEM_BUS_ADDRESS UNIX_SYSTEM_BUS_ADDRESS -#define UNIX_USER_BUS_ADDRESS_FMT "unix:path=%s/bus" +/* Note that we use the new /run prefix here (instead of /var/run) since we require them to be aliases and that way we + * become independent of /var being mounted */ +#define DEFAULT_SYSTEM_BUS_ADDRESS "unix:path=/run/dbus/system_bus_socket" +#define DEFAULT_USER_BUS_ADDRESS_FMT "unix:path=%s/bus" #define PLYMOUTH_SOCKET { \ .un.sun_family = AF_UNIX, \ diff --git a/src/basic/device-nodes.h b/src/basic/device-nodes.h index 7dd8a772a5..49f4ccc729 100644 --- a/src/basic/device-nodes.h +++ b/src/basic/device-nodes.h @@ -29,11 +29,6 @@ int encode_devnode_name(const char *str, char *str_enc, size_t len); int whitelisted_char_for_devnode(char c, const char *additional); -#define SYS_BLOCK_PATH_MAX(suffix) \ - (STRLEN("/sys/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t) + strlen_ptr(suffix)) -#define xsprintf_sys_block_path(buf, suffix, devno) \ - xsprintf(buf, "/sys/dev/block/%u:%u%s", major(devno), minor(devno), strempty(suffix)) - #define DEV_NUM_PATH_MAX \ (STRLEN("/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t)) #define xsprintf_dev_num_path(buf, type, devno) \ diff --git a/src/basic/errno-list.c b/src/basic/errno-list.c index d8eedfc0ad..7eb28b8fd1 100644 --- a/src/basic/errno-list.c +++ b/src/basic/errno-list.c @@ -18,6 +18,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <string.h> #include "errno-list.h" diff --git a/src/basic/errno-list.h b/src/basic/errno-list.h index 4e9b75a7ea..38beaf96dd 100644 --- a/src/basic/errno-list.h +++ b/src/basic/errno-list.h @@ -20,6 +20,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <stdbool.h> /* * MAX_ERRNO is defined as 4095 in linux/err.h * We use the same value here. @@ -28,3 +29,6 @@ const char *errno_to_name(int id); int errno_from_name(const char *name); +static inline bool errno_is_valid(int n) { + return n > 0 && n <= ERRNO_MAX; +} diff --git a/src/basic/ether-addr-util.c b/src/basic/ether-addr-util.c index bbe8bf0006..bfbd1a4931 100644 --- a/src/basic/ether-addr-util.c +++ b/src/basic/ether-addr-util.c @@ -18,6 +18,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <net/ethernet.h> #include <stdio.h> #include <sys/types.h> diff --git a/src/basic/exec-util.c b/src/basic/exec-util.c index 82ccbdf5ec..0829b3d836 100644 --- a/src/basic/exec-util.c +++ b/src/basic/exec-util.c @@ -48,27 +48,26 @@ assert_cc(EAGAIN == EWOULDBLOCK); static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) { pid_t _pid; + int r; if (null_or_empty_path(path)) { log_debug("%s is empty (a mask).", path); return 0; } - _pid = fork(); - if (_pid < 0) - return log_error_errno(errno, "Failed to fork: %m"); - if (_pid == 0) { + r = safe_fork("(direxec)", FORK_DEATHSIG|FORK_LOG, &_pid); + if (r < 0) + return r; + if (r == 0) { char *_argv[2]; - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - if (stdout_fd >= 0) { /* If the fd happens to be in the right place, go along with that */ if (stdout_fd != STDOUT_FILENO && dup2(stdout_fd, STDOUT_FILENO) < 0) return -errno; - fd_cloexec(STDOUT_FILENO, false); + (void) fd_cloexec(STDOUT_FILENO, false); } if (!argv) { @@ -83,7 +82,6 @@ static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) { _exit(EXIT_FAILURE); } - log_debug("Spawned %s as " PID_FMT ".", path, _pid); *pid = _pid; return 1; } @@ -107,11 +105,6 @@ static int do_execute( * If callbacks is nonnull, execution is serial. Otherwise, we default to parallel. */ - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE, (const char* const*) directories); if (r < 0) return r; @@ -153,7 +146,7 @@ static int do_execute( return log_oom(); t = NULL; } else { - r = wait_for_terminate_and_warn(t, pid, true); + r = wait_for_terminate_and_check(t, pid, WAIT_LOG); if (r < 0) continue; @@ -183,7 +176,7 @@ static int do_execute( t = hashmap_remove(pids, PID_TO_PTR(pid)); assert(t); - wait_for_terminate_and_warn(t, pid, true); + (void) wait_for_terminate_and_check(t, pid, WAIT_LOG); } return 0; @@ -196,10 +189,9 @@ int execute_directories( void* const callback_args[_STDOUT_CONSUME_MAX], char *argv[]) { - pid_t executor_pid; - char *name; char **dirs = (char**) directories; _cleanup_close_ int fd = -1; + char *name; int r; assert(!strv_isempty(dirs)); @@ -222,24 +214,14 @@ int execute_directories( * them to finish. Optionally a timeout is applied. If a file with the same name * exists in more than one directory, the earliest one wins. */ - executor_pid = fork(); - if (executor_pid < 0) - return log_error_errno(errno, "Failed to fork: %m"); - - if (executor_pid == 0) { + r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + if (r < 0) + return r; + if (r == 0) { r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv); _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); } - r = wait_for_terminate_and_warn(name, executor_pid, true); - if (r < 0) - return log_error_errno(r, "Execution failed: %m"); - if (r > 0) { - /* non-zero return code from child */ - log_error("Forker process failed."); - return -EREMOTEIO; - } - if (!callbacks) return 0; diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index a4a5c6b3f6..404361e8c1 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -192,9 +192,9 @@ int fd_cloexec(int fd, bool cloexec) { } void stdio_unset_cloexec(void) { - fd_cloexec(STDIN_FILENO, false); - fd_cloexec(STDOUT_FILENO, false); - fd_cloexec(STDERR_FILENO, false); + (void) fd_cloexec(STDIN_FILENO, false); + (void) fd_cloexec(STDOUT_FILENO, false); + (void) fd_cloexec(STDERR_FILENO, false); } _pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) { @@ -227,20 +227,21 @@ int close_all_fds(const int except[], unsigned n_except) { assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0); for (fd = 3; fd < (int) rl.rlim_max; fd ++) { + int q; if (fd_in_set(fd, except, n_except)) continue; - if (close_nointr(fd) < 0) - if (errno != EBADF && r == 0) - r = -errno; + q = close_nointr(fd); + if (q < 0 && q != -EBADF && r >= 0) + r = q; } return r; } FOREACH_DIRENT(de, d, return -errno) { - int fd = -1; + int fd = -1, q; if (safe_atoi(de->d_name, &fd) < 0) /* Let's better ignore this, just in case */ @@ -255,11 +256,9 @@ int close_all_fds(const int except[], unsigned n_except) { if (fd_in_set(fd, except, n_except)) continue; - if (close_nointr(fd) < 0) { - /* Valgrind has its own FD and doesn't want to have it closed */ - if (errno != EBADF && r == 0) - r = -errno; - } + q = close_nointr(fd); + if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */ + r = q; } return r; diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 4e02d5b344..26d6174664 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -62,12 +62,30 @@ int write_string_stream_ts( WriteStringFileFlags flags, struct timespec *ts) { + bool needs_nl; + assert(f); assert(line); - fputs(line, f); - if (!(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n")) - fputc('\n', f); + if (ferror(f)) + return -EIO; + + needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n"); + + if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) { + /* If STDIO buffering was disabled, then let's append the newline character to the string itself, so + * that the write goes out in one go, instead of two */ + + line = strjoina(line, "\n"); + needs_nl = false; + } + + if (fputs(line, f) == EOF) + return -errno; + + if (needs_nl) + if (fputc('\n', f) == EOF) + return -errno; if (ts) { struct timespec twice[2] = {*ts, *ts}; @@ -908,14 +926,16 @@ int write_env_file(const char *fname, char **l) { } int executable_is_script(const char *path, char **interpreter) { - int r; _cleanup_free_ char *line = NULL; - int len; + size_t len; char *ans; + int r; assert(path); r = read_one_line_file(path, &line); + if (r == -ENOBUFS) /* First line overly long? if so, then it's not a script */ + return 0; if (r < 0) return r; @@ -1205,8 +1225,7 @@ int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { if (!filename_is_valid(fn)) return -EINVAL; - if (!extra) - extra = ""; + extra = strempty(extra); t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1); if (!t) @@ -1239,8 +1258,7 @@ int tempfn_random(const char *p, const char *extra, char **ret) { if (!filename_is_valid(fn)) return -EINVAL; - if (!extra) - extra = ""; + extra = strempty(extra); t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1); if (!t) @@ -1280,8 +1298,7 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) { return r; } - if (!extra) - extra = ""; + extra = strempty(extra); t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1); if (!t) diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 4ca36faf09..a8e50d4c78 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -39,6 +39,7 @@ #include "mkdir.h" #include "parse-util.h" #include "path-util.h" +#include "process-util.h" #include "stat-util.h" #include "stdio-util.h" #include "string-util.h" @@ -315,43 +316,60 @@ int fd_warn_permissions(const char *path, int fd) { } int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) { - _cleanup_close_ int fd; - int r; + char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + _cleanup_close_ int fd = -1; + int r, ret = 0; assert(path); - if (parents) - mkdir_parents(path, 0755); - - fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, - IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode); - if (fd < 0) - return -errno; + /* Note that touch_file() does not follow symlinks: if invoked on an existing symlink, then it is the symlink + * itself which is updated, not its target + * + * Returns the first error we encounter, but tries to apply as much as possible. */ - if (mode != MODE_INVALID) { - r = fchmod(fd, mode); - if (r < 0) + if (parents) + (void) mkdir_parents(path, 0755); + + /* Initially, we try to open the node with O_PATH, so that we get a reference to the node. This is useful in + * case the path refers to an existing device or socket node, as we can open it successfully in all cases, and + * won't trigger any driver magic or so. */ + fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); + if (fd < 0) { + if (errno != ENOENT) return -errno; - } - if (uid != UID_INVALID || gid != GID_INVALID) { - r = fchown(fd, uid, gid); - if (r < 0) + /* if the node doesn't exist yet, we create it, but with O_EXCL, so that we only create a regular file + * here, and nothing else */ + fd = open(path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode); + if (fd < 0) return -errno; } + /* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode, + * ownership and time of the file node in all cases, even if the fd refers to an O_PATH object — which is + * something fchown(), fchmod(), futimensat() don't allow. */ + xsprintf(fdpath, "/proc/self/fd/%i", fd); + + if (mode != MODE_INVALID) + if (chmod(fdpath, mode) < 0) + ret = -errno; + + if (uid_is_valid(uid) || gid_is_valid(gid)) + if (chown(fdpath, uid, gid) < 0 && ret >= 0) + ret = -errno; + if (stamp != USEC_INFINITY) { struct timespec ts[2]; timespec_store(&ts[0], stamp); ts[1] = ts[0]; - r = futimens(fd, ts); + r = utimensat(AT_FDCWD, fdpath, ts, 0); } else - r = futimens(fd, NULL); - if (r < 0) + r = utimensat(AT_FDCWD, fdpath, NULL, 0); + if (r < 0 && ret >= 0) return -errno; - return 0; + return ret; } int touch(const char *path) { @@ -593,16 +611,35 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) { return r; } +static bool safe_transition(const struct stat *a, const struct stat *b) { + /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to + * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files + * making us believe we read something safe even though it isn't safe in the specific context we open it in. */ + + if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */ + return true; + + return a->st_uid == b->st_uid; /* Otherwise we need to stay within the same UID */ +} + int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) { _cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL; _cleanup_close_ int fd = -1; unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */ + struct stat previous_stat; bool exists = true; char *todo; int r; assert(path); + /* Either the file may be missing, or we return an fd to the final object, but both make no sense */ + if ((flags & (CHASE_NONEXISTENT|CHASE_OPEN)) == (CHASE_NONEXISTENT|CHASE_OPEN)) + return -EINVAL; + + if (isempty(path)) + return -EINVAL; + /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following * symlinks relative to a root directory, instead of the root of the host. * @@ -623,13 +660,23 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, * function what to do when encountering a symlink with an absolute path as directory: prefix it by the * specified path. */ + /* A root directory of "/" or "" is identical to none */ + if (isempty(original_root) || path_equal(original_root, "/")) + original_root = NULL; + if (original_root) { r = path_make_absolute_cwd(original_root, &root); if (r < 0) return r; - if (flags & CHASE_PREFIX_ROOT) + if (flags & CHASE_PREFIX_ROOT) { + + /* We don't support relative paths in combination with a root directory */ + if (!path_is_absolute(path)) + return -EINVAL; + path = prefix_roota(root, path); + } } r = path_make_absolute_cwd(path, &buffer); @@ -640,6 +687,11 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fd < 0) return -errno; + if (flags & CHASE_SAFE) { + if (fstat(fd, &previous_stat) < 0) + return -errno; + } + todo = buffer; for (;;) { _cleanup_free_ char *first = NULL; @@ -678,7 +730,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, /* Two dots? Then chop off the last bit of what we already found out. */ if (path_equal(first, "/..")) { _cleanup_free_ char *parent = NULL; - int fd_parent = -1; + _cleanup_close_ int fd_parent = -1; /* If we already are at the top, then going up will not change anything. This is in-line with * how the kernel handles this. */ @@ -701,8 +753,19 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fd_parent < 0) return -errno; + if (flags & CHASE_SAFE) { + if (fstat(fd_parent, &st) < 0) + return -errno; + + if (!safe_transition(&previous_stat, &st)) + return -EPERM; + + previous_stat = st; + } + safe_close(fd); fd = fd_parent; + fd_parent = -1; continue; } @@ -735,6 +798,12 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fstat(child, &st) < 0) return -errno; + if ((flags & CHASE_SAFE) && + !safe_transition(&previous_stat, &st)) + return -EPERM; + + previous_stat = st; + if ((flags & CHASE_NO_AUTOFS) && fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0) return -EREMOTE; @@ -765,6 +834,16 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fd < 0) return -errno; + if (flags & CHASE_SAFE) { + if (fstat(fd, &st) < 0) + return -errno; + + if (!safe_transition(&previous_stat, &st)) + return -EPERM; + + previous_stat = st; + } + free(done); /* Note that we do not revalidate the root, we take it as is. */ @@ -821,6 +900,19 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, done = NULL; } + if (flags & CHASE_OPEN) { + int q; + + /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a proper fd by + * opening /proc/self/fd/xyz. */ + + assert(fd >= 0); + q = fd; + fd = -1; + + return q; + } + return exists; } diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index a7ba61625d..4dba1ea56a 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -29,6 +29,7 @@ #include <unistd.h> #include "time-util.h" +#include "util.h" int unlink_noerrno(const char *path); @@ -80,22 +81,25 @@ union inotify_event_buffer { int inotify_add_watch_fd(int fd, int what, uint32_t mask); enum { - CHASE_PREFIX_ROOT = 1, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */ - CHASE_NONEXISTENT = 2, /* If set, it's OK if the path doesn't actually exist. */ - CHASE_NO_AUTOFS = 4, /* If set, return -EREMOTE if autofs mount point found */ + CHASE_PREFIX_ROOT = 1U << 0, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */ + CHASE_NONEXISTENT = 1U << 1, /* If set, it's OK if the path doesn't actually exist. */ + CHASE_NO_AUTOFS = 1U << 2, /* If set, return -EREMOTE if autofs mount point found */ + CHASE_SAFE = 1U << 3, /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */ + CHASE_OPEN = 1U << 4, /* If set, return an O_PATH object to the final component */ }; int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret); /* Useful for usage with _cleanup_(), removes a directory and frees the pointer */ static inline void rmdir_and_free(char *p) { + PROTECT_ERRNO; (void) rmdir(p); free(p); } DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free); static inline void unlink_and_free(char *p) { - (void) unlink(p); + (void) unlink_noerrno(p); free(p); } DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free); diff --git a/src/shared/gcrypt-util.c b/src/basic/gcrypt-util.c index 1bfb776725..1bfb776725 100644 --- a/src/shared/gcrypt-util.c +++ b/src/basic/gcrypt-util.c diff --git a/src/shared/gcrypt-util.h b/src/basic/gcrypt-util.h index 69faf08e56..69faf08e56 100644 --- a/src/shared/gcrypt-util.h +++ b/src/basic/gcrypt-util.h diff --git a/src/basic/generate-af-list.sh b/src/basic/generate-af-list.sh index 8d9cdd1836..fa74198e58 100755 --- a/src/basic/generate-af-list.sh +++ b/src/basic/generate-af-list.sh @@ -1,4 +1,5 @@ -#!/bin/sh -eu +#!/bin/sh +set -eu $1 -E -dM -include sys/socket.h - </dev/null | \ grep -Ev 'AF_UNSPEC|AF_MAX' | \ diff --git a/src/basic/generate-arphrd-list.sh b/src/basic/generate-arphrd-list.sh index ee207fb38e..e4e7271c4d 100755 --- a/src/basic/generate-arphrd-list.sh +++ b/src/basic/generate-arphrd-list.sh @@ -1,4 +1,5 @@ -#!/bin/sh -eu +#!/bin/sh +set -eu $1 -dM -include net/if_arp.h - </dev/null | \ awk '/^#define[ \t]+ARPHRD_[^ \t]+[ \t]+[^ \t]/ { print $2; }' | \ diff --git a/src/basic/generate-cap-list.sh b/src/basic/generate-cap-list.sh index 1d4a562e7c..0628d2425f 100755 --- a/src/basic/generate-cap-list.sh +++ b/src/basic/generate-cap-list.sh @@ -1,4 +1,5 @@ -#!/bin/sh -eu +#!/bin/sh +set -eu $1 -dM -include linux/capability.h -include "$2" -include "$3" - </dev/null | \ awk '/^#define[ \t]+CAP_[A-Z_]+[ \t]+/ { print $2; }' | \ diff --git a/src/basic/generate-errno-list.sh b/src/basic/generate-errno-list.sh index e2bab8b320..953d5e37b8 100755 --- a/src/basic/generate-errno-list.sh +++ b/src/basic/generate-errno-list.sh @@ -1,4 +1,5 @@ -#!/bin/sh -eu +#!/bin/sh +set -eu $1 -dM -include errno.h - </dev/null | \ awk '/^#define[ \t]+E[^ _]+[ \t]+/ { print $2; }' diff --git a/src/basic/generate-socket-protocol-list.sh b/src/basic/generate-socket-protocol-list.sh new file mode 100755 index 0000000000..a9b1e0fb57 --- /dev/null +++ b/src/basic/generate-socket-protocol-list.sh @@ -0,0 +1,6 @@ +#!/bin/sh +set -eu + +$1 -dM -include netinet/in.h - </dev/null | \ + awk '/^#define[ \t]+IPPROTO_[^ \t]+[ \t]+[^ \t]/ { print $2; }' | \ + sed -e 's/IPPROTO_//' diff --git a/src/basic/gunicode.c b/src/basic/gunicode.c index e6ac0545a4..8aff4a0fc5 100644 --- a/src/basic/gunicode.c +++ b/src/basic/gunicode.c @@ -4,8 +4,6 @@ * Copyright 2000, 2005 Red Hat, Inc. */ -#include <stdlib.h> - #include "gunicode.h" #define unichar uint32_t diff --git a/src/basic/hash-funcs.c b/src/basic/hash-funcs.c index e69f81558d..5267758769 100644 --- a/src/basic/hash-funcs.c +++ b/src/basic/hash-funcs.c @@ -19,6 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <string.h> + #include "hash-funcs.h" void string_hash_func(const void *p, struct siphash *state) { diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h index d837d6f28c..edae52e3db 100644 --- a/src/basic/hostname-util.h +++ b/src/basic/hostname-util.h @@ -21,6 +21,7 @@ ***/ #include <stdbool.h> +#include <stdio.h> #include "macro.h" diff --git a/src/basic/io-util.c b/src/basic/io-util.c index 77c9bdc739..08ad42ed95 100644 --- a/src/basic/io-util.c +++ b/src/basic/io-util.c @@ -33,6 +33,7 @@ int flush_fd(int fd) { .fd = fd, .events = POLLIN, }; + int count = 0; /* 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 @@ -52,7 +53,7 @@ int flush_fd(int fd) { return -errno; } else if (r == 0) - return 0; + return count; l = read(fd, buf, sizeof(buf)); if (l < 0) { @@ -61,11 +62,13 @@ int flush_fd(int fd) { continue; if (errno == EAGAIN) - return 0; + return count; return -errno; } else if (l == 0) - return 0; + return count; + + count += (int) l; } } diff --git a/src/basic/journal-importer.c b/src/basic/journal-importer.c index 6942c370cb..11054589e3 100644 --- a/src/basic/journal-importer.c +++ b/src/basic/journal-importer.c @@ -18,6 +18,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <unistd.h> #include "alloc-util.h" diff --git a/src/basic/label.c b/src/basic/label.c index ce81d286ab..18c9a23fea 100644 --- a/src/basic/label.c +++ b/src/basic/label.c @@ -22,6 +22,7 @@ #include <sys/stat.h> #include <unistd.h> +#include "btrfs-util.h" #include "label.h" #include "macro.h" #include "selinux-util.h" @@ -41,16 +42,17 @@ int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { return 0; } -int mkdir_label(const char *path, mode_t mode) { +int symlink_label(const char *old_path, const char *new_path) { int r; - assert(path); + assert(old_path); + assert(new_path); - r = mac_selinux_create_file_prepare(path, S_IFDIR); + r = mac_selinux_create_file_prepare(new_path, S_IFLNK); if (r < 0) return r; - if (mkdir(path, mode) < 0) + if (symlink(old_path, new_path) < 0) r = -errno; mac_selinux_create_file_clear(); @@ -58,26 +60,23 @@ int mkdir_label(const char *path, mode_t mode) { if (r < 0) return r; - return mac_smack_fix(path, false, false); + return mac_smack_fix(new_path, false, false); } -int symlink_label(const char *old_path, const char *new_path) { +int btrfs_subvol_make_label(const char *path) { int r; - assert(old_path); - assert(new_path); + assert(path); - r = mac_selinux_create_file_prepare(new_path, S_IFLNK); + r = mac_selinux_create_file_prepare(path, S_IFDIR); if (r < 0) return r; - if (symlink(old_path, new_path) < 0) - r = -errno; - + r = btrfs_subvol_make(path); mac_selinux_create_file_clear(); if (r < 0) return r; - return mac_smack_fix(new_path, false, false); + return mac_smack_fix(path, false, false); } diff --git a/src/basic/label.h b/src/basic/label.h index 86447c2e98..d73dacec4f 100644 --- a/src/basic/label.h +++ b/src/basic/label.h @@ -27,3 +27,5 @@ int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs); int mkdir_label(const char *path, mode_t mode); int symlink_label(const char *old_path, const char *new_path); + +int btrfs_subvol_make_label(const char *path); diff --git a/src/basic/locale-util.h b/src/basic/locale-util.h index 60ce017a15..f75dcbc3d1 100644 --- a/src/basic/locale-util.h +++ b/src/basic/locale-util.h @@ -22,6 +22,7 @@ #include <libintl.h> #include <stdbool.h> +#include <locale.h> #include "macro.h" @@ -75,3 +76,10 @@ LocaleVariable locale_variable_from_string(const char *s) _pure_; int get_keymaps(char ***l); bool keymap_is_valid(const char *name); + +static inline void freelocalep(locale_t *p) { + if (*p == (locale_t) 0) + return; + + freelocale(*p); +} diff --git a/src/basic/log.c b/src/basic/log.c index 20d9588e2f..12ee65a5c3 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -54,6 +54,7 @@ #include "syslog-util.h" #include "terminal-util.h" #include "time-util.h" +#include "utf8.h" #include "util.h" #define SNDBUF_SIZE (8*1024*1024) @@ -76,40 +77,40 @@ static bool show_location = false; static bool upgrade_syslog_to_journal = false; static bool always_reopen_console = false; static bool open_when_needed = false; +static bool prohibit_ipc = false; /* Akin to glibc's __abort_msg; which is private and we hence cannot * use here. */ static char *log_abort_msg = NULL; -void log_close_console(void) { +static void log_close_console(void) { if (console_fd < 0) return; - if (getpid_cached() == 1) { - if (console_fd >= 3) - safe_close(console_fd); + if (console_fd >= 3) + safe_close(console_fd); - console_fd = -1; - } + console_fd = -1; } static int log_open_console(void) { - if (console_fd >= 0) + if (!always_reopen_console) { + console_fd = STDERR_FILENO; return 0; + } - if (always_reopen_console) { + if (console_fd < 3) { console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); if (console_fd < 0) return console_fd; - } else - console_fd = STDERR_FILENO; + } return 0; } -void log_close_kmsg(void) { +static void log_close_kmsg(void) { kmsg_fd = safe_close(kmsg_fd); } @@ -125,7 +126,7 @@ static int log_open_kmsg(void) { return 0; } -void log_close_syslog(void) { +static void log_close_syslog(void) { syslog_fd = safe_close(syslog_fd); } @@ -197,7 +198,7 @@ fail: return r; } -void log_close_journal(void) { +static void log_close_journal(void) { journal_fd = safe_close(journal_fd); } @@ -239,7 +240,8 @@ int log_open(void) { /* If we don't use the console we close it here, to not get * killed by SAK. If we don't use syslog we close it here so * that we are not confused by somebody deleting the socket in - * the fs. If we don't use /dev/kmsg we still keep it open, + * the fs, and to make sure we don't use it if prohibit_ipc is + * set. If we don't use /dev/kmsg we still keep it open, * because there is no reason to close it. */ if (log_target == LOG_TARGET_NULL) { @@ -249,11 +251,12 @@ int log_open(void) { return 0; } - if (!IN_SET(log_target, LOG_TARGET_AUTO, LOG_TARGET_SAFE) || + if (log_target != LOG_TARGET_AUTO || getpid_cached() == 1 || isatty(STDERR_FILENO) <= 0) { - if (IN_SET(log_target, LOG_TARGET_AUTO, + if (!prohibit_ipc && + IN_SET(log_target, LOG_TARGET_AUTO, LOG_TARGET_JOURNAL_OR_KMSG, LOG_TARGET_JOURNAL)) { r = log_open_journal(); @@ -264,7 +267,8 @@ int log_open(void) { } } - if (IN_SET(log_target, LOG_TARGET_SYSLOG_OR_KMSG, + if (!prohibit_ipc && + IN_SET(log_target, LOG_TARGET_SYSLOG_OR_KMSG, LOG_TARGET_SYSLOG)) { r = log_open_syslog(); if (r >= 0) { @@ -275,7 +279,6 @@ int log_open(void) { } if (IN_SET(log_target, LOG_TARGET_AUTO, - LOG_TARGET_SAFE, LOG_TARGET_JOURNAL_OR_KMSG, LOG_TARGET_SYSLOG_OR_KMSG, LOG_TARGET_KMSG)) { @@ -627,7 +630,6 @@ int log_dispatch_internal( if (k <= 0 && IN_SET(log_target, LOG_TARGET_AUTO, - LOG_TARGET_SAFE, LOG_TARGET_SYSLOG_OR_KMSG, LOG_TARGET_JOURNAL_OR_KMSG, LOG_TARGET_KMSG)) { @@ -1219,17 +1221,18 @@ static const char *const log_target_table[_LOG_TARGET_MAX] = { [LOG_TARGET_SYSLOG] = "syslog", [LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg", [LOG_TARGET_AUTO] = "auto", - [LOG_TARGET_SAFE] = "safe", - [LOG_TARGET_NULL] = "null" + [LOG_TARGET_NULL] = "null", }; DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget); void log_received_signal(int level, const struct signalfd_siginfo *si) { - if (si->ssi_pid > 0) { + assert(si); + + if (pid_is_valid(si->ssi_pid)) { _cleanup_free_ char *p = NULL; - get_process_comm(si->ssi_pid, &p); + (void) get_process_comm(si->ssi_pid, &p); log_full(level, "Received SIG%s from PID %"PRIu32" (%s).", @@ -1239,7 +1242,6 @@ void log_received_signal(int level, const struct signalfd_siginfo *si) { log_full(level, "Received SIG%s.", signal_to_string(si->ssi_signo)); - } int log_syntax_internal( @@ -1289,8 +1291,37 @@ int log_syntax_internal( NULL); } +int log_syntax_invalid_utf8_internal( + const char *unit, + int level, + const char *config_file, + unsigned config_line, + const char *file, + int line, + const char *func, + const char *rvalue) { + + _cleanup_free_ char *p = NULL; + + if (rvalue) + p = utf8_escape_invalid(rvalue); + + log_syntax_internal(unit, level, config_file, config_line, 0, file, line, func, + "String is not UTF-8 clean, ignoring assignment: %s", strna(p)); + + return -EINVAL; +} + void log_set_upgrade_syslog_to_journal(bool b) { upgrade_syslog_to_journal = b; + + /* Make the change effective immediately */ + if (b) { + if (log_target == LOG_TARGET_SYSLOG) + log_target = LOG_TARGET_JOURNAL; + else if (log_target == LOG_TARGET_SYSLOG_OR_KMSG) + log_target = LOG_TARGET_JOURNAL_OR_KMSG; + } } void log_set_always_reopen_console(bool b) { @@ -1300,3 +1331,14 @@ void log_set_always_reopen_console(bool b) { void log_set_open_when_needed(bool b) { open_when_needed = b; } + +void log_set_prohibit_ipc(bool b) { + prohibit_ipc = b; +} + +int log_emergency_level(void) { + /* Returns the log level to use for log_emergency() logging. We use LOG_EMERG only when we are PID 1, as only + * then the system of the whole system is obviously affected. */ + + return getpid_cached() == 1 ? LOG_EMERG : LOG_ERR; +} diff --git a/src/basic/log.h b/src/basic/log.h index aa5976c3c0..5b5a25bd6d 100644 --- a/src/basic/log.h +++ b/src/basic/log.h @@ -20,18 +20,16 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <errno.h> #include <stdarg.h> #include <stdbool.h> #include <stdlib.h> -#include <sys/signalfd.h> -#include <sys/socket.h> #include <syslog.h> -#include "sd-id128.h" - #include "macro.h" -#include "process-util.h" + +/* Some structures we reference but don't want to pull in headers for */ +struct iovec; +struct signalfd_siginfo; typedef enum LogRealm { LOG_REALM_SYSTEMD, @@ -52,7 +50,6 @@ typedef enum LogTarget{ LOG_TARGET_SYSLOG, LOG_TARGET_SYSLOG_OR_KMSG, LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */ - LOG_TARGET_SAFE, /* console if stderr is tty, KMSG otherwise */ LOG_TARGET_NULL, _LOG_TARGET_MAX, _LOG_TARGET_INVALID = -1 @@ -97,11 +94,6 @@ int log_open(void); void log_close(void); void log_forget_fds(void); -void log_close_syslog(void); -void log_close_journal(void); -void log_close_kmsg(void); -void log_close_console(void); - void log_parse_environment_realm(LogRealm realm); #define log_parse_environment() \ log_parse_environment_realm(LOG_REALM) @@ -194,7 +186,7 @@ int log_struct_iovec_internal( const char *file, int line, const char *func, - const struct iovec input_iovec[], + const struct iovec *input_iovec, size_t n_input_iovec); /* This modifies the buffer passed! */ @@ -240,9 +232,9 @@ void log_assert_failed_return_realm( /* Logging with level */ #define log_full_errno_realm(realm, level, error, ...) \ ({ \ - int _level = (level), _e = (error); \ - (log_get_max_level_realm((realm)) >= LOG_PRI(_level)) \ - ? log_internal_realm(LOG_REALM_PLUS_LEVEL((realm), _level), _e, \ + int _level = (level), _e = (error), _realm = (realm); \ + (log_get_max_level_realm(_realm) >= LOG_PRI(_level)) \ + ? log_internal_realm(LOG_REALM_PLUS_LEVEL(_realm, _level), _e, \ __FILE__, __LINE__, __func__, __VA_ARGS__) \ : -abs(_e); \ }) @@ -252,13 +244,15 @@ void log_assert_failed_return_realm( #define log_full(level, ...) log_full_errno((level), 0, __VA_ARGS__) +int log_emergency_level(void); + /* Normal logging */ #define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__) #define log_info(...) log_full(LOG_INFO, __VA_ARGS__) #define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__) #define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__) #define log_error(...) log_full(LOG_ERR, __VA_ARGS__) -#define log_emergency(...) log_full(getpid_cached() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__) +#define log_emergency(...) log_full(log_emergency_level(), __VA_ARGS__) /* Logging triggered by an errno-like error */ #define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__) @@ -266,7 +260,7 @@ void log_assert_failed_return_realm( #define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__) #define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__) #define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__) -#define log_emergency_errno(error, ...) log_full_errno(getpid_cached() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__) +#define log_emergency_errno(error, ...) log_full_errno(log_emergency_level(), error, __VA_ARGS__) #ifdef LOG_TRACE # define log_trace(...) log_debug(__VA_ARGS__) @@ -302,10 +296,20 @@ LogTarget log_target_from_string(const char *s) _pure_; void log_received_signal(int level, const struct signalfd_siginfo *si); +/* If turned on, any requests for a log target involving "syslog" will be implicitly upgraded to the equivalent journal target */ void log_set_upgrade_syslog_to_journal(bool b); + +/* If turned on, and log_open() is called, we'll not use STDERR_FILENO for logging ever, but rather open /dev/console */ void log_set_always_reopen_console(bool b); + +/* If turned on, we'll open the log stream implicitly if needed on each individual log call. This is normally not + * desired as we want to reuse our logging streams. It is useful however */ void log_set_open_when_needed(bool b); +/* If turned on, then we'll never use IPC-based logging, i.e. never log to syslog or the journal. We'll only log to + * stderr, the console or kmsg */ +void log_set_prohibit_ipc(bool b); + int log_syntax_internal( const char *unit, int level, @@ -317,6 +321,16 @@ int log_syntax_internal( const char *func, const char *format, ...) _printf_(9, 10); +int log_syntax_invalid_utf8_internal( + const char *unit, + int level, + const char *config_file, + unsigned config_line, + const char *file, + int line, + const char *func, + const char *rvalue); + #define log_syntax(unit, level, config_file, config_line, error, ...) \ ({ \ int _level = (level), _e = (error); \ @@ -328,10 +342,9 @@ int log_syntax_internal( #define log_syntax_invalid_utf8(unit, level, config_file, config_line, rvalue) \ ({ \ int _level = (level); \ - if (log_get_max_level() >= LOG_PRI(_level)) { \ - _cleanup_free_ char *_p = NULL; \ - _p = utf8_escape_invalid(rvalue); \ - log_syntax_internal(unit, _level, config_file, config_line, 0, __FILE__, __LINE__, __func__, \ - "String is not UTF-8 clean, ignoring assignment: %s", strna(_p)); \ - } \ + (log_get_max_level() >= LOG_PRI(_level)) \ + ? log_syntax_invalid_utf8_internal(unit, _level, config_file, config_line, __FILE__, __LINE__, __func__, rvalue) \ + : -EINVAL; \ }) + +#define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG) diff --git a/src/basic/macro.h b/src/basic/macro.h index 02d22de833..89bdd852a9 100644 --- a/src/basic/macro.h +++ b/src/basic/macro.h @@ -139,11 +139,17 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL)); } +#ifndef __COVERITY__ +# define VOID_0 ((void)0) +#else +# define VOID_0 ((void*)0) +#endif + #define ELEMENTSOF(x) \ __extension__ (__builtin_choose_expr( \ !__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \ sizeof(x)/sizeof((x)[0]), \ - (void)0)) + VOID_0)) /* * STRLEN - return the length of a string literal, minus the trailing NUL byte. @@ -181,7 +187,7 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { __builtin_constant_p(_B) && \ __builtin_types_compatible_p(typeof(_A), typeof(_B)), \ ((_A) > (_B)) ? (_A) : (_B), \ - (void)0)) + VOID_0)) /* takes two types and returns the size of the larger one */ #define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; })) diff --git a/src/basic/meson.build b/src/basic/meson.build index a37e279e57..44cd31ecbe 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -15,7 +15,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with systemd; If not, see <http://www.gnu.org/licenses/>. -basic_sources_plain = files(''' +basic_sources = files(''' MurmurHash2.c MurmurHash2.h af-list.c @@ -35,6 +35,8 @@ basic_sources_plain = files(''' bitmap.c bitmap.h blkid-util.h + blockdev-util.c + blockdev-util.h bpf-program.c bpf-program.h btrfs-ctree.h @@ -148,6 +150,8 @@ basic_sources_plain = files(''' proc-cmdline.h process-util.c process-util.h + procfs-util.c + procfs-util.h random-util.c random-util.h ratelimit.c @@ -176,6 +180,8 @@ basic_sources_plain = files(''' smack-util.c smack-util.h socket-label.c + socket-protocol-list.c + socket-protocol-list.h socket-util.c socket-util.h sparse-endian.h @@ -201,10 +207,10 @@ basic_sources_plain = files(''' time-util.h umask-util.h unaligned.h - unit-name.c - unit-name.h unit-def.c unit-def.h + unit-name.c + unit-name.h user-util.c user-util.h utf8.c @@ -255,11 +261,19 @@ errno_list_txt = custom_target( command : [generate_errno_list, cpp], capture : true) +generate_socket_protocol_list = find_program('generate-socket-protocol-list.sh') +socket_protocol_list_txt = custom_target( + 'socket-protocol-list.txt', + output : 'socket-protocol-list.txt', + command : [generate_socket_protocol_list, cpp], + capture : true) + generated_gperf_headers = [] foreach item : [['af', af_list_txt, 'af', ''], ['arphrd', arphrd_list_txt, 'arphrd', 'ARPHRD_'], ['cap', cap_list_txt, 'capability', ''], - ['errno', errno_list_txt, 'errno', '']] + ['errno', errno_list_txt, 'errno', ''], + ['socket-protocol', socket_protocol_list_txt, 'socket_protocol', 'IPPROTO_']] fname = '@0@-from-name.gperf'.format(item[0]) gperf_file = custom_target( @@ -294,7 +308,7 @@ foreach item : [['af', af_list_txt, 'af', ''], generated_gperf_headers += [target1, target2] endforeach -basic_sources = basic_sources_plain + [missing_h] + generated_gperf_headers +basic_sources += [missing_h] + generated_gperf_headers libbasic = static_library( 'basic', @@ -303,6 +317,16 @@ libbasic = static_library( dependencies : [threads, libcap, libblkid, - libselinux, - ], + libselinux], + c_args : ['-fvisibility=default'], install : false) + +# A convenience library that is separate from libbasic to avoid +# unnecessary linking to libgcrypt. +libbasic_gcrypt = static_library( + 'basic-gcrypt', + 'gcrypt-util.c', + 'gcrypt-util.h', + include_directories : includes, + dependencies : [libgcrypt], + c_args : ['-fvisibility=default']) diff --git a/src/basic/missing.h b/src/basic/missing.h index 790f9f55a5..1280e6c410 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -206,6 +206,32 @@ struct sockaddr_vm { #endif #if ! HAVE_LINUX_BTRFS_H +#define BTRFS_IOC_QGROUP_ASSIGN _IOW(BTRFS_IOCTL_MAGIC, 41, \ + struct btrfs_ioctl_qgroup_assign_args) +#define BTRFS_IOC_QGROUP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 42, \ + struct btrfs_ioctl_qgroup_create_args) +#define BTRFS_IOC_QUOTA_RESCAN _IOW(BTRFS_IOCTL_MAGIC, 44, \ + struct btrfs_ioctl_quota_rescan_args) +#define BTRFS_IOC_QUOTA_RESCAN_STATUS _IOR(BTRFS_IOCTL_MAGIC, 45, \ + struct btrfs_ioctl_quota_rescan_args) + +struct btrfs_ioctl_quota_rescan_args { + __u64 flags; + __u64 progress; + __u64 reserved[6]; +}; + +struct btrfs_ioctl_qgroup_assign_args { + __u64 assign; + __u64 src; + __u64 dst; +}; + +struct btrfs_ioctl_qgroup_create_args { + __u64 create; + __u64 qgroupid; +}; + struct btrfs_ioctl_vol_args { int64_t fd; char name[BTRFS_PATH_NAME_MAX + 1]; @@ -543,6 +569,38 @@ struct btrfs_ioctl_quota_ctl_args { #define PR_SET_CHILD_SUBREAPER 36 #endif +#ifndef PR_SET_MM_ARG_START +#define PR_SET_MM_ARG_START 8 +#endif + +#ifndef PR_SET_MM_ARG_END +#define PR_SET_MM_ARG_END 9 +#endif + +#ifndef PR_SET_MM_ENV_START +#define PR_SET_MM_ENV_START 10 +#endif + +#ifndef PR_SET_MM_ENV_END +#define PR_SET_MM_ENV_END 11 +#endif + +#ifndef EFIVARFS_MAGIC +#define EFIVARFS_MAGIC 0xde5e81e4 +#endif + +#ifndef SMACK_MAGIC +#define SMACK_MAGIC 0x43415d53 +#endif + +#ifndef DM_DEFERRED_REMOVE +#define DM_DEFERRED_REMOVE (1 << 17) +#endif + +#ifndef MAX_HANDLE_SZ +#define MAX_HANDLE_SZ 128 +#endif + #if ! HAVE_SECURE_GETENV # if HAVE___SECURE_GETENV # define secure_getenv __secure_getenv @@ -563,6 +621,10 @@ struct btrfs_ioctl_quota_ctl_args { # define SO_REUSEPORT 15 #endif +#ifndef SO_PEERGROUPS +# define SO_PEERGROUPS 59 +#endif + #ifndef EVIOCREVOKE # define EVIOCREVOKE _IOW('E', 0x91, int) #endif @@ -653,18 +715,28 @@ struct input_mask { #define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1) #endif -#if !HAVE_IFLA_IPVLAN_MODE +#if !HAVE_IFLA_IPVLAN_FLAGS #define IFLA_IPVLAN_UNSPEC 0 #define IFLA_IPVLAN_MODE 1 -#define __IFLA_IPVLAN_MAX 2 +#define IFLA_IPVLAN_FLAGS 2 +#define __IFLA_IPVLAN_MAX 3 #define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1) #define IPVLAN_MODE_L2 0 #define IPVLAN_MODE_L3 1 +#define IPVLAN_MODE_L3S 2 #define IPVLAN_MAX 2 #endif +#if !HAVE_IPVLAN_F_PRIVATE +#define IPVLAN_F_PRIVATE 0x01 +#define IPVLAN_F_VEPA 0x02 +#define __IPVLAN_F_PRIVATE_MAX 3 + +#define HAVE_IPVLAN_F_PRIVATE_MAX (__HAVE_IPVLAN_F_PRIVATE_MAX - 1) +#endif + #if !HAVE_IFLA_VTI_REMOTE #define IFLA_VTI_UNSPEC 0 #define IFLA_VTI_LINK 1 diff --git a/src/basic/missing_syscall.h b/src/basic/missing_syscall.h index fd82c11e94..c938d0d976 100644 --- a/src/basic/missing_syscall.h +++ b/src/basic/missing_syscall.h @@ -330,10 +330,14 @@ static inline ssize_t copy_file_range(int fd_in, loff_t *off_in, # define __NR_bpf 321 # elif defined __aarch64__ # define __NR_bpf 280 +# elif defined __arm__ +# define __NR_bpf 386 # elif defined __sparc__ # define __NR_bpf 349 # elif defined __s390__ # define __NR_bpf 351 +# elif defined __tilegx__ +# define __NR_bpf 280 # else # warning "__NR_bpf not defined for your architecture" # endif diff --git a/src/basic/mkdir-label.c b/src/basic/mkdir-label.c index 16eb7ce265..6f3a46f467 100644 --- a/src/basic/mkdir-label.c +++ b/src/basic/mkdir-label.c @@ -20,11 +20,32 @@ ***/ #include <stdio.h> +#include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include "label.h" +#include "macro.h" #include "mkdir.h" +#include "selinux-util.h" +#include "smack-util.h" + +int mkdir_label(const char *path, mode_t mode) { + int r; + + assert(path); + + r = mac_selinux_create_file_prepare(path, S_IFDIR); + if (r < 0) + return r; + + r = mkdir_errno_wrapper(path, mode); + mac_selinux_create_file_clear(); + if (r < 0) + return r; + + return mac_smack_fix(path, false, false); +} int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink) { return mkdir_safe_internal(path, mode, uid, gid, follow_symlink, mkdir_label); diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c index 4386b38c4a..de4746c867 100644 --- a/src/basic/mkdir.c +++ b/src/basic/mkdir.c @@ -35,6 +35,8 @@ int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, boo struct stat st; int r; + assert(_mkdir != mkdir); + if (_mkdir(path, mode) >= 0) { r = chmod_and_chown(path, mode, uid, gid); if (r < 0) @@ -68,8 +70,14 @@ int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, boo return 0; } +int mkdir_errno_wrapper(const char *pathname, mode_t mode) { + if (mkdir(pathname, mode) < 0) + return -errno; + return 0; +} + int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink) { - return mkdir_safe_internal(path, mode, uid, gid, follow_symlink, mkdir); + return mkdir_safe_internal(path, mode, uid, gid, follow_symlink, mkdir_errno_wrapper); } int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) { @@ -77,6 +85,7 @@ int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mk int r; assert(path); + assert(_mkdir != mkdir); if (prefix && !path_startswith(path, prefix)) return -ENOTDIR; @@ -104,8 +113,7 @@ int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mk e = p + strcspn(p, "/"); p = e + strspn(e, "/"); - /* Is this the last component? If so, then we're - * done */ + /* Is this the last component? If so, then we're done */ if (*p == 0) return 0; @@ -116,13 +124,13 @@ int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mk continue; r = _mkdir(t, mode); - if (r < 0 && errno != EEXIST) - return -errno; + if (r < 0 && r != -EEXIST) + return r; } } int mkdir_parents(const char *path, mode_t mode) { - return mkdir_parents_internal(NULL, path, mode, mkdir); + return mkdir_parents_internal(NULL, path, mode, mkdir_errno_wrapper); } int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) { @@ -130,17 +138,19 @@ int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_fu /* Like mkdir -p */ + assert(_mkdir != mkdir); + r = mkdir_parents_internal(prefix, path, mode, _mkdir); if (r < 0) return r; r = _mkdir(path, mode); - if (r < 0 && (errno != EEXIST || is_dir(path, true) <= 0)) - return -errno; + if (r < 0 && (r != -EEXIST || is_dir(path, true) <= 0)) + return r; return 0; } int mkdir_p(const char *path, mode_t mode) { - return mkdir_p_internal(NULL, path, mode, mkdir); + return mkdir_p_internal(NULL, path, mode, mkdir_errno_wrapper); } diff --git a/src/basic/mkdir.h b/src/basic/mkdir.h index 04a537f8a8..d6c2d579a3 100644 --- a/src/basic/mkdir.h +++ b/src/basic/mkdir.h @@ -23,6 +23,7 @@ #include <sys/types.h> +int mkdir_errno_wrapper(const char *pathname, mode_t mode); int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink); int mkdir_parents(const char *path, mode_t mode); int mkdir_p(const char *path, mode_t mode); diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index d03f60e01a..2c22753dea 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -28,6 +28,7 @@ #include "alloc-util.h" #include "errno-list.h" #include "extract-word.h" +#include "locale-util.h" #include "macro.h" #include "parse-util.h" #include "process-util.h" @@ -83,7 +84,7 @@ int parse_mode(const char *s, mode_t *ret) { l = strtol(s, &x, 8); if (errno > 0) return -errno; - if (!x || x == s || *x) + if (!x || x == s || *x != 0) return -EINVAL; if (l < 0 || l > 07777) return -ERANGE; @@ -283,7 +284,8 @@ int parse_errno(const char *t) { if (r < 0) return r; - if (e < 0 || e > ERRNO_MAX) + /* 0 is also allowed here */ + if (!errno_is_valid(e) && e != 0) return -ERANGE; return e; @@ -390,7 +392,7 @@ int safe_atou(const char *s, unsigned *ret_u) { l = strtoul(s, &x, 0); if (errno > 0) return -errno; - if (!x || x == s || *x) + if (!x || x == s || *x != 0) return -EINVAL; if (s[0] == '-') return -ERANGE; @@ -412,7 +414,7 @@ int safe_atoi(const char *s, int *ret_i) { l = strtol(s, &x, 0); if (errno > 0) return -errno; - if (!x || x == s || *x) + if (!x || x == s || *x != 0) return -EINVAL; if ((long) (int) l != l) return -ERANGE; @@ -434,7 +436,7 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) { l = strtoull(s, &x, 0); if (errno > 0) return -errno; - if (!x || x == s || *x) + if (!x || x == s || *x != 0) return -EINVAL; if (*s == '-') return -ERANGE; @@ -454,7 +456,7 @@ int safe_atolli(const char *s, long long int *ret_lli) { l = strtoll(s, &x, 0); if (errno > 0) return -errno; - if (!x || x == s || *x) + if (!x || x == s || *x != 0) return -EINVAL; *ret_lli = l; @@ -474,7 +476,7 @@ int safe_atou8(const char *s, uint8_t *ret) { l = strtoul(s, &x, 0); if (errno > 0) return -errno; - if (!x || x == s || *x) + if (!x || x == s || *x != 0) return -EINVAL; if (s[0] == '-') return -ERANGE; @@ -498,7 +500,7 @@ int safe_atou16(const char *s, uint16_t *ret) { l = strtoul(s, &x, 0); if (errno > 0) return -errno; - if (!x || x == s || *x) + if (!x || x == s || *x != 0) return -EINVAL; if (s[0] == '-') return -ERANGE; @@ -520,7 +522,7 @@ int safe_atoi16(const char *s, int16_t *ret) { l = strtol(s, &x, 0); if (errno > 0) return -errno; - if (!x || x == s || *x) + if (!x || x == s || *x != 0) return -EINVAL; if ((long) (int16_t) l != l) return -ERANGE; @@ -530,9 +532,9 @@ int safe_atoi16(const char *s, int16_t *ret) { } int safe_atod(const char *s, double *ret_d) { + _cleanup_(freelocalep) locale_t loc = (locale_t) 0; char *x = NULL; double d = 0; - locale_t loc; assert(s); assert(ret_d); @@ -543,16 +545,11 @@ int safe_atod(const char *s, double *ret_d) { errno = 0; d = strtod_l(s, &x, loc); - if (errno > 0) { - freelocale(loc); + if (errno > 0) return -errno; - } - if (!x || x == s || *x) { - freelocale(loc); + if (!x || x == s || *x != 0) return -EINVAL; - } - freelocale(loc); *ret_d = (double) d; return 0; } @@ -595,19 +592,20 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) { int parse_percent_unbounded(const char *p) { const char *pc, *n; - unsigned v; - int r; + int r, v; pc = endswith(p, "%"); if (!pc) return -EINVAL; n = strndupa(p, pc - p); - r = safe_atou(n, &v); + r = safe_atoi(n, &v); if (r < 0) return r; + if (v < 0) + return -ERANGE; - return (int) v; + return v; } int parse_percent(const char *p) { diff --git a/src/basic/path-util.c b/src/basic/path-util.c index ab4778d4ed..df94629385 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -81,14 +81,36 @@ char *path_make_absolute(const char *p, const char *prefix) { /* Makes every item in the list an absolute path by prepending * the prefix, if specified and necessary */ - if (path_is_absolute(p) || !prefix) + if (path_is_absolute(p) || isempty(prefix)) return strdup(p); - return strjoin(prefix, "/", p); + if (endswith(prefix, "/")) + return strjoin(prefix, p); + else + return strjoin(prefix, "/", p); +} + +int safe_getcwd(char **ret) { + char *cwd; + + cwd = get_current_dir_name(); + if (!cwd) + return negative_errno(); + + /* Let's make sure the directory is really absolute, to protect us from the logic behind + * CVE-2018-1000001 */ + if (cwd[0] != '/') { + free(cwd); + return -ENOMEDIUM; + } + + *ret = cwd; + return 0; } int path_make_absolute_cwd(const char *p, char **ret) { char *c; + int r; assert(p); assert(ret); @@ -101,11 +123,14 @@ int path_make_absolute_cwd(const char *p, char **ret) { else { _cleanup_free_ char *cwd = NULL; - cwd = get_current_dir_name(); - if (!cwd) - return negative_errno(); + r = safe_getcwd(&cwd); + if (r < 0) + return r; - c = strjoin(cwd, "/", p); + if (endswith(cwd, "/")) + c = strjoin(cwd, p); + else + c = strjoin(cwd, "/", p); } if (!c) return -ENOMEM; diff --git a/src/basic/path-util.h b/src/basic/path-util.h index f79cdf928e..89c285e076 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -41,6 +41,7 @@ bool is_path(const char *p) _pure_; int path_split_and_make_absolute(const char *p, char ***ret); bool path_is_absolute(const char *p) _pure_; char* path_make_absolute(const char *p, const char *prefix); +int safe_getcwd(char **ret); int path_make_absolute_cwd(const char *p, char **ret); int path_make_relative(const char *from_dir, const char *to_path, char **_r); char* path_kill_slashes(char *path); diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 17c94f44a0..dc7c9ef9ef 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -56,6 +56,7 @@ #include "stat-util.h" #include "string-table.h" #include "string-util.h" +#include "terminal-util.h" #include "user-util.h" #include "util.h" @@ -296,10 +297,17 @@ int rename_process(const char name[]) { if (isempty(name)) return -EINVAL; /* let's not confuse users unnecessarily with an empty name */ + if (!is_main_thread()) + return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we + * cache things without locking, and we make assumptions that PR_SET_NAME sets the + * process name that isn't correct on any other threads */ + l = strlen(name); - /* First step, change the comm field. */ - (void) prctl(PR_SET_NAME, name); + /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we + * can use PR_SET_NAME, which sets the thread name for the calling thread. */ + if (prctl(PR_SET_NAME, name) < 0) + log_debug_errno(errno, "PR_SET_NAME failed: %m"); if (l > 15) /* Linux process names can be 15 chars at max */ truncated = true; @@ -679,32 +687,43 @@ int wait_for_terminate(pid_t pid, siginfo_t *status) { * A warning is emitted if the process terminates abnormally, * and also if it returns non-zero unless check_exit_code is true. */ -int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) { - int r; +int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) { + _cleanup_free_ char *buffer = NULL; siginfo_t status; + int r, prio; - assert(name); assert(pid > 1); + if (!name) { + r = get_process_comm(pid, &buffer); + if (r < 0) + log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pid); + else + name = buffer; + } + + prio = flags & WAIT_LOG_ABNORMAL ? LOG_ERR : LOG_DEBUG; + r = wait_for_terminate(pid, &status); if (r < 0) - return log_warning_errno(r, "Failed to wait for %s: %m", name); + return log_full_errno(prio, r, "Failed to wait for %s: %m", strna(name)); if (status.si_code == CLD_EXITED) { - if (status.si_status != 0) - log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG, - "%s failed with error code %i.", name, status.si_status); + if (status.si_status != EXIT_SUCCESS) + log_full(flags & WAIT_LOG_NON_ZERO_EXIT_STATUS ? LOG_ERR : LOG_DEBUG, + "%s failed with exit status %i.", strna(name), status.si_status); else log_debug("%s succeeded.", name); return status.si_status; + } else if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)) { - log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status)); + log_full(prio, "%s terminated by signal %s.", strna(name), signal_to_string(status.si_status)); return -EPROTO; } - log_warning("%s failed due to unknown reason.", name); + log_full(prio, "%s failed due to unknown reason.", strna(name)); return -EPROTO; } @@ -777,6 +796,8 @@ void sigkill_wait(pid_t pid) { } void sigkill_waitp(pid_t *pid) { + PROTECT_ERRNO; + if (!pid) return; if (*pid <= 1) @@ -928,6 +949,17 @@ noreturn void freeze(void) { sync(); + /* Let's not freeze right away, but keep reaping zombies. */ + for (;;) { + int r; + siginfo_t si = {}; + + r = waitid(P_ALL, 0, &si, WEXITED); + if (r < 0 && errno != EINTR) + break; + } + + /* waitid() failed with an unexpected error, things are really borked. Freeze now! */ for (;;) pause(); } @@ -1075,7 +1107,7 @@ int ioprio_parse_priority(const char *s, int *ret) { static pid_t cached_pid = CACHED_PID_UNSET; -static void reset_cached_pid(void) { +void reset_cached_pid(void) { /* Invoked in the child after a fork(), i.e. at the first moment the PID changed */ cached_pid = CACHED_PID_UNSET; } @@ -1134,6 +1166,244 @@ int must_be_root(void) { return -EPERM; } +int safe_fork_full( + const char *name, + const int except_fds[], + size_t n_except_fds, + ForkFlags flags, + pid_t *ret_pid) { + + pid_t original_pid, pid; + sigset_t saved_ss, ss; + bool block_signals = false; + int prio, r; + + /* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always + * returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */ + + prio = flags & FORK_LOG ? LOG_ERR : LOG_DEBUG; + + original_pid = getpid_cached(); + + if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) { + + /* We temporarily block all signals, so that the new child has them blocked initially. This way, we can + * be sure that SIGTERMs are not lost we might send to the child. */ + + if (sigfillset(&ss) < 0) + return log_full_errno(prio, errno, "Failed to reset signal set: %m"); + + block_signals = true; + + } else if (flags & FORK_WAIT) { + + /* Let's block SIGCHLD at least, so that we can safely watch for the child process */ + + if (sigemptyset(&ss) < 0) + return log_full_errno(prio, errno, "Failed to clear signal set: %m"); + + if (sigaddset(&ss, SIGCHLD) < 0) + return log_full_errno(prio, errno, "Failed to add SIGCHLD to signal set: %m"); + + block_signals = true; + } + + if (block_signals) + if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0) + return log_full_errno(prio, errno, "Failed to set signal mask: %m"); + + if (flags & FORK_NEW_MOUNTNS) + pid = raw_clone(SIGCHLD|CLONE_NEWNS); + else + pid = fork(); + if (pid < 0) { + r = -errno; + + if (block_signals) /* undo what we did above */ + (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL); + + return log_full_errno(prio, r, "Failed to fork: %m"); + } + if (pid > 0) { + /* We are in the parent process */ + + log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid); + + if (flags & FORK_WAIT) { + r = wait_for_terminate_and_check(name, pid, (flags & FORK_LOG ? WAIT_LOG : 0)); + if (r < 0) + return r; + if (r != EXIT_SUCCESS) /* exit status > 0 should be treated as failure, too */ + return -EPROTO; + } + + if (block_signals) /* undo what we did above */ + (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL); + + if (ret_pid) + *ret_pid = pid; + + return 1; + } + + /* We are in the child process */ + + if (flags & FORK_REOPEN_LOG) { + /* Close the logs if requested, before we log anything. And make sure we reopen it if needed. */ + log_close(); + log_set_open_when_needed(true); + } + + if (name) { + r = rename_process(name); + if (r < 0) + log_full_errno(flags & FORK_LOG ? LOG_WARNING : LOG_DEBUG, + r, "Failed to rename process, ignoring: %m"); + } + + if (flags & FORK_DEATHSIG) + if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) { + log_full_errno(prio, errno, "Failed to set death signal: %m"); + _exit(EXIT_FAILURE); + } + + if (flags & FORK_RESET_SIGNALS) { + r = reset_all_signal_handlers(); + if (r < 0) { + log_full_errno(prio, r, "Failed to reset signal handlers: %m"); + _exit(EXIT_FAILURE); + } + + /* This implicitly undoes the signal mask stuff we did before the fork()ing above */ + r = reset_signal_mask(); + if (r < 0) { + log_full_errno(prio, r, "Failed to reset signal mask: %m"); + _exit(EXIT_FAILURE); + } + } else if (block_signals) { /* undo what we did above */ + if (sigprocmask(SIG_SETMASK, &saved_ss, NULL) < 0) { + log_full_errno(prio, errno, "Failed to restore signal mask: %m"); + _exit(EXIT_FAILURE); + } + } + + if (flags & FORK_DEATHSIG) { + pid_t ppid; + /* Let's see if the parent PID is still the one we started from? If not, then the parent + * already died by the time we set PR_SET_PDEATHSIG, hence let's emulate the effect */ + + ppid = getppid(); + if (ppid == 0) + /* Parent is in a differn't PID namespace. */; + else if (ppid != original_pid) { + log_debug("Parent died early, raising SIGTERM."); + (void) raise(SIGTERM); + _exit(EXIT_FAILURE); + } + } + + if (flags & FORK_CLOSE_ALL_FDS) { + /* Close the logs here in case it got reopened above, as close_all_fds() would close them for us */ + log_close(); + + r = close_all_fds(except_fds, n_except_fds); + if (r < 0) { + log_full_errno(prio, r, "Failed to close all file descriptors: %m"); + _exit(EXIT_FAILURE); + } + } + + /* When we were asked to reopen the logs, do so again now */ + if (flags & FORK_REOPEN_LOG) { + log_open(); + log_set_open_when_needed(false); + } + + if (flags & FORK_NULL_STDIO) { + r = make_null_stdio(); + if (r < 0) { + log_full_errno(prio, r, "Failed to connect stdin/stdout to /dev/null: %m"); + _exit(EXIT_FAILURE); + } + } + + if (ret_pid) + *ret_pid = getpid_cached(); + + return 0; +} + +int fork_agent(const char *name, const int except[], unsigned n_except, pid_t *ret_pid, const char *path, ...) { + bool stdout_is_tty, stderr_is_tty; + unsigned n, i; + va_list ap; + char **l; + int r; + + assert(path); + + /* Spawns a temporary TTY agent, making sure it goes away when we go away */ + + r = safe_fork_full(name, except, n_except, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS, ret_pid); + if (r < 0) + return r; + if (r > 0) + return 0; + + /* In the child: */ + + stdout_is_tty = isatty(STDOUT_FILENO); + stderr_is_tty = isatty(STDERR_FILENO); + + if (!stdout_is_tty || !stderr_is_tty) { + int fd; + + /* Detach from stdout/stderr. and reopen + * /dev/tty for them. This is important to + * ensure that when systemctl is started via + * popen() or a similar call that expects to + * read EOF we actually do generate EOF and + * not delay this indefinitely by because we + * keep an unused copy of stdin around. */ + fd = open("/dev/tty", O_WRONLY); + if (fd < 0) { + log_error_errno(errno, "Failed to open /dev/tty: %m"); + _exit(EXIT_FAILURE); + } + + 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) < 0) { + log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); + _exit(EXIT_FAILURE); + } + + if (fd > STDERR_FILENO) + close(fd); + } + + /* Count arguments */ + va_start(ap, path); + for (n = 0; va_arg(ap, char*); n++) + ; + va_end(ap); + + /* Allocate strv */ + l = alloca(sizeof(char *) * (n + 1)); + + /* Fill in arguments */ + va_start(ap, path); + for (i = 0; i <= n; i++) + l[i] = va_arg(ap, char*); + va_end(ap); + + execv(path, l); + _exit(EXIT_FAILURE); +} + 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 1b7e692060..9fabe4a5be 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -21,6 +21,7 @@ ***/ #include <alloca.h> +#include <errno.h> #include <sched.h> #include <signal.h> #include <stdbool.h> @@ -61,7 +62,16 @@ int get_process_environ(pid_t pid, char **environ); 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); + +typedef enum WaitFlags { + WAIT_LOG_ABNORMAL = 1U << 0, + WAIT_LOG_NON_ZERO_EXIT_STATUS = 1U << 1, + + /* A shortcut for requesting the most complete logging */ + WAIT_LOG = WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS, +} WaitFlags; + +int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags); int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout); void sigkill_wait(pid_t pid); @@ -106,8 +116,13 @@ int sigchld_code_from_string(const char *s) _pure_; int sched_policy_to_string_alloc(int i, char **s); int sched_policy_from_string(const char *s); -#define PTR_TO_PID(p) ((pid_t) ((uintptr_t) p)) -#define PID_TO_PTR(p) ((void*) ((uintptr_t) p)) +static inline pid_t PTR_TO_PID(const void *p) { + return (pid_t) ((uintptr_t) p); +} + +static inline void* PID_TO_PTR(pid_t pid) { + return (void*) ((uintptr_t) pid); +} void valgrind_summary_hack(void); @@ -137,8 +152,54 @@ static inline bool pid_is_valid(pid_t p) { return p > 0; } +static inline int sched_policy_to_string_alloc_with_check(int n, char **s) { + if (!sched_policy_is_valid(n)) + return -EINVAL; + + return sched_policy_to_string_alloc(n, s); +} + int ioprio_parse_priority(const char *s, int *ret); pid_t getpid_cached(void); +void reset_cached_pid(void); int must_be_root(void); + +typedef enum ForkFlags { + FORK_RESET_SIGNALS = 1U << 0, + FORK_CLOSE_ALL_FDS = 1U << 1, + FORK_DEATHSIG = 1U << 2, + FORK_NULL_STDIO = 1U << 3, + FORK_REOPEN_LOG = 1U << 4, + FORK_LOG = 1U << 5, + FORK_WAIT = 1U << 6, + FORK_NEW_MOUNTNS = 1U << 7, +} ForkFlags; + +int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid); + +static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) { + return safe_fork_full(name, NULL, 0, flags, ret_pid); +} + +int fork_agent(const char *name, const int except[], unsigned n_except, pid_t *pid, const char *path, ...); + +#if SIZEOF_PID_T == 4 +/* The highest possibly (theoretic) pid_t value on this architecture. */ +#define PID_T_MAX ((pid_t) INT32_MAX) +/* The maximum number of concurrent processes Linux allows on this architecture, as well as the highest valid PID value + * the kernel will potentially assign. This reflects a value compiled into the kernel (PID_MAX_LIMIT), and sets the + * upper boundary on what may be written to the /proc/sys/kernel/pid_max sysctl (but do note that the sysctl is off by + * 1, since PID 0 can never exist and there can hence only be one process less than the limit would suggest). Since + * these values are documented in proc(5) we feel quite confident that they are stable enough for the near future at + * least to define them here too. */ +#define TASKS_MAX 4194303U +#elif SIZEOF_PID_T == 2 +#define PID_T_MAX ((pid_t) INT16_MAX) +#define TASKS_MAX 32767U +#else +#error "Unknown pid_t size" +#endif + +assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX) diff --git a/src/basic/procfs-util.c b/src/basic/procfs-util.c new file mode 100644 index 0000000000..9bb42cc7ba --- /dev/null +++ b/src/basic/procfs-util.c @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include <errno.h> + +#include "alloc-util.h" +#include "fileio.h" +#include "parse-util.h" +#include "process-util.h" +#include "procfs-util.h" +#include "stdio-util.h" +#include "string-util.h" + +int procfs_tasks_get_limit(uint64_t *ret) { + _cleanup_free_ char *value = NULL; + uint64_t pid_max, threads_max; + int r; + + assert(ret); + + /* So there are two sysctl files that control the system limit of processes: + * + * 1. kernel.threads-max: this is probably the sysctl that makes more sense, as it directly puts a limit on + * concurrent tasks. + * + * 2. kernel.pid_max: this limits the numeric range PIDs can take, and thus indirectly also limits the number + * of concurrent threads. AFAICS it's primarily a compatibility concept: some crappy old code used a signed + * 16bit type for PIDs, hence the kernel provides a way to ensure the PIDs never go beyond INT16_MAX by + * default. + * + * By default #2 is set to much lower values than #1, hence the limit people come into contact with first, as + * it's the lowest boundary they need to bump when they want higher number of processes. + * + * Also note the weird definition of #2: PIDs assigned will be kept below this value, which means the number of + * tasks that can be created is one lower, as PID 0 is not a valid process ID. */ + + r = read_one_line_file("/proc/sys/kernel/pid_max", &value); + if (r < 0) + return r; + + r = safe_atou64(value, &pid_max); + if (r < 0) + return r; + + value = mfree(value); + r = read_one_line_file("/proc/sys/kernel/threads-max", &value); + if (r < 0) + return r; + + r = safe_atou64(value, &threads_max); + if (r < 0) + return r; + + /* Subtract one from pid_max, since PID 0 is not a valid PID */ + *ret = MIN(pid_max-1, threads_max); + return 0; +} + +int procfs_tasks_set_limit(uint64_t limit) { + char buffer[DECIMAL_STR_MAX(uint64_t)+1]; + _cleanup_free_ char *value = NULL; + uint64_t pid_max; + int r; + + if (limit == 0) /* This makes no sense, we are userspace and hence count as tasks too, and we want to live, + * hence the limit conceptually has to be above 0. Also, most likely if anyone asks for a zero + * limit he/she probably means "no limit", hence let's better refuse this to avoid + * confusion. */ + return -EINVAL; + + /* The Linux kernel doesn't allow this value to go below 20, hence don't allow this either, higher values than + * TASKS_MAX are not accepted by the pid_max sysctl. We'll treat anything this high as "unbounded" and hence + * set it to the maximum. */ + limit = CLAMP(limit, 20U, TASKS_MAX); + + r = read_one_line_file("/proc/sys/kernel/pid_max", &value); + if (r < 0) + return r; + r = safe_atou64(value, &pid_max); + if (r < 0) + return r; + + /* As pid_max is about the numeric pid_t range we'll bump it if necessary, but only ever increase it, never + * decrease it, as threads-max is the much more relevant sysctl. */ + if (limit > pid_max-1) { + sprintf(buffer, "%" PRIu64, limit+1); /* Add one, since PID 0 is not a valid PID */ + r = write_string_file("/proc/sys/kernel/pid_max", buffer, WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) + return r; + } + + sprintf(buffer, "%" PRIu64, limit); + r = write_string_file("/proc/sys/kernel/threads-max", buffer, WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) { + uint64_t threads_max; + + /* Hmm, we couldn't write this? If so, maybe it was already set properly? In that case let's not + * generate an error */ + + value = mfree(value); + if (read_one_line_file("/proc/sys/kernel/threads-max", &value) < 0) + return r; /* return original error */ + + if (safe_atou64(value, &threads_max) < 0) + return r; /* return original error */ + + if (MIN(pid_max-1, threads_max) != limit) + return r; /* return original error */ + + /* Yay! Value set already matches what we were trying to set, hence consider this a success. */ + } + + return 0; +} + +int procfs_tasks_get_current(uint64_t *ret) { + _cleanup_free_ char *value = NULL; + const char *p, *nr; + size_t n; + int r; + + assert(ret); + + r = read_one_line_file("/proc/loadavg", &value); + if (r < 0) + return r; + + /* Look for the second part of the fourth field, which is separated by a slash from the first part. None of the + * earlier fields use a slash, hence let's use this to find the right spot. */ + p = strchr(value, '/'); + if (!p) + return -EINVAL; + + p++; + n = strspn(p, DIGITS); + nr = strndupa(p, n); + + return safe_atou64(nr, ret); +} diff --git a/src/basic/procfs-util.h b/src/basic/procfs-util.h new file mode 100644 index 0000000000..7466acd7f3 --- /dev/null +++ b/src/basic/procfs-util.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include <inttypes.h> + +int procfs_tasks_get_limit(uint64_t *ret); +int procfs_tasks_set_limit(uint64_t limit); +int procfs_tasks_get_current(uint64_t *ret); diff --git a/src/basic/random-util.c b/src/basic/random-util.c index 1bc8000896..7457815fa2 100644 --- a/src/basic/random-util.c +++ b/src/basic/random-util.c @@ -21,11 +21,12 @@ #include <elf.h> #include <errno.h> #include <fcntl.h> +#include <linux/random.h> #include <stdbool.h> +#include <stdint.h> #include <stdlib.h> +#include <string.h> #include <sys/time.h> -#include <linux/random.h> -#include <stdint.h> #if HAVE_SYS_AUXV_H # include <sys/auxv.h> diff --git a/src/basic/raw-clone.h b/src/basic/raw-clone.h index f01b73a8fe..8c95380305 100644 --- a/src/basic/raw-clone.h +++ b/src/basic/raw-clone.h @@ -30,28 +30,27 @@ * raw_clone() - uses clone to create a new process with clone flags * @flags: Flags to pass to the clone system call * - * Uses the clone system call to create a new process with the cloning - * flags and termination signal passed in the flags parameter. Opposed - * to glibc's clone funtion, using this function does not set up a - * separate stack for the child, but relies on copy-on-write semantics - * on the one stack at a common virtual address, just as fork does. + * Uses the clone system call to create a new process with the cloning flags and termination signal passed in the flags + * parameter. Opposed to glibc's clone funtion, using this function does not set up a separate stack for the child, but + * relies on copy-on-write semantics on the one stack at a common virtual address, just as fork does. * - * To obtain copy-on-write semantics, flags must not contain CLONE_VM, - * and thus CLONE_THREAD and CLONE_SIGHAND (which require CLONE_VM) are - * not usabale. - * Additionally, as this function does not pass the ptid, newtls and ctid - * parameters to the kernel, flags must not contain CLONE_PARENT_SETTID, - * CLONE_CHILD_SETTID, CLONE_CHILD_CLEARTID or CLONE_SETTLS. + * To obtain copy-on-write semantics, flags must not contain CLONE_VM, and thus CLONE_THREAD and CLONE_SIGHAND (which + * require CLONE_VM) are not usable. + * + * Additionally, as this function does not pass the ptid, newtls and ctid parameters to the kernel, flags must not + * contain CLONE_PARENT_SETTID, CLONE_CHILD_SETTID, CLONE_CHILD_CLEARTID or CLONE_SETTLS. * * Returns: 0 in the child process and the child process id in the parent. */ -static inline int raw_clone(unsigned long flags) { +static inline pid_t raw_clone(unsigned long flags) { + pid_t ret; + assert((flags & (CLONE_VM|CLONE_PARENT_SETTID|CLONE_CHILD_SETTID| CLONE_CHILD_CLEARTID|CLONE_SETTLS)) == 0); #if defined(__s390x__) || defined(__s390__) || defined(__CRIS__) /* On s390/s390x and cris the order of the first and second arguments * of the raw clone() system call is reversed. */ - return (int) syscall(__NR_clone, NULL, flags); + ret = (pid_t) syscall(__NR_clone, NULL, flags); #elif defined(__sparc__) && defined(__arch64__) { /** @@ -60,8 +59,8 @@ static inline int raw_clone(unsigned long flags) { * %o1. Inline assembly is needed to get the flag returned * in %o1. */ - int in_child; - int child_pid; + int in_child, child_pid; + asm volatile("mov %2, %%g1\n\t" "mov %3, %%o0\n\t" "mov 0 , %%o1\n\t" @@ -71,12 +70,15 @@ static inline int raw_clone(unsigned long flags) { "=r"(in_child), "=r"(child_pid) : "i"(__NR_clone), "r"(flags) : "%o1", "%o0", "%g1" ); - if (in_child) - return 0; - else - return child_pid; + + ret = in_child ? 0 : child_pid; } #else - return (int) syscall(__NR_clone, flags, NULL); + ret = (pid_t) syscall(__NR_clone, flags, NULL); #endif + + if (ret == 0) + reset_cached_pid(); + + return ret; } diff --git a/src/basic/rm-rf.h b/src/basic/rm-rf.h index 1127e326b2..ad63e9be40 100644 --- a/src/basic/rm-rf.h +++ b/src/basic/rm-rf.h @@ -22,6 +22,8 @@ #include <sys/stat.h> +#include "util.h" + typedef enum RemoveFlags { REMOVE_ONLY_DIRECTORIES = 1, REMOVE_ROOT = 2, @@ -34,6 +36,7 @@ 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) { + PROTECT_ERRNO; (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL); free(p); } diff --git a/src/basic/securebits-util.c b/src/basic/securebits-util.c index b5f6418a6c..441d386f9e 100644 --- a/src/basic/securebits-util.c +++ b/src/basic/securebits-util.c @@ -19,6 +19,7 @@ ***/ #include <errno.h> +#include <stdio.h> #include "alloc-util.h" #include "extract-word.h" diff --git a/src/basic/securebits-util.h b/src/basic/securebits-util.h index aaa192f0a5..069d215488 100644 --- a/src/basic/securebits-util.h +++ b/src/basic/securebits-util.h @@ -24,6 +24,14 @@ int secure_bits_to_string_alloc(int i, char **s); int secure_bits_from_string(const char *s); + static inline bool secure_bits_is_valid(int i) { return ((SECURE_ALL_BITS | SECURE_ALL_LOCKS) & i) == i; } + +static inline int secure_bits_to_string_alloc_with_check(int n, char **s) { + if (!secure_bits_is_valid(n)) + return -EINVAL; + + return secure_bits_to_string_alloc(n, s); +} diff --git a/src/basic/signal-util.h b/src/basic/signal-util.h index 76b239b1fc..f6c3396ebe 100644 --- a/src/basic/signal-util.h +++ b/src/basic/signal-util.h @@ -55,3 +55,10 @@ static inline void block_signals_reset(sigset_t *ss) { static inline bool SIGNAL_VALID(int signo) { return signo > 0 && signo < _NSIG; } + +static inline const char* signal_to_string_with_check(int n) { + if (!SIGNAL_VALID(n)) + return NULL; + + return signal_to_string(n); +} diff --git a/src/basic/smack-util.c b/src/basic/smack-util.c index e0f1c9f1c7..f0018f013f 100644 --- a/src/basic/smack-util.c +++ b/src/basic/smack-util.c @@ -136,7 +136,7 @@ int mac_smack_apply_pid(pid_t pid, const char *label) { int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { struct stat st; - int r = 0; + int r; assert(path); diff --git a/src/basic/socket-label.c b/src/basic/socket-label.c index 20be406371..97f3ebe2af 100644 --- a/src/basic/socket-label.c +++ b/src/basic/socket-label.c @@ -29,6 +29,7 @@ #include "alloc-util.h" #include "fd-util.h" +#include "fs-util.h" #include "log.h" #include "macro.h" #include "missing.h" @@ -51,6 +52,7 @@ int socket_address_listen( const char *label) { _cleanup_close_ int fd = -1; + const char *p; int r, one; assert(a); @@ -112,19 +114,23 @@ int socket_address_listen( if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) return -errno; - if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) { + p = socket_address_get_path(a); + if (p) { /* Create parents */ - (void) mkdir_parents_label(a->sockaddr.un.sun_path, directory_mode); + (void) mkdir_parents_label(p, directory_mode); /* Enforce the right access mode for the socket */ 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) + + if (unlink(p) < 0) + return r; /* didn't work, return original error */ + + r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size); + } + if (r < 0) return r; } } else { @@ -136,6 +142,11 @@ int socket_address_listen( if (listen(fd, backlog) < 0) return -errno; + /* Let's trigger an inotify event on the socket node, so that anyone waiting for this socket to be connectable + * gets notified */ + if (p) + (void) touch(p); + r = fd; fd = -1; diff --git a/src/basic/socket-protocol-list.c b/src/basic/socket-protocol-list.c new file mode 100644 index 0000000000..9ab93d1c7e --- /dev/null +++ b/src/basic/socket-protocol-list.c @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/*** + 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 <netinet/in.h> +#include <string.h> + +#include "socket-protocol-list.h" +#include "macro.h" + +static const struct socket_protocol_name* lookup_socket_protocol(register const char *str, register GPERF_LEN_TYPE len); + +#include "socket-protocol-from-name.h" +#include "socket-protocol-to-name.h" + +const char *socket_protocol_to_name(int id) { + + if (id < 0) + return NULL; + + if (id >= (int) ELEMENTSOF(socket_protocol_names)) + return NULL; + + return socket_protocol_names[id]; +} + +int socket_protocol_from_name(const char *name) { + const struct socket_protocol_name *sc; + + assert(name); + + sc = lookup_socket_protocol(name, strlen(name)); + if (!sc) + return 0; + + return sc->id; +} + +int socket_protocol_max(void) { + return ELEMENTSOF(socket_protocol_names); +} diff --git a/src/basic/socket-protocol-list.h b/src/basic/socket-protocol-list.h new file mode 100644 index 0000000000..12fd053382 --- /dev/null +++ b/src/basic/socket-protocol-list.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#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/>. +***/ + +const char *socket_protocol_to_name(int id); +int socket_protocol_from_name(const char *name); + +int socket_protocol_max(void); diff --git a/src/basic/socket-protocol-to-name.awk b/src/basic/socket-protocol-to-name.awk new file mode 100644 index 0000000000..4848a7631a --- /dev/null +++ b/src/basic/socket-protocol-to-name.awk @@ -0,0 +1,9 @@ +BEGIN{ + print "static const char* const socket_protocol_names[] = { " +} +!/HOPOPTS/ { + printf " [IPPROTO_%s] = \"%s\",\n", $1, tolower($1) +} +END{ + print "};" +} diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index a458fc2902..2c70cade14 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -41,6 +41,7 @@ #include "missing.h" #include "parse-util.h" #include "path-util.h" +#include "process-util.h" #include "socket-util.h" #include "string-table.h" #include "string-util.h" @@ -55,9 +56,19 @@ # define IDN_FLAGS 0 #endif +static const char* const socket_address_type_table[] = { + [SOCK_STREAM] = "Stream", + [SOCK_DGRAM] = "Datagram", + [SOCK_RAW] = "Raw", + [SOCK_RDM] = "ReliableDatagram", + [SOCK_SEQPACKET] = "SequentialPacket", + [SOCK_DCCP] = "DatagramCongestionControl", +}; + +DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int); + int socket_address_parse(SocketAddress *a, const char *s) { char *e, *n; - unsigned u; int r; assert(a); @@ -67,6 +78,8 @@ int socket_address_parse(SocketAddress *a, const char *s) { a->type = SOCK_STREAM; if (*s == '[') { + uint16_t port; + /* IPv6 in [x:.....:z]:p notation */ e = strchr(s+1, ']'); @@ -84,15 +97,12 @@ int socket_address_parse(SocketAddress *a, const char *s) { return -EINVAL; e++; - r = safe_atou(e, &u); + r = parse_ip_port(e, &port); if (r < 0) return r; - if (u <= 0 || u > 0xFFFF) - return -EINVAL; - a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); + a->sockaddr.in6.sin6_port = htobe16(port); a->size = sizeof(struct sockaddr_in6); } else if (*s == '/') { @@ -123,12 +133,13 @@ int socket_address_parse(SocketAddress *a, const char *s) { } else if (startswith(s, "vsock:")) { /* AF_VSOCK socket in vsock:cid:port notation */ const char *cid_start = s + STRLEN("vsock:"); + unsigned port; e = strchr(cid_start, ':'); if (!e) return -EINVAL; - r = safe_atou(e+1, &u); + r = safe_atou(e+1, &port); if (r < 0) return r; @@ -141,19 +152,18 @@ int socket_address_parse(SocketAddress *a, const char *s) { a->sockaddr.vm.svm_cid = VMADDR_CID_ANY; a->sockaddr.vm.svm_family = AF_VSOCK; - a->sockaddr.vm.svm_port = u; + a->sockaddr.vm.svm_port = port; a->size = sizeof(struct sockaddr_vm); } else { + uint16_t port; + e = strchr(s, ':'); if (e) { - r = safe_atou(e+1, &u); + r = parse_ip_port(e + 1, &port); if (r < 0) return r; - if (u <= 0 || u > 0xFFFF) - return -EINVAL; - n = strndupa(s, e-s); /* IPv4 in w.x.y.z:p notation? */ @@ -164,7 +174,7 @@ int socket_address_parse(SocketAddress *a, const char *s) { if (r > 0) { /* Gotcha, it's a traditional IPv4 address */ a->sockaddr.in.sin_family = AF_INET; - a->sockaddr.in.sin_port = htobe16((uint16_t)u); + a->sockaddr.in.sin_port = htobe16(port); a->size = sizeof(struct sockaddr_in); } else { unsigned idx; @@ -178,7 +188,7 @@ int socket_address_parse(SocketAddress *a, const char *s) { return -EINVAL; a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); + a->sockaddr.in6.sin6_port = htobe16(port); a->sockaddr.in6.sin6_scope_id = idx; a->sockaddr.in6.sin6_addr = in6addr_any; a->size = sizeof(struct sockaddr_in6); @@ -186,21 +196,18 @@ int socket_address_parse(SocketAddress *a, const char *s) { } else { /* Just a port */ - r = safe_atou(s, &u); + r = parse_ip_port(s, &port); if (r < 0) return r; - if (u <= 0 || u > 0xFFFF) - return -EINVAL; - if (socket_ipv6_is_supported()) { a->sockaddr.in6.sin6_family = AF_INET6; - a->sockaddr.in6.sin6_port = htobe16((uint16_t)u); + a->sockaddr.in6.sin6_port = htobe16(port); a->sockaddr.in6.sin6_addr = in6addr_any; a->size = sizeof(struct sockaddr_in6); } else { a->sockaddr.in.sin_family = AF_INET; - a->sockaddr.in.sin_port = htobe16((uint16_t)u); + a->sockaddr.in.sin_port = htobe16(port); a->sockaddr.in.sin_addr.s_addr = INADDR_ANY; a->size = sizeof(struct sockaddr_in); } @@ -528,22 +535,25 @@ bool socket_address_matches_fd(const SocketAddress *a, int fd) { return socket_address_equal(a, &b); } -int sockaddr_port(const struct sockaddr *_sa, unsigned *port) { +int sockaddr_port(const struct sockaddr *_sa, unsigned *ret_port) { union sockaddr_union *sa = (union sockaddr_union*) _sa; + /* Note, this returns the port as 'unsigned' rather than 'uint16_t', as AF_VSOCK knows larger ports */ + assert(sa); switch (sa->sa.sa_family) { + case AF_INET: - *port = be16toh(sa->in.sin_port); + *ret_port = be16toh(sa->in.sin_port); return 0; case AF_INET6: - *port = be16toh(sa->in6.sin6_port); + *ret_port = be16toh(sa->in6.sin6_port); return 0; case AF_VSOCK: - *port = sa->vm.svm_port; + *ret_port = sa->vm.svm_port; return 0; default: @@ -807,6 +817,18 @@ static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIN DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only); +SocketAddressBindIPv6Only parse_socket_address_bind_ipv6_only_or_bool(const char *n) { + int r; + + r = parse_boolean(n); + if (r > 0) + return SOCKET_ADDRESS_IPV6_ONLY; + if (r == 0) + return SOCKET_ADDRESS_BOTH; + + return socket_address_bind_ipv6_only_from_string(n); +} + bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) { assert(a); assert(b); @@ -941,58 +963,80 @@ int getpeercred(int fd, struct ucred *ucred) { if (n != sizeof(struct ucred)) return -EIO; - /* Check if the data is actually useful and not suppressed due - * to namespacing issues */ - if (u.pid <= 0) - return -ENODATA; - if (u.uid == UID_INVALID) - return -ENODATA; - if (u.gid == GID_INVALID) + /* Check if the data is actually useful and not suppressed due to namespacing issues */ + if (!pid_is_valid(u.pid)) return -ENODATA; + /* Note that we don't check UID/GID here, as namespace translation works differently there: instead of + * receiving in "invalid" user/group we get the overflow UID/GID. */ + *ucred = u; return 0; } int getpeersec(int fd, char **ret) { + _cleanup_free_ char *s = NULL; socklen_t n = 64; - char *s; - int r; assert(fd >= 0); assert(ret); - s = new0(char, n); - if (!s) - return -ENOMEM; + for (;;) { + s = new0(char, n+1); + if (!s) + return -ENOMEM; - r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n); - if (r < 0) { - free(s); + if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0) + break; if (errno != ERANGE) return -errno; - s = new0(char, n); - if (!s) - return -ENOMEM; - - r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n); - if (r < 0) { - free(s); - return -errno; - } + s = mfree(s); } - if (isempty(s)) { - free(s); + if (isempty(s)) return -EOPNOTSUPP; - } *ret = s; + s = NULL; + return 0; } +int getpeergroups(int fd, gid_t **ret) { + socklen_t n = sizeof(gid_t) * 64; + _cleanup_free_ gid_t *d = NULL; + + assert(fd); + assert(ret); + + for (;;) { + d = malloc(n); + if (!d) + return -ENOMEM; + + if (getsockopt(fd, SOL_SOCKET, SO_PEERGROUPS, d, &n) >= 0) + break; + + if (errno != ERANGE) + return -errno; + + d = mfree(d); + } + + assert_se(n % sizeof(gid_t) == 0); + n /= sizeof(gid_t); + + if ((socklen_t) (int) n != n) + return -E2BIG; + + *ret = d; + d = NULL; + + return (int) n; +} + int send_one_fd_sa( int transport_fd, int fd, diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index 272e74b0cc..49c937aef5 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -36,16 +36,26 @@ #include "util.h" union sockaddr_union { + /* The minimal, abstract version */ struct sockaddr sa; + + /* The libc provided version that allocates "enough room" for every protocol */ + struct sockaddr_storage storage; + + /* Protoctol-specific implementations */ struct sockaddr_in in; struct sockaddr_in6 in6; struct sockaddr_un un; struct sockaddr_nl nl; - struct sockaddr_storage storage; struct sockaddr_ll ll; struct sockaddr_vm vm; + /* Ensure there is enough space to store Infiniband addresses */ uint8_t ll_buffer[offsetof(struct sockaddr_ll, sll_addr) + CONST_MAX(ETH_ALEN, INFINIBAND_ALEN)]; + + /* Ensure there is enough space after the AF_UNIX sun_path for one more NUL byte, just to be sure that the path + * component is always followed by at least one NUL byte. */ + uint8_t un_buffer[sizeof(struct sockaddr_un) + 1]; }; typedef struct SocketAddress { @@ -72,6 +82,9 @@ typedef enum SocketAddressBindIPv6Only { #define socket_address_family(a) ((a)->sockaddr.sa.sa_family) +const char* socket_address_type_to_string(int t) _const_; +int socket_address_type_from_string(const char *s) _pure_; + int socket_address_parse(SocketAddress *a, const char *s); int socket_address_parse_and_warn(SocketAddress *a, const char *s); int socket_address_parse_netlink(SocketAddress *a, const char *s); @@ -117,6 +130,7 @@ int getnameinfo_pretty(int fd, char **ret); const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_; SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_; +SocketAddressBindIPv6Only parse_socket_address_bind_ipv6_only_or_bool(const char *s); int netlink_family_to_string_alloc(int b, char **s); int netlink_family_from_string(const char *s) _pure_; @@ -134,6 +148,7 @@ bool address_label_valid(const char *p); int getpeercred(int fd, struct ucred *ucred); int getpeersec(int fd, char **ret); +int getpeergroups(int fd, gid_t **ret); int send_one_fd_sa(int transport_fd, int fd, diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index 96fc8b3787..3a54103f1b 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -21,10 +21,11 @@ #include <dirent.h> #include <errno.h> #include <fcntl.h> -#include <sys/stat.h> -#include <sys/types.h> #include <linux/magic.h> +#include <sched.h> +#include <sys/stat.h> #include <sys/statvfs.h> +#include <sys/types.h> #include <unistd.h> #include "dirent-util.h" diff --git a/src/basic/string-util.c b/src/basic/string-util.c index 7e2f596edc..9f2c01d864 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -30,6 +30,7 @@ #include "gunicode.h" #include "macro.h" #include "string-util.h" +#include "terminal-util.h" #include "utf8.h" #include "util.h" @@ -648,7 +649,17 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin return ret; } -char *strip_tab_ansi(char **ibuf, size_t *_isz) { +static void advance_offsets(ssize_t diff, size_t offsets[2], size_t shift[2], size_t size) { + if (!offsets) + return; + + if ((size_t) diff < offsets[0]) + shift[0] += size; + if ((size_t) diff < offsets[1]) + shift[1] += size; +} + +char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) { const char *i, *begin = NULL; enum { STATE_OTHER, @@ -656,7 +667,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) { STATE_BRACKET } state = STATE_OTHER; char *obuf = NULL; - size_t osz = 0, isz; + size_t osz = 0, isz, shift[2] = {}; FILE *f; assert(ibuf); @@ -684,15 +695,18 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) { break; else if (*i == '\x1B') state = STATE_ESCAPE; - else if (*i == '\t') + else if (*i == '\t') { fputs(" ", f); - else + advance_offsets(i - *ibuf, highlight, shift, 7); + } else fputc(*i, f); + break; case STATE_ESCAPE: if (i >= *ibuf + isz) { /* EOT */ fputc('\x1B', f); + advance_offsets(i - *ibuf, highlight, shift, 1); break; } else if (*i == '[') { state = STATE_BRACKET; @@ -700,6 +714,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) { } else { fputc('\x1B', f); fputc(*i, f); + advance_offsets(i - *ibuf, highlight, shift, 1); state = STATE_OTHER; } @@ -711,6 +726,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) { (!(*i >= '0' && *i <= '9') && !IN_SET(*i, ';', 'm'))) { fputc('\x1B', f); fputc('[', f); + advance_offsets(i - *ibuf, highlight, shift, 2); state = STATE_OTHER; i = begin-1; } else if (*i == 'm') @@ -732,6 +748,11 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) { if (_isz) *_isz = osz; + if (highlight) { + highlight[0] += shift[0]; + highlight[1] += shift[1]; + } + return obuf; } diff --git a/src/basic/string-util.h b/src/basic/string-util.h index 09a737ad37..08eda4fce0 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -52,15 +52,15 @@ static inline bool streq_ptr(const char *a, const char *b) { } static inline const char* strempty(const char *s) { - return s ? s : ""; + return s ?: ""; } static inline const char* strnull(const char *s) { - return s ? s : "(null)"; + return s ?: "(null)"; } static inline const char *strna(const char *s) { - return s ? s : "n/a"; + return s ?: "n/a"; } static inline bool isempty(const char *p) { @@ -177,7 +177,7 @@ char* strshorten(char *s, size_t l); char *strreplace(const char *text, const char *old_string, const char *new_string); -char *strip_tab_ansi(char **p, size_t *l); +char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]); char *strextend_with_separator(char **x, const char *separator, ...) _sentinel_; diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index 48ee799ad4..42336e8fdf 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -875,31 +875,30 @@ bool on_tty(void) { } int make_stdio(int fd) { - int r, s, t; + int r = 0; assert(fd >= 0); - r = dup2(fd, STDIN_FILENO); - s = dup2(fd, STDOUT_FILENO); - t = dup2(fd, STDERR_FILENO); + if (dup2(fd, STDIN_FILENO) < 0 && r >= 0) + r = -errno; + if (dup2(fd, STDOUT_FILENO) < 0 && r >= 0) + r = -errno; + if (dup2(fd, STDERR_FILENO) < 0 && r >= 0) + r = -errno; if (fd >= 3) safe_close(fd); - if (r < 0 || s < 0 || t < 0) - return -errno; - - /* Explicitly unset O_CLOEXEC, since if fd was < 3, then - * dup2() was a NOP and the bit hence possibly set. */ + /* Explicitly unset O_CLOEXEC, since if fd was < 3, then dup2() was a NOP and the bit hence possibly set. */ stdio_unset_cloexec(); - return 0; + return r; } int make_null_stdio(void) { int null_fd; - null_fd = open("/dev/null", O_RDWR|O_NOCTTY); + null_fd = open("/dev/null", O_RDWR|O_NOCTTY|O_CLOEXEC); if (null_fd < 0) return -errno; @@ -1094,7 +1093,6 @@ int ptsname_namespace(int pty, char **ret) { int openpt_in_namespace(pid_t pid, int flags) { _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1; _cleanup_close_pair_ int pair[2] = { -1, -1 }; - siginfo_t si; pid_t child; int r; @@ -1107,11 +1105,10 @@ int openpt_in_namespace(pid_t pid, int flags) { if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) return -errno; - child = fork(); - if (child < 0) - return -errno; - - if (child == 0) { + r = safe_fork("(sd-openpt)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child); + if (r < 0) + return r; + if (r == 0) { int master; pair[0] = safe_close(pair[0]); @@ -1135,10 +1132,10 @@ int openpt_in_namespace(pid_t pid, int flags) { pair[1] = safe_close(pair[1]); - r = wait_for_terminate(child, &si); + r = wait_for_terminate_and_check("(sd-openpt)", child, 0); if (r < 0) return r; - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + if (r != EXIT_SUCCESS) return -EIO; return receive_one_fd(pair[0], 0); @@ -1147,7 +1144,6 @@ int openpt_in_namespace(pid_t pid, int flags) { int open_terminal_in_namespace(pid_t pid, const char *name, int mode) { _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1; _cleanup_close_pair_ int pair[2] = { -1, -1 }; - siginfo_t si; pid_t child; int r; @@ -1158,11 +1154,10 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) { if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) return -errno; - child = fork(); - if (child < 0) - return -errno; - - if (child == 0) { + r = safe_fork("(sd-terminal)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child); + if (r < 0) + return r; + if (r == 0) { int master; pair[0] = safe_close(pair[0]); @@ -1183,10 +1178,10 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) { pair[1] = safe_close(pair[1]); - r = wait_for_terminate(child, &si); + r = wait_for_terminate_and_check("(sd-terminal)", child, 0); if (r < 0) return r; - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + if (r != EXIT_SUCCESS) return -EIO; return receive_one_fd(pair[0], 0); diff --git a/src/basic/time-util.c b/src/basic/time-util.c index d56576ddbe..4a341e208f 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -38,6 +38,7 @@ #include "macro.h" #include "parse-util.h" #include "path-util.h" +#include "process-util.h" #include "string-util.h" #include "strv.h" #include "time-util.h" @@ -887,7 +888,6 @@ int parse_timestamp(const char *t, usec_t *usec) { char *last_space, *tz = NULL; ParseTimestampResult *shared, tmp; int r; - pid_t pid; last_space = strrchr(t, ' '); if (last_space != NULL && timezone_is_valid(last_space + 1)) @@ -900,15 +900,12 @@ int parse_timestamp(const char *t, usec_t *usec) { if (shared == MAP_FAILED) return negative_errno(); - pid = fork(); - - if (pid == -1) { - int fork_errno = errno; + r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL); + if (r < 0) { (void) munmap(shared, sizeof *shared); - return -fork_errno; + return r; } - - if (pid == 0) { + if (r == 0) { bool with_tz = true; if (setenv("TZ", tz, 1) != 0) { @@ -931,12 +928,6 @@ int parse_timestamp(const char *t, usec_t *usec) { _exit(EXIT_SUCCESS); } - r = wait_for_terminate(pid, NULL); - if (r < 0) { - (void) munmap(shared, sizeof *shared); - return r; - } - tmp = *shared; if (munmap(shared, sizeof *shared) != 0) return negative_errno(); diff --git a/src/basic/unaligned.h b/src/basic/unaligned.h index 201b3d227e..73302b4239 100644 --- a/src/basic/unaligned.h +++ b/src/basic/unaligned.h @@ -26,89 +26,77 @@ /* BE */ static inline uint16_t unaligned_read_be16(const void *_u) { - const uint8_t *u = _u; + const struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u; - return (((uint16_t) u[0]) << 8) | - ((uint16_t) u[1]); + return be16toh(u->x); } static inline uint32_t unaligned_read_be32(const void *_u) { - const uint8_t *u = _u; + const struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u; - return (((uint32_t) unaligned_read_be16(u)) << 16) | - ((uint32_t) unaligned_read_be16(u + 2)); + return be32toh(u->x); } static inline uint64_t unaligned_read_be64(const void *_u) { - const uint8_t *u = _u; + const struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u; - return (((uint64_t) unaligned_read_be32(u)) << 32) | - ((uint64_t) unaligned_read_be32(u + 4)); + return be64toh(u->x); } static inline void unaligned_write_be16(void *_u, uint16_t a) { - uint8_t *u = _u; + struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u; - u[0] = (uint8_t) (a >> 8); - u[1] = (uint8_t) a; + u->x = be16toh(a); } static inline void unaligned_write_be32(void *_u, uint32_t a) { - uint8_t *u = _u; + struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u; - unaligned_write_be16(u, (uint16_t) (a >> 16)); - unaligned_write_be16(u + 2, (uint16_t) a); + u->x = be32toh(a); } static inline void unaligned_write_be64(void *_u, uint64_t a) { - uint8_t *u = _u; + struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u; - unaligned_write_be32(u, (uint32_t) (a >> 32)); - unaligned_write_be32(u + 4, (uint32_t) a); + u->x = be64toh(a); } /* LE */ static inline uint16_t unaligned_read_le16(const void *_u) { - const uint8_t *u = _u; + const struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u; - return (((uint16_t) u[1]) << 8) | - ((uint16_t) u[0]); + return le16toh(u->x); } static inline uint32_t unaligned_read_le32(const void *_u) { - const uint8_t *u = _u; + const struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u; - return (((uint32_t) unaligned_read_le16(u + 2)) << 16) | - ((uint32_t) unaligned_read_le16(u)); + return le32toh(u->x); } static inline uint64_t unaligned_read_le64(const void *_u) { - const uint8_t *u = _u; + const struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u; - return (((uint64_t) unaligned_read_le32(u + 4)) << 32) | - ((uint64_t) unaligned_read_le32(u)); + return le64toh(u->x); } static inline void unaligned_write_le16(void *_u, uint16_t a) { - uint8_t *u = _u; + struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u; - u[0] = (uint8_t) a; - u[1] = (uint8_t) (a >> 8); + u->x = le16toh(a); } static inline void unaligned_write_le32(void *_u, uint32_t a) { - uint8_t *u = _u; + struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u; - unaligned_write_le16(u, (uint16_t) a); - unaligned_write_le16(u + 2, (uint16_t) (a >> 16)); + u->x = le32toh(a); } static inline void unaligned_write_le64(void *_u, uint64_t a) { - uint8_t *u = _u; + struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u; - unaligned_write_le32(u, (uint32_t) a); - unaligned_write_le32(u + 4, (uint32_t) (a >> 32)); + u->x = le64toh(a); } #if __BYTE_ORDER == __BIG_ENDIAN diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c index 403f288b57..0fa0472ee1 100644 --- a/src/basic/unit-name.c +++ b/src/basic/unit-name.c @@ -28,6 +28,7 @@ #include "glob-util.h" #include "hexdecoct.h" #include "path-util.h" +#include "special.h" #include "string-util.h" #include "strv.h" #include "unit-name.h" @@ -673,7 +674,7 @@ int slice_build_parent_slice(const char *slice, char **ret) { if (!slice_name_is_valid(slice)) return -EINVAL; - if (streq(slice, "-.slice")) { + if (streq(slice, SPECIAL_ROOT_SLICE)) { *ret = NULL; return 0; } @@ -686,7 +687,7 @@ int slice_build_parent_slice(const char *slice, char **ret) { if (dash) strcpy(dash, ".slice"); else { - r = free_and_strdup(&s, "-.slice"); + r = free_and_strdup(&s, SPECIAL_ROOT_SLICE); if (r < 0) { free(s); return r; @@ -710,7 +711,7 @@ int slice_build_subslice(const char *slice, const char*name, char **ret) { if (!unit_prefix_is_valid(name)) return -EINVAL; - if (streq(slice, "-.slice")) + if (streq(slice, SPECIAL_ROOT_SLICE)) subslice = strappend(name, ".slice"); else { char *e; @@ -735,7 +736,7 @@ bool slice_name_is_valid(const char *name) { if (!unit_name_is_valid(name, UNIT_NAME_PLAIN)) return false; - if (streq(name, "-.slice")) + if (streq(name, SPECIAL_ROOT_SLICE)) return true; e = endswith(name, ".slice"); diff --git a/src/basic/user-util.c b/src/basic/user-util.c index abb0b76866..17a9b5a8f1 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -137,7 +137,8 @@ int get_user_creds( return 0; } - if (STR_IN_SET(*username, NOBODY_USER_NAME, "65534")) { + if (synthesize_nobody() && + STR_IN_SET(*username, NOBODY_USER_NAME, "65534")) { *username = NOBODY_USER_NAME; if (uid) @@ -243,7 +244,8 @@ int get_group_creds(const char **groupname, gid_t *gid) { return 0; } - if (STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534")) { + if (synthesize_nobody() && + STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534")) { *groupname = NOBODY_GROUP_NAME; if (gid) @@ -283,7 +285,8 @@ char* uid_to_name(uid_t uid) { /* Shortcut things to avoid NSS lookups */ if (uid == 0) return strdup("root"); - if (uid == UID_NOBODY) + if (synthesize_nobody() && + uid == UID_NOBODY) return strdup(NOBODY_USER_NAME); if (uid_is_valid(uid)) { @@ -323,7 +326,8 @@ char* gid_to_name(gid_t gid) { if (gid == 0) return strdup("root"); - if (gid == GID_NOBODY) + if (synthesize_nobody() && + gid == GID_NOBODY) return strdup(NOBODY_GROUP_NAME); if (gid_is_valid(gid)) { @@ -358,8 +362,9 @@ char* gid_to_name(gid_t gid) { } int in_gid(gid_t gid) { + long ngroups_max; gid_t *gids; - int ngroups_max, r, i; + int r, i; if (getgid() == gid) return 1; @@ -373,7 +378,7 @@ int in_gid(gid_t gid) { ngroups_max = sysconf(_SC_NGROUPS_MAX); assert(ngroups_max > 0); - gids = alloca(sizeof(gid_t) * ngroups_max); + gids = newa(gid_t, ngroups_max); r = getgroups(ngroups_max, gids); if (r < 0) @@ -426,7 +431,8 @@ int get_home_dir(char **_h) { *_h = h; return 0; } - if (u == UID_NOBODY) { + if (synthesize_nobody() && + u == UID_NOBODY) { h = strdup("/"); if (!h) return -ENOMEM; @@ -481,7 +487,8 @@ int get_shell(char **_s) { *_s = s; return 0; } - if (u == UID_NOBODY) { + if (synthesize_nobody() && + u == UID_NOBODY) { s = strdup("/sbin/nologin"); if (!s) return -ENOMEM; @@ -689,3 +696,24 @@ int maybe_setgroups(size_t size, const gid_t *list) { return 0; } + +bool synthesize_nobody(void) { + +#ifdef NOLEGACY + return true; +#else + /* Returns true when we shall synthesize the "nobody" user (which we do by default). This can be turned off by + * touching /etc/systemd/dont-synthesize-nobody in order to provide upgrade compatibility with legacy systems + * that used the "nobody" user name and group name for other UIDs/GIDs than 65534. + * + * Note that we do not employ any kind of synchronization on the following caching variable. If the variable is + * accessed in multi-threaded programs in the worst case it might happen that we initialize twice, but that + * shouldn't matter as each initialization should come to the same result. */ + static int cache = -1; + + if (cache < 0) + cache = access("/etc/systemd/dont-synthesize-nobody", F_OK) < 0; + + return cache; +#endif +} diff --git a/src/basic/user-util.h b/src/basic/user-util.h index 79adf91ee9..5f0391f2b8 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -97,3 +97,5 @@ bool valid_gecos(const char *d); bool valid_home(const char *p); int maybe_setgroups(size_t size, const gid_t *list); + +bool synthesize_nobody(void); diff --git a/src/basic/util.c b/src/basic/util.c index 8f9f2b902b..c7f1513f3e 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -52,6 +52,7 @@ #include "parse-util.h" #include "path-util.h" #include "process-util.h" +#include "procfs-util.h" #include "set.h" #include "signal-util.h" #include "stat-util.h" @@ -61,6 +62,7 @@ #include "umask-util.h" #include "user-util.h" #include "util.h" +#include "virt.h" int saved_argc = 0; char **saved_argv = NULL; @@ -118,45 +120,6 @@ int socket_from_display(const char *display, char **path) { return 0; } -int block_get_whole_disk(dev_t d, dev_t *ret) { - char p[SYS_BLOCK_PATH_MAX("/partition")]; - _cleanup_free_ char *s = NULL; - int r; - unsigned n, m; - - assert(ret); - - /* If it has a queue this is good enough for us */ - xsprintf_sys_block_path(p, "/queue", d); - if (access(p, F_OK) >= 0) { - *ret = d; - return 0; - } - - /* If it is a partition find the originating device */ - xsprintf_sys_block_path(p, "/partition", d); - if (access(p, F_OK) < 0) - return -ENOENT; - - /* Get parent dev_t */ - xsprintf_sys_block_path(p, "/../dev", d); - r = read_one_line_file(p, &s); - if (r < 0) - return r; - - r = sscanf(s, "%u:%u", &m, &n); - if (r != 2) - return -EINVAL; - - /* Only return this if it is really good enough for us. */ - xsprintf_sys_block_path(p, "/queue", makedev(m, n)); - if (access(p, F_OK) < 0) - return -ENOENT; - - *ret = makedev(m, n); - return 0; -} - bool kexec_loaded(void) { _cleanup_free_ char *s = NULL; @@ -184,112 +147,6 @@ int prot_from_flags(int flags) { } } -int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) { - bool stdout_is_tty, stderr_is_tty; - pid_t parent_pid, agent_pid; - sigset_t ss, saved_ss; - unsigned n, i; - va_list ap; - char **l; - - assert(pid); - assert(path); - - /* Spawns a temporary TTY agent, making sure it goes away when - * we go away */ - - parent_pid = getpid_cached(); - - /* First we temporarily block all signals, so that the new - * child has them blocked initially. This way, we can be sure - * that SIGTERMs are not lost we might send to the agent. */ - assert_se(sigfillset(&ss) >= 0); - assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0); - - agent_pid = fork(); - if (agent_pid < 0) { - assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0); - return -errno; - } - - if (agent_pid != 0) { - assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0); - *pid = agent_pid; - return 0; - } - - /* In the child: - * - * Make sure the agent goes away when the parent dies */ - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) - _exit(EXIT_FAILURE); - - /* Make sure we actually can kill the agent, if we need to, in - * case somebody invoked us from a shell script that trapped - * SIGTERM or so... */ - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - /* Check whether our parent died before we were able - * to set the death signal and unblock the signals */ - if (getppid() != parent_pid) - _exit(EXIT_SUCCESS); - - /* Don't leak fds to the agent */ - close_all_fds(except, n_except); - - stdout_is_tty = isatty(STDOUT_FILENO); - stderr_is_tty = isatty(STDERR_FILENO); - - if (!stdout_is_tty || !stderr_is_tty) { - int fd; - - /* Detach from stdout/stderr. and reopen - * /dev/tty for them. This is important to - * ensure that when systemctl is started via - * popen() or a similar call that expects to - * read EOF we actually do generate EOF and - * not delay this indefinitely by because we - * keep an unused copy of stdin around. */ - fd = open("/dev/tty", O_WRONLY); - if (fd < 0) { - log_error_errno(errno, "Failed to open /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - 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) < 0) { - log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - if (fd > STDERR_FILENO) - close(fd); - } - - /* Count arguments */ - va_start(ap, path); - for (n = 0; va_arg(ap, char*); n++) - ; - va_end(ap); - - /* Allocate strv */ - l = alloca(sizeof(char *) * (n + 1)); - - /* Fill in arguments */ - va_start(ap, path); - for (i = 0; i <= n; i++) - l[i] = va_arg(ap, char*); - va_end(ap); - - execv(path, l); - _exit(EXIT_FAILURE); -} - bool in_initrd(void) { struct statfs s; @@ -617,31 +474,22 @@ uint64_t physical_memory_scale(uint64_t v, uint64_t max) { uint64_t system_tasks_max(void) { -#if SIZEOF_PID_T == 4 -#define TASKS_MAX ((uint64_t) (INT32_MAX-1)) -#elif SIZEOF_PID_T == 2 -#define TASKS_MAX ((uint64_t) (INT16_MAX-1)) -#else -#error "Unknown pid_t size" -#endif - - _cleanup_free_ char *value = NULL, *root = NULL; uint64_t a = TASKS_MAX, b = TASKS_MAX; + _cleanup_free_ char *root = NULL; /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this * limit: * - * a) the maximum value for the pid_t type + * a) the maximum tasks value the kernel allows on this architecture * b) the cgroups pids_max attribute for the system - * c) the kernel's configure maximum PID value + * c) the kernel's configured maximum PID value * * And then pick the smallest of the three */ - if (read_one_line_file("/proc/sys/kernel/pid_max", &value) >= 0) - (void) safe_atou64(value, &a); + (void) procfs_tasks_get_limit(&a); if (cg_get_root_path(&root) >= 0) { - value = mfree(value); + _cleanup_free_ char *value = NULL; if (cg_get_attribute("pids", root, "pids.max", &value) >= 0) (void) safe_atou64(value, &b); @@ -699,131 +547,76 @@ int version(void) { return 0; } -int get_block_device(const char *path, dev_t *dev) { - struct stat st; - struct statfs sfs; - - assert(path); - assert(dev); - - /* Get's the block device directly backing a file system. If - * the block device is encrypted, returns the device mapper - * block device. */ - - if (lstat(path, &st)) - return -errno; - - if (major(st.st_dev) != 0) { - *dev = st.st_dev; - return 1; - } - - if (statfs(path, &sfs) < 0) - return -errno; - - if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) - return btrfs_get_block_device(path, dev); - - return 0; +/* This is a direct translation of str_verscmp from boot.c */ +static bool is_digit(int c) { + return c >= '0' && c <= '9'; } -int get_block_device_harder(const char *path, dev_t *dev) { - _cleanup_closedir_ DIR *d = NULL; - _cleanup_free_ char *t = NULL; - char p[SYS_BLOCK_PATH_MAX("/slaves")]; - struct dirent *de, *found = NULL; - const char *q; - unsigned maj, min; - dev_t dt; - int r; - - assert(path); - assert(dev); +static int c_order(int c) { + if (c == 0 || is_digit(c)) + return 0; - /* Gets the backing block device for a file system, and - * handles LUKS encrypted file systems, looking for its - * immediate parent, if there is one. */ + if ((c >= 'a') && (c <= 'z')) + return c; - r = get_block_device(path, &dt); - if (r <= 0) - return r; + return c + 0x10000; +} - xsprintf_sys_block_path(p, "/slaves", dt); - d = opendir(p); - if (!d) { - if (errno == ENOENT) - goto fallback; +int str_verscmp(const char *s1, const char *s2) { + const char *os1, *os2; - return -errno; - } + assert(s1); + assert(s2); - FOREACH_DIRENT_ALL(de, d, return -errno) { + os1 = s1; + os2 = s2; - if (dot_or_dot_dot(de->d_name)) - continue; + while (*s1 || *s2) { + int first; - if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN)) - continue; + while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) { + int order; - if (found) { - _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL; - - /* We found a device backed by multiple other devices. We don't really support automatic - * discovery on such setups, with the exception of dm-verity partitions. In this case there are - * two backing devices: the data partition and the hash partition. We are fine with such - * setups, however, only if both partitions are on the same physical device. Hence, let's - * verify this. */ - - u = strjoin(p, "/", de->d_name, "/../dev"); - if (!u) - return -ENOMEM; - - v = strjoin(p, "/", found->d_name, "/../dev"); - if (!v) - return -ENOMEM; - - r = read_one_line_file(u, &a); - if (r < 0) { - log_debug_errno(r, "Failed to read %s: %m", u); - goto fallback; - } - - r = read_one_line_file(v, &b); - if (r < 0) { - log_debug_errno(r, "Failed to read %s: %m", v); - goto fallback; - } - - /* Check if the parent device is the same. If not, then the two backing devices are on - * different physical devices, and we don't support that. */ - if (!streq(a, b)) - goto fallback; + order = c_order(*s1) - c_order(*s2); + if (order != 0) + return order; + s1++; + s2++; } - found = de; - } - - if (!found) - goto fallback; + while (*s1 == '0') + s1++; + while (*s2 == '0') + s2++; + + first = 0; + while (is_digit(*s1) && is_digit(*s2)) { + if (first == 0) + first = *s1 - *s2; + s1++; + s2++; + } - q = strjoina(p, "/", found->d_name, "/dev"); + if (is_digit(*s1)) + return 1; + if (is_digit(*s2)) + return -1; - r = read_one_line_file(q, &t); - if (r == -ENOENT) - goto fallback; - if (r < 0) - return r; + if (first != 0) + return first; + } - if (sscanf(t, "%u:%u", &maj, &min) != 2) - return -EINVAL; + return strcmp(os1, os2); +} - if (maj == 0) - goto fallback; +/* Turn off core dumps but only if we're running outside of a container. */ +void disable_coredumps(void) { + int r; - *dev = makedev(maj, min); - return 1; + if (detect_container() > 0) + return; -fallback: - *dev = dt; - return 1; + r = write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0); + if (r < 0) + log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m"); } diff --git a/src/basic/util.h b/src/basic/util.h index a79907de3e..9d1b10756b 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -71,8 +71,6 @@ bool plymouth_running(void); bool display_is_local(const char *display) _pure_; int socket_from_display(const char *display, char **path); -int block_get_whole_disk(dev_t d, dev_t *ret); - #define NULSTR_FOREACH(i, l) \ for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) @@ -86,8 +84,6 @@ bool kexec_loaded(void); int prot_from_flags(int flags) _const_; -int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...); - bool in_initrd(void); void in_initrd_force(bool value); @@ -194,5 +190,6 @@ int update_reboot_parameter_and_warn(const char *param); int version(void); -int get_block_device(const char *path, dev_t *dev); -int get_block_device_harder(const char *path, dev_t *dev); +int str_verscmp(const char *s1, const char *s2); + +void disable_coredumps(void); diff --git a/src/basic/verbs.c b/src/basic/verbs.c index cb42e6dd08..47644670da 100644 --- a/src/basic/verbs.c +++ b/src/basic/verbs.c @@ -22,13 +22,48 @@ #include <getopt.h> #include <stdbool.h> #include <stddef.h> +#include <string.h> +#include "env-util.h" #include "log.h" #include "macro.h" +#include "process-util.h" #include "string-util.h" #include "verbs.h" #include "virt.h" +/* Wraps running_in_chroot() which is used in various places, but also adds an environment variable check so external + * processes can reliably force this on. + */ +bool running_in_chroot_or_offline(void) { + int r; + + /* Added to support use cases like rpm-ostree, where from %post scripts we only want to execute "preset", but + * not "start"/"restart" for example. + * + * See ENVIRONMENT.md for docs. + */ + r = getenv_bool("SYSTEMD_OFFLINE"); + if (r < 0 && r != -ENXIO) + log_debug_errno(r, "Failed to parse $SYSTEMD_OFFLINE: %m"); + else if (r >= 0) + return r > 0; + + /* We've had this condition check for a long time which basically checks for legacy chroot case like Fedora's + * "mock", which is used for package builds. We don't want to try to start systemd services there, since + * without --new-chroot we don't even have systemd running, and even if we did, adding a concept of background + * daemons to builds would be an enormous change, requiring considering things like how the journal output is + * handled, etc. And there's really not a use case today for a build talking to a service. + * + * Note this call itself also looks for a different variable SYSTEMD_IGNORE_CHROOT=1. + */ + r = running_in_chroot(); + if (r < 0) + log_debug_errno(r, "running_in_chroot(): %m"); + + return r > 0; +} + int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) { const Verb *verb; const char *name; @@ -84,12 +119,15 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) { return -EINVAL; } - if ((verb->flags & VERB_NOCHROOT) && running_in_chroot() > 0) { - log_info("Running in chroot, ignoring request."); + if ((verb->flags & VERB_ONLINE_ONLY) && running_in_chroot_or_offline()) { + if (name) + log_info("Running in chroot, ignoring request: %s", name); + else + log_info("Running in chroot, ignoring request."); return 0; } - if (verb->flags & VERB_MUSTBEROOT) { + if (verb->flags & VERB_MUST_BE_ROOT) { r = must_be_root(); if (r < 0) return r; diff --git a/src/basic/verbs.h b/src/basic/verbs.h index 5f44a18f8e..d9259fc45f 100644 --- a/src/basic/verbs.h +++ b/src/basic/verbs.h @@ -23,9 +23,9 @@ #define VERB_ANY ((unsigned) -1) typedef enum VerbFlags { - VERB_DEFAULT = 1 << 0, - VERB_NOCHROOT = 1 << 1, - VERB_MUSTBEROOT = 1 << 2, + VERB_DEFAULT = 1 << 0, + VERB_ONLINE_ONLY = 1 << 1, + VERB_MUST_BE_ROOT = 1 << 2, } VerbFlags; typedef struct { @@ -35,4 +35,6 @@ typedef struct { int (* const dispatch)(int argc, char *argv[], void *userdata); } Verb; +bool running_in_chroot_or_offline(void); + int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata); diff --git a/src/basic/virt.c b/src/basic/virt.c index b0db28add6..f4796b53bc 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -18,6 +18,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#if defined(__i386__) || defined(__x86_64__) +#include <cpuid.h> +#endif #include <errno.h> #include <stdint.h> #include <stdlib.h> @@ -56,30 +59,14 @@ static int detect_vm_cpuid(void) { { "bhyve bhyve ", VIRTUALIZATION_BHYVE }, }; - uint32_t eax, ecx; + uint32_t eax, ebx, ecx, edx; bool hypervisor; /* http://lwn.net/Articles/301888/ */ -#if defined (__i386__) -#define REG_a "eax" -#define REG_b "ebx" -#elif defined (__amd64__) -#define REG_a "rax" -#define REG_b "rbx" -#endif - /* First detect whether there is a hypervisor */ - eax = 1; - __asm__ __volatile__ ( - /* ebx/rbx is being used for PIC! */ - " push %%"REG_b" \n\t" - " cpuid \n\t" - " pop %%"REG_b" \n\t" - - : "=a" (eax), "=c" (ecx) - : "0" (eax) - ); + if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0) + return VIRTUALIZATION_NONE; hypervisor = !!(ecx & 0x80000000U); @@ -91,17 +78,11 @@ static int detect_vm_cpuid(void) { unsigned j; /* There is a hypervisor, see what it is */ - eax = 0x40000000U; - __asm__ __volatile__ ( - /* ebx/rbx is being used for PIC! */ - " push %%"REG_b" \n\t" - " cpuid \n\t" - " mov %%ebx, %1 \n\t" - " pop %%"REG_b" \n\t" - - : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2]) - : "0" (eax) - ); + __cpuid(0x40000000U, eax, ebx, ecx, edx); + + sig.sig32[0] = ebx; + sig.sig32[1] = ecx; + sig.sig32[2] = edx; log_debug("Virtualization found, CPUID=%s", sig.text); @@ -241,8 +222,10 @@ static int detect_vm_xen_dom0(void) { if (r == 0) { unsigned long features; - r = safe_atolu(domcap, &features); - if (r == 0) { + /* Here, we need to use sscanf() instead of safe_atoul() + * as the string lacks the leading "0x". */ + r = sscanf(domcap, "%lx", &features); + if (r == 1) { r = !!(features & (1U << XENFEAT_dom0)); log_debug("Virtualization XEN, found %s with value %08lx, " "XENFEAT_dom0 (indicating the 'hardware domain') is%s set.", diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 59c1af73de..ae034f5cdb 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -36,6 +36,8 @@ #include <sys/statfs.h> #include <unistd.h> +#include "sd-id128.h" + #include "alloc-util.h" #include "blkid-util.h" #include "bootspec.h" @@ -294,7 +296,7 @@ static int status_entries(const char *esp_path, sd_id128_t partition) { esp_path); if (config.default_entry < 0) - printf("%zu entries, no entry suitable as default", config.n_entries); + printf("%zu entries, no entry suitable as default\n", config.n_entries); else { const BootEntry *e = &config.entries[config.default_entry]; @@ -948,12 +950,13 @@ static int verb_status(int argc, char *argv[], void *userdata) { * can show */ if (is_efi_boot()) { - _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL; + _cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL; sd_id128_t loader_part_uuid = SD_ID128_NULL; read_loader_efi_var("LoaderFirmwareType", &fw_type); read_loader_efi_var("LoaderFirmwareInfo", &fw_info); read_loader_efi_var("LoaderInfo", &loader); + read_loader_efi_var("StubInfo", &stub); read_loader_efi_var("LoaderImageIdentifier", &loader_path); if (loader_path) @@ -981,6 +984,8 @@ static int verb_status(int argc, char *argv[], void *userdata) { printf("Current Loader:\n"); printf(" Product: %s\n", strna(loader)); + if (stub) + printf(" Stub: %s\n", stub); if (!sd_id128_is_null(loader_part_uuid)) printf(" ESP: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", SD_ID128_FORMAT_VAL(loader_part_uuid)); @@ -1043,7 +1048,7 @@ static int verb_list(int argc, char *argv[], void *userdata) { boot_entry_title(e), ansi_normal(), ansi_highlight_green(), - n == config.default_entry ? " (default)" : "", + n == (unsigned) config.default_entry ? " (default)" : "", ansi_normal()); if (e->version) printf(" version: %s\n", e->version); @@ -1139,12 +1144,12 @@ static int verb_remove(int argc, char *argv[], void *userdata) { static int bootctl_main(int argc, char *argv[]) { static const Verb verbs[] = { - { "help", VERB_ANY, VERB_ANY, 0, help }, - { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status }, - { "list", VERB_ANY, 1, 0, verb_list }, - { "install", VERB_ANY, 1, VERB_MUSTBEROOT, verb_install }, - { "update", VERB_ANY, 1, VERB_MUSTBEROOT, verb_install }, - { "remove", VERB_ANY, 1, VERB_MUSTBEROOT, verb_remove }, + { "help", VERB_ANY, VERB_ANY, 0, help }, + { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status }, + { "list", VERB_ANY, 1, 0, verb_list }, + { "install", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install }, + { "update", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install }, + { "remove", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_remove }, {} }; diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index ea9f39a7e7..06331da2d4 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -166,7 +166,7 @@ static BOOLEAN line_edit(CHAR16 *line_in, CHAR16 **line_out, UINTN x_max, UINTN case KEYPRESS(EFI_ALT_PRESSED, 0, 'f'): case KEYPRESS(EFI_CONTROL_PRESSED, SCAN_RIGHT, 0): /* forward-word */ - while (line[first + cursor] && line[first + cursor] == ' ') + while (line[first + cursor] == ' ') cursor_right(&cursor, &first, x_max, len); while (line[first + cursor] && line[first + cursor] != ' ') cursor_right(&cursor, &first, x_max, len); diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c index be4fea84a2..5aaffe8fa5 100644 --- a/src/boot/efi/measure.c +++ b/src/boot/efi/measure.c @@ -232,8 +232,11 @@ static EFI_STATUS tpm1_measure_to_pcr_and_event_log(const EFI_TCG *tcg, UINT32 p */ static EFI_STATUS trigger_tcg2_final_events_table(const EFI_TCG2 *tcg, EFI_TCG2_EVENT_LOG_FORMAT log_fmt) { + EFI_PHYSICAL_ADDRESS loc; + EFI_PHYSICAL_ADDRESS last_loc; + BOOLEAN truncated; return uefi_call_wrapper(tcg->GetEventLog, 5, (EFI_TCG2 *) tcg, - log_fmt, 0, 0, 0); + log_fmt, &loc, &last_loc, &truncated); } static EFI_STATUS tpm2_measure_to_pcr_and_event_log(const EFI_TCG2 *tcg, UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, diff --git a/src/boot/efi/no-undefined-symbols.sh b/src/boot/efi/no-undefined-symbols.sh index 08b266c455..8572ceedfa 100755 --- a/src/boot/efi/no-undefined-symbols.sh +++ b/src/boot/efi/no-undefined-symbols.sh @@ -1,4 +1,5 @@ -#!/bin/sh -eu +#!/bin/sh +set -eu if nm -D -u "$1" | grep ' U '; then echo "Undefined symbols detected!" diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index e917019c0c..ff45cebd45 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -104,6 +104,30 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS) efivar_set(L"LoaderDevicePartUUID", uuid, FALSE); + /* if LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from UEFI */ + if (efivar_get_raw(&global_guid, L"LoaderImageIdentifier", &b, &size) != EFI_SUCCESS) { + CHAR16 *loaded_image_path = DevicePathToStr(loaded_image->FilePath); + efivar_set(L"LoaderImageIdentifier", loaded_image_path, FALSE); + FreePool(loaded_image_path); + } + + /* if LoaderFirmwareInfo is not set, let's set it */ + if (efivar_get_raw(&global_guid, L"LoaderFirmwareInfo", &b, &size) != EFI_SUCCESS) { + CHAR16 *loader_firmware_info = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); + efivar_set(L"LoaderFirmwareInfo", loader_firmware_info, FALSE); + FreePool(loader_firmware_info); + } + /* ditto for LoaderFirmwareType */ + if (efivar_get_raw(&global_guid, L"LoaderFirmwareType", &b, &size) != EFI_SUCCESS) { + CHAR16 *loader_firmware_type = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); + efivar_set(L"LoaderFirmwareType", loader_firmware_type, FALSE); + FreePool(loader_firmware_type); + } + + /* add StubInfo */ + if (efivar_get_raw(&global_guid, L"StubInfo", &b, &size) != EFI_SUCCESS) + efivar_set(L"StubInfo", L"systemd-stub " PACKAGE_VERSION, FALSE); + if (szs[3] > 0) graphics_splash((UINT8 *)((UINTN)loaded_image->ImageBase + addrs[3]), szs[3], NULL); diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c index 7a8d6ba5ac..f8c43b5079 100644 --- a/src/busctl/busctl.c +++ b/src/busctl/busctl.c @@ -62,6 +62,7 @@ static bool arg_expect_reply = true; static bool arg_auto_start = true; static bool arg_allow_interactive_authorization = true; static bool arg_augment_creds = true; +static bool arg_watch_bind = false; static usec_t arg_timeout = 0; #define NAME_IS_ACQUIRED INT_TO_PTR(1) @@ -1735,7 +1736,9 @@ static int help(void) { " --allow-interactive-authorization=BOOL\n" " Allow interactive authorization for operation\n" " --timeout=SECS Maximum time to wait for method call completion\n" - " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n\n" + " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n" + " --watch-bind=BOOL Wait for bus AF_UNIX socket to be bound in the file\n" + " system\n\n" "Commands:\n" " list List bus names\n" " status [SERVICE] Show bus service, process or bus owner credentials\n" @@ -1777,6 +1780,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_ALLOW_INTERACTIVE_AUTHORIZATION, ARG_TIMEOUT, ARG_AUGMENT_CREDS, + ARG_WATCH_BIND, }; static const struct option options[] = { @@ -1803,6 +1807,7 @@ static int parse_argv(int argc, char *argv[]) { { "allow-interactive-authorization", required_argument, NULL, ARG_ALLOW_INTERACTIVE_AUTHORIZATION }, { "timeout", required_argument, NULL, ARG_TIMEOUT }, { "augment-creds",required_argument, NULL, ARG_AUGMENT_CREDS}, + { "watch-bind", required_argument, NULL, ARG_WATCH_BIND }, {}, }; @@ -1953,6 +1958,16 @@ static int parse_argv(int argc, char *argv[]) { arg_augment_creds = !!r; break; + case ARG_WATCH_BIND: + r = parse_boolean(optarg); + if (r < 0) { + log_error("Failed to parse --watch-bind= parameter."); + return r; + } + + arg_watch_bind = !!r; + break; + case '?': return -EINVAL; @@ -2002,7 +2017,7 @@ static int busctl_main(sd_bus *bus, int argc, char *argv[]) { } int main(int argc, char *argv[]) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + sd_bus *bus = NULL; int r; log_parse_environment(); @@ -2051,6 +2066,12 @@ int main(int argc, char *argv[]) { goto finish; } + r = sd_bus_set_watch_bind(bus, arg_watch_bind); + if (r < 0) { + log_error_errno(r, "Failed to set watch-bind setting to '%s': %m", yes_no(arg_watch_bind)); + goto finish; + } + if (arg_address) r = sd_bus_set_address(bus, arg_address); else { @@ -2092,6 +2113,9 @@ int main(int argc, char *argv[]) { r = busctl_main(bus, argc, argv); finish: + /* make sure we terminate the bus connection first, and then close the + * pager, see issue #3543 for the details. */ + sd_bus_flush_close_unref(bus); pager_close(); strv_free(arg_matches); diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c index fb44b9f669..bd8c6a0059 100644 --- a/src/cgls/cgls.c +++ b/src/cgls/cgls.c @@ -277,9 +277,9 @@ int main(int argc, char *argv[]) { if (!arg_machine) { _cleanup_free_ char *cwd = NULL; - cwd = get_current_dir_name(); - if (!cwd) { - r = log_error_errno(errno, "Cannot determine current working directory: %m"); + r = safe_getcwd(&cwd); + if (r < 0) { + log_error_errno(r, "Cannot determine current working directory: %m"); goto finish; } diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index fe339eb493..1a73fb099d 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -40,7 +40,9 @@ #include "parse-util.h" #include "path-util.h" #include "process-util.h" +#include "procfs-util.h" #include "stdio-util.h" +#include "strv.h" #include "terminal-util.h" #include "unit-name.h" #include "util.h" @@ -193,26 +195,33 @@ static int process( g->n_tasks_valid = true; } else if (streq(controller, "pids") && arg_count == COUNT_PIDS) { - _cleanup_free_ char *p = NULL, *v = NULL; - r = cg_get_path(controller, path, "pids.current", &p); - if (r < 0) - return r; + if (isempty(path) || path_equal(path, "/")) { + r = procfs_tasks_get_current(&g->n_tasks); + if (r < 0) + return r; + } else { + _cleanup_free_ char *p = NULL, *v = NULL; - r = read_one_line_file(p, &v); - if (r == -ENOENT) - return 0; - if (r < 0) - return r; + r = cg_get_path(controller, path, "pids.current", &p); + if (r < 0) + return r; - r = safe_atou64(v, &g->n_tasks); - if (r < 0) - return r; + r = read_one_line_file(p, &v); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + + r = safe_atou64(v, &g->n_tasks); + if (r < 0) + return r; + } if (g->n_tasks > 0) g->n_tasks_valid = true; - } else if (streq(controller, "cpu") || streq(controller, "cpuacct")) { + } else if (STR_IN_SET(controller, "cpu", "cpuacct")) { _cleanup_free_ char *p = NULL, *v = NULL; uint64_t new_usage; nsec_t timestamp; diff --git a/src/core/automount.c b/src/core/automount.c index 28d5cc3917..c191336c0e 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -578,7 +578,7 @@ static void automount_enter_waiting(Automount *a) { set_clear(a->tokens); - r = unit_fail_if_symlink(UNIT(a), a->where); + r = unit_fail_if_noncanonical(UNIT(a), a->where); if (r < 0) goto fail; diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 78ef885b06..97b3756567 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -22,6 +22,7 @@ #include <fnmatch.h> #include "alloc-util.h" +#include "blockdev-util.h" #include "bpf-firewall.h" #include "cgroup-util.h" #include "cgroup.h" @@ -31,6 +32,7 @@ #include "parse-util.h" #include "path-util.h" #include "process-util.h" +#include "procfs-util.h" #include "special.h" #include "stdio-util.h" #include "string-table.h" @@ -38,6 +40,18 @@ #define CGROUP_CPU_QUOTA_PERIOD_USEC ((usec_t) 100 * USEC_PER_MSEC) +bool unit_has_root_cgroup(Unit *u) { + assert(u); + + /* Returns whether this unit manages the root cgroup. Note that this is different from being named "-.slice", + * as inside of containers the root slice won't be identical to the root cgroup. */ + + if (!u->cgroup_path) + return false; + + return isempty(u->cgroup_path) || path_equal(u->cgroup_path, "/"); +} + static void cgroup_compat_warn(void) { static bool cgroup_compat_warned = false; @@ -307,7 +321,7 @@ static int lookup_block_device(const char *p, dev_t *dev) { /* If this is a partition, try to get the originating * block device */ - block_get_whole_disk(*dev, dev); + (void) block_get_whole_disk(*dev, dev); } else { log_warning("%s is not a block device and file system block device cannot be determined or is not local.", p); return -ENODEV; @@ -707,21 +721,17 @@ static void cgroup_context_apply( assert(u); - c = unit_get_cgroup_context(u); - path = u->cgroup_path; - - assert(c); - assert(path); - /* Nothing to do? Exit early! */ if (apply_mask == 0 && !apply_bpf) return; - /* Some cgroup attributes are not supported on the root cgroup, - * hence silently ignore */ - is_root = isempty(path) || path_equal(path, "/"); - if (is_root) - /* Make sure we don't try to display messages with an empty path. */ + /* Some cgroup attributes are not supported on the root cgroup, hence silently ignore */ + is_root = unit_has_root_cgroup(u); + + assert_se(c = unit_get_cgroup_context(u)); + assert_se(path = u->cgroup_path); + + if (is_root) /* Make sure we don't try to display messages with an empty path. */ path = "/"; /* We generally ignore errors caused by read-only mounted @@ -977,7 +987,7 @@ static void cgroup_context_apply( "/dev/random\0" "rwm\0" "/dev/urandom\0" "rwm\0" "/dev/tty\0" "rwm\0" - "/dev/pts/ptmx\0" "rw\0" /* /dev/pts/ptmx may not be duplicated, but accessed */ + "/dev/ptmx\0" "rwm\0" /* Allow /run/systemd/inaccessible/{chr,blk} devices for mapping InaccessiblePaths */ "-/run/systemd/inaccessible/chr\0" "rwm\0" "-/run/systemd/inaccessible/blk\0" "rwm\0"; @@ -987,6 +997,7 @@ static void cgroup_context_apply( NULSTR_FOREACH_PAIR(x, y, auto_devices) whitelist_device(path, x, y); + /* PTS (/dev/pts) devices may not be duplicated, but accessed */ whitelist_major(path, "pts", 'c', "rw"); } @@ -1017,19 +1028,46 @@ static void cgroup_context_apply( } } - if ((apply_mask & CGROUP_MASK_PIDS) && !is_root) { + if (apply_mask & CGROUP_MASK_PIDS) { + + if (is_root) { + /* So, the "pids" controller does not expose anything on the root cgroup, in order not to + * replicate knobs exposed elsewhere needlessly. We abstract this away here however, and when + * the knobs of the root cgroup are modified propagate this to the relevant sysctls. There's a + * non-obvious asymmetry however: unlike the cgroup properties we don't really want to take + * exclusive ownership of the sysctls, but we still want to honour things if the user sets + * limits. Hence we employ sort of a one-way strategy: when the user sets a bounded limit + * through us it counts. When the user afterwards unsets it again (i.e. sets it to unbounded) + * it also counts. But if the user never set a limit through us (i.e. we are the default of + * "unbounded") we leave things unmodified. For this we manage a global boolean that we turn on + * the first time we set a limit. Note that this boolean is flushed out on manager reload, + * which is desirable so that there's an offical way to release control of the sysctl from + * systemd: set the limit to unbounded and reload. */ + + if (c->tasks_max != CGROUP_LIMIT_MAX) { + u->manager->sysctl_pid_max_changed = true; + r = procfs_tasks_set_limit(c->tasks_max); + } else if (u->manager->sysctl_pid_max_changed) + r = procfs_tasks_set_limit(TASKS_MAX); + else + r = 0; - if (c->tasks_max != CGROUP_LIMIT_MAX) { - char buf[DECIMAL_STR_MAX(uint64_t) + 2]; + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to write to tasks limit sysctls: %m"); - sprintf(buf, "%" PRIu64 "\n", c->tasks_max); - r = cg_set_attribute("pids", path, "pids.max", buf); - } else - r = cg_set_attribute("pids", path, "pids.max", "max"); + } else { + if (c->tasks_max != CGROUP_LIMIT_MAX) { + char buf[DECIMAL_STR_MAX(uint64_t) + 2]; - if (r < 0) - log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set pids.max: %m"); + sprintf(buf, "%" PRIu64 "\n", c->tasks_max); + r = cg_set_attribute("pids", path, "pids.max", buf); + } else + r = cg_set_attribute("pids", path, "pids.max", "max"); + if (r < 0) + log_unit_full(u, IN_SET(r, -ENOENT, -EROFS, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set pids.max: %m"); + } } if (apply_bpf) @@ -1060,7 +1098,7 @@ CGroupMask cgroup_context_get_mask(CGroupContext *c) { mask |= CGROUP_MASK_DEVICES; if (c->tasks_accounting || - c->tasks_max != (uint64_t) -1) + c->tasks_max != CGROUP_LIMIT_MAX) mask |= CGROUP_MASK_PIDS; return mask; @@ -1825,6 +1863,31 @@ static int unit_watch_pids_in_path(Unit *u, const char *path) { return ret; } +int unit_synthesize_cgroup_empty_event(Unit *u) { + int r; + + assert(u); + + /* Enqueue a synthetic cgroup empty event if this unit doesn't watch any PIDs anymore. This is compatibility + * support for non-unified systems where notifications aren't reliable, and hence need to take whatever we can + * get as notification source as soon as we stopped having any useful PIDs to watch for. */ + + if (!u->cgroup_path) + return -ENOENT; + + r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER); + if (r < 0) + return r; + if (r > 0) /* On unified we have reliable notifications, and don't need this */ + return 0; + + if (!set_isempty(u->pids)) + return 0; + + unit_add_to_cgroup_empty_queue(u); + return 0; +} + int unit_watch_all_pids(Unit *u) { int r; @@ -2159,40 +2222,46 @@ Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) { Unit *manager_get_unit_by_pid_cgroup(Manager *m, pid_t pid) { _cleanup_free_ char *cgroup = NULL; - int r; assert(m); - if (pid <= 0) + if (!pid_is_valid(pid)) return NULL; - r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup); - if (r < 0) + if (cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup) < 0) return NULL; return manager_get_unit_by_cgroup(m, cgroup); } Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) { - Unit *u; + Unit *u, **array; assert(m); - if (pid <= 0) + /* Note that a process might be owned by multiple units, we return only one here, which is good enough for most + * cases, though not strictly correct. We prefer the one reported by cgroup membership, as that's the most + * relevant one as children of the process will be assigned to that one, too, before all else. */ + + if (!pid_is_valid(pid)) return NULL; - if (pid == 1) + if (pid == getpid_cached()) return hashmap_get(m->units, SPECIAL_INIT_SCOPE); - u = hashmap_get(m->watch_pids1, PID_TO_PTR(pid)); + u = manager_get_unit_by_pid_cgroup(m, pid); if (u) return u; - u = hashmap_get(m->watch_pids2, PID_TO_PTR(pid)); + u = hashmap_get(m->watch_pids, PID_TO_PTR(pid)); if (u) return u; - return manager_get_unit_by_pid_cgroup(m, pid); + array = hashmap_get(m->watch_pids, PID_TO_PTR(-pid)); + if (array) + return array[0]; + + return NULL; } int manager_notify_cgroup_empty(Manager *m, const char *cgroup) { @@ -2261,6 +2330,10 @@ int unit_get_tasks_current(Unit *u, uint64_t *ret) { if ((u->cgroup_realized_mask & CGROUP_MASK_PIDS) == 0) return -ENODATA; + /* The root cgroup doesn't expose this information, let's get it from /proc instead */ + if (unit_has_root_cgroup(u)) + return procfs_tasks_get_current(ret); + r = cg_get_attribute("pids", u->cgroup_path, "pids.current", &v); if (r == -ENOENT) return -ENODATA; diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 0c5bb4a2c8..1f50441412 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -192,6 +192,8 @@ Unit* manager_get_unit_by_pid(Manager *m, pid_t pid); int unit_search_main_pid(Unit *u, pid_t *ret); int unit_watch_all_pids(Unit *u); +int unit_synthesize_cgroup_empty_event(Unit *u); + int unit_get_memory_current(Unit *u, uint64_t *ret); int unit_get_tasks_current(Unit *u, uint64_t *ret); int unit_get_cpu_usage(Unit *u, nsec_t *ret); @@ -206,6 +208,8 @@ int unit_reset_ip_accounting(Unit *u); cc ? cc->name : false; \ }) +bool unit_has_root_cgroup(Unit *u); + int manager_notify_cgroup_empty(Manager *m, const char *group); void unit_invalidate_cgroup(Unit *u, CGroupMask m); diff --git a/src/core/dbus-automount.c b/src/core/dbus-automount.c index 4b1b86f7b9..113d352a43 100644 --- a/src/core/dbus-automount.c +++ b/src/core/dbus-automount.c @@ -21,6 +21,7 @@ #include "automount.h" #include "bus-util.h" #include "dbus-automount.h" +#include "dbus-util.h" #include "string-util.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, automount_result, AutomountResult); @@ -41,7 +42,7 @@ static int bus_automount_set_transient_property( UnitWriteFlags flags, sd_bus_error *error) { - int r; + Unit *u = UNIT(a); assert(a); assert(name); @@ -49,24 +50,16 @@ static int bus_automount_set_transient_property( flags |= UNIT_PRIVATE; - if (streq(name, "TimeoutIdleUSec")) { - usec_t timeout_idle_usec; + if (streq(name, "Where")) + return bus_set_transient_path(u, name, &a->where, message, flags, error); - r = sd_bus_message_read(message, "t", &timeout_idle_usec); - if (r < 0) - return r; + if (streq(name, "TimeoutIdleUSec")) + return bus_set_transient_usec_fix_0(u, name, &a->timeout_idle_usec, message, flags, error); - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - char time[FORMAT_TIMESPAN_MAX]; + if (streq(name, "DirectoryMode")) + return bus_set_transient_mode_t(u, name, &a->directory_mode, message, flags, error); - a->timeout_idle_usec = timeout_idle_usec; - unit_write_settingf(UNIT(a), flags, name, "TimeoutIdleSec=%s\n", - format_timespan(time, sizeof(time), timeout_idle_usec, USEC_PER_MSEC)); - } - } else - return 0; - - return 1; + return 0; } int bus_automount_set_property( diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c index abca4e112d..f8d90d4b3a 100644 --- a/src/core/dbus-cgroup.c +++ b/src/core/dbus-cgroup.c @@ -28,6 +28,7 @@ #include "cgroup-util.h" #include "cgroup.h" #include "dbus-cgroup.h" +#include "dbus-util.h" #include "fd-util.h" #include "fileio.h" #include "path-util.h" @@ -413,6 +414,41 @@ static int bus_cgroup_set_transient_property( return 0; } +static int bus_cgroup_set_boolean( + Unit *u, + const char *name, + bool *p, + CGroupMask mask, + sd_bus_message *message, + UnitWriteFlags flags, + sd_bus_error *error) { + + int b, r; + + assert(p); + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + *p = b; + unit_invalidate_cgroup(u, mask); + unit_write_settingf(u, flags, name, "%s=%s", name, yes_no(b)); + } + + return 1; +} + +static BUS_DEFINE_SET_CGROUP_WEIGHT(cpu_weight, CGROUP_MASK_CPU, CGROUP_WEIGHT_IS_OK, CGROUP_WEIGHT_INVALID,); +static BUS_DEFINE_SET_CGROUP_WEIGHT(cpu_shares, CGROUP_MASK_CPU, CGROUP_CPU_SHARES_IS_OK, CGROUP_CPU_SHARES_INVALID,); +static BUS_DEFINE_SET_CGROUP_WEIGHT(io_weight, CGROUP_MASK_IO, CGROUP_WEIGHT_IS_OK, CGROUP_WEIGHT_INVALID,); +static BUS_DEFINE_SET_CGROUP_WEIGHT(blockio_weight, CGROUP_MASK_BLKIO, CGROUP_BLKIO_WEIGHT_IS_OK, CGROUP_BLKIO_WEIGHT_INVALID,); +static BUS_DEFINE_SET_CGROUP_WEIGHT(memory, CGROUP_MASK_MEMORY, , CGROUP_LIMIT_MAX, "infinity"); +static BUS_DEFINE_SET_CGROUP_WEIGHT(tasks_max, CGROUP_MASK_PIDS, , (uint64_t) -1, "infinity"); +static BUS_DEFINE_SET_CGROUP_SCALE(memory, CGROUP_MASK_MEMORY, physical_memory_scale); +static BUS_DEFINE_SET_CGROUP_SCALE(tasks_max, CGROUP_MASK_PIDS, system_tasks_max_scale); + int bus_cgroup_set_property( Unit *u, CGroupContext *c, @@ -431,110 +467,82 @@ int bus_cgroup_set_property( flags |= UNIT_PRIVATE; - if (streq(name, "CPUAccounting")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->cpu_accounting = b; - unit_invalidate_cgroup(u, CGROUP_MASK_CPUACCT|CGROUP_MASK_CPU); - unit_write_settingf(u, flags, name, "CPUAccounting=%s", yes_no(b)); - } + if (streq(name, "CPUAccounting")) + return bus_cgroup_set_boolean(u, name, &c->cpu_accounting, CGROUP_MASK_CPUACCT|CGROUP_MASK_CPU, message, flags, error); - return 1; + if (streq(name, "CPUWeight")) + return bus_cgroup_set_cpu_weight(u, name, &c->cpu_weight, message, flags, error); - } else if (streq(name, "CPUWeight")) { - uint64_t weight; - - r = sd_bus_message_read(message, "t", &weight); - if (r < 0) - return r; + if (streq(name, "StartupCPUWeight")) + return bus_cgroup_set_cpu_weight(u, name, &c->startup_cpu_weight, message, flags, error); - if (!CGROUP_WEIGHT_IS_OK(weight)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "CPUWeight= value out of range"); + if (streq(name, "CPUShares")) + return bus_cgroup_set_cpu_shares(u, name, &c->cpu_shares, message, flags, error); - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->cpu_weight = weight; - unit_invalidate_cgroup(u, CGROUP_MASK_CPU); + if (streq(name, "StartupCPUShares")) + return bus_cgroup_set_cpu_shares(u, name, &c->startup_cpu_shares, message, flags, error); - if (weight == CGROUP_WEIGHT_INVALID) - unit_write_setting(u, flags, name, "CPUWeight="); - else - unit_write_settingf(u, flags, name, "CPUWeight=%" PRIu64, weight); - } + if (streq(name, "IOAccounting")) + return bus_cgroup_set_boolean(u, name, &c->io_accounting, CGROUP_MASK_IO, message, flags, error); - return 1; + if (streq(name, "IOWeight")) + return bus_cgroup_set_io_weight(u, name, &c->io_weight, message, flags, error); - } else if (streq(name, "StartupCPUWeight")) { - uint64_t weight; + if (streq(name, "StartupIOWeight")) + return bus_cgroup_set_io_weight(u, name, &c->startup_io_weight, message, flags, error); - r = sd_bus_message_read(message, "t", &weight); - if (r < 0) - return r; + if (streq(name, "BlockIOAccounting")) + return bus_cgroup_set_boolean(u, name, &c->blockio_accounting, CGROUP_MASK_BLKIO, message, flags, error); - if (!CGROUP_WEIGHT_IS_OK(weight)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "StartupCPUWeight= value out of range"); + if (streq(name, "BlockIOWeight")) + return bus_cgroup_set_blockio_weight(u, name, &c->blockio_weight, message, flags, error); - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->startup_cpu_weight = weight; - unit_invalidate_cgroup(u, CGROUP_MASK_CPU); + if (streq(name, "StartupBlockIOWeight")) + return bus_cgroup_set_blockio_weight(u, name, &c->startup_blockio_weight, message, flags, error); - if (weight == CGROUP_CPU_SHARES_INVALID) - unit_write_setting(u, flags, name, "StartupCPUWeight="); - else - unit_write_settingf(u, flags, name, "StartupCPUWeight=%" PRIu64, weight); - } + if (streq(name, "MemoryAccounting")) + return bus_cgroup_set_boolean(u, name, &c->memory_accounting, CGROUP_MASK_MEMORY, message, flags, error); - return 1; + if (streq(name, "MemoryLow")) + return bus_cgroup_set_memory(u, name, &c->memory_low, message, flags, error); - } else if (streq(name, "CPUShares")) { - uint64_t shares; + if (streq(name, "MemoryHigh")) + return bus_cgroup_set_memory(u, name, &c->memory_high, message, flags, error); - r = sd_bus_message_read(message, "t", &shares); - if (r < 0) - return r; + if (streq(name, "MemorySwapMax")) + return bus_cgroup_set_memory(u, name, &c->memory_swap_max, message, flags, error); - if (!CGROUP_CPU_SHARES_IS_OK(shares)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "CPUShares= value out of range"); + if (streq(name, "MemoryMax")) + return bus_cgroup_set_memory(u, name, &c->memory_max, message, flags, error); - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->cpu_shares = shares; - unit_invalidate_cgroup(u, CGROUP_MASK_CPU); + if (streq(name, "MemoryLimit")) + return bus_cgroup_set_memory(u, name, &c->memory_limit, message, flags, error); - if (shares == CGROUP_CPU_SHARES_INVALID) - unit_write_setting(u, flags, name, "CPUShares="); - else - unit_write_settingf(u, flags, name, "CPUShares=%" PRIu64, shares); - } + if (streq(name, "MemoryLowScale")) + return bus_cgroup_set_memory_scale(u, name, &c->memory_low, message, flags, error); - return 1; + if (streq(name, "MemoryHighScale")) + return bus_cgroup_set_memory_scale(u, name, &c->memory_high, message, flags, error); - } else if (streq(name, "StartupCPUShares")) { - uint64_t shares; + if (streq(name, "MemorySwapMaxScale")) + return bus_cgroup_set_memory_scale(u, name, &c->memory_swap_max, message, flags, error); - r = sd_bus_message_read(message, "t", &shares); - if (r < 0) - return r; + if (streq(name, "MemoryMaxScale")) + return bus_cgroup_set_memory_scale(u, name, &c->memory_max, message, flags, error); - if (!CGROUP_CPU_SHARES_IS_OK(shares)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "StartupCPUShares= value out of range"); + if (streq(name, "MemoryLimitScale")) + return bus_cgroup_set_memory_scale(u, name, &c->memory_limit, message, flags, error); - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->startup_cpu_shares = shares; - unit_invalidate_cgroup(u, CGROUP_MASK_CPU); + if (streq(name, "TasksAccounting")) + return bus_cgroup_set_boolean(u, name, &c->tasks_accounting, CGROUP_MASK_PIDS, message, flags, error); - if (shares == CGROUP_CPU_SHARES_INVALID) - unit_write_setting(u, flags, name, "StartupCPUShares="); - else - unit_write_settingf(u, flags, name, "StartupCPUShares=%" PRIu64, shares); - } + if (streq(name, "TasksMax")) + return bus_cgroup_set_tasks_max(u, name, &c->tasks_max, message, flags, error); - return 1; + if (streq(name, "TasksMaxScale")) + return bus_cgroup_set_tasks_max_scale(u, name, &c->tasks_max, message, flags, error); - } else if (streq(name, "CPUQuotaPerSecUSec")) { + if (streq(name, "CPUQuotaPerSecUSec")) { uint64_t u64; r = sd_bus_message_read(message, "t", &u64); @@ -560,65 +568,6 @@ 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 (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->io_accounting = b; - unit_invalidate_cgroup(u, CGROUP_MASK_IO); - unit_write_settingf(u, flags, name, "IOAccounting=%s", yes_no(b)); - } - - 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_setf(error, SD_BUS_ERROR_INVALID_ARGS, "IOWeight= value out of range"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->io_weight = weight; - unit_invalidate_cgroup(u, CGROUP_MASK_IO); - - if (weight == CGROUP_WEIGHT_INVALID) - unit_write_setting(u, flags, name, "IOWeight="); - else - unit_write_settingf(u, flags, 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_setf(error, SD_BUS_ERROR_INVALID_ARGS, "StartupIOWeight= value out of range"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->startup_io_weight = weight; - unit_invalidate_cgroup(u, CGROUP_MASK_IO); - - if (weight == CGROUP_WEIGHT_INVALID) - unit_write_setting(u, flags, name, "StartupIOWeight="); - else - unit_write_settingf(u, flags, name, "StartupIOWeight=%" PRIu64, weight); - } - - return 1; - } else if ((iol_type = cgroup_io_limit_type_from_string(name)) >= 0) { const char *path; unsigned n = 0; @@ -630,6 +579,10 @@ int bus_cgroup_set_property( while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) { + if (!path_startswith(path, "/dev") && + !path_startswith(path, "/run/systemd/inaccessible/")) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s specified in %s= is not a device file in /dev", name, path); + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { CGroupIODeviceLimit *a = NULL, *b; @@ -714,6 +667,10 @@ int bus_cgroup_set_property( while ((r = sd_bus_message_read(message, "(st)", &path, &weight)) > 0) { + if (!path_startswith(path, "/dev") && + !path_startswith(path, "/run/systemd/inaccessible/")) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s specified in %s= is not a device file in /dev", name, path); + if (!CGROUP_WEIGHT_IS_OK(weight) || weight == CGROUP_WEIGHT_INVALID) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "IODeviceWeight= value out of range"); @@ -737,7 +694,7 @@ int bus_cgroup_set_property( free(a); return -ENOMEM; } - LIST_PREPEND(device_weights,c->io_device_weights, a); + LIST_PREPEND(device_weights, c->io_device_weights, a); } a->weight = weight; @@ -781,65 +738,6 @@ int bus_cgroup_set_property( return 1; - } else if (streq(name, "BlockIOAccounting")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->blockio_accounting = b; - unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); - unit_write_settingf(u, flags, name, "BlockIOAccounting=%s", yes_no(b)); - } - - return 1; - - } else if (streq(name, "BlockIOWeight")) { - uint64_t weight; - - r = sd_bus_message_read(message, "t", &weight); - if (r < 0) - return r; - - if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "BlockIOWeight= value out of range"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->blockio_weight = weight; - unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); - - if (weight == CGROUP_BLKIO_WEIGHT_INVALID) - unit_write_setting(u, flags, name, "BlockIOWeight="); - else - unit_write_settingf(u, flags, name, "BlockIOWeight=%" PRIu64, weight); - } - - return 1; - - } else if (streq(name, "StartupBlockIOWeight")) { - uint64_t weight; - - r = sd_bus_message_read(message, "t", &weight); - if (r < 0) - return r; - - if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "StartupBlockIOWeight= value out of range"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->startup_blockio_weight = weight; - unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); - - if (weight == CGROUP_BLKIO_WEIGHT_INVALID) - unit_write_setting(u, flags, name, "StartupBlockIOWeight="); - else - unit_write_settingf(u, flags, name, "StartupBlockIOWeight=%" PRIu64, weight); - } - - return 1; - } else if (STR_IN_SET(name, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) { const char *path; bool read = true; @@ -855,6 +753,10 @@ int bus_cgroup_set_property( while ((r = sd_bus_message_read(message, "(st)", &path, &u64)) > 0) { + if (!path_startswith(path, "/dev") && + !path_startswith(path, "/run/systemd/inaccessible/")) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s specified in %s= is not a device file in /dev", name, path); + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { CGroupBlockIODeviceBandwidth *a = NULL, *b; @@ -951,6 +853,10 @@ int bus_cgroup_set_property( while ((r = sd_bus_message_read(message, "(st)", &path, &weight)) > 0) { + if (!path_startswith(path, "/dev") && + !path_startswith(path, "/run/systemd/inaccessible/")) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s specified in %s= is not a device file in /dev", name, path); + if (!CGROUP_BLKIO_WEIGHT_IS_OK(weight) || weight == CGROUP_BLKIO_WEIGHT_INVALID) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "BlockIODeviceWeight= out of range"); @@ -974,7 +880,7 @@ int bus_cgroup_set_property( free(a); return -ENOMEM; } - LIST_PREPEND(device_weights,c->blockio_device_weights, a); + LIST_PREPEND(device_weights, c->blockio_device_weights, a); } a->weight = weight; @@ -1019,127 +925,6 @@ int bus_cgroup_set_property( return 1; - } else if (streq(name, "MemoryAccounting")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->memory_accounting = b; - unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); - unit_write_settingf(u, flags, name, "MemoryAccounting=%s", yes_no(b)); - } - - return 1; - - } else if (STR_IN_SET(name, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax")) { - uint64_t v; - - r = sd_bus_message_read(message, "t", &v); - if (r < 0) - return r; - if (v <= 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= is too small", name); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - if (streq(name, "MemoryLow")) - c->memory_low = v; - else if (streq(name, "MemoryHigh")) - c->memory_high = v; - else if (streq(name, "MemorySwapMax")) - c->memory_swap_max = v; - else - c->memory_max = v; - - unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); - - if (v == CGROUP_LIMIT_MAX) - unit_write_settingf(u, flags, name, "%s=infinity", name); - else - unit_write_settingf(u, flags, name, "%s=%" PRIu64, name, v); - } - - return 1; - - } else if (STR_IN_SET(name, "MemoryLowScale", "MemoryHighScale", "MemoryMaxScale", "MemorySwapMaxScale")) { - uint32_t raw; - uint64_t v; - - r = sd_bus_message_read(message, "u", &raw); - if (r < 0) - return r; - - v = physical_memory_scale(raw, UINT32_MAX); - if (v <= 0 || v == UINT64_MAX) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= is out of range", name); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - const char *e; - - /* Chop off suffix */ - assert_se(e = endswith(name, "Scale")); - name = strndupa(name, e - name); - - if (streq(name, "MemoryLow")) - c->memory_low = v; - else if (streq(name, "MemoryHigh")) - c->memory_high = v; - else if (streq(name, "MemorySwapMaxScale")) - c->memory_swap_max = v; - else /* MemoryMax */ - c->memory_max = v; - - unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); - unit_write_settingf(u, flags, name, "%s=%" PRIu32 "%%", name, - (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX))); - } - - return 1; - - } else if (streq(name, "MemoryLimit")) { - uint64_t limit; - - r = sd_bus_message_read(message, "t", &limit); - if (r < 0) - return r; - if (limit <= 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= is too small", name); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->memory_limit = limit; - unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); - - if (limit == CGROUP_LIMIT_MAX) - unit_write_setting(u, flags, name, "MemoryLimit=infinity"); - else - unit_write_settingf(u, flags, name, "MemoryLimit=%" PRIu64, limit); - } - - return 1; - - } else if (streq(name, "MemoryLimitScale")) { - uint64_t limit; - uint32_t raw; - - r = sd_bus_message_read(message, "u", &raw); - if (r < 0) - return r; - - limit = physical_memory_scale(raw, UINT32_MAX); - if (limit <= 0 || limit == UINT64_MAX) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= is out of range", name); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->memory_limit = limit; - unit_invalidate_cgroup(u, CGROUP_MASK_MEMORY); - unit_write_settingf(u, flags, "MemoryLimit", "MemoryLimit=%" PRIu32 "%%", - (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX))); - } - - return 1; - } else if (streq(name, "DevicePolicy")) { const char *policy; CGroupDevicePolicy p; @@ -1170,10 +955,8 @@ int bus_cgroup_set_property( while ((r = sd_bus_message_read(message, "(ss)", &path, &rwm)) > 0) { - if ((!path_startswith(path, "/dev/") && - !path_startswith(path, "/run/systemd/inaccessible/") && - !startswith(path, "block-") && - !startswith(path, "char-")) || + if ((!is_deviceallow_pattern(path) && + !path_startswith(path, "/run/systemd/inaccessible/")) || strpbrk(path, WHITESPACE)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "DeviceAllow= requires device node"); @@ -1252,63 +1035,6 @@ int bus_cgroup_set_property( return 1; - } else if (streq(name, "TasksAccounting")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->tasks_accounting = b; - unit_invalidate_cgroup(u, CGROUP_MASK_PIDS); - unit_write_settingf(u, flags, name, "TasksAccounting=%s", yes_no(b)); - } - - return 1; - - } else if (streq(name, "TasksMax")) { - uint64_t limit; - - r = sd_bus_message_read(message, "t", &limit); - if (r < 0) - return r; - if (limit <= 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= is too small", name); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->tasks_max = limit; - unit_invalidate_cgroup(u, CGROUP_MASK_PIDS); - - if (limit == (uint64_t) -1) - unit_write_setting(u, flags, name, "TasksMax=infinity"); - else - unit_write_settingf(u, flags, name, "TasksMax=%" PRIu64, limit); - } - - return 1; - - } else if (streq(name, "TasksMaxScale")) { - uint64_t limit; - uint32_t raw; - - r = sd_bus_message_read(message, "u", &raw); - if (r < 0) - return r; - - limit = system_tasks_max_scale(raw, UINT32_MAX); - if (limit <= 0 || limit >= UINT64_MAX) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= is out of range", name); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->tasks_max = limit; - unit_invalidate_cgroup(u, CGROUP_MASK_PIDS); - unit_write_settingf(u, flags, name, "TasksMax=%" PRIu32 "%%", - (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX))); - } - - return 1; - } else if (streq(name, "IPAccounting")) { int b; @@ -1450,12 +1176,8 @@ int bus_cgroup_set_property( return 1; } - if (u->transient && u->load_state == UNIT_STUB) { - r = bus_cgroup_set_transient_property(u, c, name, message, flags, error); - if (r != 0) - return r; - - } + if (u->transient && u->load_state == UNIT_STUB) + return bus_cgroup_set_transient_property(u, c, name, message, flags, error); return 0; } diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index be25b6e987..628fdcd1e5 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -32,6 +32,7 @@ #include "capability-util.h" #include "cpu-set-util.h" #include "dbus-execute.h" +#include "dbus-util.h" #include "env-util.h" #include "errno-list.h" #include "escape.h" @@ -1036,6 +1037,162 @@ int bus_property_get_exec_command_list( return sd_bus_message_close_container(reply); } +int bus_set_transient_exec_command( + Unit *u, + const char *name, + ExecCommand **exec_command, + sd_bus_message *message, + UnitWriteFlags flags, + sd_bus_error *error) { + unsigned n = 0; + int r; + + r = sd_bus_message_enter_container(message, 'a', "(sasb)"); + if (r < 0) + return r; + + while ((r = sd_bus_message_enter_container(message, 'r', "sasb")) > 0) { + _cleanup_strv_free_ char **argv = NULL; + const char *path; + int b; + + r = sd_bus_message_read(message, "s", &path); + if (r < 0) + return r; + + if (!path_is_absolute(path)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute.", path); + + r = sd_bus_message_read_strv(message, &argv); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + ExecCommand *c; + + c = new0(ExecCommand, 1); + if (!c) + return -ENOMEM; + + c->path = strdup(path); + if (!c->path) { + free(c); + return -ENOMEM; + } + + c->argv = argv; + argv = NULL; + + c->flags = b ? EXEC_COMMAND_IGNORE_FAILURE : 0; + + path_kill_slashes(c->path); + exec_command_append_list(exec_command, c); + } + + n++; + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + _cleanup_free_ char *buf = NULL; + _cleanup_fclose_ FILE *f = NULL; + ExecCommand *c; + size_t size = 0; + + if (n == 0) + *exec_command = exec_command_free_list(*exec_command); + + f = open_memstream(&buf, &size); + if (!f) + return -ENOMEM; + + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + + fputs("ExecStart=\n", f); + + LIST_FOREACH(command, c, *exec_command) { + _cleanup_free_ char *a = NULL, *t = NULL; + const char *p; + + p = unit_escape_setting(c->path, UNIT_ESCAPE_C|UNIT_ESCAPE_SPECIFIERS, &t); + if (!p) + return -ENOMEM; + + a = unit_concat_strv(c->argv, UNIT_ESCAPE_C|UNIT_ESCAPE_SPECIFIERS); + if (!a) + return -ENOMEM; + + fprintf(f, "%s=%s@%s %s\n", + name, + c->flags & EXEC_COMMAND_IGNORE_FAILURE ? "-" : "", + p, + a); + } + + r = fflush_and_check(f); + if (r < 0) + return r; + + unit_write_setting(u, flags, name, buf); + } + + return 1; +} + +static int parse_personality(const char *s, unsigned long *p) { + unsigned long v; + + assert(p); + + v = personality_from_string(s); + if (v == PERSONALITY_INVALID) + return -EINVAL; + + *p = v; + return 0; +} + +static const char* mount_propagation_flags_to_string_with_check(unsigned long n) { + if (!IN_SET(n, 0, MS_SHARED, MS_PRIVATE, MS_SLAVE)) + return NULL; + + return mount_propagation_flags_to_string(n); +} + +static BUS_DEFINE_SET_TRANSIENT(nsec, "t", uint64_t, nsec_t, NSEC_FMT); +static BUS_DEFINE_SET_TRANSIENT_IS_VALID(log_level, "i", int32_t, int, "%" PRIi32, log_level_is_valid); +#if HAVE_SECCOMP +static BUS_DEFINE_SET_TRANSIENT_IS_VALID(errno, "i", int32_t, int, "%" PRIi32, errno_is_valid); +#endif +static BUS_DEFINE_SET_TRANSIENT_IS_VALID(sched_priority, "i", int32_t, int, "%" PRIi32, sched_priority_is_valid); +static BUS_DEFINE_SET_TRANSIENT_IS_VALID(nice, "i", int32_t, int, "%" PRIi32, nice_is_valid); +static BUS_DEFINE_SET_TRANSIENT_PARSE(std_input, ExecInput, exec_input_from_string); +static BUS_DEFINE_SET_TRANSIENT_PARSE(std_output, ExecOutput, exec_output_from_string); +static BUS_DEFINE_SET_TRANSIENT_PARSE(utmp_mode, ExecUtmpMode, exec_utmp_mode_from_string); +static BUS_DEFINE_SET_TRANSIENT_PARSE(protect_system, ProtectSystem, parse_protect_system_or_bool); +static BUS_DEFINE_SET_TRANSIENT_PARSE(protect_home, ProtectHome, parse_protect_home_or_bool); +static BUS_DEFINE_SET_TRANSIENT_PARSE(keyring_mode, ExecKeyringMode, exec_keyring_mode_from_string); +static BUS_DEFINE_SET_TRANSIENT_PARSE(preserve_mode, ExecPreserveMode, exec_preserve_mode_from_string); +static BUS_DEFINE_SET_TRANSIENT_PARSE_PTR(personality, unsigned long, parse_personality); +static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(secure_bits, "i", int32_t, int, "%" PRIi32, secure_bits_to_string_alloc_with_check); +static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(capability, "t", uint64_t, uint64_t, "%" PRIu64, capability_set_to_string_alloc); +static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(sched_policy, "i", int32_t, int, "%" PRIi32, sched_policy_to_string_alloc_with_check); +static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(namespace_flag, "t", uint64_t, unsigned long, "%" PRIu64, namespace_flag_to_string_many_with_check); +static BUS_DEFINE_SET_TRANSIENT_TO_STRING(mount_flags, "t", uint64_t, unsigned long, "%" PRIu64, mount_propagation_flags_to_string_with_check); + int bus_exec_context_set_transient_property( Unit *u, ExecContext *c, @@ -1054,49 +1211,172 @@ int bus_exec_context_set_transient_property( flags |= UNIT_PRIVATE; - if (streq(name, "User")) { - const char *uu; + if (streq(name, "User")) + return bus_set_transient_user(u, name, &c->user, message, flags, error); - r = sd_bus_message_read(message, "s", &uu); - if (r < 0) - return r; + if (streq(name, "Group")) + return bus_set_transient_user(u, name, &c->group, message, flags, error); - if (!isempty(uu) && !valid_user_group_name_or_id(uu)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid user name: %s", uu); + if (streq(name, "TTYPath")) + return bus_set_transient_path(u, name, &c->tty_path, message, flags, error); - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + if (streq(name, "RootImage")) + return bus_set_transient_path(u, name, &c->root_image, message, flags, error); - r = free_and_strdup(&c->user, empty_to_null(uu)); - if (r < 0) - return r; + if (streq(name, "RootDirectory")) + return bus_set_transient_path(u, name, &c->root_directory, message, flags, error); - unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "User=%s", uu); - } + if (streq(name, "SyslogIdentifier")) + return bus_set_transient_string(u, name, &c->syslog_identifier, message, flags, error); - return 1; + if (streq(name, "LogLevelMax")) + return bus_set_transient_log_level(u, name, &c->log_level_max, message, flags, error); - } else if (streq(name, "Group")) { - const char *gg; + if (streq(name, "CPUSchedulingPriority")) + return bus_set_transient_sched_priority(u, name, &c->cpu_sched_priority, message, flags, error); - r = sd_bus_message_read(message, "s", &gg); - if (r < 0) - return r; + if (streq(name, "Personality")) + return bus_set_transient_personality(u, name, &c->personality, message, flags, error); - if (!isempty(gg) && !valid_user_group_name_or_id(gg)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid group name: %s", gg); + if (streq(name, "Nice")) + return bus_set_transient_nice(u, name, &c->nice, message, flags, error); - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + if (streq(name, "StandardInput")) + return bus_set_transient_std_input(u, name, &c->std_input, message, flags, error); - r = free_and_strdup(&c->group, empty_to_null(gg)); - if (r < 0) - return r; + if (streq(name, "StandardOutput")) + return bus_set_transient_std_output(u, name, &c->std_output, message, flags, error); - unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "Group=%s", gg); - } + if (streq(name, "StandardError")) + return bus_set_transient_std_output(u, name, &c->std_error, message, flags, error); - return 1; + if (streq(name, "IgnoreSIGPIPE")) + return bus_set_transient_bool(u, name, &c->ignore_sigpipe, message, flags, error); + + if (streq(name, "TTYVHangup")) + return bus_set_transient_bool(u, name, &c->tty_vhangup, message, flags, error); + + if (streq(name, "TTYReset")) + return bus_set_transient_bool(u, name, &c->tty_reset, message, flags, error); + + if (streq(name, "TTYVTDisallocate")) + return bus_set_transient_bool(u, name, &c->tty_vt_disallocate, message, flags, error); + + if (streq(name, "PrivateTmp")) + return bus_set_transient_bool(u, name, &c->private_tmp, message, flags, error); + + if (streq(name, "PrivateDevices")) + return bus_set_transient_bool(u, name, &c->private_devices, message, flags, error); + + if (streq(name, "PrivateNetwork")) + return bus_set_transient_bool(u, name, &c->private_network, message, flags, error); + + if (streq(name, "PrivateUsers")) + return bus_set_transient_bool(u, name, &c->private_users, message, flags, error); + + if (streq(name, "NoNewPrivileges")) + return bus_set_transient_bool(u, name, &c->no_new_privileges, message, flags, error); + + if (streq(name, "SyslogLevelPrefix")) + return bus_set_transient_bool(u, name, &c->syslog_level_prefix, message, flags, error); + + if (streq(name, "MemoryDenyWriteExecute")) + return bus_set_transient_bool(u, name, &c->memory_deny_write_execute, message, flags, error); + + if (streq(name, "RestrictRealtime")) + return bus_set_transient_bool(u, name, &c->restrict_realtime, message, flags, error); + + if (streq(name, "DynamicUser")) + return bus_set_transient_bool(u, name, &c->dynamic_user, message, flags, error); + + if (streq(name, "RemoveIPC")) + return bus_set_transient_bool(u, name, &c->remove_ipc, message, flags, error); + + if (streq(name, "ProtectKernelTunables")) + return bus_set_transient_bool(u, name, &c->protect_kernel_tunables, message, flags, error); + + if (streq(name, "ProtectKernelModules")) + return bus_set_transient_bool(u, name, &c->protect_kernel_modules, message, flags, error); + + if (streq(name, "ProtectControlGroups")) + return bus_set_transient_bool(u, name, &c->protect_control_groups, message, flags, error); + + if (streq(name, "MountAPIVFS")) + return bus_set_transient_bool(u, name, &c->mount_apivfs, message, flags, error); + + if (streq(name, "CPUSchedulingResetOnFork")) + return bus_set_transient_bool(u, name, &c->cpu_sched_reset_on_fork, message, flags, error); + + if (streq(name, "NonBlocking")) + return bus_set_transient_bool(u, name, &c->non_blocking, message, flags, error); + + if (streq(name, "LockPersonality")) + return bus_set_transient_bool(u, name, &c->lock_personality, message, flags, error); + + if (streq(name, "UtmpIdentifier")) + return bus_set_transient_string(u, name, &c->utmp_id, message, flags, error); + + if (streq(name, "UtmpMode")) + return bus_set_transient_utmp_mode(u, name, &c->utmp_mode, message, flags, error); + + if (streq(name, "PAMName")) + return bus_set_transient_string(u, name, &c->pam_name, message, flags, error); + + if (streq(name, "TimerSlackNSec")) + return bus_set_transient_nsec(u, name, &c->timer_slack_nsec, message, flags, error); + + if (streq(name, "ProtectSystem")) + return bus_set_transient_protect_system(u, name, &c->protect_system, message, flags, error); + + if (streq(name, "ProtectHome")) + return bus_set_transient_protect_home(u, name, &c->protect_home, message, flags, error); + + if (streq(name, "KeyringMode")) + return bus_set_transient_keyring_mode(u, name, &c->keyring_mode, message, flags, error); + + if (streq(name, "RuntimeDirectoryPreserve")) + return bus_set_transient_preserve_mode(u, name, &c->runtime_directory_preserve_mode, message, flags, error); + + if (streq(name, "UMask")) + return bus_set_transient_mode_t(u, name, &c->umask, message, flags, error); + + if (streq(name, "RuntimeDirectoryMode")) + return bus_set_transient_mode_t(u, name, &c->directories[EXEC_DIRECTORY_RUNTIME].mode, message, flags, error); - } else if (streq(name, "SupplementaryGroups")) { + if (streq(name, "StateDirectoryMode")) + return bus_set_transient_mode_t(u, name, &c->directories[EXEC_DIRECTORY_STATE].mode, message, flags, error); + + if (streq(name, "CacheDirectoryMode")) + return bus_set_transient_mode_t(u, name, &c->directories[EXEC_DIRECTORY_CACHE].mode, message, flags, error); + + if (streq(name, "LogsDirectoryMode")) + return bus_set_transient_mode_t(u, name, &c->directories[EXEC_DIRECTORY_LOGS].mode, message, flags, error); + + if (streq(name, "ConfigurationDirectoryMode")) + return bus_set_transient_mode_t(u, name, &c->directories[EXEC_DIRECTORY_CONFIGURATION].mode, message, flags, error); + + if (streq(name, "SELinuxContext")) + return bus_set_transient_string(u, name, &c->selinux_context, message, flags, error); + + if (streq(name, "SecureBits")) + return bus_set_transient_secure_bits(u, name, &c->secure_bits, message, flags, error); + + if (streq(name, "CapabilityBoundingSet")) + return bus_set_transient_capability(u, name, &c->capability_bounding_set, message, flags, error); + + if (streq(name, "AmbientCapabilities")) + return bus_set_transient_capability(u, name, &c->capability_ambient_set, message, flags, error); + + if (streq(name, "CPUSchedulingPolicy")) + return bus_set_transient_sched_policy(u, name, &c->cpu_sched_policy, message, flags, error); + + if (streq(name, "RestrictNamespaces")) + return bus_set_transient_namespace_flag(u, name, &c->restrict_namespaces, message, flags, error); + + if (streq(name, "MountFlags")) + return bus_set_transient_mount_flags(u, name, &c->mount_flags, message, flags, error); + + if (streq(name, "SupplementaryGroups")) { _cleanup_strv_free_ char **l = NULL; char **p; @@ -1130,24 +1410,6 @@ int bus_exec_context_set_transient_property( return 1; - } else if (streq(name, "SyslogIdentifier")) { - const char *id; - - r = sd_bus_message_read(message, "s", &id); - if (r < 0) - return r; - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - - if (isempty(id)) - c->syslog_identifier = mfree(c->syslog_identifier); - else if (free_and_strdup(&c->syslog_identifier, id) < 0) - return -ENOMEM; - - unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "SyslogIdentifier=%s", id); - } - - return 1; } else if (streq(name, "SyslogLevel")) { int32_t level; @@ -1164,6 +1426,7 @@ int bus_exec_context_set_transient_property( } return 1; + } else if (streq(name, "SyslogFacility")) { int32_t facility; @@ -1181,23 +1444,6 @@ int bus_exec_context_set_transient_property( return 1; - } else if (streq(name, "LogLevelMax")) { - int32_t level; - - r = sd_bus_message_read(message, "i", &level); - if (r < 0) - return r; - - if (!log_level_is_valid(level)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Maximum log level value out of range"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->log_level_max = level; - unit_write_settingf(u, flags, name, "LogLevelMax=%i", level); - } - - return 1; - } else if (streq(name, "LogExtraFields")) { size_t n = 0; @@ -1271,75 +1517,14 @@ int bus_exec_context_set_transient_property( } return 1; - - } else if (streq(name, "SecureBits")) { - int n; - - r = sd_bus_message_read(message, "i", &n); - if (r < 0) - return r; - - if (!secure_bits_is_valid(n)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid secure bits"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - _cleanup_free_ char *str = NULL; - - c->secure_bits = n; - r = secure_bits_to_string_alloc(n, &str); - if (r < 0) - return r; - - unit_write_settingf(u, flags, name, "SecureBits=%s", str); - } - - return 1; - } else if (STR_IN_SET(name, "CapabilityBoundingSet", "AmbientCapabilities")) { - uint64_t n; - - r = sd_bus_message_read(message, "t", &n); - if (r < 0) - return r; - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - _cleanup_free_ char *str = NULL; - - if (streq(name, "CapabilityBoundingSet")) - c->capability_bounding_set = n; - else /* "AmbientCapabilities" */ - c->capability_ambient_set = n; - - r = capability_set_to_string_alloc(n, &str); - if (r < 0) - return r; - - unit_write_settingf(u, flags, name, "%s=%s", name, str); - } - - return 1; - - } else if (streq(name, "Personality")) { - const char *s; - unsigned long p; - - r = sd_bus_message_read(message, "s", &s); - if (r < 0) - return r; - - p = personality_from_string(s); - if (p == PERSONALITY_INVALID) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid personality"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->personality = p; - unit_write_settingf(u, flags, name, "%s=%s", name, s); - } - - return 1; + } #if HAVE_SECCOMP - } else if (streq(name, "SystemCallFilter")) { + if (streq(name, "SystemCallErrorNumber")) + return bus_set_transient_errno(u, name, &c->syscall_errno, message, flags, error); + + if (streq(name, "SystemCallFilter")) { int whitelist; _cleanup_strv_free_ char **l = NULL; @@ -1361,59 +1546,42 @@ int bus_exec_context_set_transient_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *joined = NULL; + bool invert = !whitelist; + char **s; if (strv_isempty(l)) { c->syscall_whitelist = false; c->syscall_filter = hashmap_free(c->syscall_filter); - } else { - char **s; - c->syscall_whitelist = whitelist; + unit_write_settingf(u, flags, name, "SystemCallFilter="); + return 1; + } - r = hashmap_ensure_allocated(&c->syscall_filter, NULL); - if (r < 0) - return r; + if (!c->syscall_filter) { + c->syscall_filter = hashmap_new(NULL); + if (!c->syscall_filter) + return log_oom(); - STRV_FOREACH(s, l) { - _cleanup_free_ char *n = NULL; - int e; + c->syscall_whitelist = whitelist; - r = parse_syscall_and_errno(*s, &n, &e); + if (c->syscall_whitelist) { + r = seccomp_parse_syscall_filter(invert, "@default", -1, c->syscall_filter, true); if (r < 0) return r; + } + } - if (*n == '@') { - const SyscallFilterSet *set; - const char *i; - - set = syscall_filter_set_find(n); - if (!set) - return -EINVAL; - - NULSTR_FOREACH(i, set->value) { - int id; - - id = seccomp_syscall_resolve_name(i); - if (id == __NR_SCMP_ERROR) - return -EINVAL; - - r = hashmap_put(c->syscall_filter, INT_TO_PTR(id + 1), INT_TO_PTR(e)); - if (r < 0) - return r; - } - - } else { - int id; + STRV_FOREACH(s, l) { + _cleanup_free_ char *n = NULL; + int e; - id = seccomp_syscall_resolve_name(n); - if (id == __NR_SCMP_ERROR) - return -EINVAL; + r = parse_syscall_and_errno(*s, &n, &e); + if (r < 0) + return r; - r = hashmap_put(c->syscall_filter, INT_TO_PTR(id + 1), INT_TO_PTR(e)); - if (r < 0) - return r; - } - } + r = seccomp_parse_syscall_filter(invert, n, e, c->syscall_filter, c->syscall_whitelist); + if (r < 0) + return r; } joined = strv_join(l, " "); @@ -1467,24 +1635,6 @@ int bus_exec_context_set_transient_property( return 1; - } else if (streq(name, "SystemCallErrorNumber")) { - int32_t n; - - r = sd_bus_message_read(message, "i", &n); - if (r < 0) - return r; - - if (n <= 0 || n > ERRNO_MAX) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid SystemCallErrorNumber"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->syscall_errno = n; - - unit_write_settingf(u, flags, name, "SystemCallErrorNumber=%d", n); - } - - return 1; - } else if (streq(name, "RestrictAddressFamilies")) { int whitelist; _cleanup_strv_free_ char **l = NULL; @@ -1507,30 +1657,38 @@ int bus_exec_context_set_transient_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *joined = NULL; + bool invert = !whitelist; + char **s; if (strv_isempty(l)) { c->address_families_whitelist = false; c->address_families = set_free(c->address_families); - } else { - char **s; - c->address_families_whitelist = whitelist; + unit_write_settingf(u, flags, name, "RestrictAddressFamilies="); + return 1; + } - r = set_ensure_allocated(&c->address_families, NULL); - if (r < 0) - return r; + if (!c->address_families) { + c->address_families = set_new(NULL); + if (!c->address_families) + return log_oom(); - STRV_FOREACH(s, l) { - int af; + c->address_families_whitelist = whitelist; + } - af = af_from_name(*s); - if (af <= 0) - return -EINVAL; + STRV_FOREACH(s, l) { + int af; + af = af_from_name(*s); + if (af <= 0) + return -EINVAL; + + if (!invert == c->address_families_whitelist) { r = set_put(c->address_families, INT_TO_PTR(af)); if (r < 0) return r; - } + } else + (void) set_remove(c->address_families, INT_TO_PTR(af)); } joined = strv_join(l, " "); @@ -1541,49 +1699,9 @@ int bus_exec_context_set_transient_property( } return 1; + } #endif - - } else if (streq(name, "CPUSchedulingPolicy")) { - int32_t n; - - r = sd_bus_message_read(message, "i", &n); - if (r < 0) - return r; - - if (!sched_policy_is_valid(n)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid CPU scheduling policy"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - _cleanup_free_ char *str = NULL; - - c->cpu_sched_policy = n; - r = sched_policy_to_string_alloc(n, &str); - if (r < 0) - return r; - - unit_write_settingf(u, flags, name, "CPUSchedulingPolicy=%s", str); - } - - return 1; - - } else if (streq(name, "CPUSchedulingPriority")) { - int32_t n; - - r = sd_bus_message_read(message, "i", &n); - if (r < 0) - return r; - - if (!ioprio_priority_is_valid(n)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid CPU scheduling priority"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->cpu_sched_priority = n; - unit_write_settingf(u, flags, name, "CPUSchedulingPriority=%i", n); - } - - return 1; - - } else if (streq(name, "CPUAffinity")) { + if (streq(name, "CPUAffinity")) { const void *a; size_t n = 0; @@ -1649,22 +1767,6 @@ int bus_exec_context_set_transient_property( } return 1; - } else if (streq(name, "Nice")) { - int32_t n; - - r = sd_bus_message_read(message, "i", &n); - if (r < 0) - return r; - - if (!nice_is_valid(n)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Nice value out of range"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->nice = n; - unit_write_settingf(u, flags, name, "Nice=%i", n); - } - - return 1; } else if (streq(name, "IOSchedulingClass")) { int32_t q; @@ -1710,33 +1812,6 @@ int bus_exec_context_set_transient_property( return 1; - } else if (STR_IN_SET(name, "TTYPath", "RootDirectory", "RootImage")) { - const char *s; - - r = sd_bus_message_read(message, "s", &s); - if (r < 0) - return r; - - if (!path_is_absolute(s)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s takes an absolute path", name); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - if (streq(name, "TTYPath")) - r = free_and_strdup(&c->tty_path, s); - else if (streq(name, "RootImage")) - r = free_and_strdup(&c->root_image, s); - else { - assert(streq(name, "RootDirectory")); - r = free_and_strdup(&c->root_directory, s); - } - if (r < 0) - return r; - - unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, s); - } - - return 1; - } else if (streq(name, "WorkingDirectory")) { const char *s; bool missing_ok; @@ -1751,7 +1826,7 @@ int bus_exec_context_set_transient_property( } else missing_ok = false; - if (!streq(s, "~") && !path_is_absolute(s)) + if (!isempty(s) && !streq(s, "~") && !path_is_absolute(s)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "WorkingDirectory= expects an absolute path or '~'"); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { @@ -1759,7 +1834,7 @@ int bus_exec_context_set_transient_property( c->working_directory = mfree(c->working_directory); c->working_directory_home = true; } else { - r = free_and_strdup(&c->working_directory, s); + r = free_and_strdup(&c->working_directory, empty_to_null(s)); if (r < 0) return r; @@ -1772,66 +1847,6 @@ int bus_exec_context_set_transient_property( return 1; - } else if (streq(name, "StandardInput")) { - const char *s; - ExecInput p; - - r = sd_bus_message_read(message, "s", &s); - if (r < 0) - return r; - - p = exec_input_from_string(s); - if (p < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid standard input name"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->std_input = p; - - unit_write_settingf(u, flags, name, "StandardInput=%s", exec_input_to_string(p)); - } - - return 1; - - } else if (streq(name, "StandardOutput")) { - const char *s; - ExecOutput p; - - r = sd_bus_message_read(message, "s", &s); - if (r < 0) - return r; - - p = exec_output_from_string(s); - if (p < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid standard output name"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->std_output = p; - - unit_write_settingf(u, flags, name, "StandardOutput=%s", exec_output_to_string(p)); - } - - return 1; - - } else if (streq(name, "StandardError")) { - const char *s; - ExecOutput p; - - r = sd_bus_message_read(message, "s", &s); - if (r < 0) - return r; - - p = exec_output_from_string(s); - if (p < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid standard error name"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->std_error = p; - - unit_write_settingf(u, flags, name, "StandardError=%s", exec_output_to_string(p)); - } - - return 1; - } else if (STR_IN_SET(name, "StandardInputFileDescriptorName", "StandardOutputFileDescriptorName", "StandardErrorFileDescriptorName")) { const char *s; @@ -1840,15 +1855,13 @@ int bus_exec_context_set_transient_property( if (r < 0) return r; - if (isempty(s)) - s = NULL; - else if (!fdname_is_valid(s)) + if (!isempty(s) && !fdname_is_valid(s)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid file descriptor name"); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { if (streq(name, "StandardInputFileDescriptorName")) { - r = free_and_strdup(c->stdio_fdname + STDIN_FILENO, s); + r = free_and_strdup(c->stdio_fdname + STDIN_FILENO, empty_to_null(s)); if (r < 0) return r; @@ -1856,7 +1869,7 @@ int bus_exec_context_set_transient_property( unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardInput=fd:%s", exec_context_fdname(c, STDIN_FILENO)); } else if (streq(name, "StandardOutputFileDescriptorName")) { - r = free_and_strdup(c->stdio_fdname + STDOUT_FILENO, s); + r = free_and_strdup(c->stdio_fdname + STDOUT_FILENO, empty_to_null(s)); if (r < 0) return r; @@ -1866,7 +1879,7 @@ int bus_exec_context_set_transient_property( } else { assert(streq(name, "StandardErrorFileDescriptorName")); - r = free_and_strdup(&c->stdio_fdname[STDERR_FILENO], s); + r = free_and_strdup(&c->stdio_fdname[STDERR_FILENO], empty_to_null(s)); if (r < 0) return r; @@ -1884,15 +1897,17 @@ int bus_exec_context_set_transient_property( if (r < 0) return r; - if (!path_is_absolute(s)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute", s); - if (!path_is_normalized(s)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not normalized", s); + if (!isempty(s)) { + if (!path_is_absolute(s)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute", s); + if (!path_is_normalized(s)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not normalized", s); + } if (!UNIT_WRITE_FLAGS_NOOP(flags)) { if (streq(name, "StandardInputFile")) { - r = free_and_strdup(&c->stdio_file[STDIN_FILENO], s); + r = free_and_strdup(&c->stdio_file[STDIN_FILENO], empty_to_null(s)); if (r < 0) return r; @@ -1900,7 +1915,7 @@ int bus_exec_context_set_transient_property( unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "StandardInput=file:%s", s); } else if (streq(name, "StandardOutputFile")) { - r = free_and_strdup(&c->stdio_file[STDOUT_FILENO], s); + r = free_and_strdup(&c->stdio_file[STDOUT_FILENO], empty_to_null(s)); if (r < 0) return r; @@ -1910,7 +1925,7 @@ int bus_exec_context_set_transient_property( } else { assert(streq(name, "StandardErrorFile")); - r = free_and_strdup(&c->stdio_file[STDERR_FILENO], s); + r = free_and_strdup(&c->stdio_file[STDERR_FILENO], empty_to_null(s)); if (r < 0) return r; @@ -1964,124 +1979,6 @@ int bus_exec_context_set_transient_property( return 1; - } else if (STR_IN_SET(name, - "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate", - "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", - "NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute", - "RestrictRealtime", "DynamicUser", "RemoveIPC", "ProtectKernelTunables", - "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS", - "CPUSchedulingResetOnFork", "NonBlocking", "LockPersonality")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - if (streq(name, "IgnoreSIGPIPE")) - c->ignore_sigpipe = b; - else if (streq(name, "TTYVHangup")) - c->tty_vhangup = b; - else if (streq(name, "TTYReset")) - c->tty_reset = b; - else if (streq(name, "TTYVTDisallocate")) - c->tty_vt_disallocate = b; - else if (streq(name, "PrivateTmp")) - c->private_tmp = b; - else if (streq(name, "PrivateDevices")) - c->private_devices = b; - else if (streq(name, "PrivateNetwork")) - c->private_network = b; - else if (streq(name, "PrivateUsers")) - c->private_users = b; - else if (streq(name, "NoNewPrivileges")) - c->no_new_privileges = b; - else if (streq(name, "SyslogLevelPrefix")) - c->syslog_level_prefix = b; - else if (streq(name, "MemoryDenyWriteExecute")) - c->memory_deny_write_execute = b; - else if (streq(name, "RestrictRealtime")) - c->restrict_realtime = b; - else if (streq(name, "DynamicUser")) - c->dynamic_user = b; - else if (streq(name, "RemoveIPC")) - c->remove_ipc = b; - else if (streq(name, "ProtectKernelTunables")) - c->protect_kernel_tunables = b; - else if (streq(name, "ProtectKernelModules")) - c->protect_kernel_modules = b; - else if (streq(name, "ProtectControlGroups")) - c->protect_control_groups = b; - else if (streq(name, "MountAPIVFS")) - c->mount_apivfs = b; - else if (streq(name, "CPUSchedulingResetOnFork")) - c->cpu_sched_reset_on_fork = b; - else if (streq(name, "NonBlocking")) - c->non_blocking = b; - else if (streq(name, "LockPersonality")) - c->lock_personality = b; - - unit_write_settingf(u, flags, name, "%s=%s", name, yes_no(b)); - } - - return 1; - - } else if (streq(name, "UtmpIdentifier")) { - const char *id; - - r = sd_bus_message_read(message, "s", &id); - if (r < 0) - return r; - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - - r = free_and_strdup(&c->utmp_id, empty_to_null(id)); - if (r < 0) - return r; - - unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "UtmpIdentifier=%s", strempty(id)); - } - - return 1; - - } else if (streq(name, "UtmpMode")) { - const char *s; - ExecUtmpMode m; - - r = sd_bus_message_read(message, "s", &s); - if (r < 0) - return r; - - m = exec_utmp_mode_from_string(s); - if (m < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid utmp mode"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->utmp_mode = m; - - unit_write_settingf(u, flags, name, "UtmpMode=%s", exec_utmp_mode_to_string(m)); - } - - return 1; - - } else if (streq(name, "PAMName")) { - const char *n; - - r = sd_bus_message_read(message, "s", &n); - if (r < 0) - return r; - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - - r = free_and_strdup(&c->pam_name, empty_to_null(n)); - if (r < 0) - return r; - - unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "PAMName=%s", strempty(n)); - } - - return 1; - } else if (streq(name, "Environment")) { _cleanup_strv_free_ char **l = NULL; @@ -2154,21 +2051,6 @@ int bus_exec_context_set_transient_property( return 1; - } else if (streq(name, "TimerSlackNSec")) { - - nsec_t n; - - r = sd_bus_message_read(message, "t", &n); - if (r < 0) - return r; - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->timer_slack_nsec = n; - unit_write_settingf(u, flags, name, "TimerSlackNSec=" NSEC_FMT, n); - } - - return 1; - } else if (streq(name, "OOMScoreAdjust")) { int oa; @@ -2323,16 +2205,15 @@ int bus_exec_context_set_transient_property( return r; STRV_FOREACH(p, l) { - const char *i = *p; + char *i = *p; size_t offset; - if (!utf8_is_valid(i)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name); - offset = i[0] == '-'; offset += i[offset] == '+'; if (!path_is_absolute(i + offset)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name); + + path_kill_slashes(i + offset); } if (!UNIT_WRITE_FLAGS_NOOP(flags)) { @@ -2363,123 +2244,6 @@ int bus_exec_context_set_transient_property( return 1; - } else if (streq(name, "ProtectSystem")) { - const char *s; - ProtectSystem ps; - - r = sd_bus_message_read(message, "s", &s); - if (r < 0) - return r; - - r = parse_boolean(s); - if (r > 0) - ps = PROTECT_SYSTEM_YES; - else if (r == 0) - ps = PROTECT_SYSTEM_NO; - else { - ps = protect_system_from_string(s); - if (ps < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to parse protect system value"); - } - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->protect_system = ps; - unit_write_settingf(u, flags, name, "%s=%s", name, s); - } - - return 1; - - } else if (streq(name, "ProtectHome")) { - const char *s; - ProtectHome ph; - - r = sd_bus_message_read(message, "s", &s); - if (r < 0) - return r; - - r = parse_boolean(s); - if (r > 0) - ph = PROTECT_HOME_YES; - else if (r == 0) - ph = PROTECT_HOME_NO; - else { - ph = protect_home_from_string(s); - if (ph < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to parse protect home value"); - } - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->protect_home = ph; - unit_write_settingf(u, flags, name, "%s=%s", name, s); - } - - return 1; - - } else if (streq(name, "KeyringMode")) { - - const char *s; - ExecKeyringMode m; - - r = sd_bus_message_read(message, "s", &s); - if (r < 0) - return r; - - m = exec_keyring_mode_from_string(s); - if (m < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid keyring mode"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->keyring_mode = m; - - unit_write_settingf(u, flags, name, "KeyringMode=%s", exec_keyring_mode_to_string(m)); - } - - return 1; - - } else if (streq(name, "RuntimeDirectoryPreserve")) { - const char *s; - ExecPreserveMode m; - - r = sd_bus_message_read(message, "s", &s); - if (r < 0) - return r; - - m = exec_preserve_mode_from_string(s); - if (m < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid preserve mode"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->runtime_directory_preserve_mode = m; - - unit_write_settingf(u, flags, name, "RuntimeDirectoryPreserve=%s", exec_preserve_mode_to_string(m)); - } - - return 1; - - } else if (STR_IN_SET(name, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) { - mode_t m; - - r = sd_bus_message_read(message, "u", &m); - if (r < 0) - return r; - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - ExecDirectoryType i; - - if (streq(name, "UMask")) - c->umask = m; - else - for (i = 0; i < _EXEC_DIRECTORY_TYPE_MAX; i++) - if (startswith(name, exec_directory_type_to_string(i))) { - c->directories[i].mode = m; - break; - } - - unit_write_settingf(u, flags, name, "%s=%040o", name, m); - } - - return 1; - } else if (STR_IN_SET(name, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) { _cleanup_strv_free_ char **l = NULL; char **p; @@ -2525,23 +2289,6 @@ 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 (!UNIT_WRITE_FLAGS_NOOP(flags)) { - if (isempty(s)) - c->selinux_context = mfree(c->selinux_context); - else if (free_and_strdup(&c->selinux_context, s) < 0) - return -ENOMEM; - - unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, strempty(s)); - } - - return 1; - } else if (STR_IN_SET(name, "AppArmorProfile", "SmackProcessLabel")) { int ignore; const char *s; @@ -2580,43 +2327,6 @@ int bus_exec_context_set_transient_property( return 1; - } else if (streq(name, "RestrictNamespaces")) { - uint64_t rf; - - r = sd_bus_message_read(message, "t", &rf); - if (r < 0) - return r; - if ((rf & NAMESPACE_FLAGS_ALL) != rf) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown namespace types"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - _cleanup_free_ char *s = NULL; - - r = namespace_flag_to_string_many(rf, &s); - if (r < 0) - return r; - - c->restrict_namespaces = rf; - unit_write_settingf(u, flags, name, "%s=%s", name, s); - } - - return 1; - } else if (streq(name, "MountFlags")) { - uint64_t fl; - - r = sd_bus_message_read(message, "t", &fl); - if (r < 0) - return r; - if (!IN_SET(fl, 0, MS_SHARED, MS_PRIVATE, MS_SLAVE)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown mount propagation flags"); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->mount_flags = fl; - - unit_write_settingf(u, flags, name, "%s=%s", name, mount_propagation_flags_to_string(fl)); - } - - return 1; } else if (STR_IN_SET(name, "BindPaths", "BindReadOnlyPaths")) { unsigned empty = true; diff --git a/src/core/dbus-execute.h b/src/core/dbus-execute.h index f30f0774cd..4d9b368f81 100644 --- a/src/core/dbus-execute.h +++ b/src/core/dbus-execute.h @@ -44,3 +44,4 @@ int bus_property_get_exec_command(sd_bus *bus, const char *path, const char *int int bus_property_get_exec_command_list(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error); int bus_exec_context_set_transient_property(Unit *u, ExecContext *c, const char *name, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); +int bus_set_transient_exec_command(Unit *u, const char *name, ExecCommand **exec_command, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c index bf3bbb2047..53d0aad63b 100644 --- a/src/core/dbus-kill.c +++ b/src/core/dbus-kill.c @@ -20,6 +20,7 @@ #include "bus-util.h" #include "dbus-kill.h" +#include "dbus-util.h" #include "kill.h" #include "signal-util.h" @@ -34,6 +35,9 @@ const sd_bus_vtable bus_kill_vtable[] = { SD_BUS_VTABLE_END }; +static BUS_DEFINE_SET_TRANSIENT_PARSE(kill_mode, KillMode, kill_mode_from_string); +static BUS_DEFINE_SET_TRANSIENT_TO_STRING(kill_signal, "i", int32_t, int, "%" PRIi32, signal_to_string_with_check); + int bus_kill_context_set_transient_property( Unit *u, KillContext *c, @@ -42,8 +46,6 @@ int bus_kill_context_set_transient_property( UnitWriteFlags flags, sd_bus_error *error) { - int r; - assert(u); assert(c); assert(name); @@ -51,75 +53,17 @@ int bus_kill_context_set_transient_property( flags |= UNIT_PRIVATE; - if (streq(name, "KillMode")) { - const char *m; - KillMode k; - - r = sd_bus_message_read(message, "s", &m); - if (r < 0) - return r; - - k = kill_mode_from_string(m); - if (k < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Kill mode '%s' not known.", m); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->kill_mode = k; - - unit_write_settingf(u, flags, name, "KillMode=%s", kill_mode_to_string(k)); - } - - return 1; - - } else if (streq(name, "KillSignal")) { - int sig; - - r = sd_bus_message_read(message, "i", &sig); - if (r < 0) - return r; - - if (!SIGNAL_VALID(sig)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal %i out of range", sig); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->kill_signal = sig; - - unit_write_settingf(u, flags, name, "KillSignal=%s", signal_to_string(sig)); - } - - return 1; - - } else if (streq(name, "SendSIGHUP")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->send_sighup = b; - - unit_write_settingf(u, flags, name, "SendSIGHUP=%s", yes_no(b)); - } - - return 1; - - } else if (streq(name, "SendSIGKILL")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - c->send_sigkill = b; + if (streq(name, "KillMode")) + return bus_set_transient_kill_mode(u, name, &c->kill_mode, message, flags, error); - unit_write_settingf(u, flags, name, "SendSIGKILL=%s", yes_no(b)); - } + if (streq(name, "SendSIGHUP")) + return bus_set_transient_bool(u, name, &c->send_sighup, message, flags, error); - return 1; + if (streq(name, "SendSIGKILL")) + return bus_set_transient_bool(u, name, &c->send_sigkill, message, flags, error); - } + if (streq(name, "KillSignal")) + return bus_set_transient_kill_signal(u, name, &c->kill_signal, message, flags, error); return 0; } diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index ec9e65879f..4fe374867c 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -2416,6 +2416,7 @@ const sd_bus_vtable bus_manager_vtable[] = { 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), SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, shutdown_watchdog), 0), + SD_BUS_WRITABLE_PROPERTY("ServiceWatchdogs", "b", bus_property_get_bool, bus_property_set_bool, offsetof(Manager, service_watchdogs), 0), SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Manager, cgroup_root), 0), SD_BUS_PROPERTY("SystemState", "s", property_get_system_state, 0, 0), SD_BUS_PROPERTY("ExitCode", "y", bus_property_get_unsigned, offsetof(Manager, return_value), 0), @@ -2423,8 +2424,10 @@ 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("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("DefaultStartLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST), + /* The following two items are obsolete alias */ + SD_BUS_PROPERTY("DefaultStartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("DefaultStartLimitInterval", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), 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), diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c index 628bce0b6a..9e52f55fa5 100644 --- a/src/core/dbus-mount.c +++ b/src/core/dbus-mount.c @@ -23,6 +23,7 @@ #include "dbus-execute.h" #include "dbus-kill.h" #include "dbus-mount.h" +#include "dbus-util.h" #include "mount.h" #include "string-util.h" #include "unit.h" @@ -129,9 +130,7 @@ static int bus_mount_set_transient_property( UnitWriteFlags flags, sd_bus_error *error) { - const char *new_property; - char **property; - int r; + Unit *u = UNIT(m); assert(m); assert(name); @@ -139,29 +138,34 @@ static int bus_mount_set_transient_property( flags |= UNIT_PRIVATE; + if (streq(name, "Where")) + return bus_set_transient_path(u, name, &m->where, message, flags, error); + if (streq(name, "What")) - property = &m->parameters_fragment.what; - else if (streq(name, "Options")) - property = &m->parameters_fragment.options; - else if (streq(name, "Type")) - property = &m->parameters_fragment.fstype; - else - return 0; - - r = sd_bus_message_read(message, "s", &new_property); - if (r < 0) - return r; + return bus_set_transient_string(u, name, &m->parameters_fragment.what, message, flags, error); - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + if (streq(name, "Options")) + return bus_set_transient_string(u, name, &m->parameters_fragment.options, message, flags, error); - r = free_and_strdup(property, new_property); - if (r < 0) - return r; + if (streq(name, "Type")) + return bus_set_transient_string(u, name, &m->parameters_fragment.fstype, message, flags, error); - unit_write_settingf(UNIT(m), flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, new_property); - } + if (streq(name, "TimeoutUSec")) + return bus_set_transient_usec_fix_0(u, name, &m->timeout_usec, message, flags, error); - return 1; + if (streq(name, "DirectoryMode")) + return bus_set_transient_mode_t(u, name, &m->directory_mode, message, flags, error); + + if (streq(name, "SloppyOptions")) + return bus_set_transient_bool(u, name, &m->sloppy_options, message, flags, error); + + if (streq(name, "LazyUnmount")) + return bus_set_transient_bool(u, name, &m->lazy_unmount, message, flags, error); + + if (streq(name, "ForceUnmount")) + return bus_set_transient_bool(u, name, &m->force_unmount, message, flags, error); + + return 0; } int bus_mount_set_property( diff --git a/src/core/dbus-path.c b/src/core/dbus-path.c index 0f54b04f76..b3f502f4c9 100644 --- a/src/core/dbus-path.c +++ b/src/core/dbus-path.c @@ -18,9 +18,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "alloc-util.h" #include "bus-util.h" #include "dbus-path.h" +#include "dbus-util.h" +#include "list.h" #include "path.h" +#include "path-util.h" #include "string-util.h" #include "unit.h" @@ -85,3 +89,108 @@ const sd_bus_vtable bus_path_vtable[] = { SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Path, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_VTABLE_END }; + +static int bus_path_set_transient_property( + Path *p, + const char *name, + sd_bus_message *message, + UnitWriteFlags flags, + sd_bus_error *error) { + + Unit *u = UNIT(p); + int r; + + assert(p); + assert(name); + assert(message); + + flags |= UNIT_PRIVATE; + + if (streq(name, "MakeDirectory")) + return bus_set_transient_bool(u, name, &p->make_directory, message, flags, error); + + if (streq(name, "DirectoryMode")) + return bus_set_transient_mode_t(u, name, &p->directory_mode, message, flags, error); + + if (streq(name, "Paths")) { + const char *type_name, *path; + bool empty = true; + + r = sd_bus_message_enter_container(message, 'a', "(ss)"); + if (r < 0) + return r; + + while ((r = sd_bus_message_read(message, "(ss)", &type_name, &path)) > 0) { + PathType t; + + t = path_type_from_string(type_name); + if (t < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown path type: %s", type_name); + + if (isempty(path)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path in %s is empty", type_name); + + if (!path_is_absolute(path)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path in %s is not absolute: %s", type_name, path); + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + _cleanup_free_ char *k; + PathSpec *s; + + k = strdup(path); + if (!k) + return -ENOMEM; + + s = new0(PathSpec, 1); + if (!s) + return -ENOMEM; + + s->unit = u; + s->path = path_kill_slashes(k); + k = NULL; + s->type = t; + s->inotify_fd = -1; + + LIST_PREPEND(spec, p->specs, s); + + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", type_name, path); + } + + empty = false; + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags) && empty) { + path_free_specs(p); + unit_write_settingf(u, flags, name, "PathExists="); + } + + return 1; + } + + return 0; +} + +int bus_path_set_property( + Unit *u, + const char *name, + sd_bus_message *message, + UnitWriteFlags mode, + sd_bus_error *error) { + + Path *p = PATH(u); + + assert(p); + assert(name); + assert(message); + + if (u->transient && u->load_state == UNIT_STUB) + return bus_path_set_transient_property(p, name, message, mode, error); + + return 0; +} diff --git a/src/core/dbus-path.h b/src/core/dbus-path.h index 5e7e859b56..ccd88c7f86 100644 --- a/src/core/dbus-path.h +++ b/src/core/dbus-path.h @@ -20,6 +20,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "sd-bus.h" +#include "unit.h" extern const sd_bus_vtable bus_path_vtable[]; + +int bus_path_set_property(Unit *u, const char *name, sd_bus_message *i, UnitWriteFlags flags, sd_bus_error *error); diff --git a/src/core/dbus-scope.c b/src/core/dbus-scope.c index 9195ad36d0..a0c4a65b33 100644 --- a/src/core/dbus-scope.c +++ b/src/core/dbus-scope.c @@ -26,6 +26,7 @@ #include "dbus-kill.h" #include "dbus-scope.h" #include "dbus-unit.h" +#include "dbus-util.h" #include "dbus.h" #include "scope.h" #include "selinux-access.h" @@ -84,6 +85,9 @@ static int bus_scope_set_transient_property( flags |= UNIT_PRIVATE; + if (streq(name, "TimeoutStopUSec")) + return bus_set_transient_usec(UNIT(s), name, &s->timeout_stop_usec, message, flags, error); + if (streq(name, "PIDs")) { unsigned n = 0; uint32_t pid; @@ -139,21 +143,6 @@ static int bus_scope_set_transient_property( } return 1; - - } else if (streq(name, "TimeoutStopUSec")) { - uint64_t t; - - r = sd_bus_message_read(message, "t", &t); - if (r < 0) - return r; - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - s->timeout_stop_usec = t; - - unit_write_settingf(UNIT(s), flags, name, "TimeoutStopSec=" USEC_FMT "us", t); - } - - return 1; } return 0; diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index 0189952124..6de905b69c 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -22,15 +22,20 @@ #include "alloc-util.h" #include "async.h" +#include "bus-internal.h" #include "bus-util.h" #include "dbus-cgroup.h" #include "dbus-execute.h" #include "dbus-kill.h" #include "dbus-service.h" +#include "dbus-util.h" +#include "exit-status.h" #include "fd-util.h" #include "fileio.h" +#include "parse-util.h" #include "path-util.h" #include "service.h" +#include "signal-util.h" #include "string-util.h" #include "strv.h" #include "unit.h" @@ -41,6 +46,71 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_restart, service_restart, Servi static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_notify_access, notify_access, NotifyAccess); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_emergency_action, emergency_action, EmergencyAction); +static int property_get_exit_status_set( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + ExitStatusSet *status_set = userdata; + Iterator i; + void *id; + int r; + + assert(bus); + assert(reply); + assert(status_set); + + r = sd_bus_message_open_container(reply, 'r', "aiai"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "i"); + if (r < 0) + return r; + + SET_FOREACH(id, status_set->status, i) { + int val = PTR_TO_INT(id); + + if (val < 0 || val > 255) + continue; + + r = sd_bus_message_append_basic(reply, 'i', &val); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "i"); + if (r < 0) + return r; + + SET_FOREACH(id, status_set->signal, i) { + int val = PTR_TO_INT(id); + const char *str; + + str = signal_to_string(val); + if (!str) + continue; + + r = sd_bus_message_append_basic(reply, 'i', &val); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_message_close_container(reply); +} + const sd_bus_vtable bus_service_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Service, type), SD_BUS_VTABLE_PROPERTY_CONST), @@ -57,6 +127,9 @@ const sd_bus_vtable bus_service_vtable[] = { SD_BUS_PROPERTY("RootDirectoryStartOnly", "b", bus_property_get_bool, offsetof(Service, root_directory_start_only), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RemainAfterExit", "b", bus_property_get_bool, offsetof(Service, remain_after_exit), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("GuessMainPID", "b", bus_property_get_bool, offsetof(Service, guess_main_pid), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RestartPreventExitStatus", "(aiai)", property_get_exit_status_set, offsetof(Service, restart_prevent_status), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RestartForceExitStatus", "(aiai)", property_get_exit_status_set, offsetof(Service, restart_force_status), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SuccessExitStatus", "(aiai)", property_get_exit_status_set, offsetof(Service, success_status), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("MainPID", "u", bus_property_get_pid, offsetof(Service, main_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Service, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("BusName", "s", NULL, offsetof(Service, bus_name), SD_BUS_VTABLE_PROPERTY_CONST), @@ -88,265 +161,210 @@ const sd_bus_vtable bus_service_vtable[] = { SD_BUS_VTABLE_END }; -static int bus_service_set_transient_property( - Service *s, +static int bus_set_transient_exit_status( + Unit *u, const char *name, + ExitStatusSet *status_set, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error) { - ServiceExecCommand ci; + const int *status, *signal; + size_t sz_status, sz_signal, i; int r; - assert(s); - assert(name); - assert(message); - - flags |= UNIT_PRIVATE; + r = sd_bus_message_enter_container(message, 'r', "aiai"); + if (r < 0) + return r; - if (streq(name, "RemainAfterExit")) { - int b; + r = sd_bus_message_read_array(message, 'i', (const void **) &status, &sz_status); + if (r < 0) + return r; - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; + r = sd_bus_message_read_array(message, 'i', (const void **) &signal, &sz_signal); + if (r < 0) + return r; - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - s->remain_after_exit = b; - unit_write_settingf(UNIT(s), flags, name, "RemainAfterExit=%s", yes_no(b)); - } + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + if (sz_status == 0 && sz_signal == 0 && !UNIT_WRITE_FLAGS_NOOP(flags)) { + exit_status_set_free(status_set); + unit_write_settingf(u, flags, name, "%s=", name); return 1; + } - } else if (streq(name, "Type")) { - const char *t; - ServiceType k; - - r = sd_bus_message_read(message, "s", &t); - if (r < 0) - return r; - - k = service_type_from_string(t); - if (k < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid service type %s", t); + for (i = 0; i < sz_status; i++) { + if (status[i] < 0 || status[i] > 255) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid status code in %s: %i", name, status[i]); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - s->type = k; - unit_write_settingf(UNIT(s), flags, name, "Type=%s", service_type_to_string(s->type)); - } - - return 1; - } else if (streq(name, "RuntimeMaxUSec")) { - usec_t u; + r = set_ensure_allocated(&status_set->status, NULL); + if (r < 0) + return r; - r = sd_bus_message_read(message, "t", &u); - if (r < 0) - return r; + r = set_put(status_set->status, INT_TO_PTR(status[i])); + if (r < 0) + return r; - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - s->runtime_max_usec = u; - unit_write_settingf(UNIT(s), flags, name, "RuntimeMaxSec=" USEC_FMT "us", u); + unit_write_settingf(u, flags, name, "%s=%i", name, status[i]); } + } - return 1; - - } else if (streq(name, "Restart")) { - ServiceRestart sr; - const char *v; - - r = sd_bus_message_read(message, "s", &v); - if (r < 0) - return r; + for (i = 0; i < sz_signal; i++) { + const char *str; - if (isempty(v)) - sr = SERVICE_RESTART_NO; - else { - sr = service_restart_from_string(v); - if (sr < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid restart setting: %s", v); - } + str = signal_to_string(signal[i]); + if (!str) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal in %s: %i", name, signal[i]); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - s->restart = sr; - unit_write_settingf(UNIT(s), flags, name, "Restart=%s", service_restart_to_string(sr)); - } - - return 1; - - } else if (STR_IN_SET(name, - "StandardInputFileDescriptor", - "StandardOutputFileDescriptor", - "StandardErrorFileDescriptor")) { - int fd; + r = set_ensure_allocated(&status_set->signal, NULL); + if (r < 0) + return r; - r = sd_bus_message_read(message, "h", &fd); - if (r < 0) - return r; + r = set_put(status_set->signal, INT_TO_PTR(signal[i])); + if (r < 0) + return r; - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - int copy; - - copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); - if (copy < 0) - return -errno; - - if (streq(name, "StandardInputFileDescriptor")) { - asynchronous_close(s->stdin_fd); - s->stdin_fd = copy; - } else if (streq(name, "StandardOutputFileDescriptor")) { - asynchronous_close(s->stdout_fd); - s->stdout_fd = copy; - } else { - asynchronous_close(s->stderr_fd); - s->stderr_fd = copy; - } - - s->exec_context.stdio_as_fds = true; + unit_write_settingf(u, flags, name, "%s=%s", name, str); } + } - return 1; - - } else if (streq(name, "FileDescriptorStoreMax")) { - uint32_t u; + return 1; +} - r = sd_bus_message_read(message, "u", &u); - if (r < 0) - return r; +static int bus_set_transient_std_fd( + Unit *u, + const char *name, + int *p, + bool *b, + sd_bus_message *message, + UnitWriteFlags flags, + sd_bus_error *error) { - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - s->n_fd_store_max = (unsigned) u; - unit_write_settingf(UNIT(s), flags, name, "FileDescriptorStoreMax=%" PRIu32, u); - } + int fd, r; - return 1; + assert(p); + assert(b); - } else if (streq(name, "NotifyAccess")) { - const char *t; - NotifyAccess k; + r = sd_bus_message_read(message, "h", &fd); + if (r < 0) + return r; - r = sd_bus_message_read(message, "s", &t); - if (r < 0) - return r; + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + int copy; - k = notify_access_from_string(t); - if (k < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid notify access setting %s", t); + copy = fcntl(fd, F_DUPFD_CLOEXEC, 3); + if (copy < 0) + return -errno; - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - s->notify_access = k; - unit_write_settingf(UNIT(s), flags, name, "NotifyAccess=%s", notify_access_to_string(s->notify_access)); - } + asynchronous_close(*p); + *p = copy; + *b = true; + } - return 1; + return 1; +} +static BUS_DEFINE_SET_TRANSIENT_PARSE(notify_access, NotifyAccess, notify_access_from_string); +static BUS_DEFINE_SET_TRANSIENT_PARSE(service_type, ServiceType, service_type_from_string); +static BUS_DEFINE_SET_TRANSIENT_PARSE(service_restart, ServiceRestart, service_restart_from_string); +static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(bus_name, service_name_is_valid); - } else if ((ci = service_exec_command_from_string(name)) >= 0) { - unsigned n = 0; +static int bus_service_set_transient_property( + Service *s, + const char *name, + sd_bus_message *message, + UnitWriteFlags flags, + sd_bus_error *error) { - r = sd_bus_message_enter_container(message, 'a', "(sasb)"); - if (r < 0) - return r; + Unit *u = UNIT(s); + ServiceExecCommand ci; + int r; - while ((r = sd_bus_message_enter_container(message, 'r', "sasb")) > 0) { - _cleanup_strv_free_ char **argv = NULL; - const char *path; - int b; + assert(s); + assert(name); + assert(message); - r = sd_bus_message_read(message, "s", &path); - if (r < 0) - return r; + flags |= UNIT_PRIVATE; - if (!path_is_absolute(path)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute.", path); + if (streq(name, "PermissionsStartOnly")) + return bus_set_transient_bool(u, name, &s->permissions_start_only, message, flags, error); - r = sd_bus_message_read_strv(message, &argv); - if (r < 0) - return r; + if (streq(name, "RootDirectoryStartOnly")) + return bus_set_transient_bool(u, name, &s->root_directory_start_only, message, flags, error); - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; + if (streq(name, "RemainAfterExit")) + return bus_set_transient_bool(u, name, &s->remain_after_exit, message, flags, error); - r = sd_bus_message_exit_container(message); - if (r < 0) - return r; + if (streq(name, "GuessMainPID")) + return bus_set_transient_bool(u, name, &s->guess_main_pid, message, flags, error); - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - ExecCommand *c; + if (streq(name, "Type")) + return bus_set_transient_service_type(u, name, &s->type, message, flags, error); - c = new0(ExecCommand, 1); - if (!c) - return -ENOMEM; + if (streq(name, "RestartUSec")) + return bus_set_transient_usec(u, name, &s->restart_usec, message, flags, error); - c->path = strdup(path); - if (!c->path) { - free(c); - return -ENOMEM; - } + if (streq(name, "TimeoutStartUSec")) { + r = bus_set_transient_usec(u, name, &s->timeout_start_usec, message, flags, error); + if (r >= 0 && !UNIT_WRITE_FLAGS_NOOP(flags)) + s->start_timeout_defined = true; - c->argv = argv; - argv = NULL; + return r; + } - c->flags = b ? EXEC_COMMAND_IGNORE_FAILURE : 0; + if (streq(name, "TimeoutStopUSec")) + return bus_set_transient_usec(u, name, &s->timeout_stop_usec, message, flags, error); - path_kill_slashes(c->path); - exec_command_append_list(&s->exec_command[ci], c); - } + if (streq(name, "RuntimeMaxUSec")) + return bus_set_transient_usec(u, name, &s->runtime_max_usec, message, flags, error); - n++; - } + if (streq(name, "WatchdogUSec")) + return bus_set_transient_usec(u, name, &s->watchdog_usec, message, flags, error); - if (r < 0) - return r; + if (streq(name, "FileDescriptorStoreMax")) + return bus_set_transient_unsigned(u, name, &s->n_fd_store_max, message, flags, error); - r = sd_bus_message_exit_container(message); - if (r < 0) - return r; + if (streq(name, "NotifyAccess")) + return bus_set_transient_notify_access(u, name, &s->notify_access, message, flags, error); - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - _cleanup_free_ char *buf = NULL; - _cleanup_fclose_ FILE *f = NULL; - ExecCommand *c; - size_t size = 0; + if (streq(name, "PIDFile")) + return bus_set_transient_path(u, name, &s->pid_file, message, flags, error); - if (n == 0) - s->exec_command[ci] = exec_command_free_list(s->exec_command[ci]); + if (streq(name, "USBFunctionDescriptors")) + return bus_set_transient_path(u, name, &s->usb_function_descriptors, message, flags, error); - f = open_memstream(&buf, &size); - if (!f) - return -ENOMEM; + if (streq(name, "USBFunctionStrings")) + return bus_set_transient_path(u, name, &s->usb_function_strings, message, flags, error); - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + if (streq(name, "BusName")) + return bus_set_transient_bus_name(u, name, &s->bus_name, message, flags, error); - fputs("ExecStart=\n", f); + if (streq(name, "Restart")) + return bus_set_transient_service_restart(u, name, &s->restart, message, flags, error); - LIST_FOREACH(command, c, s->exec_command[ci]) { - _cleanup_free_ char *a = NULL, *t = NULL; - const char *p; + if (streq(name, "RestartPreventExitStatus")) + return bus_set_transient_exit_status(u, name, &s->restart_prevent_status, message, flags, error); - p = unit_escape_setting(c->path, UNIT_ESCAPE_C|UNIT_ESCAPE_SPECIFIERS, &t); - if (!p) - return -ENOMEM; + if (streq(name, "RestartForceExitStatus")) + return bus_set_transient_exit_status(u, name, &s->restart_force_status, message, flags, error); - a = unit_concat_strv(c->argv, UNIT_ESCAPE_C|UNIT_ESCAPE_SPECIFIERS); - if (!a) - return -ENOMEM; + if (streq(name, "SuccessExitStatus")) + return bus_set_transient_exit_status(u, name, &s->success_status, message, flags, error); - fprintf(f, "%s=%s@%s %s\n", - name, - c->flags & EXEC_COMMAND_IGNORE_FAILURE ? "-" : "", - p, - a); - } + if ((ci = service_exec_command_from_string(name)) >= 0) + return bus_set_transient_exec_command(u, name, &s->exec_command[ci], message, flags, error); - r = fflush_and_check(f); - if (r < 0) - return r; + if (streq(name, "StandardInputFileDescriptor")) + return bus_set_transient_std_fd(u, name, &s->stdin_fd, &s->exec_context.stdio_as_fds, message, flags, error); - unit_write_setting(UNIT(s), flags, name, buf); - } + if (streq(name, "StandardOutputFileDescriptor")) + return bus_set_transient_std_fd(u, name, &s->stdout_fd, &s->exec_context.stdio_as_fds, message, flags, error); - return 1; - } + if (streq(name, "StandardErrorFileDescriptor")) + return bus_set_transient_std_fd(u, name, &s->stderr_fd, &s->exec_context.stdio_as_fds, message, flags, error); return 0; } diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index 930b7fa87d..035651f213 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -22,8 +22,15 @@ #include "bus-util.h" #include "dbus-cgroup.h" #include "dbus-execute.h" +#include "dbus-kill.h" #include "dbus-socket.h" +#include "dbus-util.h" +#include "fd-util.h" +#include "parse-util.h" +#include "path-util.h" #include "socket.h" +#include "socket-protocol-list.h" +#include "socket-util.h" #include "string-util.h" #include "unit.h" @@ -141,6 +148,7 @@ const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_PROPERTY("MaxConnectionsPerSource", "u", bus_property_get_unsigned, offsetof(Socket, max_connections_per_source), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("MessageQueueMaxMessages", "x", bus_property_get_long, offsetof(Socket, mq_maxmsg), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("MessageQueueMessageSize", "x", bus_property_get_long, offsetof(Socket, mq_msgsize), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TCPCongestion", "s", NULL, offsetof(Socket, tcp_congestion), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ReusePort", "b", bus_property_get_bool, offsetof(Socket, reuse_port), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SmackLabel", "s", NULL, offsetof(Socket, smack), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SmackLabelIPIn", "s", NULL, offsetof(Socket, smack_ip_in), SD_BUS_VTABLE_PROPERTY_CONST), @@ -162,6 +170,286 @@ const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_VTABLE_END }; +static inline bool check_size_t_truncation(uint64_t t) { + return (size_t) t == t; +} + +static inline const char* socket_protocol_to_name_supported(int32_t i) { + if (!IN_SET(i, IPPROTO_UDPLITE, IPPROTO_SCTP)) + return NULL; + + return socket_protocol_to_name(i); +} + +static BUS_DEFINE_SET_TRANSIENT(int, "i", int32_t, int, "%" PRIi32); +static BUS_DEFINE_SET_TRANSIENT(message_queue, "x", int64_t, long, "%" PRIi64); +static BUS_DEFINE_SET_TRANSIENT_IS_VALID(size_t_check_truncation, "t", uint64_t, size_t, "%" PRIu64, check_size_t_truncation); +static BUS_DEFINE_SET_TRANSIENT_PARSE(bind_ipv6_only, SocketAddressBindIPv6Only, parse_socket_address_bind_ipv6_only_or_bool); +static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(fdname, fdname_is_valid); +static BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(ifname, ifname_valid); +static BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(ip_tos, "i", int32_t, int, "%" PRIi32, ip_tos_to_string_alloc); +static BUS_DEFINE_SET_TRANSIENT_TO_STRING(socket_protocol, "i", int32_t, int, "%" PRIi32, socket_protocol_to_name_supported); + +static int bus_socket_set_transient_property( + Socket *s, + const char *name, + sd_bus_message *message, + UnitWriteFlags flags, + sd_bus_error *error) { + + SocketExecCommand ci; + Unit *u = UNIT(s); + int r; + + assert(s); + assert(name); + assert(message); + + flags |= UNIT_PRIVATE; + + if (streq(name, "Accept")) + return bus_set_transient_bool(u, name, &s->accept, message, flags, error); + + if (streq(name, "Writable")) + return bus_set_transient_bool(u, name, &s->writable, message, flags, error); + + if (streq(name, "KeepAlive")) + return bus_set_transient_bool(u, name, &s->keep_alive, message, flags, error); + + if (streq(name, "NoDelay")) + return bus_set_transient_bool(u, name, &s->no_delay, message, flags, error); + + if (streq(name, "FreeBind")) + return bus_set_transient_bool(u, name, &s->free_bind, message, flags, error); + + if (streq(name, "Transparent")) + return bus_set_transient_bool(u, name, &s->transparent, message, flags, error); + + if (streq(name, "Broadcast")) + return bus_set_transient_bool(u, name, &s->broadcast, message, flags, error); + + if (streq(name, "PassCredentials")) + return bus_set_transient_bool(u, name, &s->pass_cred, message, flags, error); + + if (streq(name, "PassSecurity")) + return bus_set_transient_bool(u, name, &s->pass_sec, message, flags, error); + + if (streq(name, "ReusePort")) + return bus_set_transient_bool(u, name, &s->reuse_port, message, flags, error); + + if (streq(name, "RemoveOnStop")) + return bus_set_transient_bool(u, name, &s->remove_on_stop, message, flags, error); + + if (streq(name, "SELinuxContextFromNet")) + return bus_set_transient_bool(u, name, &s->selinux_context_from_net, message, flags, error); + + if (streq(name, "Priority")) + return bus_set_transient_int(u, name, &s->priority, message, flags, error); + + if (streq(name, "IPTTL")) + return bus_set_transient_int(u, name, &s->ip_ttl, message, flags, error); + + if (streq(name, "Mark")) + return bus_set_transient_int(u, name, &s->mark, message, flags, error); + + if (streq(name, "Backlog")) + return bus_set_transient_unsigned(u, name, &s->backlog, message, flags, error); + + if (streq(name, "MaxConnections")) + return bus_set_transient_unsigned(u, name, &s->max_connections, message, flags, error); + + if (streq(name, "MaxConnectionsPerSource")) + return bus_set_transient_unsigned(u, name, &s->max_connections_per_source, message, flags, error); + + if (streq(name, "KeepAliveProbes")) + return bus_set_transient_unsigned(u, name, &s->keep_alive_cnt, message, flags, error); + + if (streq(name, "TriggerLimitBurst")) + return bus_set_transient_unsigned(u, name, &s->trigger_limit.burst, message, flags, error); + + if (streq(name, "SocketMode")) + return bus_set_transient_mode_t(u, name, &s->socket_mode, message, flags, error); + + if (streq(name, "DirectoryMode")) + return bus_set_transient_mode_t(u, name, &s->directory_mode, message, flags, error); + + if (streq(name, "MessageQueueMaxMessages")) + return bus_set_transient_message_queue(u, name, &s->mq_maxmsg, message, flags, error); + + if (streq(name, "MessageQueueMessageSize")) + return bus_set_transient_message_queue(u, name, &s->mq_msgsize, message, flags, error); + + if (streq(name, "TimeoutUSec")) + return bus_set_transient_usec_fix_0(u, name, &s->timeout_usec, message, flags, error); + + if (streq(name, "KeepAliveTimeUSec")) + return bus_set_transient_usec(u, name, &s->keep_alive_time, message, flags, error); + + if (streq(name, "KeepAliveIntervalUSec")) + return bus_set_transient_usec(u, name, &s->keep_alive_interval, message, flags, error); + + if (streq(name, "DeferAcceptUSec")) + return bus_set_transient_usec(u, name, &s->defer_accept, message, flags, error); + + if (streq(name, "TriggerLimitIntervalUSec")) + return bus_set_transient_usec(u, name, &s->trigger_limit.interval, message, flags, error); + + if (streq(name, "SmackLabel")) + return bus_set_transient_string(u, name, &s->smack, message, flags, error); + + if (streq(name, "SmackLabelIPin")) + return bus_set_transient_string(u, name, &s->smack_ip_in, message, flags, error); + + if (streq(name, "SmackLabelIPOut")) + return bus_set_transient_string(u, name, &s->smack_ip_out, message, flags, error); + + if (streq(name, "TCPCongestion")) + return bus_set_transient_string(u, name, &s->tcp_congestion, message, flags, error); + + if (streq(name, "FileDescriptorName")) + return bus_set_transient_fdname(u, name, &s->fdname, message, flags, error); + + if (streq(name, "SocketUser")) + return bus_set_transient_user(u, name, &s->user, message, flags, error); + + if (streq(name, "SocketGroup")) + return bus_set_transient_user(u, name, &s->group, message, flags, error); + + if (streq(name, "BindIPv6Only")) + return bus_set_transient_bind_ipv6_only(u, name, &s->bind_ipv6_only, message, flags, error); + + if (streq(name, "ReceiveBuffer")) + return bus_set_transient_size_t_check_truncation(u, name, &s->receive_buffer, message, flags, error); + + if (streq(name, "SendBuffer")) + return bus_set_transient_size_t_check_truncation(u, name, &s->send_buffer, message, flags, error); + + if (streq(name, "PipeSize")) + return bus_set_transient_size_t_check_truncation(u, name, &s->pipe_size, message, flags, error); + + if (streq(name, "BindToDevice")) + return bus_set_transient_ifname(u, name, &s->bind_to_device, message, flags, error); + + if (streq(name, "IPTOS")) + return bus_set_transient_ip_tos(u, name, &s->ip_tos, message, flags, error); + + if (streq(name, "SocketProtocol")) + return bus_set_transient_socket_protocol(u, name, &s->socket_protocol, message, flags, error); + + if ((ci = socket_exec_command_from_string(name)) >= 0) + return bus_set_transient_exec_command(u, name, &s->exec_command[ci], message, flags, error); + + if (streq(name, "Symlinks")) { + _cleanup_strv_free_ char **l = NULL; + char **p; + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + STRV_FOREACH(p, l) { + if (!path_is_absolute(*p)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Symlink path is not absolute: %s", *p); + } + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + if (strv_isempty(l)) { + s->symlinks = strv_free(s->symlinks); + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=", name); + } else { + _cleanup_free_ char *joined = NULL; + + r = strv_extend_strv(&s->symlinks, l, true); + if (r < 0) + return -ENOMEM; + + joined = strv_join(l, " "); + if (!joined) + return -ENOMEM; + + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, joined); + } + } + + return 1; + + } else if (streq(name, "Listen")) { + const char *t, *a; + bool empty = true; + + r = sd_bus_message_enter_container(message, 'a', "(ss)"); + if (r < 0) + return r; + + while ((r = sd_bus_message_read(message, "(ss)", &t, &a)) > 0) { + _cleanup_free_ SocketPort *p = NULL; + + p = new0(SocketPort, 1); + if (!p) + return log_oom(); + + p->type = socket_port_type_from_string(t); + if (p->type < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown Socket type: %s", t); + + if (p->type != SOCKET_SOCKET) { + p->path = strdup(a); + path_kill_slashes(p->path); + + } else if (streq(t, "Netlink")) { + r = socket_address_parse_netlink(&p->address, a); + if (r < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid netlink address: %s", a); + + } else { + r = socket_address_parse(&p->address, a); + if (r < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address: %s", a); + + p->address.type = socket_address_type_from_string(t); + if (p->address.type < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid address type: %s", t); + + if (socket_address_family(&p->address) != AF_LOCAL && p->address.type == SOCK_SEQPACKET) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Address family not supported: %s", a); + } + + p->fd = -1; + p->auxiliary_fds = NULL; + p->n_auxiliary_fds = 0; + p->socket = s; + + empty = false; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + SocketPort *tail; + + LIST_FIND_TAIL(port, s->ports, tail); + LIST_INSERT_AFTER(port, s->ports, tail, p); + + p = NULL; + + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "Listen%s=%s", t, a); + } + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags) && empty) { + socket_free_ports(s); + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "ListenStream="); + } + + return 1; + } + + return 0; +} + int bus_socket_set_property( Unit *u, const char *name, @@ -170,12 +458,37 @@ int bus_socket_set_property( sd_bus_error *error) { Socket *s = SOCKET(u); + int r; assert(s); assert(name); assert(message); - return bus_cgroup_set_property(u, &s->cgroup_context, name, message, flags, error); + assert(s); + assert(name); + assert(message); + + r = bus_cgroup_set_property(u, &s->cgroup_context, name, message, flags, error); + if (r != 0) + return r; + + if (u->transient && u->load_state == UNIT_STUB) { + /* This is a transient unit, let's load a little more */ + + r = bus_socket_set_transient_property(s, name, message, flags, error); + if (r != 0) + return r; + + r = bus_exec_context_set_transient_property(u, &s->exec_context, name, message, flags, error); + if (r != 0) + return r; + + r = bus_kill_context_set_transient_property(u, &s->kill_context, name, message, flags, error); + if (r != 0) + return r; + } + + return 0; } int bus_socket_commit_properties(Unit *u) { diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c index 3e64536b17..1eedf217fe 100644 --- a/src/core/dbus-timer.c +++ b/src/core/dbus-timer.c @@ -21,6 +21,7 @@ #include "alloc-util.h" #include "bus-util.h" #include "dbus-timer.h" +#include "dbus-util.h" #include "strv.h" #include "timer.h" #include "unit.h" @@ -179,6 +180,7 @@ static int bus_timer_set_transient_property( UnitWriteFlags flags, sd_bus_error *error) { + Unit *u = UNIT(t); int r; assert(t); @@ -187,7 +189,130 @@ static int bus_timer_set_transient_property( flags |= UNIT_PRIVATE; - if (STR_IN_SET(name, + if (streq(name, "AccuracyUSec")) + return bus_set_transient_usec(u, name, &t->accuracy_usec, message, flags, error); + + if (streq(name, "AccuracySec")) { + log_notice("Client is using obsolete AccuracySec= transient property, please use AccuracyUSec= instead."); + return bus_set_transient_usec(u, "AccuracyUSec", &t->accuracy_usec, message, flags, error); + } + + if (streq(name, "RandomizedDelayUSec")) + return bus_set_transient_usec(u, name, &t->random_usec, message, flags, error); + + if (streq(name, "WakeSystem")) + return bus_set_transient_bool(u, name, &t->wake_system, message, flags, error); + + if (streq(name, "Persistent")) + return bus_set_transient_bool(u, name, &t->persistent, message, flags, error); + + if (streq(name, "RemainAfterElapse")) + return bus_set_transient_bool(u, name, &t->remain_after_elapse, message, flags, error); + + if (streq(name, "TimersMonotonic")) { + const char *base_name; + usec_t usec = 0; + bool empty = true; + + r = sd_bus_message_enter_container(message, 'a', "(st)"); + if (r < 0) + return r; + + while ((r = sd_bus_message_read(message, "(st)", &base_name, &usec)) > 0) { + TimerBase b; + + b = timer_base_from_string(base_name); + if (b < 0 || b == TIMER_CALENDAR) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid timer base: %s", base_name); + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + char ts[FORMAT_TIMESPAN_MAX]; + TimerValue *v; + + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", base_name, + format_timespan(ts, sizeof(ts), usec, USEC_PER_MSEC)); + + v = new0(TimerValue, 1); + if (!v) + return -ENOMEM; + + v->base = b; + v->value = usec; + + LIST_PREPEND(value, t->values, v); + } + + empty = false; + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags) && empty) { + timer_free_values(t); + unit_write_setting(u, flags, name, "OnActiveSec="); + } + + return 1; + + } else if (streq(name, "TimersCalendar")) { + const char *base_name, *str; + bool empty = true; + + r = sd_bus_message_enter_container(message, 'a', "(ss)"); + if (r < 0) + return r; + + while ((r = sd_bus_message_read(message, "(ss)", &base_name, &str)) > 0) { + _cleanup_(calendar_spec_freep) CalendarSpec *c = NULL; + TimerBase b; + + b = timer_base_from_string(base_name); + if (b != TIMER_CALENDAR) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid timer base: %s", base_name); + + r = calendar_spec_from_string(str, &c); + if (r == -EINVAL) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid calendar spec: %s", str); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + TimerValue *v; + + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", base_name, str); + + v = new0(TimerValue, 1); + if (!v) + return -ENOMEM; + + v->base = b; + v->calendar_spec = c; + c = NULL; + + LIST_PREPEND(value, t->values, v); + } + + empty = false; + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags) && empty) { + timer_free_values(t); + unit_write_setting(u, flags, name, "OnCalendar="); + } + + return 1; + + } else if (STR_IN_SET(name, "OnActiveSec", "OnBootSec", "OnStartupSec", @@ -196,27 +321,30 @@ static int bus_timer_set_transient_property( TimerValue *v; TimerBase b = _TIMER_BASE_INVALID; - usec_t u = 0; + usec_t usec = 0; + + log_notice("Client is using obsolete %s= transient property, please use TimersMonotonic= instead.", name); b = timer_base_from_string(name); if (b < 0) - return -EINVAL; + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown timer base"); - r = sd_bus_message_read(message, "t", &u); + r = sd_bus_message_read(message, "t", &usec); if (r < 0) return r; if (!UNIT_WRITE_FLAGS_NOOP(flags)) { char time[FORMAT_TIMESPAN_MAX]; - unit_write_settingf(UNIT(t), flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, format_timespan(time, sizeof(time), u, USEC_PER_MSEC)); + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, + format_timespan(time, sizeof(time), usec, USEC_PER_MSEC)); v = new0(TimerValue, 1); if (!v) return -ENOMEM; v->base = b; - v->value = u; + v->value = usec; LIST_PREPEND(value, t->values, v); } @@ -226,84 +354,36 @@ static int bus_timer_set_transient_property( } else if (streq(name, "OnCalendar")) { TimerValue *v; - CalendarSpec *c = NULL; + _cleanup_(calendar_spec_freep) CalendarSpec *c = NULL; const char *str; + log_notice("Client is using obsolete %s= transient property, please use TimersCalendar= instead.", name); + r = sd_bus_message_read(message, "s", &str); if (r < 0) return r; if (!UNIT_WRITE_FLAGS_NOOP(flags)) { r = calendar_spec_from_string(str, &c); + if (r == -EINVAL) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid calendar spec"); if (r < 0) return r; - unit_write_settingf(UNIT(t), flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, str); + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, str); v = new0(TimerValue, 1); - if (!v) { - calendar_spec_free(c); + if (!v) return -ENOMEM; - } v->base = TIMER_CALENDAR; v->calendar_spec = c; + c = NULL; LIST_PREPEND(value, t->values, v); } return 1; - - } else if (STR_IN_SET(name, "AccuracyUSec", "AccuracySec")) { - usec_t u = 0; - - if (streq(name, "AccuracySec")) - log_notice("Client is using obsolete AccuracySec= transient property, please use AccuracyUSec= instead."); - - r = sd_bus_message_read(message, "t", &u); - if (r < 0) - return r; - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - t->accuracy_usec = u; - unit_write_settingf(UNIT(t), flags, name, "AccuracySec=" USEC_FMT "us", u); - } - - return 1; - - } else if (streq(name, "RandomizedDelayUSec")) { - usec_t u = 0; - - r = sd_bus_message_read(message, "t", &u); - if (r < 0) - return r; - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - t->random_usec = u; - unit_write_settingf(UNIT(t), flags, name, "RandomizedDelaySec=" USEC_FMT "us", u); - } - - return 1; - - } else if (STR_IN_SET(name, "WakeSystem", "Persistent", "RemainAfterElapse")) { - int b; - - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - if (streq(name, "WakeSystem")) - t->wake_system = b; - else if (streq(name, "Persistent")) - t->persistent = b; - else /* RemainAfterElapse */ - t->remain_after_elapse = b; - - unit_write_settingf(UNIT(t), flags, name, "%s=%s", name, yes_no(b)); - } - - return 1; } return 0; diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index cdab461d58..7085eee930 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -24,12 +24,15 @@ #include "bpf-firewall.h" #include "bus-common-errors.h" #include "cgroup-util.h" +#include "condition.h" #include "dbus-job.h" #include "dbus-unit.h" +#include "dbus-util.h" #include "dbus.h" #include "fd-util.h" #include "locale-util.h" #include "log.h" +#include "path-util.h" #include "process-util.h" #include "selinux-access.h" #include "signal-util.h" @@ -37,6 +40,7 @@ #include "string-util.h" #include "strv.h" #include "user-util.h" +#include "web-util.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_collect_mode, collect_mode, CollectMode); static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_load_state, unit_load_state, UnitLoadState); @@ -795,7 +799,7 @@ const sd_bus_vtable bus_unit_vtable[] = { 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("Perpetual", "b", bus_property_get_bool, offsetof(Unit, perpetual), 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("StartLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), 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_emergency_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("FailureAction", "s", property_get_emergency_action, offsetof(Unit, failure_action), SD_BUS_VTABLE_PROPERTY_CONST), @@ -823,6 +827,7 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_obsolete_dependencies, 0, SD_BUS_VTABLE_HIDDEN), SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), + SD_BUS_PROPERTY("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), SD_BUS_VTABLE_END }; @@ -1352,6 +1357,81 @@ static int bus_unit_set_live_property( return 0; } +static BUS_DEFINE_SET_TRANSIENT_PARSE(collect_mode, CollectMode, collect_mode_from_string); +static BUS_DEFINE_SET_TRANSIENT_PARSE(emergency_action, EmergencyAction, emergency_action_from_string); +static BUS_DEFINE_SET_TRANSIENT_PARSE(job_mode, JobMode, job_mode_from_string); + +static int bus_set_transient_conditions( + Unit *u, + const char *name, + Condition **list, + bool is_condition, + sd_bus_message *message, + UnitWriteFlags flags, + sd_bus_error *error) { + + const char *type_name, *param; + int trigger, negate, r; + bool empty = true; + + assert(list); + + r = sd_bus_message_enter_container(message, 'a', "(sbbs)"); + if (r < 0) + return r; + + while ((r = sd_bus_message_read(message, "(sbbs)", &type_name, &trigger, &negate, ¶m)) > 0) { + ConditionType t; + + t = is_condition ? condition_type_from_string(type_name) : assert_type_from_string(type_name); + if (t < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid condition type: %s", type_name); + + if (t != CONDITION_NULL) { + if (isempty(param)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Condition parameter in %s is empty", type_name); + + if (condition_takes_path(t) && !path_is_absolute(param)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path in condition %s is not absolute: %s", type_name, param); + } else + param = NULL; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + Condition *c; + + c = condition_new(t, param, trigger, negate); + if (!c) + return -ENOMEM; + + LIST_PREPEND(conditions, *list, c); + + if (t != CONDITION_NULL) + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, + "%s=%s%s%s", type_name, + trigger ? "|" : "", negate ? "!" : "", param); + else + unit_write_settingf(u, flags, name, + "%s=%s%s", type_name, + trigger ? "|" : "", yes_no(!negate)); + } + + empty = false; + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags) && empty) { + *list = condition_free_list(*list); + unit_write_settingf(u, flags, name, "%sNull=", is_condition ? "Condition" : "Assert"); + } + + return 1; +} + static int bus_unit_set_transient_property( Unit *u, const char *name, @@ -1359,6 +1439,7 @@ static int bus_unit_set_transient_property( UnitWriteFlags flags, sd_bus_error *error) { + UnitDependency d = _UNIT_DEPENDENCY_INVALID; int r; assert(u); @@ -1368,35 +1449,100 @@ static int bus_unit_set_transient_property( /* Handles settings when transient units are created. This settings cannot be altered anymore after the unit * has been created. */ - if (streq(name, "DefaultDependencies")) { - int b; + if (streq(name, "SourcePath")) + return bus_set_transient_path(u, name, &u->source_path, message, flags, error); - r = sd_bus_message_read(message, "b", &b); - if (r < 0) - return r; + if (streq(name, "StopWhenUnneeded")) + return bus_set_transient_bool(u, name, &u->stop_when_unneeded, message, flags, error); - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - u->default_dependencies = b; - unit_write_settingf(u, flags, name, "DefaultDependencies=%s", yes_no(b)); - } + if (streq(name, "RefuseManualStart")) + return bus_set_transient_bool(u, name, &u->refuse_manual_start, message, flags, error); - return 1; + if (streq(name, "RefuseManualStop")) + return bus_set_transient_bool(u, name, &u->refuse_manual_stop, message, flags, error); - } else if (streq(name, "CollectMode")) { - const char *s; - CollectMode m; + if (streq(name, "AllowIsolate")) + return bus_set_transient_bool(u, name, &u->allow_isolate, message, flags, error); - r = sd_bus_message_read(message, "s", &s); + if (streq(name, "DefaultDependencies")) + return bus_set_transient_bool(u, name, &u->default_dependencies, message, flags, error); + + if (streq(name, "OnFailureJobMode")) + return bus_set_transient_job_mode(u, name, &u->on_failure_job_mode, message, flags, error); + + if (streq(name, "IgnoreOnIsolate")) + return bus_set_transient_bool(u, name, &u->ignore_on_isolate, message, flags, error); + + if (streq(name, "JobTimeoutUSec")) { + r = bus_set_transient_usec_fix_0(u, name, &u->job_timeout, message, flags, error); + if (r >= 0 && !UNIT_WRITE_FLAGS_NOOP(flags) && !u->job_running_timeout_set) + u->job_running_timeout = u->job_timeout; + } + + if (streq(name, "JobRunningTimeoutUSec")) { + r = bus_set_transient_usec_fix_0(u, name, &u->job_running_timeout, message, flags, error); + if (r >= 0 && !UNIT_WRITE_FLAGS_NOOP(flags)) + u->job_running_timeout_set = true; + + return r; + } + + if (streq(name, "JobTimeoutAction")) + return bus_set_transient_emergency_action(u, name, &u->job_timeout_action, message, flags, error); + + if (streq(name, "JobTimeoutRebootArgument")) + return bus_set_transient_string(u, name, &u->job_timeout_reboot_arg, message, flags, error); + + if (streq(name, "StartLimitIntervalUSec")) + return bus_set_transient_usec(u, name, &u->start_limit.interval, message, flags, error); + + if (streq(name, "StartLimitBurst")) + return bus_set_transient_unsigned(u, name, &u->start_limit.burst, message, flags, error); + + if (streq(name, "StartLimitAction")) + return bus_set_transient_emergency_action(u, name, &u->start_limit_action, message, flags, error); + + if (streq(name, "FailureAction")) + return bus_set_transient_emergency_action(u, name, &u->failure_action, message, flags, error); + + if (streq(name, "SuccessAction")) + return bus_set_transient_emergency_action(u, name, &u->success_action, message, flags, error); + + if (streq(name, "RebootArgument")) + return bus_set_transient_string(u, name, &u->reboot_arg, message, flags, error); + + if (streq(name, "CollectMode")) + return bus_set_transient_collect_mode(u, name, &u->collect_mode, message, flags, error); + + if (streq(name, "Conditions")) + return bus_set_transient_conditions(u, name, &u->conditions, true, message, flags, error); + + if (streq(name, "Asserts")) + return bus_set_transient_conditions(u, name, &u->asserts, false, message, flags, error); + + if (streq(name, "Documentation")) { + _cleanup_strv_free_ char **l = NULL; + char **p; + + r = sd_bus_message_read_strv(message, &l); if (r < 0) return r; - m = collect_mode_from_string(s); - if (m < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown garbage collection mode: %s", s); + STRV_FOREACH(p, l) { + if (!documentation_url_is_valid(*p)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid URL in %s: %s", name, *p); + } if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - u->collect_mode = m; - unit_write_settingf(u, flags, name, "CollectMode=%s", collect_mode_to_string(m)); + if (strv_isempty(l)) { + u->documentation = strv_free(u->documentation); + unit_write_settingf(u, flags, name, "%s=", name); + } else { + strv_extend_strv(&u->documentation, l, false); + + STRV_FOREACH(p, l) + unit_write_settingf(u, flags, name, "%s=%s", name, *p); + } } return 1; @@ -1439,30 +1585,40 @@ static int bus_unit_set_transient_property( return 1; - } else if (STR_IN_SET(name, - "Requires", "RequiresOverridable", - "Requisite", "RequisiteOverridable", - "Wants", - "BindsTo", - "Conflicts", - "Before", "After", - "OnFailure", - "PropagatesReloadTo", "ReloadPropagatedFrom", - "PartOf")) { - - UnitDependency d; - const char *other; + } else if (streq(name, "RequiresMountsFor")) { + _cleanup_strv_free_ char **l = NULL; + char **p; - if (streq(name, "RequiresOverridable")) - d = UNIT_REQUIRES; /* redirect for obsolete unit dependency type */ - else if (streq(name, "RequisiteOverridable")) - d = UNIT_REQUISITE; /* same here */ - else { - d = unit_dependency_from_string(name); - if (d < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit dependency: %s", name); + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + STRV_FOREACH(p, l) { + if (!path_is_absolute(*p)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path specified in %s is not absolute: %s", name, *p); + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + r = unit_require_mounts_for(u, *p, UNIT_DEPENDENCY_FILE); + if (r < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to add required mount \"%s\": %m", *p); + + unit_write_settingf(u, flags, name, "%s=%s", name, *p); + } } + return 1; + } + + if (streq(name, "RequiresOverridable")) + d = UNIT_REQUIRES; /* redirect for obsolete unit dependency type */ + else if (streq(name, "RequisiteOverridable")) + d = UNIT_REQUISITE; /* same here */ + else + d = unit_dependency_from_string(name); + + if (d >= 0) { + const char *other; + r = sd_bus_message_enter_container(message, 'a', "s"); if (r < 0) return r; @@ -1482,7 +1638,7 @@ static int bus_unit_set_transient_property( if (!label) return -ENOMEM; - unit_write_settingf(u, flags, label, "%s=%s", name, other); + unit_write_settingf(u, flags, label, "%s=%s", unit_dependency_to_string(d), other); } } @@ -1495,30 +1651,6 @@ static int bus_unit_set_transient_property( return 1; - } else if (STR_IN_SET(name, "FailureAction", "SuccessAction")) { - EmergencyAction action; - const char *s; - - r = sd_bus_message_read(message, "s", &s); - if (r < 0) - return r; - - action = emergency_action_from_string(s); - if (action < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid emergency action: %s", s); - - if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - - if (streq(name, "FailureAction")) - u->failure_action = action; - else - u->success_action = action; - - unit_write_settingf(u, flags, name, "%s=%s", name, emergency_action_to_string(action)); - } - - return 1; - } else if (streq(name, "AddRef")) { int b; diff --git a/src/core/dbus-util.c b/src/core/dbus-util.c new file mode 100644 index 0000000000..75bbd07604 --- /dev/null +++ b/src/core/dbus-util.c @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/*** + 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 "bus-util.h" +#include "dbus-util.h" +#include "parse-util.h" +#include "path-util.h" +#include "unit-printf.h" +#include "user-util.h" +#include "unit.h" + +BUS_DEFINE_SET_TRANSIENT(mode_t, "u", uint32_t, mode_t, "%040o"); +BUS_DEFINE_SET_TRANSIENT(unsigned, "u", uint32_t, unsigned, "%" PRIu32); +BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(user, valid_user_group_name_or_id); +BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(path, path_is_absolute); + +int bus_set_transient_string( + Unit *u, + const char *name, + char **p, + sd_bus_message *message, + UnitWriteFlags flags, + sd_bus_error *error) { + + const char *v; + int r; + + assert(p); + + r = sd_bus_message_read(message, "s", &v); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + r = free_and_strdup(p, empty_to_null(v)); + if (r < 0) + return r; + + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, + "%s=%s", name, strempty(v)); + } + + return 1; +} + +int bus_set_transient_bool( + Unit *u, + const char *name, + bool *p, + sd_bus_message *message, + UnitWriteFlags flags, + sd_bus_error *error) { + + int v, r; + + assert(p); + + r = sd_bus_message_read(message, "b", &v); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + *p = v; + unit_write_settingf(u, flags, name, "%s=%s", name, yes_no(v)); + } + + return 1; +} + +int bus_set_transient_usec_internal( + Unit *u, + const char *name, + usec_t *p, + bool fix_0, + sd_bus_message *message, + UnitWriteFlags flags, + sd_bus_error *error) { + + uint64_t v; + int r; + + assert(p); + + r = sd_bus_message_read(message, "t", &v); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + char *n, ts[FORMAT_TIMESPAN_MAX]; + + if (fix_0) + *p = v != 0 ? v: USEC_INFINITY; + else + *p = v; + + n = strndupa(name, strlen(name) - 4); + unit_write_settingf(u, flags, name, "%sSec=%s", n, + format_timespan(ts, sizeof(ts), v, USEC_PER_MSEC)); + } + + return 1; +} diff --git a/src/core/dbus-util.h b/src/core/dbus-util.h new file mode 100644 index 0000000000..8260298577 --- /dev/null +++ b/src/core/dbus-util.h @@ -0,0 +1,351 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +/*** + 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 "sd-bus.h" +#include "unit.h" + +#define BUS_DEFINE_SET_TRANSIENT(function, bus_type, type, cast_type, fmt) \ + int bus_set_transient_##function( \ + Unit *u, \ + const char *name, \ + cast_type *p, \ + sd_bus_message *message, \ + UnitWriteFlags flags, \ + sd_bus_error *error) { \ + \ + type v; \ + int r; \ + \ + assert(p); \ + \ + r = sd_bus_message_read(message, bus_type, &v); \ + if (r < 0) \ + return r; \ + \ + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \ + *p = (cast_type) v; \ + unit_write_settingf(u, flags, name, \ + "%s=" fmt, name, v); \ + } \ + \ + return 1; \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +#define BUS_DEFINE_SET_TRANSIENT_IS_VALID(function, bus_type, type, cast_type, fmt, check) \ + int bus_set_transient_##function( \ + Unit *u, \ + const char *name, \ + cast_type *p, \ + sd_bus_message *message, \ + UnitWriteFlags flags, \ + sd_bus_error *error) { \ + \ + type v; \ + int r; \ + \ + assert(p); \ + \ + r = sd_bus_message_read(message, bus_type, &v); \ + if (r < 0) \ + return r; \ + \ + if (!check(v)) \ + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \ + "Invalid %s setting: " fmt, name, v); \ + \ + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \ + *p = (cast_type) v; \ + unit_write_settingf(u, flags, name, \ + "%s=" fmt, name, v); \ + } \ + \ + return 1; \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +#define BUS_DEFINE_SET_TRANSIENT_TO_STRING(function, bus_type, type, cast_type, fmt, to_string) \ + int bus_set_transient_##function( \ + Unit *u, \ + const char *name, \ + cast_type *p, \ + sd_bus_message *message, \ + UnitWriteFlags flags, \ + sd_bus_error *error) { \ + \ + const char *s; \ + type v; \ + int r; \ + \ + assert(p); \ + \ + r = sd_bus_message_read(message, bus_type, &v); \ + if (r < 0) \ + return r; \ + \ + s = to_string(v); \ + if (!s) \ + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \ + "Invalid %s setting: " fmt, name, v); \ + \ + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \ + *p = (cast_type) v; \ + unit_write_settingf(u, flags, name, \ + "%s=%s", name, s); \ + } \ + \ + return 1; \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +#define BUS_DEFINE_SET_TRANSIENT_TO_STRING_ALLOC(function, bus_type, type, cast_type, fmt, to_string) \ + int bus_set_transient_##function( \ + Unit *u, \ + const char *name, \ + cast_type *p, \ + sd_bus_message *message, \ + UnitWriteFlags flags, \ + sd_bus_error *error) { \ + \ + _cleanup_free_ char *s = NULL; \ + type v; \ + int r; \ + \ + assert(p); \ + \ + r = sd_bus_message_read(message, bus_type, &v); \ + if (r < 0) \ + return r; \ + \ + r = to_string(v, &s); \ + if (r == -EINVAL) \ + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \ + "Invalid %s setting: " fmt, name, v); \ + if (r < 0) \ + return r; \ + \ + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \ + *p = (cast_type) v; \ + unit_write_settingf(u, flags, name, \ + "%s=%s", name, s); \ + } \ + \ + return 1; \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +#define BUS_DEFINE_SET_TRANSIENT_PARSE(function, type, parse) \ + int bus_set_transient_##function( \ + Unit *u, \ + const char *name, \ + type *p, \ + sd_bus_message *message, \ + UnitWriteFlags flags, \ + sd_bus_error *error) { \ + \ + const char *s; \ + type v; \ + int r; \ + \ + assert(p); \ + \ + r = sd_bus_message_read(message, "s", &s); \ + if (r < 0) \ + return r; \ + \ + v = parse(s); \ + if (v < 0) \ + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \ + "Invalid %s setting: %s", name, s); \ + \ + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \ + *p = v; \ + unit_write_settingf(u, flags, name, \ + "%s=%s", name, s); \ + } \ + \ + return 1; \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +#define BUS_DEFINE_SET_TRANSIENT_PARSE_PTR(function, type, parse) \ + int bus_set_transient_##function( \ + Unit *u, \ + const char *name, \ + type *p, \ + sd_bus_message *message, \ + UnitWriteFlags flags, \ + sd_bus_error *error) { \ + \ + const char *s; \ + type v; \ + int r; \ + \ + assert(p); \ + \ + r = sd_bus_message_read(message, "s", &s); \ + if (r < 0) \ + return r; \ + \ + r = parse(s, &v); \ + if (r < 0) \ + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \ + "Invalid %s setting: %s", name, s); \ + \ + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \ + *p = v; \ + unit_write_settingf(u, flags, name, \ + "%s=%s", name, strempty(s)); \ + } \ + \ + return 1; \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +#define BUS_DEFINE_SET_TRANSIENT_STRING_WITH_CHECK(function, check) \ + int bus_set_transient_##function( \ + Unit *u, \ + const char *name, \ + char **p, \ + sd_bus_message *message, \ + UnitWriteFlags flags, \ + sd_bus_error *error) { \ + \ + const char *v; \ + int r; \ + \ + assert(p); \ + \ + r = sd_bus_message_read(message, "s", &v); \ + if (r < 0) \ + return r; \ + \ + if (!isempty(v) && !check(v)) \ + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \ + "Invalid %s setting: %s", name, v); \ + \ + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \ + r = free_and_strdup(p, empty_to_null(v)); \ + if (r < 0) \ + return r; \ + \ + unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, \ + "%s=%s", name, strempty(v)); \ + } \ + \ + return 1; \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +#define BUS_DEFINE_SET_CGROUP_WEIGHT(function, mask, check, val, str) \ + int bus_cgroup_set_##function( \ + Unit *u, \ + const char *name, \ + uint64_t *p, \ + sd_bus_message *message, \ + UnitWriteFlags flags, \ + sd_bus_error *error) { \ + \ + uint64_t v; \ + int r; \ + \ + assert(p); \ + \ + r = sd_bus_message_read(message, "t", &v); \ + if (r < 0) \ + return r; \ + \ + if (!check(v)) \ + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \ + "Value specified in %s is out of range", name); \ + \ + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \ + *p = v; \ + unit_invalidate_cgroup(u, (mask)); \ + \ + if (v == (val)) \ + unit_write_settingf(u, flags, name, \ + "%s=" str, name); \ + else \ + unit_write_settingf(u, flags, name, \ + "%s=%" PRIu64, name, v); \ + } \ + \ + return 1; \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +#define BUS_DEFINE_SET_CGROUP_SCALE(function, mask, scale) \ + int bus_cgroup_set_##function##_scale( \ + Unit *u, \ + const char *name, \ + uint64_t *p, \ + sd_bus_message *message, \ + UnitWriteFlags flags, \ + sd_bus_error *error) { \ + \ + uint64_t v; \ + uint32_t raw; \ + int r; \ + \ + assert(p); \ + \ + r = sd_bus_message_read(message, "u", &raw); \ + if (r < 0) \ + return r; \ + \ + v = scale(raw, UINT32_MAX); \ + if (v <= 0 || v >= UINT64_MAX) \ + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, \ + "Value specified in %s is out of range", name); \ + \ + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { \ + const char *e; \ + \ + *p = v; \ + unit_invalidate_cgroup(u, (mask)); \ + \ + /* Chop off suffix */ \ + assert_se(e = endswith(name, "Scale")); \ + name = strndupa(name, e - name); \ + \ + unit_write_settingf(u, flags, name, "%s=%" PRIu32 "%%", name, \ + (uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX))); \ + } \ + \ + return 1; \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +int bus_set_transient_mode_t(Unit *u, const char *name, mode_t *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); +int bus_set_transient_unsigned(Unit *u, const char *name, unsigned *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); +int bus_set_transient_user(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); +int bus_set_transient_path(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); +int bus_set_transient_string(Unit *u, const char *name, char **p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); +int bus_set_transient_bool(Unit *u, const char *name, bool *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); +int bus_set_transient_usec_internal(Unit *u, const char *name, usec_t *p, bool fix_0, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error); +static inline int bus_set_transient_usec(Unit *u, const char *name, usec_t *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error) { + return bus_set_transient_usec_internal(u, name, p, false, message, flags, error); +} +static inline int bus_set_transient_usec_fix_0(Unit *u, const char *name, usec_t *p, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error) { + return bus_set_transient_usec_internal(u, name, p, true, message, flags, error); +} diff --git a/src/core/dbus.c b/src/core/dbus.c index b7d8af9396..1c3fca353a 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -37,9 +37,11 @@ #include "dbus-unit.h" #include "dbus.h" #include "fd-util.h" +#include "fs-util.h" #include "log.h" #include "missing.h" #include "mkdir.h" +#include "process-util.h" #include "selinux-access.h" #include "special.h" #include "string-util.h" @@ -603,18 +605,16 @@ static int bus_setup_disconnected_match(Manager *m, sd_bus *bus) { assert(m); assert(bus); - r = sd_bus_add_match( + r = sd_bus_match_signal_async( bus, NULL, - "sender='org.freedesktop.DBus.Local'," - "type='signal'," - "path='/org/freedesktop/DBus/Local'," - "interface='org.freedesktop.DBus.Local'," - "member='Disconnected'", - signal_disconnected, m); - + "org.freedesktop.DBus.Local", + "/org/freedesktop/DBus/Local", + "org.freedesktop.DBus.Local", + "Disconnected", + signal_disconnected, NULL, m); if (r < 0) - return log_error_errno(r, "Failed to register match for Disconnected message: %m"); + return log_error_errno(r, "Failed to request match for Disconnected message: %m"); return 0; } @@ -683,6 +683,12 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void return 0; } + r = sd_bus_set_sender(bus, "org.freedesktop.systemd1"); + if (r < 0) { + log_warning_errno(r, "Failed to set direct connection sender: %m"); + return 0; + } + r = sd_bus_start(bus); if (r < 0) { log_warning_errno(r, "Failed to start new connection bus: %m"); @@ -813,26 +819,23 @@ static int bus_setup_api(Manager *m, sd_bus *bus) { log_error_errno(r, "Failed to subscribe to NameOwnerChanged signal for '%s': %m", name); } - r = sd_bus_add_match( + r = sd_bus_match_signal_async( bus, NULL, - "type='signal'," - "sender='org.freedesktop.DBus'," - "path='/org/freedesktop/DBus'," - "interface='org.freedesktop.systemd1.Activator'," - "member='ActivationRequest'", - signal_activation_request, m); + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.systemd1.Activator", + "ActivationRequest", + signal_activation_request, NULL, m); if (r < 0) log_warning_errno(r, "Failed to subscribe to activation signal: %m"); - /* Allow replacing of our name, to ease implementation of - * reexecution, where we keep the old connection open until - * after the new connection is set up and the name installed - * to allow clients to synchronously wait for reexecution to - * finish */ - r = sd_bus_request_name(bus,"org.freedesktop.systemd1", SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_ALLOW_REPLACEMENT); + /* Allow replacing of our name, to ease implementation of reexecution, where we keep the old connection open + * until after the new connection is set up and the name installed to allow clients to synchronously wait for + * reexecution to finish */ + r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.systemd1", SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_ALLOW_REPLACEMENT, NULL, NULL); if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); + return log_error_errno(r, "Failed to request name: %m"); r = manager_sync_bus_names(m, bus); if (r < 0) @@ -857,28 +860,21 @@ static int bus_init_api(Manager *m) { r = sd_bus_open_system(&bus); else r = sd_bus_open_user(&bus); - - if (r < 0) { - log_debug("Failed to connect to API bus, retrying later..."); - return 0; - } + if (r < 0) + return log_error_errno(r, "Failed to connect to API bus: %m"); r = sd_bus_attach_event(bus, m->event, SD_EVENT_PRIORITY_NORMAL); - if (r < 0) { - log_error_errno(r, "Failed to attach API bus to event loop: %m"); - return 0; - } + if (r < 0) + return log_error_errno(r, "Failed to attach API bus to event loop: %m"); r = bus_setup_disconnected_match(m, bus); if (r < 0) - return 0; + return r; } r = bus_setup_api(m, bus); - if (r < 0) { - log_error_errno(r, "Failed to set up API bus: %m"); - return 0; - } + if (r < 0) + return log_error_errno(r, "Failed to set up API bus: %m"); m->api_bus = bus; bus = NULL; @@ -894,16 +890,16 @@ static int bus_setup_system(Manager *m, sd_bus *bus) { /* 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( + r = sd_bus_match_signal_async( bus, NULL, - "type='signal'," - "interface='org.freedesktop.systemd1.Agent'," - "member='Released'," - "path='/org/freedesktop/systemd1/agent'", - signal_agent_released, m); + NULL, + "/org/freedesktop/systemd1/agent", + "org.freedesktop.systemd1.Agent", + "Released", + signal_agent_released, NULL, m); if (r < 0) - log_warning_errno(r, "Failed to register Released match on system bus: %m"); + log_warning_errno(r, "Failed to request Released match on system bus: %m"); } log_debug("Successfully connected to system bus."); @@ -924,26 +920,20 @@ static int bus_init_system(Manager *m) { } r = sd_bus_open_system(&bus); - if (r < 0) { - log_debug("Failed to connect to system bus, retrying later..."); - return 0; - } + if (r < 0) + return log_error_errno(r, "Failed to connect to system bus: %m"); r = bus_setup_disconnected_match(m, bus); if (r < 0) - return 0; + return r; r = sd_bus_attach_event(bus, m->event, SD_EVENT_PRIORITY_NORMAL); - if (r < 0) { - log_error_errno(r, "Failed to attach system bus to event loop: %m"); - return 0; - } + if (r < 0) + return log_error_errno(r, "Failed to attach system bus to event loop: %m"); r = bus_setup_system(m, bus); - if (r < 0) { - log_error_errno(r, "Failed to set up system bus: %m"); - return 0; - } + if (r < 0) + return log_error_errno(r, "Failed to set up system bus: %m"); m->system_bus = bus; bus = NULL; @@ -1005,6 +995,9 @@ static int bus_init_private(Manager *m) { if (r < 0) return log_error_errno(errno, "Failed to make private socket listening: %m"); + /* Generate an inotify event in case somebody waits for this socket to appear using inotify() */ + (void) touch(sa.un.sun_path); + r = sd_event_add_io(m->event, &s, fd, EPOLLIN, bus_on_connection, m); if (r < 0) return log_error_errno(r, "Failed to allocate event source: %m"); @@ -1026,16 +1019,16 @@ int bus_init(Manager *m, bool try_bus_connect) { if (try_bus_connect) { r = bus_init_system(m); if (r < 0) - return r; + return log_error_errno(r, "Failed to initialize D-Bus connection: %m"); r = bus_init_api(m); if (r < 0) - return r; + return log_error_errno(r, "Error occured during D-Bus APIs initialization: %m"); } r = bus_init_private(m); if (r < 0) - return r; + return log_error_errno(r, "Failed to create private D-Bus server: %m"); return 0; } diff --git a/src/core/device.c b/src/core/device.c index dec6e74f95..a43664d3bd 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -796,14 +796,12 @@ static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, } if (streq(action, "change")) { - _cleanup_free_ char *e = NULL; Unit *u; + Device *d, *l, *n; - r = unit_name_from_path(sysfs, ".device", &e); - if (r < 0) - log_error_errno(r, "Failed to generate unit name from device path: %m"); - else { - u = manager_get_unit(m, e); + l = hashmap_get(m->devices_by_sysfs, sysfs); + LIST_FOREACH_SAFE(same_sysfs, d, n, l) { + u = &d->meta; if (u && UNIT_VTABLE(u)->active_state(u) == UNIT_ACTIVE) { r = manager_propagate_reload(m, u, JOB_REPLACE, NULL); if (r < 0) diff --git a/src/core/emergency-action.c b/src/core/emergency-action.c index 308608e426..decfacd600 100644 --- a/src/core/emergency-action.c +++ b/src/core/emergency-action.c @@ -49,6 +49,11 @@ int emergency_action( if (action == EMERGENCY_ACTION_NONE) return -ECANCELED; + if (!m->service_watchdogs) { + log_warning("Watchdog disabled! Not acting on: %s", reason); + return -ECANCELED; + } + if (!MANAGER_IS_SYSTEM(m)) { /* Downgrade all options to simply exiting if we run * in user mode */ diff --git a/src/core/execute.c b/src/core/execute.c index f20246796f..0df3971df6 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -1207,27 +1207,19 @@ static int setup_pam( parent_pid = getpid_cached(); - pam_pid = fork(); - if (pam_pid < 0) { - r = -errno; + r = safe_fork("(sd-pam)", 0, &pam_pid); + if (r < 0) goto fail; - } - - if (pam_pid == 0) { + if (r == 0) { int sig, ret = EXIT_PAM; /* The child's job is to reset the PAM session on * termination */ barrier_set_role(&barrier, BARRIER_CHILD); - /* This string must fit in 10 chars (i.e. the length - * of "/sbin/init"), to look pretty in /bin/ps */ - rename_process("(sd-pam)"); - - /* Make sure we don't keep open the passed fds in this - child. We assume that otherwise only those fds are - open here that have been opened by PAM. */ - close_many(fds, n_fds); + /* Make sure we don't keep open the passed fds in this child. We assume that otherwise only those fds + * are open here that have been opened by PAM. */ + (void) close_many(fds, n_fds); /* Drop privileges - we don't need any to pam_close_session * and this will make PR_SET_PDEATHSIG work in most cases. @@ -1784,7 +1776,7 @@ static int build_pass_environment(const ExecContext *c, char ***ret) { static bool exec_needs_mount_namespace( const ExecContext *context, const ExecParameters *params, - ExecRuntime *runtime) { + const ExecRuntime *runtime) { assert(context); assert(params); @@ -1797,12 +1789,7 @@ static bool exec_needs_mount_namespace( !strv_isempty(context->inaccessible_paths)) return true; - if (context->n_bind_mounts > 0 || - !strv_isempty(context->directories[EXEC_DIRECTORY_RUNTIME].paths) || - !strv_isempty(context->directories[EXEC_DIRECTORY_STATE].paths) || - !strv_isempty(context->directories[EXEC_DIRECTORY_CACHE].paths) || - !strv_isempty(context->directories[EXEC_DIRECTORY_LOGS].paths) || - !strv_isempty(context->directories[EXEC_DIRECTORY_CONFIGURATION].paths)) + if (context->n_bind_mounts > 0) return true; if (context->mount_flags != 0) @@ -1822,6 +1809,12 @@ static bool exec_needs_mount_namespace( if (context->mount_apivfs && (context->root_image || context->root_directory)) return true; + if (context->dynamic_user && + (!strv_isempty(context->directories[EXEC_DIRECTORY_STATE].paths) || + !strv_isempty(context->directories[EXEC_DIRECTORY_CACHE].paths) || + !strv_isempty(context->directories[EXEC_DIRECTORY_LOGS].paths))) + return true; + return false; } @@ -1831,7 +1824,6 @@ static int setup_private_users(uid_t uid, gid_t gid) { _cleanup_close_ int unshare_ready_fd = -1; _cleanup_(sigkill_waitp) pid_t pid = 0; uint64_t c = 1; - siginfo_t si; ssize_t n; int r; @@ -1879,11 +1871,10 @@ static int setup_private_users(uid_t uid, gid_t gid) { if (pipe2(errno_pipe, O_CLOEXEC) < 0) return -errno; - pid = fork(); - if (pid < 0) - return -errno; - - if (pid == 0) { + r = safe_fork("(sd-userns)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid); + if (r < 0) + return r; + if (r == 0) { _cleanup_close_ int fd = -1; const char *a; pid_t ppid; @@ -1972,13 +1963,11 @@ static int setup_private_users(uid_t uid, gid_t gid) { if (n != 0) /* on success we should have read 0 bytes */ return -EIO; - r = wait_for_terminate(pid, &si); + r = wait_for_terminate_and_check("(sd-userns)", pid, 0); + pid = 0; if (r < 0) return r; - pid = 0; - - /* If something strange happened with the child, let's consider this fatal, too */ - if (si.si_code != CLD_EXITED || si.si_status != 0) + if (r != EXIT_SUCCESS) /* If something strange happened with the child, let's consider this fatal, too */ return -EIO; return 0; @@ -3418,7 +3407,7 @@ static int exec_child( return log_oom(); } - if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) { + if (DEBUG_LOGGING) { _cleanup_free_ char *line; line = exec_command_line(final_argv); @@ -4162,19 +4151,19 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { if (c->pam_name) fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name); - if (strv_length(c->read_write_paths) > 0) { + if (!strv_isempty(c->read_write_paths)) { fprintf(f, "%sReadWritePaths:", prefix); strv_fprintf(f, c->read_write_paths); fputs("\n", f); } - if (strv_length(c->read_only_paths) > 0) { + if (!strv_isempty(c->read_only_paths)) { fprintf(f, "%sReadOnlyPaths:", prefix); strv_fprintf(f, c->read_only_paths); fputs("\n", f); } - if (strv_length(c->inaccessible_paths) > 0) { + if (!strv_isempty(c->inaccessible_paths)) { fprintf(f, "%sInaccessiblePaths:", prefix); strv_fprintf(f, c->inaccessible_paths); fputs("\n", f); diff --git a/src/core/ip-address-access.c b/src/core/ip-address-access.c index 8d72fc03bf..08bd4c0bce 100644 --- a/src/core/ip-address-access.c +++ b/src/core/ip-address-access.c @@ -210,13 +210,12 @@ IPAddressAccessItem* ip_address_access_reduce(IPAddressAccessItem *first) { &b->address, b->prefixlen, &a->address); - if (r <= 0) - continue; - - /* b covers a fully, then let's drop a */ - - LIST_REMOVE(items, first, a); - free(a); + if (r > 0) { + /* b covers a fully, then let's drop a */ + LIST_REMOVE(items, first, a); + free(a); + break; + } } } diff --git a/src/core/job.c b/src/core/job.c index 2ea7834dfd..c6de8d27e4 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -306,8 +306,7 @@ void job_dump(Job *j, FILE*f, const char *prefix) { assert(j); assert(f); - if (!prefix) - prefix = ""; + prefix = strempty(prefix); fprintf(f, "%s-> Job %u:\n" @@ -1244,7 +1243,7 @@ void job_shutdown_magic(Job *j) { if (detect_container() > 0) return; - asynchronous_sync(); + (void) asynchronous_sync(NULL); } int job_get_timeout(Job *j, usec_t *timeout) { diff --git a/src/core/kill.c b/src/core/kill.c index f438c4d8fa..5dfcb780fa 100644 --- a/src/core/kill.c +++ b/src/core/kill.c @@ -34,8 +34,7 @@ void kill_context_init(KillContext *c) { void kill_context_dump(KillContext *c, FILE *f, const char *prefix) { assert(c); - if (!prefix) - prefix = ""; + prefix = strempty(prefix); fprintf(f, "%sKillMode: %s\n" diff --git a/src/core/killall.c b/src/core/killall.c index e77763e161..daa9c4ea20 100644 --- a/src/core/killall.c +++ b/src/core/killall.c @@ -89,7 +89,7 @@ static bool ignore_proc(pid_t pid, bool warn_rootfs) { return true; } -static void wait_for_children(Set *pids, sigset_t *mask) { +static void wait_for_children(Set *pids, sigset_t *mask, usec_t timeout) { usec_t until; assert(mask); @@ -97,7 +97,7 @@ static void wait_for_children(Set *pids, sigset_t *mask) { if (set_isempty(pids)) return; - until = now(CLOCK_MONOTONIC) + DEFAULT_TIMEOUT_USEC; + until = now(CLOCK_MONOTONIC) + timeout; for (;;) { struct timespec ts; int k; @@ -221,7 +221,7 @@ static int killall(int sig, Set *pids, bool send_sighup) { return set_size(pids); } -void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup) { +void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup, usec_t timeout) { sigset_t mask, oldmask; _cleanup_set_free_ Set *pids = NULL; @@ -241,7 +241,7 @@ void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup) { log_warning_errno(errno, "kill(-1, SIGCONT) failed: %m"); if (wait_for_exit) - wait_for_children(pids, &mask); + wait_for_children(pids, &mask, timeout); assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0); } diff --git a/src/core/killall.h b/src/core/killall.h index 01bd6e52b3..45e97ab594 100644 --- a/src/core/killall.h +++ b/src/core/killall.h @@ -20,4 +20,6 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup); +#include "time-util.h" + +void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup, usec_t timeout); diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 240f331778..dde5010e02 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -214,6 +214,7 @@ Unit.RefuseManualStop, config_parse_bool, 0, Unit.AllowIsolate, config_parse_bool, 0, offsetof(Unit, allow_isolate) Unit.DefaultDependencies, config_parse_bool, 0, offsetof(Unit, default_dependencies) Unit.OnFailureJobMode, config_parse_job_mode, 0, offsetof(Unit, on_failure_job_mode) +m4_dnl The following is a legacy alias name for compatibility Unit.OnFailureIsolate, config_parse_job_mode_isolate, 0, offsetof(Unit, on_failure_job_mode) Unit.IgnoreOnIsolate, config_parse_bool, 0, offsetof(Unit, ignore_on_isolate) Unit.IgnoreOnSnapshot, config_parse_warn_compat, DISABLED_LEGACY, 0 @@ -241,6 +242,7 @@ Unit.ConditionFileIsExecutable, config_parse_unit_condition_path, CONDITION_F Unit.ConditionNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, offsetof(Unit, conditions) Unit.ConditionFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, offsetof(Unit, conditions) Unit.ConditionKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, conditions) +Unit.ConditionKernelVersion, config_parse_unit_condition_string, CONDITION_KERNEL_VERSION, offsetof(Unit, conditions) Unit.ConditionArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, offsetof(Unit, conditions) Unit.ConditionVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, conditions) Unit.ConditionSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, conditions) @@ -249,6 +251,7 @@ Unit.ConditionHost, config_parse_unit_condition_string, CONDITION_H Unit.ConditionACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, conditions) Unit.ConditionUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, conditions) Unit.ConditionGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, conditions) +Unit.ConditionControlGroupController, config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER, offsetof(Unit, conditions) Unit.ConditionNull, config_parse_unit_condition_null, 0, offsetof(Unit, conditions) Unit.AssertPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, asserts) Unit.AssertPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, asserts) @@ -262,6 +265,7 @@ Unit.AssertFileIsExecutable, config_parse_unit_condition_path, CONDITION_F Unit.AssertNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, offsetof(Unit, asserts) Unit.AssertFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, offsetof(Unit, asserts) Unit.AssertKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, asserts) +Unit.AssertKernelVersion, config_parse_unit_condition_string, CONDITION_KERNEL_VERSION, offsetof(Unit, asserts) Unit.AssertArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, offsetof(Unit, asserts) Unit.AssertVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, asserts) Unit.AssertSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, asserts) @@ -270,6 +274,7 @@ Unit.AssertHost, config_parse_unit_condition_string, CONDITION_H Unit.AssertACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, asserts) Unit.AssertUser, config_parse_unit_condition_string, CONDITION_USER, offsetof(Unit, asserts) Unit.AssertGroup, config_parse_unit_condition_string, CONDITION_GROUP, offsetof(Unit, asserts) +Unit.AssertControlGroupController, config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER, offsetof(Unit, asserts) Unit.AssertNull, config_parse_unit_condition_null, 0, offsetof(Unit, asserts) Unit.CollectMode, config_parse_collect_mode, 0, offsetof(Unit, collect_mode) m4_dnl diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index d6c616502b..00408c4b84 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -64,6 +64,7 @@ #include "securebits.h" #include "securebits-util.h" #include "signal-util.h" +#include "socket-protocol-list.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" @@ -433,11 +434,9 @@ int config_parse_socket_listen(const char *unit, p->n_auxiliary_fds = 0; p->socket = s; - if (s->ports) { - LIST_FIND_TAIL(port, s->ports, tail); - LIST_INSERT_AFTER(port, s->ports, tail, p); - } else - LIST_PREPEND(port, s->ports, p); + LIST_FIND_TAIL(port, s->ports, tail); + LIST_INSERT_AFTER(port, s->ports, tail, p); + p = NULL; return 0; @@ -454,6 +453,7 @@ int config_parse_socket_protocol(const char *unit, void *data, void *userdata) { Socket *s; + int r; assert(filename); assert(lvalue); @@ -462,15 +462,17 @@ int config_parse_socket_protocol(const char *unit, s = SOCKET(data); - if (streq(rvalue, "udplite")) - s->socket_protocol = IPPROTO_UDPLITE; - else if (streq(rvalue, "sctp")) - s->socket_protocol = IPPROTO_SCTP; - else { + r = socket_protocol_from_name(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid socket protocol, ignoring: %s", rvalue); + return 0; + } else if (!IN_SET(r, IPPROTO_UDPLITE, IPPROTO_SCTP)) { log_syntax(unit, LOG_ERR, filename, line, 0, "Socket protocol not supported, ignoring: %s", rvalue); return 0; } + s->socket_protocol = r; + return 0; } @@ -495,19 +497,13 @@ int config_parse_socket_bind(const char *unit, s = SOCKET(data); - b = socket_address_bind_ipv6_only_from_string(rvalue); + b = parse_socket_address_bind_ipv6_only_or_bool(rvalue); if (b < 0) { - int r; - - r = parse_boolean(rvalue); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue); - return 0; - } + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse bind IPv6 only value, ignoring: %s", rvalue); + return 0; + } - s->bind_ipv6_only = r ? SOCKET_ADDRESS_IPV6_ONLY : SOCKET_ADDRESS_BOTH; - } else - s->bind_ipv6_only = b; + s->bind_ipv6_only = b; return 0; } @@ -2891,60 +2887,6 @@ int config_parse_documentation(const char *unit, } #if HAVE_SECCOMP - -static int syscall_filter_parse_one( - const char *unit, - const char *filename, - unsigned line, - ExecContext *c, - bool invert, - const char *t, - bool warn, - int errno_num) { - int r; - - if (t[0] == '@') { - const SyscallFilterSet *set; - const char *i; - - set = syscall_filter_set_find(t); - if (!set) { - if (warn) - log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown system call group, ignoring: %s", t); - return 0; - } - - NULSTR_FOREACH(i, set->value) { - r = syscall_filter_parse_one(unit, filename, line, c, invert, i, false, errno_num); - if (r < 0) - return r; - } - } else { - int id; - - id = seccomp_syscall_resolve_name(t); - if (id == __NR_SCMP_ERROR) { - if (warn) - log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse system call, ignoring: %s", t); - return 0; - } - - /* If we previously wanted to forbid a syscall and now - * we want to allow it, then remove it from the list. - */ - if (!invert == c->syscall_whitelist) { - r = hashmap_put(c->syscall_filter, INT_TO_PTR(id + 1), INT_TO_PTR(errno_num)); - if (r == 0) - return 0; - if (r < 0) - return log_oom(); - } else - (void) hashmap_remove(c->syscall_filter, INT_TO_PTR(id + 1)); - } - - return 0; -} - int config_parse_syscall_filter( const char *unit, const char *filename, @@ -2993,7 +2935,7 @@ int config_parse_syscall_filter( c->syscall_whitelist = true; /* Accept default syscalls if we are on a whitelist */ - r = syscall_filter_parse_one(unit, filename, line, c, false, "@default", false, -1); + r = seccomp_parse_syscall_filter(false, "@default", -1, c->syscall_filter, true); if (r < 0) return r; } @@ -3020,7 +2962,7 @@ int config_parse_syscall_filter( continue; } - r = syscall_filter_parse_one(unit, filename, line, c, invert, name, true, num); + r = seccomp_parse_syscall_filter_and_warn(invert, name, num, c->syscall_filter, c->syscall_whitelist, unit, filename, line); if (r < 0) return r; } @@ -3575,7 +3517,8 @@ int config_parse_device_allow( if (!path) return log_oom(); - if (!is_deviceallow_pattern(path)) { + if (!is_deviceallow_pattern(path) && + !path_startswith(path, "/run/systemd/inaccessible/")) { log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); return 0; } @@ -3675,7 +3618,8 @@ int config_parse_io_device_weight( if (!path) return log_oom(); - if (!path_startswith(path, "/dev")) { + if (!path_startswith(path, "/dev") && + !path_startswith(path, "/run/systemd/inaccessible/")) { log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); return 0; } @@ -3748,7 +3692,8 @@ int config_parse_io_limit( if (!path) return log_oom(); - if (!path_startswith(path, "/dev")) { + if (!path_startswith(path, "/dev") && + !path_startswith(path, "/run/systemd/inaccessible/")) { log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); return 0; } @@ -3862,7 +3807,8 @@ int config_parse_blockio_device_weight( if (!path) return log_oom(); - if (!path_startswith(path, "/dev")) { + if (!path_startswith(path, "/dev") && + !path_startswith(path, "/run/systemd/inaccessible/")) { log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); return 0; } @@ -3936,7 +3882,8 @@ int config_parse_blockio_bandwidth( if (!path) return log_oom(); - if (!path_startswith(path, "/dev")) { + if (!path_startswith(path, "/dev") && + !path_startswith(path, "/run/systemd/inaccessible/")) { log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid device node path '%s'. Ignoring.", path); return 0; } @@ -4002,6 +3949,8 @@ int config_parse_job_mode_isolate( return 0; } + log_notice("%s is deprecated. Please use OnFailureJobMode= instead", lvalue); + *m = r ? JOB_ISOLATE : JOB_REPLACE; return 0; } @@ -4412,7 +4361,7 @@ int config_parse_protect_home( void *userdata) { ExecContext *c = data; - int k; + ProtectHome h; assert(filename); assert(lvalue); @@ -4422,23 +4371,14 @@ int config_parse_protect_home( /* Our enum shall be a superset of booleans, hence first try * to parse as boolean, and then as enum */ - k = parse_boolean(rvalue); - if (k > 0) - c->protect_home = PROTECT_HOME_YES; - else if (k == 0) - c->protect_home = PROTECT_HOME_NO; - else { - ProtectHome h; - - h = protect_home_from_string(rvalue); - if (h < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue); - return 0; - } - - c->protect_home = h; + h = parse_protect_home_or_bool(rvalue); + if (h < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect home value, ignoring: %s", rvalue); + return 0; } + c->protect_home = h; + return 0; } @@ -4455,7 +4395,7 @@ int config_parse_protect_system( void *userdata) { ExecContext *c = data; - int k; + ProtectSystem s; assert(filename); assert(lvalue); @@ -4465,23 +4405,14 @@ int config_parse_protect_system( /* Our enum shall be a superset of booleans, hence first try * to parse as boolean, and then as enum */ - k = parse_boolean(rvalue); - if (k > 0) - c->protect_system = PROTECT_SYSTEM_YES; - else if (k == 0) - c->protect_system = PROTECT_SYSTEM_NO; - else { - ProtectSystem s; - - s = protect_system_from_string(rvalue); - if (s < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue); - return 0; - } - - c->protect_system = s; + s = parse_protect_system_or_bool(rvalue); + if (s < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse protect system value, ignoring: %s", rvalue); + return 0; } + c->protect_system = s; + return 0; } diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c index 6240a83197..0c43cf2418 100644 --- a/src/core/locale-setup.c +++ b/src/core/locale-setup.c @@ -20,6 +20,7 @@ #include <errno.h> #include <stdlib.h> +#include <string.h> #include "env-util.h" #include "fileio.h" diff --git a/src/core/loopback-setup.c b/src/core/loopback-setup.c index 9a75525894..1528034e81 100644 --- a/src/core/loopback-setup.c +++ b/src/core/loopback-setup.c @@ -32,21 +32,27 @@ struct state { unsigned n_messages; int rcode; - const char *title; + const char *error_message; + const char *success_message; }; static int generic_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { struct state *s = userdata; + int r; assert(s); assert(s->n_messages > 0); s->n_messages--; errno = 0; - log_debug_errno(sd_netlink_message_get_errno(m), "Failed to %s: %m", s->title); - s->rcode = sd_netlink_message_get_errno(m); + r = sd_netlink_message_get_errno(m); + if (r < 0) + log_debug_errno(r, "%s: %m", s->error_message); + else + log_debug("%s", s->success_message); + s->rcode = r; return 0; } @@ -165,9 +171,16 @@ static bool check_loopback(sd_netlink *rtnl) { int loopback_setup(void) { _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - struct state state_4 = { .title = "add address 127.0.0.1 to loopback interface" }, - state_6 = { .title = "add address ::1 to loopback interface"}, - state_up = { .title = "bring loopback interface up" }; + struct state state_4 = { + .error_message = "Failed to add address 127.0.0.1 to loopback interface", + .success_message = "Successfully added address 127.0.0.1 to loopback interface", + }, state_6 = { + .error_message = "Failed to add address ::1 to loopback interface", + .success_message = "Successfully added address ::1 to loopback interface", + }, state_up = { + .error_message = "Failed to bring loopback interface up", + .success_message = "Successfully brought loopback interface up", + }; int r; r = sd_netlink_open(&rtnl); diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c index 767e97206c..1b7424800c 100644 --- a/src/core/machine-id-setup.c +++ b/src/core/machine-id-setup.c @@ -119,16 +119,15 @@ int machine_id_setup(const char *root, sd_id128_t machine_id, sd_id128_t *ret) { fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY); if (fd < 0) { if (old_errno == EROFS && errno == ENOENT) - log_error_errno(errno, + return log_error_errno(errno, "System cannot boot: Missing /etc/machine-id and /etc is mounted read-only.\n" "Booting up is supported only when:\n" "1) /etc/machine-id exists and is populated.\n" "2) /etc/machine-id exists and is empty.\n" "3) /etc/machine-id is missing and /etc is writable.\n"); else - log_error_errno(errno, "Cannot open %s: %m", etc_machine_id); - - return -errno; + return log_error_errno(errno, + "Cannot open %s: %m", etc_machine_id); } writable = false; diff --git a/src/core/main.c b/src/core/main.c index 2ad5073368..56200a8fad 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -91,6 +91,7 @@ #include "terminal-util.h" #include "umask-util.h" #include "user-util.h" +#include "util.h" #include "virt.h" #include "watchdog.h" @@ -111,6 +112,7 @@ static char *arg_confirm_spawn = NULL; static ShowStatus arg_show_status = _SHOW_STATUS_UNSET; static bool arg_switched_root = false; static bool arg_no_pager = false; +static bool arg_service_watchdogs = true; static char ***arg_join_controllers = NULL; static ExecOutput arg_default_std_output = EXEC_OUTPUT_JOURNAL; static ExecOutput arg_default_std_error = EXEC_OUTPUT_INHERIT; @@ -395,6 +397,14 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat arg_confirm_spawn = s; } + } else if (proc_cmdline_key_streq(key, "systemd.service_watchdogs")) { + + r = value ? parse_boolean(value) : true; + if (r < 0) + log_warning("Failed to parse service watchdog switch %s. Ignoring.", value); + else + arg_service_watchdogs = r; + } else if (proc_cmdline_key_streq(key, "systemd.show_status")) { if (value) { @@ -854,6 +864,7 @@ static void set_manager_settings(Manager *m) { assert(m); m->confirm_spawn = arg_confirm_spawn; + m->service_watchdogs = arg_service_watchdogs; m->runtime_watchdog = arg_runtime_watchdog; m->shutdown_watchdog = arg_shutdown_watchdog; m->cad_burst_action = arg_cad_burst_action; @@ -885,7 +896,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_SWITCHED_ROOT, ARG_DEFAULT_STD_OUTPUT, ARG_DEFAULT_STD_ERROR, - ARG_MACHINE_ID + ARG_MACHINE_ID, + ARG_SERVICE_WATCHDOGS, }; static const struct option options[] = { @@ -912,6 +924,7 @@ static int parse_argv(int argc, char *argv[]) { { "default-standard-output", required_argument, NULL, ARG_DEFAULT_STD_OUTPUT, }, { "default-standard-error", required_argument, NULL, ARG_DEFAULT_STD_ERROR, }, { "machine-id", required_argument, NULL, ARG_MACHINE_ID }, + { "service-watchdogs", required_argument, NULL, ARG_SERVICE_WATCHDOGS }, {} }; @@ -1066,6 +1079,13 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(r, "Failed to parse confirm spawn option: %m"); break; + case ARG_SERVICE_WATCHDOGS: + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse service watchdogs boolean: %s", optarg); + arg_service_watchdogs = r; + break; + case ARG_SHOW_STATUS: if (optarg) { r = parse_show_status(optarg, &arg_show_status); @@ -1336,20 +1356,23 @@ static int enforce_syscall_archs(Set *archs) { static int status_welcome(void) { _cleanup_free_ char *pretty_name = NULL, *ansi_color = NULL; + const char *fn; int r; - r = parse_env_file("/etc/os-release", NEWLINE, - "PRETTY_NAME", &pretty_name, - "ANSI_COLOR", &ansi_color, - NULL); - if (r == -ENOENT) - r = parse_env_file("/usr/lib/os-release", NEWLINE, + if (arg_show_status <= 0) + return 0; + + FOREACH_STRING(fn, "/etc/os-release", "/usr/lib/os-release") { + r = parse_env_file(fn, NEWLINE, "PRETTY_NAME", &pretty_name, "ANSI_COLOR", &ansi_color, NULL); + if (r != -ENOENT) + break; + } if (r < 0 && r != -ENOENT) - log_warning_errno(r, "Failed to read os-release file: %m"); + log_warning_errno(r, "Failed to read os-release file, ignoring: %m"); if (log_get_show_color()) return status_printf(NULL, false, false, @@ -1413,32 +1436,29 @@ static int bump_unix_max_dgram_qlen(void) { static int fixup_environment(void) { _cleanup_free_ char *term = NULL; + const char *t; int r; - /* We expect the environment to be set correctly - * if run inside a container. */ + /* Only fix up the environment when we are started as PID 1 */ + if (getpid_cached() != 1) + return 0; + + /* We expect the environment to be set correctly if run inside a container. */ if (detect_container() > 0) return 0; - /* When started as PID1, the kernel uses /dev/console - * for our stdios and uses TERM=linux whatever the - * backend device used by the console. We try to make - * a better guess here since some consoles might not - * have support for color mode for example. + /* When started as PID1, the kernel uses /dev/console for our stdios and uses TERM=linux whatever the backend + * device used by the console. We try to make a better guess here since some consoles might not have support + * for color mode for example. * - * However if TERM was configured through the kernel - * command line then leave it alone. */ - + * However if TERM was configured through the kernel command line then leave it alone. */ r = proc_cmdline_get_key("TERM", 0, &term); if (r < 0) return r; - if (r == 0) { - term = strdup(default_term_for_tty("/dev/console")); - if (!term) - return -ENOMEM; - } - if (setenv("TERM", term, 1) < 0) + t = term ?: default_term_for_tty("/dev/console"); + + if (setenv("TERM", t, 1) < 0) return -errno; return 0; @@ -1457,7 +1477,7 @@ static void redirect_telinit(int argc, char *argv[]) { execv(SYSTEMCTL_BINARY_PATH, argv); log_error_errno(errno, "Failed to exec " SYSTEMCTL_BINARY_PATH ": %m"); - exit(1); + exit(EXIT_FAILURE); #endif } @@ -1466,17 +1486,19 @@ static int become_shutdown( int retval) { char log_level[DECIMAL_STR_MAX(int) + 1], - exit_code[DECIMAL_STR_MAX(uint8_t) + 1]; + exit_code[DECIMAL_STR_MAX(uint8_t) + 1], + timeout[DECIMAL_STR_MAX(usec_t) + 1]; - const char* command_line[11] = { + const char* command_line[13] = { SYSTEMD_SHUTDOWN_BINARY_PATH, shutdown_verb, + "--timeout", timeout, "--log-level", log_level, "--log-target", }; _cleanup_strv_free_ char **env_block = NULL; - size_t pos = 5; + size_t pos = 7; int r; assert(shutdown_verb); @@ -1484,6 +1506,7 @@ static int become_shutdown( env_block = strv_copy(environ); xsprintf(log_level, "%d", log_get_max_level()); + xsprintf(timeout, "%" PRI_USEC "us", arg_default_timeout_stop_usec); switch (log_get_target()) { @@ -1603,7 +1626,7 @@ static void initialize_coredump(bool skip_setup) { /* But at the same time, turn off the core_pattern logic by default, so that no coredumps are stored * until the systemd-coredump tool is enabled via sysctl. */ if (!skip_setup) - (void) write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0); + disable_coredumps(); } static void do_reexecute( @@ -1639,7 +1662,7 @@ static void do_reexecute( if (switch_root_dir) { /* Kill all remaining processes from the initrd, but don't wait for them, so that we can handle the * SIGCHLD for them after deserializing. */ - broadcast_signal(SIGTERM, false, true); + broadcast_signal(SIGTERM, false, true, arg_default_timeout_stop_usec); /* And switch root with MS_MOVE, because we remove the old directory afterwards and detach it. */ r = switch_root(switch_root_dir, "/mnt", true, MS_MOVE); @@ -1889,11 +1912,13 @@ static void log_execution_mode(bool *ret_first_boot) { log_info("Running with unpopulated /etc."); } } else { - _cleanup_free_ char *t; + if (DEBUG_LOGGING) { + _cleanup_free_ char *t; - t = uid_to_name(getuid()); - log_debug(PACKAGE_STRING " running in %suser mode for user " UID_FMT "/%s. (" SYSTEMD_FEATURES ")", - arg_action == ACTION_TEST ? " test" : "", getuid(), strna(t)); + t = uid_to_name(getuid()); + log_debug(PACKAGE_STRING " running in %suser mode for user " UID_FMT "/%s. (" SYSTEMD_FEATURES ")", + arg_action == ACTION_TEST ? " test" : "", getuid(), strna(t)); + } *ret_first_boot = false; } @@ -1916,31 +1941,42 @@ static int initialize_runtime( * - Some only apply when we first start up, but not when we reexecute */ - if (arg_system && !skip_setup) { - if (arg_show_status > 0) + if (arg_action != ACTION_RUN) + return 0; + + if (arg_system) { + /* Make sure we leave a core dump without panicing the kernel. */ + install_crash_handler(); + + if (!skip_setup) { + r = mount_cgroup_controllers(arg_join_controllers); + if (r < 0) { + *ret_error_message = "Failed to mount cgroup hierarchies"; + return r; + } + status_welcome(); + hostname_setup(); + machine_id_setup(NULL, arg_machine_id, NULL); + loopback_setup(); + bump_unix_max_dgram_qlen(); + test_usr(); + write_container_id(); + } - hostname_setup(); - machine_id_setup(NULL, arg_machine_id, NULL); - loopback_setup(); - bump_unix_max_dgram_qlen(); - test_usr(); - write_container_id(); - } + if (arg_watchdog_device) { + r = watchdog_set_device(arg_watchdog_device); + if (r < 0) + log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m", arg_watchdog_device); + } - if (arg_system && arg_watchdog_device) { - r = watchdog_set_device(arg_watchdog_device); - if (r < 0) - log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m", - arg_watchdog_device); + if (arg_runtime_watchdog > 0 && arg_runtime_watchdog != USEC_INFINITY) + watchdog_set_timeout(&arg_runtime_watchdog); } - 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) if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0) - log_error_errno(errno, "Failed to adjust timer slack: %m"); + log_warning_errno(errno, "Failed to adjust timer slack, ignoring: %m"); if (arg_system && !cap_test_all(arg_capability_bounding_set)) { r = capability_bounding_set_drop_usermode(arg_capability_bounding_set); @@ -2053,60 +2089,256 @@ static void free_arguments(void) { arg_syscall_archs = set_free(arg_syscall_archs); } +static int load_configuration(int argc, char **argv, const char **ret_error_message) { + int r; + + assert(ret_error_message); + + r = initialize_join_controllers(); + if (r < 0) { + *ret_error_message = "Failed to initialize cgroup controller joining table"; + return r; + } + + arg_default_tasks_max = system_tasks_max_scale(DEFAULT_TASKS_MAX_PERCENTAGE, 100U); + + r = parse_config_file(); + if (r < 0) { + *ret_error_message = "Failed to parse config file"; + return r; + } + + if (arg_system) { + r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + } + + /* Note that this also parses bits from the kernel command line, including "debug". */ + log_parse_environment(); + + r = parse_argv(argc, argv); + if (r < 0) { + *ret_error_message = "Failed to parse commandline arguments"; + return r; + } + + /* Initialize default unit */ + if (!arg_default_unit) { + arg_default_unit = strdup(SPECIAL_DEFAULT_TARGET); + if (!arg_default_unit) { + *ret_error_message = "Failed to set default unit"; + return log_oom(); + } + } + + /* Initialize the show status setting if it hasn't been set explicitly yet */ + if (arg_show_status == _SHOW_STATUS_UNSET) + arg_show_status = SHOW_STATUS_YES; + + return 0; +} + +static int safety_checks(void) { + + if (getpid_cached() == 1 && + arg_action != ACTION_RUN) { + log_error("Unsupported execution mode while PID 1."); + return -EPERM; + } + + if (getpid_cached() == 1 && + !arg_system) { + log_error("Can't run --user mode as PID 1."); + return -EPERM; + } + + if (arg_action == ACTION_RUN && + arg_system && + getpid_cached() != 1) { + log_error("Can't run system mode unless PID 1."); + return -EPERM; + } + + if (arg_action == ACTION_TEST && + geteuid() == 0) { + log_error("Don't run test mode as root."); + return -EPERM; + } + + 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."); + return -EOPNOTSUPP; + } + + if (!arg_system && + arg_action == ACTION_RUN && + !getenv("XDG_RUNTIME_DIR")) { + log_error("Trying to run as user instance, but $XDG_RUNTIME_DIR is not set."); + return -EUNATCH; + } + + if (arg_system && + arg_action == ACTION_RUN && + running_in_chroot() > 0) { + log_error("Cannot be run in a chroot() environment."); + return -EOPNOTSUPP; + } + + return 0; +} + +static int initialize_security( + bool *loaded_policy, + dual_timestamp *security_start_timestamp, + dual_timestamp *security_finish_timestamp, + const char **ret_error_message) { + + int r; + + assert(loaded_policy); + assert(security_start_timestamp); + assert(security_finish_timestamp); + assert(ret_error_message); + + dual_timestamp_get(security_start_timestamp); + + r = mac_selinux_setup(loaded_policy); + if (r < 0) { + *ret_error_message = "Failed to load SELinux policy"; + return r; + } + + r = mac_smack_setup(loaded_policy); + if (r < 0) { + *ret_error_message = "Failed to load SMACK policy"; + return r; + } + + r = ima_setup(); + if (r < 0) { + *ret_error_message = "Failed to load IMA policy"; + return r; + } + + dual_timestamp_get(security_finish_timestamp); + return 0; +} + +static void test_summary(Manager *m) { + assert(m); + + printf("-> By units:\n"); + manager_dump_units(m, stdout, "\t"); + + printf("-> By jobs:\n"); + manager_dump_jobs(m, stdout, "\t"); +} + +static int collect_fds(FDSet **ret_fds, const char **ret_error_message) { + int r; + + assert(ret_fds); + assert(ret_error_message); + + r = fdset_new_fill(ret_fds); + if (r < 0) { + *ret_error_message = "Failed to allocate fd set"; + return log_emergency_errno(r, "Failed to allocate fd set: %m"); + } + + fdset_cloexec(*ret_fds, true); + + if (arg_serialization) + assert_se(fdset_remove(*ret_fds, fileno(arg_serialization)) >= 0); + + return 0; +} + +static void setup_console_terminal(bool skip_setup) { + + if (!arg_system) + return; + + /* Become a session leader if we aren't one yet. */ + (void) setsid(); + + /* If we are init, we connect stdin/stdout/stderr to /dev/null and make sure we don't have a controlling + * tty. */ + (void) release_terminal(); + + /* Reset the console, but only if this is really init and we are freshly booted */ + if (getpid_cached() == 1 && !skip_setup) + (void) console_setup(); +} + +static bool early_skip_setup_check(int argc, char *argv[]) { + bool found_deserialize = false; + int i; + + /* Determine if this is a reexecution or normal bootup. We do the full command line parsing much later, so + * let's just have a quick peek here. Note that if we have switched root, do all the special setup things + * anyway, even if in that case we also do deserialization. */ + + for (i = 1; i < argc; i++) { + + if (streq(argv[i], "--switched-root")) + return false; /* If we switched root, don't skip the setup. */ + else if (streq(argv[i], "--deserialize")) + found_deserialize = true; + } + + return found_deserialize; /* When we are deserializing, then we are reexecuting, hence avoid the extensive setup */ +} + int main(int argc, char *argv[]) { - Manager *m = NULL; - int r, retval = EXIT_FAILURE; + + dual_timestamp initrd_timestamp = DUAL_TIMESTAMP_NULL, userspace_timestamp = DUAL_TIMESTAMP_NULL, kernel_timestamp = DUAL_TIMESTAMP_NULL, + security_start_timestamp = DUAL_TIMESTAMP_NULL, security_finish_timestamp = DUAL_TIMESTAMP_NULL; + struct rlimit saved_rlimit_nofile = RLIMIT_MAKE_CONST(0), saved_rlimit_memlock = RLIMIT_MAKE_CONST((rlim_t) -1); + bool skip_setup, loaded_policy = false, queue_default_job = false, first_boot = false, reexecute = false; + char *switch_root_dir = NULL, *switch_root_init = NULL; usec_t before_startup, after_startup; + static char systemd[] = "systemd"; char timespan[FORMAT_TIMESPAN_MAX]; + const char *shutdown_verb = NULL, *error_message = NULL; + int r, retval = EXIT_FAILURE; + Manager *m = NULL; FDSet *fds = NULL; - bool reexecute = false; - const char *shutdown_verb = NULL; - dual_timestamp initrd_timestamp = DUAL_TIMESTAMP_NULL; - dual_timestamp userspace_timestamp = DUAL_TIMESTAMP_NULL; - dual_timestamp kernel_timestamp = DUAL_TIMESTAMP_NULL; - dual_timestamp security_start_timestamp = DUAL_TIMESTAMP_NULL; - dual_timestamp security_finish_timestamp = DUAL_TIMESTAMP_NULL; - static char systemd[] = "systemd"; - bool skip_setup = false; - bool loaded_policy = false; - bool queue_default_job = false; - bool first_boot = false; - char *switch_root_dir = NULL, *switch_root_init = NULL; - struct rlimit saved_rlimit_nofile = RLIMIT_MAKE_CONST(0), saved_rlimit_memlock = RLIMIT_MAKE_CONST((rlim_t) -1); - const char *error_message = NULL; + /* SysV compatibility: redirect init → telinit */ redirect_telinit(argc, argv); + /* Take timestamps early on */ dual_timestamp_from_monotonic(&kernel_timestamp, 0); dual_timestamp_get(&userspace_timestamp); - /* Determine if this is a reexecution or normal bootup. We do - * the full command line parsing much later, so let's just - * have a quick peek here. */ - if (strv_find(argv+1, "--deserialize")) - skip_setup = true; + /* Figure out whether we need to do initialize the system, or if we already did that because we are + * reexecuting */ + skip_setup = early_skip_setup_check(argc, argv); - /* If we have switched root, do all the special setup - * things */ - if (strv_find(argv+1, "--switched-root")) - skip_setup = false; - - /* If we get started via the /sbin/init symlink then we are - called 'init'. After a subsequent reexecution we are then - called 'systemd'. That is confusing, hence let's call us - systemd right-away. */ + /* If we get started via the /sbin/init symlink then we are called 'init'. After a subsequent reexecution we + * are then called 'systemd'. That is confusing, hence let's call us systemd right-away. */ program_invocation_short_name = systemd; (void) prctl(PR_SET_NAME, systemd); + /* Save the original command line */ saved_argv = argv; saved_argc = argc; + /* Make sure that if the user says "syslog" we actually log to the journal. */ log_set_upgrade_syslog_to_journal(true); if (getpid_cached() == 1) { /* Disable the umask logic */ umask(0); + /* Make sure that at least initially we do not ever log to journald/syslogd, because it might not be activated + * yet (even though the log socket for it exists). */ + log_set_prohibit_ipc(true); + /* Always reopen /dev/console when running as PID 1 or one of its pre-execve() children. This is * important so that we never end up logging to any foreign stderr, for example if we have to log in a * child process right before execve()'ing the actual binary, at a point in time where socket @@ -2131,18 +2363,13 @@ int main(int argc, char *argv[]) { goto finish; } - dual_timestamp_get(&security_start_timestamp); - if (mac_selinux_setup(&loaded_policy) < 0) { - error_message = "Failed to load SELinux policy"; - goto finish; - } else if (mac_smack_setup(&loaded_policy) < 0) { - error_message = "Failed to load SMACK policy"; - goto finish; - } else if (ima_setup() < 0) { - error_message = "Failed to load IMA policy"; + r = initialize_security( + &loaded_policy, + &security_start_timestamp, + &security_finish_timestamp, + &error_message); + if (r < 0) goto finish; - } - dual_timestamp_get(&security_finish_timestamp); } if (mac_selinux_init() < 0) { @@ -2165,7 +2392,6 @@ int main(int argc, char *argv[]) { /* Running inside a container, as PID 1 */ arg_system = true; log_set_target(LOG_TARGET_CONSOLE); - log_close_console(); /* force reopen of /dev/console */ log_open(); /* For later on, see above... */ @@ -2187,11 +2413,14 @@ int main(int argc, char *argv[]) { initialize_coredump(skip_setup); + r = fixup_environment(); + if (r < 0) { + log_emergency_errno(r, "Failed to fix up PID 1 environment: %m"); + error_message = "Failed to fix up PID1 environment"; + goto finish; + } + if (arg_system) { - if (fixup_environment() < 0) { - error_message = "Failed to fix up PID1 environment"; - goto finish; - } /* Try to figure out if we can use colors with the console. No * need to do that for user instances since they never log @@ -2202,12 +2431,6 @@ int main(int argc, char *argv[]) { log_warning_errno(r, "Failed to redirect standard streams to /dev/null: %m"); } - r = initialize_join_controllers(); - if (r < 0) { - error_message = "Failed to initialize cgroup controllers"; - goto finish; - } - /* Mount /proc, /sys and friends, so that /proc/cmdline and * /proc/$PID/fd is available. */ if (getpid_cached() == 1) { @@ -2227,62 +2450,19 @@ int main(int argc, char *argv[]) { (void) reset_all_signal_handlers(); (void) ignore_signals(SIGNALS_IGNORE, -1); - arg_default_tasks_max = system_tasks_max_scale(DEFAULT_TASKS_MAX_PERCENTAGE, 100U); - - if (parse_config_file() < 0) { - error_message = "Failed to parse config file"; - goto finish; - } - - if (arg_system) { - r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0); - if (r < 0) - log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); - } - - /* Note that this also parses bits from the kernel command - * line, including "debug". */ - log_parse_environment(); - - if (parse_argv(argc, argv) < 0) { - error_message = "Failed to parse commandline arguments"; - goto finish; - } - - /* Initialize default unit */ - if (!arg_default_unit) { - arg_default_unit = strdup(SPECIAL_DEFAULT_TARGET); - if (!arg_default_unit) { - r = log_oom(); - error_message = "Failed to set default unit"; - goto finish; - } - } - - if (arg_action == ACTION_TEST && - geteuid() == 0) { - log_error("Don't run test mode as root."); - goto finish; - } - - 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."); + r = load_configuration(argc, argv, &error_message); + if (r < 0) goto finish; - } - if (arg_system && - arg_action == ACTION_RUN && - running_in_chroot() > 0) { - log_error("Cannot be run in a chroot() environment."); + r = safety_checks(); + if (r < 0) goto finish; - } - if (IN_SET(arg_action, ACTION_TEST, ACTION_HELP)) { + if (IN_SET(arg_action, ACTION_TEST, ACTION_HELP, ACTION_DUMP_CONFIGURATION_ITEMS)) pager_open(arg_no_pager, false); + + if (arg_action != ACTION_RUN) skip_setup = true; - } if (arg_action == ACTION_HELP) { retval = help(); @@ -2291,83 +2471,41 @@ int main(int argc, char *argv[]) { retval = version(); goto finish; } else if (arg_action == ACTION_DUMP_CONFIGURATION_ITEMS) { - pager_open(arg_no_pager, false); unit_dump_config_items(stdout); retval = EXIT_SUCCESS; goto finish; } - if (!arg_system && - !getenv("XDG_RUNTIME_DIR")) { - log_error("Trying to run as user instance, but $XDG_RUNTIME_DIR is not set."); - goto finish; - } - assert_se(IN_SET(arg_action, ACTION_RUN, ACTION_TEST)); - /* Close logging fds, in order not to confuse fdset below */ - log_close(); - - /* Remember open file descriptors for later deserialization */ - if (arg_action == ACTION_RUN) { - r = fdset_new_fill(&fds); - if (r < 0) { - log_emergency_errno(r, "Failed to allocate fd set: %m"); - error_message = "Failed to allocate fd set"; - goto finish; - } else - fdset_cloexec(fds, true); - - if (arg_serialization) - assert_se(fdset_remove(fds, fileno(arg_serialization)) >= 0); - - if (arg_system) - /* Become a session leader if we aren't one yet. */ - setsid(); - } - /* Move out of the way, so that we won't block unmounts */ assert_se(chdir("/") == 0); - /* Reset the console, but only if this is really init and we - * are freshly booted */ - 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 - * tty. */ - release_terminal(); - - if (getpid_cached() == 1 && !skip_setup) - console_setup(); - } - - /* Open the logging devices, if possible and necessary */ - log_open(); - - if (arg_show_status == _SHOW_STATUS_UNSET) - arg_show_status = SHOW_STATUS_YES; + if (arg_action == ACTION_RUN) { - /* Make sure we leave a core dump without panicing the - * kernel. */ - if (getpid_cached() == 1) { - install_crash_handler(); + /* Close logging fds, in order not to confuse collecting passed fds and terminal logic below */ + log_close(); - r = mount_cgroup_controllers(arg_join_controllers); + /* Remember open file descriptors for later deserialization */ + r = collect_fds(&fds, &error_message); if (r < 0) goto finish; + + /* Give up any control of the console, but make sure its initialized. */ + setup_console_terminal(skip_setup); + + /* Open the logging devices, if possible and necessary */ + log_open(); } log_execution_mode(&first_boot); - if (arg_action == ACTION_RUN) { - r = initialize_runtime(skip_setup, - &saved_rlimit_nofile, - &saved_rlimit_memlock, - &error_message); - if (r < 0) - goto finish; - } + r = initialize_runtime(skip_setup, + &saved_rlimit_nofile, + &saved_rlimit_memlock, + &error_message); + if (r < 0) + goto finish; r = manager_new(arg_system ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, arg_action == ACTION_TEST ? MANAGER_TEST_FULL : 0, @@ -2416,36 +2554,20 @@ int main(int argc, char *argv[]) { "Loaded units and determined initial transaction in %s.", format_timespan(timespan, sizeof(timespan), after_startup - before_startup, 100 * USEC_PER_MSEC)); - if (arg_system) { - _cleanup_free_ char *taint; - - taint = manager_taint_string(m); - if (!isempty(taint)) - log_struct(LOG_NOTICE, - LOG_MESSAGE("System is tainted: %s", taint), - "TAINT=%s", taint, - "MESSAGE_ID=" SD_MESSAGE_TAINTED_STR, - NULL); - } - if (arg_action == ACTION_TEST) { - printf("-> By units:\n"); - manager_dump_units(m, stdout, "\t"); - - printf("-> By jobs:\n"); - manager_dump_jobs(m, stdout, "\t"); + test_summary(m); retval = EXIT_SUCCESS; goto finish; } - r = invoke_main_loop(m, - &reexecute, - &retval, - &shutdown_verb, - &fds, - &switch_root_dir, - &switch_root_init, - &error_message); + (void) invoke_main_loop(m, + &reexecute, + &retval, + &shutdown_verb, + &fds, + &switch_root_dir, + &switch_root_init, + &error_message); finish: pager_close(); diff --git a/src/core/manager.c b/src/core/manager.c index 81c4d5289b..e837a46f56 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -83,6 +83,7 @@ #include "string-table.h" #include "string-util.h" #include "strv.h" +#include "strxcpyx.h" #include "terminal-util.h" #include "time-util.h" #include "transaction.h" @@ -109,6 +110,7 @@ static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32 static int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int manager_dispatch_jobs_in_progress(sd_event_source *source, usec_t usec, void *userdata); static int manager_dispatch_run_queue(sd_event_source *source, void *userdata); +static int manager_dispatch_sigchld(sd_event_source *source, void *userdata); static int manager_run_environment_generators(Manager *m); static int manager_run_generators(Manager *m); @@ -263,7 +265,7 @@ static int manager_dispatch_ask_password_fd(sd_event_source *source, assert(m); - flush_fd(fd); + (void) flush_fd(fd); m->have_ask_password = have_ask_password(); if (m->have_ask_password < 0) @@ -513,23 +515,31 @@ static int manager_setup_signals(Manager *m) { return 0; } -static void manager_clean_environment(Manager *m) { +static void manager_sanitize_environment(Manager *m) { assert(m); - /* Let's remove some environment variables that we - * need ourselves to communicate with our clients */ + /* Let's remove some environment variables that we need ourselves to communicate with our clients */ strv_env_unset_many( m->environment, - "NOTIFY_SOCKET", + "EXIT_CODE", + "EXIT_STATUS", + "INVOCATION_ID", + "JOURNAL_STREAM", + "LISTEN_FDNAMES", + "LISTEN_FDS", + "LISTEN_PID", "MAINPID", "MANAGERPID", - "LISTEN_PID", - "LISTEN_FDS", - "LISTEN_FDNAMES", + "NOTIFY_SOCKET", + "REMOTE_ADDR", + "REMOTE_PORT", + "SERVICE_RESULT", "WATCHDOG_PID", "WATCHDOG_USEC", - "INVOCATION_ID", NULL); + + /* Let's order the environment alphabetically, just to make it pretty */ + strv_sort(m->environment); } static int manager_default_environment(Manager *m) { @@ -556,8 +566,7 @@ static int manager_default_environment(Manager *m) { if (!m->environment) return -ENOMEM; - manager_clean_environment(m); - strv_sort(m->environment); + manager_sanitize_environment(m); return 0; } @@ -627,6 +636,29 @@ static int manager_setup_run_queue(Manager *m) { return 0; } +static int manager_setup_sigchld_event_source(Manager *m) { + int r; + + assert(m); + assert(!m->sigchld_event_source); + + r = sd_event_add_defer(m->event, &m->sigchld_event_source, manager_dispatch_sigchld, m); + if (r < 0) + return r; + + r = sd_event_source_set_priority(m->sigchld_event_source, SD_EVENT_PRIORITY_NORMAL-7); + if (r < 0) + return r; + + r = sd_event_source_set_enabled(m->sigchld_event_source, SD_EVENT_OFF); + if (r < 0) + return r; + + (void) sd_event_source_set_description(m->sigchld_event_source, "manager-sigchld"); + + return 0; +} + int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) { Manager *m; int r; @@ -727,6 +759,10 @@ int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **_m) { if (r < 0) goto fail; + r = manager_setup_sigchld_event_source(m); + if (r < 0) + goto fail; + m->udev = udev_new(); if (!m->udev) { r = -ENOMEM; @@ -810,7 +846,7 @@ static int manager_setup_notify(Manager *m) { /* 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); + r = sd_event_source_set_priority(m->notify_event_source, SD_EVENT_PRIORITY_NORMAL-8); if (r < 0) return log_error_errno(r, "Failed to set priority of notify event source: %m"); @@ -939,7 +975,7 @@ static int manager_setup_user_lookup_fd(Manager *m) { /* Process even earlier than the notify event source, so that we always know first about valid UID/GID * resolutions */ - r = sd_event_source_set_priority(m->user_lookup_event_source, SD_EVENT_PRIORITY_NORMAL-8); + r = sd_event_source_set_priority(m->user_lookup_event_source, SD_EVENT_PRIORITY_NORMAL-11); if (r < 0) return log_error_errno(errno, "Failed to set priority ot user lookup event source: %m"); @@ -1170,14 +1206,14 @@ Manager* manager_free(Manager *m) { hashmap_free(m->units); hashmap_free(m->units_by_invocation_id); hashmap_free(m->jobs); - hashmap_free(m->watch_pids1); - hashmap_free(m->watch_pids2); + hashmap_free(m->watch_pids); hashmap_free(m->watch_bus); set_free(m->startup_units); set_free(m->failed_units); sd_event_source_unref(m->signal_event_source); + sd_event_source_unref(m->sigchld_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); @@ -1872,27 +1908,40 @@ static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, ui return 0; } -static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, const char *buf, FDSet *fds) { - _cleanup_strv_free_ char **tags = NULL; +static void manager_invoke_notify_message( + Manager *m, + Unit *u, + const struct ucred *ucred, + const char *buf, + FDSet *fds) { assert(m); assert(u); + assert(ucred); assert(buf); - tags = strv_split(buf, "\n\r"); - if (!tags) { - log_oom(); + if (u->notifygen == m->notifygen) /* Already invoked on this same unit in this same iteration? */ return; - } + u->notifygen = m->notifygen; + + if (UNIT_VTABLE(u)->notify_message) { + _cleanup_strv_free_ char **tags = NULL; - if (UNIT_VTABLE(u)->notify_message) - UNIT_VTABLE(u)->notify_message(u, pid, tags, fds); - else if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) { + tags = strv_split(buf, NEWLINE); + if (!tags) { + log_oom(); + return; + } + + UNIT_VTABLE(u)->notify_message(u, ucred, tags, fds); + + } else if (DEBUG_LOGGING) { _cleanup_free_ char *x = NULL, *y = NULL; - x = cescape(buf); + x = ellipsize(buf, 20, 90); if (x) - y = ellipsize(x, 20, 90); + y = cescape(x); + log_unit_debug(u, "Got notification message \"%s\", ignoring.", strnull(y)); } } @@ -1920,9 +1969,11 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t struct cmsghdr *cmsg; struct ucred *ucred = NULL; - Unit *u1, *u2, *u3; + _cleanup_free_ Unit **array_copy = NULL; + Unit *u1, *u2, **array; int r, *fd_array = NULL; unsigned n_fds = 0; + bool found = false; ssize_t n; assert(m); @@ -1969,7 +2020,7 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t } } - if (!ucred || ucred->pid <= 0) { + if (!ucred || !pid_is_valid(ucred->pid)) { log_warning("Received notify message without valid credentials. Ignoring."); return 0; } @@ -1989,22 +2040,41 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t /* Make sure it's NUL-terminated. */ buf[n] = 0; - /* Notify every unit that might be interested, but try - * to avoid notifying the same one multiple times. */ + /* Increase the generation counter used for filtering out duplicate unit invocations. */ + m->notifygen++; + + /* Notify every unit that might be interested, which might be multiple. */ u1 = manager_get_unit_by_pid_cgroup(m, ucred->pid); - if (u1) - manager_invoke_notify_message(m, u1, ucred->pid, buf, fds); + u2 = hashmap_get(m->watch_pids, PID_TO_PTR(ucred->pid)); + array = hashmap_get(m->watch_pids, PID_TO_PTR(-ucred->pid)); + if (array) { + size_t k = 0; - u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(ucred->pid)); - if (u2 && u2 != u1) - manager_invoke_notify_message(m, u2, ucred->pid, buf, fds); + while (array[k]) + k++; - u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(ucred->pid)); - if (u3 && u3 != u2 && u3 != u1) - manager_invoke_notify_message(m, u3, ucred->pid, buf, fds); + array_copy = newdup(Unit*, array, k+1); + if (!array_copy) + log_oom(); + } + /* And now invoke the per-unit callbacks. Note that manager_invoke_notify_message() will handle duplicate units + * make sure we only invoke each unit's handler once. */ + if (u1) { + manager_invoke_notify_message(m, u1, ucred, buf, fds); + found = true; + } + if (u2) { + manager_invoke_notify_message(m, u2, ucred, buf, fds); + found = true; + } + if (array_copy) + for (size_t i = 0; array_copy[i]; i++) { + manager_invoke_notify_message(m, array_copy[i], ucred, buf, fds); + found = true; + } - if (!u1 && !u2 && !u3) - log_warning("Cannot find unit for notify message of PID "PID_FMT".", ucred->pid); + if (!found) + log_warning("Cannot find unit for notify message of PID "PID_FMT", ignoring.", ucred->pid); if (fdset_size(fds) > 0) log_warning("Got extra auxiliary fds with notification message, closing them."); @@ -2012,89 +2082,111 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t return 0; } -static void invoke_sigchld_event(Manager *m, Unit *u, const siginfo_t *si) { - uint64_t iteration; +static void manager_invoke_sigchld_event( + Manager *m, + Unit *u, + const siginfo_t *si) { assert(m); assert(u); assert(si); - sd_event_get_iteration(m->event, &iteration); - - log_unit_debug(u, "Child "PID_FMT" belongs to %s", si->si_pid, u->id); + /* Already invoked the handler of this unit in this iteration? Then don't process this again */ + if (u->sigchldgen == m->sigchldgen) + return; + u->sigchldgen = m->sigchldgen; + log_unit_debug(u, "Child "PID_FMT" belongs to %s.", si->si_pid, u->id); unit_unwatch_pid(u, si->si_pid); - if (UNIT_VTABLE(u)->sigchld_event) { - if (set_size(u->pids) <= 1 || - iteration != u->sigchldgen || - unit_main_pid(u) == si->si_pid || - unit_control_pid(u) == si->si_pid) { - UNIT_VTABLE(u)->sigchld_event(u, si->si_pid, si->si_code, si->si_status); - u->sigchldgen = iteration; - } else - log_debug("%s already issued a sigchld this iteration %" PRIu64 ", skipping. Pids still being watched %d", u->id, iteration, set_size(u->pids)); - } + 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) { +static int manager_dispatch_sigchld(sd_event_source *source, void *userdata) { + Manager *m = userdata; + siginfo_t si = {}; + int r; + + assert(source); assert(m); - for (;;) { - siginfo_t si = {}; + /* First we call waitd() for a PID and do not reap the zombie. That way we can still access /proc/$PID for it + * while it is a zombie. */ - /* First we call waitd() for a PID and do not reap the - * zombie. That way we can still access /proc/$PID for - * it while it is a zombie. */ - if (waitid(P_ALL, 0, &si, WEXITED|WNOHANG|WNOWAIT) < 0) { + if (waitid(P_ALL, 0, &si, WEXITED|WNOHANG|WNOWAIT) < 0) { - if (errno == ECHILD) - break; + if (errno == ECHILD) + goto turn_off; - if (errno == EINTR) - continue; + log_error_errno(errno, "Failed to peek for child with waitid(), ignoring: %m"); + return 0; + } - return -errno; + if (si.si_pid <= 0) + goto turn_off; + + if (IN_SET(si.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED)) { + _cleanup_free_ Unit **array_copy = NULL; + _cleanup_free_ char *name = NULL; + Unit *u1, *u2, **array; + + (void) get_process_comm(si.si_pid, &name); + + log_debug("Child "PID_FMT" (%s) died (code=%s, status=%i/%s)", + si.si_pid, strna(name), + sigchld_code_to_string(si.si_code), + si.si_status, + strna(si.si_code == CLD_EXITED + ? exit_status_to_string(si.si_status, EXIT_STATUS_FULL) + : signal_to_string(si.si_status))); + + /* Increase the generation counter used for filtering out duplicate unit invocations */ + m->sigchldgen++; + + /* And now figure out the unit this belongs to, it might be multiple... */ + u1 = manager_get_unit_by_pid_cgroup(m, si.si_pid); + u2 = hashmap_get(m->watch_pids, PID_TO_PTR(si.si_pid)); + array = hashmap_get(m->watch_pids, PID_TO_PTR(-si.si_pid)); + if (array) { + size_t n = 0; + + /* Cound how many entries the array has */ + while (array[n]) + n++; + + /* Make a copy of the array so that we don't trip up on the array changing beneath us */ + array_copy = newdup(Unit*, array, n+1); + if (!array_copy) + log_oom(); } - if (si.si_pid <= 0) - break; + /* Finally, execute them all. Note that u1, u2 and the array might contain duplicates, but + * that's fine, manager_invoke_sigchld_event() will ensure we only invoke the handlers once for + * each iteration. */ + if (u1) + manager_invoke_sigchld_event(m, u1, &si); + if (u2) + manager_invoke_sigchld_event(m, u2, &si); + if (array_copy) + for (size_t i = 0; array_copy[i]; i++) + manager_invoke_sigchld_event(m, array_copy[i], &si); + } - if (IN_SET(si.si_code, CLD_EXITED, CLD_KILLED, CLD_DUMPED)) { - _cleanup_free_ char *name = NULL; - Unit *u1, *u2, *u3; - - get_process_comm(si.si_pid, &name); - - log_debug("Child "PID_FMT" (%s) died (code=%s, status=%i/%s)", - si.si_pid, strna(name), - sigchld_code_to_string(si.si_code), - si.si_status, - strna(si.si_code == CLD_EXITED - ? exit_status_to_string(si.si_status, EXIT_STATUS_FULL) - : signal_to_string(si.si_status))); - - /* And now figure out the unit this belongs - * to, it might be multiple... */ - u1 = manager_get_unit_by_pid_cgroup(m, si.si_pid); - if (u1) - invoke_sigchld_event(m, u1, &si); - u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(si.si_pid)); - if (u2 && u2 != u1) - invoke_sigchld_event(m, u2, &si); - u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(si.si_pid)); - if (u3 && u3 != u2 && u3 != u1) - invoke_sigchld_event(m, u3, &si); - } + /* And now, we actually reap the zombie. */ + if (waitid(P_PID, si.si_pid, &si, WEXITED) < 0) { + log_error_errno(errno, "Failed to dequeue child, ignoring: %m"); + return 0; + } - /* And now, we actually reap the zombie. */ - if (waitid(P_PID, si.si_pid, &si, WEXITED) < 0) { - if (errno == EINTR) - continue; + return 0; - return -errno; - } - } +turn_off: + /* All children processed for now, turn off event source */ + + r = sd_event_source_set_enabled(m->sigchld_event_source, SD_EVENT_OFF); + if (r < 0) + return log_error_errno(r, "Failed to disable SIGCHLD event source: %m"); return 0; } @@ -2126,7 +2218,6 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t Manager *m = userdata; ssize_t n; struct signalfd_siginfo sfsi; - bool sigchld = false; int r; assert(m); @@ -2137,195 +2228,192 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t return 0; } - for (;;) { - n = read(m->signal_fd, &sfsi, sizeof(sfsi)); - if (n != sizeof(sfsi)) { - if (n >= 0) { - log_warning("Truncated read from signal fd (%zu bytes)!", n); - return 0; - } + n = read(m->signal_fd, &sfsi, sizeof(sfsi)); + if (n != sizeof(sfsi)) { + if (n >= 0) { + log_warning("Truncated read from signal fd (%zu bytes), ignoring!", n); + return 0; + } - if (IN_SET(errno, EINTR, EAGAIN)) - break; + if (IN_SET(errno, EINTR, EAGAIN)) + return 0; - /* We return an error here, which will kill this handler, - * to avoid a busy loop on read error. */ - return log_error_errno(errno, "Reading from signal fd failed: %m"); - } + /* We return an error here, which will kill this handler, + * to avoid a busy loop on read error. */ + return log_error_errno(errno, "Reading from signal fd failed: %m"); + } - log_received_signal(sfsi.ssi_signo == SIGCHLD || - (sfsi.ssi_signo == SIGTERM && MANAGER_IS_USER(m)) - ? LOG_DEBUG : LOG_INFO, - &sfsi); + log_received_signal(sfsi.ssi_signo == SIGCHLD || + (sfsi.ssi_signo == SIGTERM && MANAGER_IS_USER(m)) + ? LOG_DEBUG : LOG_INFO, + &sfsi); - switch (sfsi.ssi_signo) { + switch (sfsi.ssi_signo) { - case SIGCHLD: - sigchld = true; - break; + case SIGCHLD: + r = sd_event_source_set_enabled(m->sigchld_event_source, SD_EVENT_ON); + if (r < 0) + log_warning_errno(r, "Failed to enable SIGCHLD even source, ignoring: %m"); - case SIGTERM: - if (MANAGER_IS_SYSTEM(m)) { - /* This is for compatibility with the - * original sysvinit */ - r = verify_run_space_and_log("Refusing to reexecute"); - if (r >= 0) - m->exit_code = MANAGER_REEXECUTE; - break; - } + break; - _fallthrough_; - case SIGINT: - if (MANAGER_IS_SYSTEM(m)) - manager_handle_ctrl_alt_del(m); - else - manager_start_target(m, SPECIAL_EXIT_TARGET, - JOB_REPLACE_IRREVERSIBLY); + case SIGTERM: + if (MANAGER_IS_SYSTEM(m)) { + /* This is for compatibility with the + * original sysvinit */ + r = verify_run_space_and_log("Refusing to reexecute"); + if (r >= 0) + m->exit_code = MANAGER_REEXECUTE; break; + } - case SIGWINCH: - if (MANAGER_IS_SYSTEM(m)) - manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE); + _fallthrough_; + case SIGINT: + if (MANAGER_IS_SYSTEM(m)) + manager_handle_ctrl_alt_del(m); + else + manager_start_target(m, SPECIAL_EXIT_TARGET, + JOB_REPLACE_IRREVERSIBLY); + break; - /* This is a nop on non-init */ - break; + case SIGWINCH: + if (MANAGER_IS_SYSTEM(m)) + manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE); - case SIGPWR: - if (MANAGER_IS_SYSTEM(m)) - manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE); + /* This is a nop on non-init */ + break; - /* This is a nop on non-init */ - break; + case SIGPWR: + if (MANAGER_IS_SYSTEM(m)) + manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE); - case SIGUSR1: { - Unit *u; + /* This is a nop on non-init */ + break; - u = manager_get_unit(m, SPECIAL_DBUS_SERVICE); + case SIGUSR1: { + Unit *u; - if (!u || UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) { - log_info("Trying to reconnect to bus..."); - bus_init(m, true); - } + u = manager_get_unit(m, SPECIAL_DBUS_SERVICE); - if (!u || !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) { - log_info("Loading D-Bus service..."); - manager_start_target(m, SPECIAL_DBUS_SERVICE, JOB_REPLACE); - } + if (!u || UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) { + log_info("Trying to reconnect to bus..."); + bus_init(m, true); + } + + if (!u || !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u))) { + log_info("Loading D-Bus service..."); + manager_start_target(m, SPECIAL_DBUS_SERVICE, JOB_REPLACE); + } + break; + } + + case SIGUSR2: { + _cleanup_free_ char *dump = NULL; + + r = manager_get_dump_string(m, &dump); + if (r < 0) { + log_warning_errno(errno, "Failed to acquire manager dump: %m"); break; } - case SIGUSR2: { - _cleanup_free_ char *dump = NULL; + log_dump(LOG_INFO, dump); + break; + } - r = manager_get_dump_string(m, &dump); - if (r < 0) { - log_warning_errno(errno, "Failed to acquire manager dump: %m"); - break; - } + case SIGHUP: + r = verify_run_space_and_log("Refusing to reload"); + if (r >= 0) + m->exit_code = MANAGER_RELOAD; + break; + + default: { + + /* Starting SIGRTMIN+0 */ + static const struct { + const char *target; + JobMode mode; + } target_table[] = { + [0] = { SPECIAL_DEFAULT_TARGET, JOB_ISOLATE }, + [1] = { SPECIAL_RESCUE_TARGET, JOB_ISOLATE }, + [2] = { SPECIAL_EMERGENCY_TARGET, JOB_ISOLATE }, + [3] = { SPECIAL_HALT_TARGET, JOB_REPLACE_IRREVERSIBLY }, + [4] = { SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY }, + [5] = { SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY }, + [6] = { SPECIAL_KEXEC_TARGET, JOB_REPLACE_IRREVERSIBLY }, + }; + + /* Starting SIGRTMIN+13, so that target halt and system halt are 10 apart */ + static const ManagerExitCode code_table[] = { + [0] = MANAGER_HALT, + [1] = MANAGER_POWEROFF, + [2] = MANAGER_REBOOT, + [3] = MANAGER_KEXEC, + }; - log_dump(LOG_INFO, dump); + if ((int) sfsi.ssi_signo >= SIGRTMIN+0 && + (int) sfsi.ssi_signo < SIGRTMIN+(int) ELEMENTSOF(target_table)) { + int idx = (int) sfsi.ssi_signo - SIGRTMIN; + manager_start_target(m, target_table[idx].target, + target_table[idx].mode); break; } - case SIGHUP: - r = verify_run_space_and_log("Refusing to reload"); - if (r >= 0) - m->exit_code = MANAGER_RELOAD; + if ((int) sfsi.ssi_signo >= SIGRTMIN+13 && + (int) sfsi.ssi_signo < SIGRTMIN+13+(int) ELEMENTSOF(code_table)) { + m->exit_code = code_table[sfsi.ssi_signo - SIGRTMIN - 13]; break; + } - default: { - - /* Starting SIGRTMIN+0 */ - static const struct { - const char *target; - JobMode mode; - } target_table[] = { - [0] = { SPECIAL_DEFAULT_TARGET, JOB_ISOLATE }, - [1] = { SPECIAL_RESCUE_TARGET, JOB_ISOLATE }, - [2] = { SPECIAL_EMERGENCY_TARGET, JOB_ISOLATE }, - [3] = { SPECIAL_HALT_TARGET, JOB_REPLACE_IRREVERSIBLY }, - [4] = { SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY }, - [5] = { SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY }, - [6] = { SPECIAL_KEXEC_TARGET, JOB_REPLACE_IRREVERSIBLY } - }; - - /* Starting SIGRTMIN+13, so that target halt and system halt are 10 apart */ - static const ManagerExitCode code_table[] = { - [0] = MANAGER_HALT, - [1] = MANAGER_POWEROFF, - [2] = MANAGER_REBOOT, - [3] = MANAGER_KEXEC - }; - - if ((int) sfsi.ssi_signo >= SIGRTMIN+0 && - (int) sfsi.ssi_signo < SIGRTMIN+(int) ELEMENTSOF(target_table)) { - int idx = (int) sfsi.ssi_signo - SIGRTMIN; - manager_start_target(m, target_table[idx].target, - target_table[idx].mode); - break; - } + switch (sfsi.ssi_signo - SIGRTMIN) { - if ((int) sfsi.ssi_signo >= SIGRTMIN+13 && - (int) sfsi.ssi_signo < SIGRTMIN+13+(int) ELEMENTSOF(code_table)) { - m->exit_code = code_table[sfsi.ssi_signo - SIGRTMIN - 13]; - break; - } + case 20: + manager_set_show_status(m, SHOW_STATUS_YES); + break; + + case 21: + manager_set_show_status(m, SHOW_STATUS_NO); + break; + + case 22: + log_set_max_level(LOG_DEBUG); + log_info("Setting log level to debug."); + break; + + case 23: + log_set_max_level(LOG_INFO); + log_info("Setting log level to info."); + break; - switch (sfsi.ssi_signo - SIGRTMIN) { - - case 20: - manager_set_show_status(m, SHOW_STATUS_YES); - break; - - case 21: - manager_set_show_status(m, SHOW_STATUS_NO); - break; - - case 22: - log_set_max_level(LOG_DEBUG); - log_info("Setting log level to debug."); - break; - - case 23: - log_set_max_level(LOG_INFO); - log_info("Setting log level to info."); - break; - - case 24: - if (MANAGER_IS_USER(m)) { - m->exit_code = MANAGER_EXIT; - return 0; - } - - /* This is a nop on init */ - break; - - case 26: - case 29: /* compatibility: used to be mapped to LOG_TARGET_SYSLOG_OR_KMSG */ - log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); - log_notice("Setting log target to journal-or-kmsg."); - break; - - case 27: - log_set_target(LOG_TARGET_CONSOLE); - log_notice("Setting log target to console."); - break; - - case 28: - log_set_target(LOG_TARGET_KMSG); - log_notice("Setting log target to kmsg."); - break; - - default: - log_warning("Got unhandled signal <%s>.", signal_to_string(sfsi.ssi_signo)); + case 24: + if (MANAGER_IS_USER(m)) { + m->exit_code = MANAGER_EXIT; + return 0; } - } - } - } - if (sigchld) - manager_dispatch_sigchld(m); + /* This is a nop on init */ + break; + + case 26: + case 29: /* compatibility: used to be mapped to LOG_TARGET_SYSLOG_OR_KMSG */ + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); + log_notice("Setting log target to journal-or-kmsg."); + break; + + case 27: + log_set_target(LOG_TARGET_CONSOLE); + log_notice("Setting log target to console."); + break; + + case 28: + log_set_target(LOG_TARGET_KMSG); + log_notice("Setting log target to kmsg."); + break; + + default: + log_warning("Got unhandled signal <%s>.", signal_to_string(sfsi.ssi_signo)); + } + }} return 0; } @@ -2362,8 +2450,15 @@ static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32 assert(m); assert(m->idle_pipe[2] == fd); + /* There's at least one Type=idle child that just gave up on us waiting for the boot process to complete. Let's + * now turn off any further console output if there's at least one service that needs console access, so that + * from now on our own output should not spill into that service's output anymore. After all, we support + * Type=idle only to beautify console output and it generally is set on services that want to own the console + * exclusively without our interference. */ m->no_console_output = m->n_on_console > 0; + /* Acknowledge the child's request, and let all all other children know too that they shouldn't wait any longer + * by closing the pipes towards them, which is what they are waiting for. */ manager_close_idle_pipe(m); return 0; @@ -2400,11 +2495,10 @@ int manager_loop(Manager *m) { manager_check_finished(m); - /* There might still be some zombies hanging around from - * before we were exec()'ed. Let's reap them. */ - r = manager_dispatch_sigchld(m); + /* There might still be some zombies hanging around from before we were exec()'ed. Let's reap them. */ + r = sd_event_source_set_enabled(m->sigchld_event_source, SD_EVENT_ON); if (r < 0) - return r; + return log_error_errno(r, "Failed to enable SIGCHLD event source: %m"); while (m->exit_code == MANAGER_OK) { usec_t wait_usec; @@ -2643,6 +2737,8 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { fprintf(f, "n-failed-jobs=%u\n", m->n_failed_jobs); fprintf(f, "taint-usr=%s\n", yes_no(m->taint_usr)); fprintf(f, "ready-sent=%s\n", yes_no(m->ready_sent)); + fprintf(f, "taint-logged=%s\n", yes_no(m->taint_logged)); + fprintf(f, "service-watchdogs=%s\n", yes_no(m->service_watchdogs)); for (q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) { /* The userspace and finish timestamps only apply to the host system, hence only serialize them there */ @@ -2805,6 +2901,24 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { else m->ready_sent = m->ready_sent || b; + } else if ((val = startswith(l, "taint-logged="))) { + int b; + + b = parse_boolean(val); + if (b < 0) + log_notice("Failed to parse taint-logged flag %s", val); + else + m->taint_logged = m->taint_logged || b; + + } else if ((val = startswith(l, "service-watchdogs="))) { + int b; + + b = parse_boolean(val); + if (b < 0) + log_notice("Failed to parse service-watchdogs flag %s", val); + else + m->service_watchdogs = b; + } else if (startswith(l, "env=")) { r = deserialize_environment(&m->environment, l); if (r == -ENOMEM) @@ -3026,6 +3140,9 @@ int manager_reload(Manager *m) { manager_vacuum_uid_refs(m); manager_vacuum_gid_refs(m); + /* It might be safe to log to the journal now. */ + manager_recheck_journal(m); + /* Sync current state of bus names with our set of listening units */ if (m->api_bus) manager_sync_bus_names(m, m->api_bus); @@ -3062,6 +3179,27 @@ bool manager_unit_inactive_or_pending(Manager *m, const char *name) { return unit_inactive_or_pending(u); } +static void log_taint_string(Manager *m) { + _cleanup_free_ char *taint = NULL; + + assert(m); + + if (MANAGER_IS_USER(m) || m->taint_logged) + return; + + m->taint_logged = true; /* only check for taint once */ + + taint = manager_taint_string(m); + if (isempty(taint)) + return; + + log_struct(LOG_NOTICE, + LOG_MESSAGE("System is tainted: %s", taint), + "TAINT=%s", taint, + "MESSAGE_ID=" SD_MESSAGE_TAINTED_STR, + NULL); +} + static void manager_notify_finished(Manager *m) { char userspace[FORMAT_TIMESPAN_MAX], initrd[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX]; usec_t firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec; @@ -3070,6 +3208,11 @@ static void manager_notify_finished(Manager *m) { return; if (MANAGER_IS_SYSTEM(m) && detect_container() <= 0) { + char ts[FORMAT_TIMESPAN_MAX]; + char buf[FORMAT_TIMESPAN_MAX + STRLEN(" (firmware) + ") + FORMAT_TIMESPAN_MAX + STRLEN(" (loader) + ")] + = {}; + char *p = buf; + size_t size = sizeof buf; /* Note that MANAGER_TIMESTAMP_KERNEL's monotonic value is always at 0, and * MANAGER_TIMESTAMP_FIRMWARE's and MANAGER_TIMESTAMP_LOADER's monotonic value should be considered @@ -3080,6 +3223,11 @@ static void manager_notify_finished(Manager *m) { userspace_usec = m->timestamps[MANAGER_TIMESTAMP_FINISH].monotonic - m->timestamps[MANAGER_TIMESTAMP_USERSPACE].monotonic; total_usec = m->timestamps[MANAGER_TIMESTAMP_FIRMWARE].monotonic + m->timestamps[MANAGER_TIMESTAMP_FINISH].monotonic; + if (firmware_usec > 0) + size = strpcpyf(&p, size, "%s (firmware) + ", format_timespan(ts, sizeof(ts), firmware_usec, USEC_PER_MSEC)); + if (loader_usec > 0) + size = strpcpyf(&p, size, "%s (loader) + ", format_timespan(ts, sizeof(ts), loader_usec, USEC_PER_MSEC)); + if (dual_timestamp_is_set(&m->timestamps[MANAGER_TIMESTAMP_INITRD])) { /* The initrd case on bare-metal*/ @@ -3091,7 +3239,8 @@ static void manager_notify_finished(Manager *m) { "KERNEL_USEC="USEC_FMT, kernel_usec, "INITRD_USEC="USEC_FMT, initrd_usec, "USERSPACE_USEC="USEC_FMT, userspace_usec, - LOG_MESSAGE("Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.", + LOG_MESSAGE("Startup finished in %s%s (kernel) + %s (initrd) + %s (userspace) = %s.", + buf, format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC), format_timespan(initrd, sizeof(initrd), initrd_usec, USEC_PER_MSEC), format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC), @@ -3107,14 +3256,15 @@ static void manager_notify_finished(Manager *m) { "MESSAGE_ID=" SD_MESSAGE_STARTUP_FINISHED_STR, "KERNEL_USEC="USEC_FMT, kernel_usec, "USERSPACE_USEC="USEC_FMT, userspace_usec, - LOG_MESSAGE("Startup finished in %s (kernel) + %s (userspace) = %s.", + LOG_MESSAGE("Startup finished in %s%s (kernel) + %s (userspace) = %s.", + buf, format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC), format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC), format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)), NULL); } } else { - /* The container case */ + /* The container and --user case */ firmware_usec = loader_usec = initrd_usec = kernel_usec = 0; total_usec = userspace_usec = m->timestamps[MANAGER_TIMESTAMP_FINISH].monotonic - m->timestamps[MANAGER_TIMESTAMP_USERSPACE].monotonic; @@ -3134,32 +3284,55 @@ static void manager_notify_finished(Manager *m) { "STATUS=Startup finished in %s.", format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)); m->ready_sent = true; + + log_taint_string(m); } -void manager_check_finished(Manager *m) { +static void manager_send_ready(Manager *m) { assert(m); - if (MANAGER_IS_RELOADING(m)) + /* We send READY=1 on reaching basic.target only when running in --user mode. */ + if (!MANAGER_IS_USER(m) || m->ready_sent) + return; + + m->ready_sent = true; + + sd_notifyf(false, + "READY=1\n" + "STATUS=Reached " SPECIAL_BASIC_TARGET "."); +} + +static void manager_check_basic_target(Manager *m) { + Unit *u; + + assert(m); + + /* Small shortcut */ + if (m->ready_sent && m->taint_logged) return; - /* Verify that we are actually running currently. Initially - * the exit code is set to invalid, and during operation it is - * then set to MANAGER_OK */ - if (m->exit_code != MANAGER_OK) + u = manager_get_unit(m, SPECIAL_BASIC_TARGET); + if (!u || !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) return; /* For user managers, send out READY=1 as soon as we reach basic.target */ - if (MANAGER_IS_USER(m) && !m->ready_sent) { - Unit *u; + manager_send_ready(m); - u = manager_get_unit(m, SPECIAL_BASIC_TARGET); - if (u && !u->job) { - sd_notifyf(false, - "READY=1\n" - "STATUS=Reached " SPECIAL_BASIC_TARGET "."); - m->ready_sent = true; - } - } + /* Log the taint string as soon as we reach basic.target */ + log_taint_string(m); +} + +void manager_check_finished(Manager *m) { + assert(m); + + if (MANAGER_IS_RELOADING(m)) + return; + + /* Verify that we have entered the event loop already, and not left it again. */ + if (!MANAGER_IS_RUNNING(m)) + return; + + manager_check_basic_target(m); if (hashmap_size(m->jobs) > 0) { if (m->jobs_in_progress_event_source) @@ -3308,8 +3481,7 @@ int manager_environment_add(Manager *m, char **minus, char **plus) { strv_free(b); m->environment = l; - manager_clean_environment(m); - strv_sort(m->environment); + manager_sanitize_environment(m); return 0; } @@ -3333,28 +3505,50 @@ int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit) { return 0; } -void manager_recheck_journal(Manager *m) { +static bool manager_journal_is_running(Manager *m) { Unit *u; assert(m); + /* If we are the user manager we can safely assume that the journal is up */ if (!MANAGER_IS_SYSTEM(m)) - return; + return true; + /* Check that the socket is not only up, but in RUNNING state */ u = manager_get_unit(m, SPECIAL_JOURNALD_SOCKET); - if (u && SOCKET(u)->state != SOCKET_RUNNING) { - log_close_journal(); - return; - } + if (!u) + return false; + if (SOCKET(u)->state != SOCKET_RUNNING) + return false; + /* Similar, check if the daemon itself is fully up, too */ u = manager_get_unit(m, SPECIAL_JOURNALD_SERVICE); - if (u && SERVICE(u)->state != SERVICE_RUNNING) { - log_close_journal(); + if (!u) + return false; + if (SERVICE(u)->state != SERVICE_RUNNING) + return false; + + return true; +} + +void manager_recheck_journal(Manager *m) { + + assert(m); + + /* Don't bother with this unless we are in the special situation of being PID 1 */ + if (getpid_cached() != 1) return; - } - /* Hmm, OK, so the socket is fully up and the service is up - * too, then let's make use of the thing. */ + if (manager_journal_is_running(m)) { + + /* The journal is fully and entirely up? If so, let's permit logging to it, if that's configured. */ + log_set_prohibit_ipc(false); + } else { + + /* If the journal is down, don't ever log to it, otherwise we might end up deadlocking ourselves as we + * might trigger an activation ourselves we can't fulfill */ + log_set_prohibit_ipc(true); + } log_open(); } @@ -3392,10 +3586,7 @@ static bool manager_get_show_status(Manager *m, StatusType type) { if (type != STATUS_TYPE_EMERGENCY && manager_check_ask_password(m) > 0) return false; - if (m->show_status > 0) - return true; - - return false; + return m->show_status > 0; } const char *manager_get_confirm_spawn(Manager *m) { @@ -3919,6 +4110,21 @@ char *manager_taint_string(Manager *m) { return buf; } +void manager_ref_console(Manager *m) { + assert(m); + + m->n_on_console++; +} + +void manager_unref_console(Manager *m) { + + assert(m->n_on_console > 0); + m->n_on_console--; + + if (m->n_on_console == 0) + m->no_console_output = false; /* unset no_console_output flag, since the console is definitely free now */ +} + static const char *const manager_state_table[_MANAGER_STATE_MAX] = { [MANAGER_INITIALIZING] = "initializing", [MANAGER_STARTING] = "starting", diff --git a/src/core/manager.h b/src/core/manager.h index 902af2609d..0eed67b46a 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -145,14 +145,14 @@ struct Manager { sd_event *event; - /* We use two hash tables here, since the same PID might be - * watched by two different units: once the unit that forked - * it off, and possibly a different unit to which it was - * joined as cgroup member. Since we know that it is either - * one or two units for each PID we just use to hashmaps - * here. */ - Hashmap *watch_pids1; /* pid => Unit object n:1 */ - Hashmap *watch_pids2; /* pid => Unit object n:1 */ + /* This maps PIDs we care about to units that are interested in. We allow multiple units to he interested in + * the same PID and multiple PIDs to be relevant to the same unit. Since in most cases only a single unit will + * be interested in the same PID we use a somewhat special encoding here: the first unit interested in a PID is + * stored directly in the hashmap, keyed by the PID unmodified. If there are other units interested too they'll + * be stored in a NULL-terminated array, and keyed by the negative PID. This is safe as pid_t is signed and + * negative PIDs are not used for regular processes but process groups, which we don't care about in this + * context, but this allows us to use the negative range for our own purposes. */ + Hashmap *watch_pids; /* pid => unit as well as -pid => array of units */ /* A set contains all units which cgroup should be refreshed after startup */ Set *startup_units; @@ -172,6 +172,8 @@ struct Manager { int signal_fd; sd_event_source *signal_event_source; + sd_event_source *sigchld_event_source; + int time_change_fd; sd_event_source *time_change_event_source; @@ -261,8 +263,15 @@ struct Manager { bool taint_usr:1; + /* Have we already sent out the READY=1 notification? */ bool ready_sent:1; + /* Have we already printed the taint line if necessary? */ + bool taint_logged:1; + + /* Have we ever changed the "kernel.pid_max" sysctl? */ + bool sysctl_pid_max_changed:1; + unsigned test_run_flags:8; /* If non-zero, exit with the following value when the systemd @@ -273,6 +282,7 @@ struct Manager { ShowStatus show_status; char *confirm_spawn; bool no_console_output; + bool service_watchdogs; ExecOutput default_std_output, default_std_error; @@ -343,8 +353,13 @@ struct Manager { int first_boot; /* tri-state */ - /* prefixes of e.g. RuntimeDirectory= */ + /* Prefixes of e.g. RuntimeDirectory= */ char *prefix[_EXEC_DIRECTORY_TYPE_MAX]; + + /* Used in the SIGCHLD and sd_notify() message invocation logic to avoid that we dispatch the same event + * multiple times on the same unit. */ + unsigned sigchldgen; + unsigned notifygen; }; #define MANAGER_IS_SYSTEM(m) ((m)->unit_file_scope == UNIT_FILE_SYSTEM) @@ -354,6 +369,9 @@ struct Manager { #define MANAGER_IS_FINISHED(m) (dual_timestamp_is_set((m)->timestamps + MANAGER_TIMESTAMP_FINISH)) +/* The exit code is set to OK as soon as we enter the main loop, and set otherwise as soon as we are done with it */ +#define MANAGER_IS_RUNNING(m) ((m)->exit_code == MANAGER_OK) + int manager_new(UnitFileScope scope, unsigned test_run_flags, Manager **m); Manager* manager_free(Manager *m); @@ -437,6 +455,9 @@ void manager_deserialize_gid_refs_one(Manager *m, const char *value); char *manager_taint_string(Manager *m); +void manager_ref_console(Manager *m); +void manager_unref_console(Manager *m); + const char *manager_state_to_string(ManagerState m) _const_; ManagerState manager_state_from_string(const char *s) _pure_; diff --git a/src/core/meson.build b/src/core/meson.build index 535ccde468..bc034082a5 100644 --- a/src/core/meson.build +++ b/src/core/meson.build @@ -60,6 +60,8 @@ libcore_la_sources = ''' dbus-timer.h dbus-unit.c dbus-unit.h + dbus-util.c + dbus-util.h dbus.c dbus.h device.c diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c index 7171d8fda4..a0c5f5aaae 100644 --- a/src/core/mount-setup.c +++ b/src/core/mount-setup.c @@ -155,10 +155,12 @@ bool mount_point_ignore(const char *path) { } static int mount_one(const MountPoint *p, bool relabel) { - int r; + int r, priority; assert(p); + priority = (p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG; + if (p->condition_fn && !p->condition_fn()) return 0; @@ -168,7 +170,7 @@ static int mount_one(const MountPoint *p, bool relabel) { r = path_is_mount_point(p->where, NULL, AT_SYMLINK_FOLLOW); if (r < 0 && r != -ENOENT) { - log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, r, "Failed to determine whether %s is a mount point: %m", p->where); + log_full_errno(priority, r, "Failed to determine whether %s is a mount point: %m", p->where); return (p->mode & MNT_FATAL) ? r : 0; } if (r > 0) @@ -196,7 +198,7 @@ static int mount_one(const MountPoint *p, bool relabel) { p->type, p->flags, p->options) < 0) { - log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, errno, "Failed to mount %s at %s: %m", p->type, p->where); + log_full_errno(priority, errno, "Failed to mount %s at %s: %m", p->type, p->where); return (p->mode & MNT_FATAL) ? -errno : 0; } @@ -205,10 +207,13 @@ static int mount_one(const MountPoint *p, bool relabel) { (void) label_fix(p->where, false, false); if (p->mode & MNT_CHECK_WRITABLE) { - r = access(p->where, W_OK); - if (r < 0) { + if (access(p->where, W_OK) < 0) { + r = -errno; + (void) umount(p->where); (void) rmdir(p->where); + + log_full_errno(priority, r, "Mount point %s not writable after mounting: %m", p->where); return (p->mode & MNT_FATAL) ? r : 0; } } diff --git a/src/core/mount.c b/src/core/mount.c index b25bb9cb40..4c12542bd7 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -55,7 +55,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_iter*, mnt_free_iter); static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = { [MOUNT_DEAD] = UNIT_INACTIVE, [MOUNT_MOUNTING] = UNIT_ACTIVATING, - [MOUNT_MOUNTING_DONE] = UNIT_ACTIVE, + [MOUNT_MOUNTING_DONE] = UNIT_ACTIVATING, [MOUNT_MOUNTED] = UNIT_ACTIVE, [MOUNT_REMOUNTING] = UNIT_RELOADING, [MOUNT_UNMOUNTING] = UNIT_DEACTIVATING, @@ -164,6 +164,10 @@ static void mount_init(Unit *u) { assert(u->load_state == UNIT_STUB); m->timeout_usec = u->manager->default_timeout_start_usec; + + m->exec_context.std_output = u->manager->default_std_output; + m->exec_context.std_error = u->manager->default_std_error; + m->directory_mode = 0755; /* We need to make sure that /usr/bin/mount is always called @@ -938,7 +942,7 @@ static void mount_enter_mounting(Mount *m) { assert(m); - r = unit_fail_if_symlink(UNIT(m), m->where); + r = unit_fail_if_noncanonical(UNIT(m), m->where); if (r < 0) goto fail; @@ -1131,10 +1135,6 @@ static int mount_reload(Unit *u) { Mount *m = MOUNT(u); assert(m); - - if (m->state == MOUNT_MOUNTING_DONE) /* not yet ready to reload, try again */ - return -EAGAIN; - assert(m->state == MOUNT_MOUNTED); mount_enter_remounting(m); @@ -1276,23 +1276,25 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { log_unit_full(u, f == MOUNT_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0, "Mount process exited, code=%s status=%i", sigchld_code_to_string(code), status); - /* Note that mount(8) returning and the kernel sending us a mount table change event might happen - * out-of-order. If an operation succeed we assume the kernel will follow soon too and already change into the - * resulting state. If it fails we check if the kernel still knows about the mount. and change state - * accordingly. */ + /* Note that due to the io event priority logic, we can be sure the new mountinfo is loaded + * before we process the SIGCHLD for the mount command. */ switch (m->state) { case MOUNT_MOUNTING: - case MOUNT_MOUNTING_DONE: + /* Our mount point has not appeared in mountinfo. Something went wrong. */ - if (f == MOUNT_SUCCESS || m->from_proc_self_mountinfo) - /* If /bin/mount returned success, or if we see the mount point in /proc/self/mountinfo we are - * happy. If we see the first condition first, we should see the second condition - * immediately after – or /bin/mount lies to us and is broken. */ - mount_enter_mounted(m, f); - else - mount_enter_dead(m, f); + if (f == MOUNT_SUCCESS) { + /* Either /bin/mount has an unexpected definition of success, + * or someone raced us and we lost. */ + log_unit_warning(UNIT(m), "Mount process finished, but there is no mount."); + f = MOUNT_FAILURE_PROTOCOL; + } + mount_enter_dead(m, f); + break; + + case MOUNT_MOUNTING_DONE: + mount_enter_mounted(m, f); break; case MOUNT_REMOUNTING: @@ -1302,28 +1304,31 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { break; case MOUNT_UNMOUNTING: - case MOUNT_UNMOUNTING_SIGKILL: - case MOUNT_UNMOUNTING_SIGTERM: - if (m->from_proc_self_mountinfo) { + if (f == MOUNT_SUCCESS && m->from_proc_self_mountinfo) { /* Still a mount point? If so, let's try again. Most likely there were multiple mount points - * stacked on top of each other. Note that due to the io event priority logic we can be sure - * the new mountinfo is loaded before we process the SIGCHLD for the mount command. */ + * stacked on top of each other. We might exceed the timeout specified by the user overall, + * but we will stop as soon as any one umount times out. */ if (m->n_retry_umount < RETRY_UMOUNT_MAX) { log_unit_debug(u, "Mount still present, trying again."); m->n_retry_umount++; mount_enter_unmounting(m); } else { - log_unit_debug(u, "Mount still present after %u attempts to unmount, giving up.", m->n_retry_umount); + log_unit_warning(u, "Mount still present after %u attempts to unmount, giving up.", m->n_retry_umount); mount_enter_mounted(m, f); } } else - mount_enter_dead(m, f); + mount_enter_dead_or_mounted(m, f); break; + case MOUNT_UNMOUNTING_SIGKILL: + case MOUNT_UNMOUNTING_SIGTERM: + mount_enter_dead_or_mounted(m, f); + break; + default: assert_not_reached("Uh, control process died at wrong time."); } @@ -1486,7 +1491,7 @@ static int mount_setup_existing_unit( flags->just_changed = r1 > 0 || r2 > 0 || r3 > 0; flags->is_mounted = true; - flags->just_mounted = !MOUNT(u)->from_proc_self_mountinfo; + flags->just_mounted = !MOUNT(u)->from_proc_self_mountinfo || MOUNT(u)->just_mounted; MOUNT(u)->from_proc_self_mountinfo = true; @@ -1748,7 +1753,7 @@ static void mount_enumerate(Manager *m) { goto fail; } - r = sd_event_source_set_priority(m->mount_event_source, -10); + r = sd_event_source_set_priority(m->mount_event_source, SD_EVENT_PRIORITY_NORMAL-10); if (r < 0) { log_error_errno(r, "Failed to adjust mount watch priority: %m"); goto fail; @@ -1947,6 +1952,7 @@ static const char* const mount_result_table[_MOUNT_RESULT_MAX] = { [MOUNT_FAILURE_SIGNAL] = "signal", [MOUNT_FAILURE_CORE_DUMP] = "core-dump", [MOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit", + [MOUNT_FAILURE_PROTOCOL] = "protocol", }; DEFINE_STRING_TABLE_LOOKUP(mount_result, MountResult); diff --git a/src/core/mount.h b/src/core/mount.h index 44fe3b889e..1a496def84 100644 --- a/src/core/mount.h +++ b/src/core/mount.h @@ -35,12 +35,13 @@ typedef enum MountExecCommand { typedef enum MountResult { MOUNT_SUCCESS, - MOUNT_FAILURE_RESOURCES, + MOUNT_FAILURE_RESOURCES, /* a bit of a misnomer, just our catch-all error for errnos we didn't expect */ MOUNT_FAILURE_TIMEOUT, MOUNT_FAILURE_EXIT_CODE, MOUNT_FAILURE_SIGNAL, MOUNT_FAILURE_CORE_DUMP, MOUNT_FAILURE_START_LIMIT_HIT, + MOUNT_FAILURE_PROTOCOL, _MOUNT_RESULT_MAX, _MOUNT_RESULT_INVALID = -1 } MountResult; diff --git a/src/core/namespace.c b/src/core/namespace.c index a3262fcc4d..70089f212a 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -42,6 +42,7 @@ #include "path-util.h" #include "selinux-util.h" #include "socket-util.h" +#include "stat-util.h" #include "string-table.h" #include "string-util.h" #include "strv.h" @@ -496,6 +497,35 @@ static void drop_outside_root(const char *root_directory, MountEntry *m, unsigne *n = t - m; } +static int clone_device_node(const char *d, const char *temporary_mount) { + const char *dn; + struct stat st; + int r; + + if (stat(d, &st) < 0) { + if (errno == ENOENT) + return 0; + return -errno; + } + + if (!S_ISBLK(st.st_mode) && + !S_ISCHR(st.st_mode)) + return -EINVAL; + + if (st.st_rdev == 0) + return 0; + + dn = strjoina(temporary_mount, d); + + mac_selinux_create_file_prepare(d, st.st_mode); + r = mknod(dn, st.st_mode, st.st_rdev); + mac_selinux_create_file_clear(); + if (r < 0) + return log_debug_errno(errno, "mknod failed for %s: %m", d); + + return 1; +} + static int mount_private_dev(MountEntry *m) { static const char devnodes[] = "/dev/null\0" @@ -531,14 +561,33 @@ static int mount_private_dev(MountEntry *m) { goto fail; } - devptmx = strjoina(temporary_mount, "/dev/ptmx"); - if (symlink("pts/ptmx", devptmx) < 0) { - r = -errno; + /* /dev/ptmx can either be a device node or a symlink to /dev/pts/ptmx + * when /dev/ptmx a device node, /dev/pts/ptmx has 000 permissions making it inaccessible + * thus, in that case make a clone + * + * in nspawn and other containers it will be a symlink, in that case make it a symlink + */ + r = is_symlink("/dev/ptmx"); + if (r < 0) goto fail; + if (r > 0) { + devptmx = strjoina(temporary_mount, "/dev/ptmx"); + if (symlink("pts/ptmx", devptmx) < 0) { + r = -errno; + goto fail; + } + } else { + r = clone_device_node("/dev/ptmx", temporary_mount); + if (r < 0) + goto fail; + if (r == 0) { + r = -ENXIO; + goto fail; + } } devshm = strjoina(temporary_mount, "/dev/shm"); - (void) mkdir(devshm, 01777); + (void) mkdir(devshm, 0755); r = mount("/dev/shm", devshm, NULL, MS_BIND, NULL); if (r < 0) { r = -errno; @@ -557,42 +606,9 @@ static int mount_private_dev(MountEntry *m) { (void) symlink("/run/systemd/journal/dev-log", devlog); NULSTR_FOREACH(d, devnodes) { - _cleanup_free_ char *dn = NULL; - struct stat st; - - r = stat(d, &st); - if (r < 0) { - - if (errno == ENOENT) - continue; - - r = -errno; - goto fail; - } - - if (!S_ISBLK(st.st_mode) && - !S_ISCHR(st.st_mode)) { - r = -EINVAL; - goto fail; - } - - if (st.st_rdev == 0) - continue; - - dn = strappend(temporary_mount, d); - if (!dn) { - r = -ENOMEM; - goto fail; - } - - mac_selinux_create_file_prepare(d, st.st_mode); - r = mknod(dn, st.st_mode, st.st_rdev); - mac_selinux_create_file_clear(); - - if (r < 0) { - r = -errno; + r = clone_device_node(d, temporary_mount); + if (r < 0) goto fail; - } } dev_setup(temporary_mount, UID_INVALID, GID_INVALID); @@ -1450,6 +1466,18 @@ static const char *const protect_home_table[_PROTECT_HOME_MAX] = { DEFINE_STRING_TABLE_LOOKUP(protect_home, ProtectHome); +ProtectHome parse_protect_home_or_bool(const char *s) { + int r; + + r = parse_boolean(s); + if (r > 0) + return PROTECT_HOME_YES; + if (r == 0) + return PROTECT_HOME_NO; + + return protect_home_from_string(s); +} + static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = { [PROTECT_SYSTEM_NO] = "no", [PROTECT_SYSTEM_YES] = "yes", @@ -1459,6 +1487,18 @@ static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = { DEFINE_STRING_TABLE_LOOKUP(protect_system, ProtectSystem); +ProtectSystem parse_protect_system_or_bool(const char *s) { + int r; + + r = parse_boolean(s); + if (r > 0) + return PROTECT_SYSTEM_YES; + if (r == 0) + return PROTECT_SYSTEM_NO; + + return protect_system_from_string(s); +} + static const char* const namespace_type_table[] = { [NAMESPACE_MOUNT] = "mnt", [NAMESPACE_CGROUP] = "cgroup", diff --git a/src/core/namespace.h b/src/core/namespace.h index f0f198362c..42d841c4d2 100644 --- a/src/core/namespace.h +++ b/src/core/namespace.h @@ -101,9 +101,11 @@ int setup_netns(int netns_storage_socket[2]); const char* protect_home_to_string(ProtectHome p) _const_; ProtectHome protect_home_from_string(const char *s) _pure_; +ProtectHome parse_protect_home_or_bool(const char *s); const char* protect_system_to_string(ProtectSystem p) _const_; ProtectSystem protect_system_from_string(const char *s) _pure_; +ProtectSystem parse_protect_system_or_bool(const char *s); void bind_mount_free_many(BindMount *b, unsigned n); int bind_mount_add(BindMount **b, unsigned *n, const BindMount *item); diff --git a/src/core/path.c b/src/core/path.c index 6b22451a08..8a5ec0a72f 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -772,6 +772,9 @@ const UnitVTable path_vtable = { "Unit\0" "Path\0" "Install\0", + .private_section = "Path", + + .can_transient = true, .init = path_init, .done = path_done, @@ -794,5 +797,6 @@ const UnitVTable path_vtable = { .reset_failed = path_reset_failed, - .bus_vtable = bus_path_vtable + .bus_vtable = bus_path_vtable, + .bus_set_property = bus_path_set_property, }; diff --git a/src/core/scope.c b/src/core/scope.c index 10454d56b0..468dd81217 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -472,19 +472,16 @@ static void scope_notify_cgroup_empty_event(Unit *u) { static void scope_sigchld_event(Unit *u, pid_t pid, int code, int status) { - /* If we get a SIGCHLD event for one of the processes we were - interested in, then we look for others to watch, under the - assumption that we'll sooner or later get a SIGCHLD for - them, as the original process we watched was probably the - parent of them, and they are hence now our children. */ + assert(u); + /* If we get a SIGCHLD event for one of the processes we were interested in, then we look for others to + * watch, under the assumption that we'll sooner or later get a SIGCHLD for them, as the original + * process we watched was probably the parent of them, and they are hence now our children. */ unit_tidy_watch_pids(u, 0, 0); unit_watch_all_pids(u); - /* If the PID set is empty now, then let's finish this off - (On unified we use proper notifications) */ - if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) == 0 && set_isempty(u->pids)) - scope_notify_cgroup_empty_event(u); + /* If the PID set is empty now, then let's finish this off. */ + unit_synthesize_cgroup_empty_event(u); } static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) { diff --git a/src/core/service.c b/src/core/service.c index ef1be33260..6476dc68c5 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -866,9 +866,45 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { cgroup_context_dump(&s->cgroup_context, f, prefix); } +static int service_is_suitable_main_pid(Service *s, pid_t pid, int prio) { + Unit *owner; + + assert(s); + assert(pid_is_valid(pid)); + + /* Checks whether the specified PID is suitable as main PID for this service. returns negative if not, 0 if the + * PID is questionnable but should be accepted if the source of configuration is trusted. > 0 if the PID is + * good */ + + if (pid == getpid_cached() || pid == 1) { + log_unit_full(UNIT(s), prio, 0, "New main PID "PID_FMT" is the manager, refusing.", pid); + return -EPERM; + } + + if (pid == s->control_pid) { + log_unit_full(UNIT(s), prio, 0, "New main PID "PID_FMT" is the control process, refusing.", pid); + return -EPERM; + } + + if (!pid_is_alive(pid)) { + log_unit_full(UNIT(s), prio, 0, "New main PID "PID_FMT" does not exist or is a zombie.", pid); + return -ESRCH; + } + + owner = manager_get_unit_by_pid(UNIT(s)->manager, pid); + if (owner == UNIT(s)) { + log_unit_debug(UNIT(s), "New main PID "PID_FMT" belongs to service, we are happy.", pid); + return 1; /* Yay, it's definitely a good PID */ + } + + return 0; /* Hmm it's a suspicious PID, let's accept it if configuration source is trusted */ +} + static int service_load_pid_file(Service *s, bool may_warn) { + char procfs[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; _cleanup_free_ char *k = NULL; - int r; + _cleanup_close_ int fd = -1; + int r, prio; pid_t pid; assert(s); @@ -876,30 +912,47 @@ static int service_load_pid_file(Service *s, bool may_warn) { if (!s->pid_file) return -ENOENT; - r = read_one_line_file(s->pid_file, &k); - if (r < 0) { - if (may_warn) - log_unit_info_errno(UNIT(s), r, "PID file %s not readable (yet?) after %s: %m", s->pid_file, service_state_to_string(s->state)); - return r; - } + prio = may_warn ? LOG_INFO : LOG_DEBUG; + + fd = chase_symlinks(s->pid_file, NULL, CHASE_OPEN|CHASE_SAFE, NULL); + if (fd == -EPERM) + return log_unit_full(UNIT(s), prio, fd, "Permission denied while opening PID file or unsafe symlink chain: %s", s->pid_file); + if (fd < 0) + return log_unit_full(UNIT(s), prio, fd, "Can't open PID file %s (yet?) after %s: %m", s->pid_file, service_state_to_string(s->state)); + + /* Let's read the PID file now that we chased it down. But we need to convert the O_PATH fd chase_symlinks() returned us into a proper fd first. */ + xsprintf(procfs, "/proc/self/fd/%i", fd); + r = read_one_line_file(procfs, &k); + if (r < 0) + return log_unit_error_errno(UNIT(s), r, "Can't convert PID files %s O_PATH file descriptor to proper file descriptor: %m", s->pid_file); r = parse_pid(k, &pid); - if (r < 0) { - if (may_warn) - log_unit_info_errno(UNIT(s), r, "Failed to read PID from file %s: %m", s->pid_file); + if (r < 0) + return log_unit_full(UNIT(s), prio, r, "Failed to parse PID from file %s: %m", s->pid_file); + + if (s->main_pid_known && pid == s->main_pid) + return 0; + + r = service_is_suitable_main_pid(s, pid, prio); + if (r < 0) return r; - } + if (r == 0) { + struct stat st; - if (!pid_is_alive(pid)) { - if (may_warn) - log_unit_info(UNIT(s), "PID "PID_FMT" read from file %s does not exist or is a zombie.", pid, s->pid_file); - return -ESRCH; + /* Hmm, it's not clear if the new main PID is safe. Let's allow this if the PID file is owned by root */ + + if (fstat(fd, &st) < 0) + return log_unit_error_errno(UNIT(s), errno, "Failed to fstat() PID file O_PATH fd: %m"); + + if (st.st_uid != 0) { + log_unit_error(UNIT(s), "New main PID "PID_FMT" does not belong to service, and PID file is not owned by root. Refusing.", pid); + return -EPERM; + } + + log_unit_debug(UNIT(s), "New main PID "PID_FMT" does not belong to service, but we'll accept it since PID file is owned by root.", pid); } if (s->main_pid_known) { - if (pid == s->main_pid) - return 0; - log_unit_debug(UNIT(s), "Main PID changing: "PID_FMT" -> "PID_FMT, s->main_pid, pid); service_unwatch_main_pid(s); @@ -915,7 +968,7 @@ static int service_load_pid_file(Service *s, bool may_warn) { if (r < 0) /* FIXME: we need to do something here */ return log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" for service: %m", pid); - return 0; + return 1; } static void service_search_main_pid(Service *s) { @@ -1007,26 +1060,6 @@ static void service_set_state(Service *s, ServiceState state) { 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 - * hold on the console, since unit_notify() only does that in case of - * change of state */ - if (state == SERVICE_EXITED && - s->remain_after_exit && - UNIT(s)->manager->n_on_console > 0) { - - ExecContext *ec; - - ec = unit_get_exec_context(UNIT(s)); - if (ec && exec_context_may_touch_console(ec)) { - Manager *m = UNIT(s)->manager; - - 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; - } - } - if (old_state != state) log_unit_debug(UNIT(s), "Changed %s -> %s", service_state_to_string(old_state), service_state_to_string(state)); @@ -1080,8 +1113,7 @@ static int service_coldplug(Unit *u) { if (s->main_pid > 0 && pid_is_unwaited(s->main_pid) && - ((s->deserialized_state == SERVICE_START && IN_SET(s->type, SERVICE_FORKING, SERVICE_DBUS, SERVICE_ONESHOT, SERVICE_NOTIFY)) || - IN_SET(s->deserialized_state, + (IN_SET(s->deserialized_state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST, @@ -2586,10 +2618,8 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, if (parse_pid(value, &pid) < 0) log_unit_debug(u, "Failed to parse main-pid value: %s", value); - else { - service_set_main_pid(s, pid); - unit_watch_pid(UNIT(s), pid); - } + else + (void) service_set_main_pid(s, pid); } else if (streq(key, "main-pid-known")) { int b; @@ -2960,6 +2990,7 @@ static void service_notify_cgroup_empty_event(Unit *u) { } static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { + bool notify_dbus = true; Service *s = SERVICE(u); ServiceResult f; @@ -2981,7 +3012,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { /* Forking services may occasionally move to a new PID. * As long as they update the PID file before exiting the old * PID, they're fine. */ - if (service_load_pid_file(s, false) == 0) + if (service_load_pid_file(s, false) > 0) return; s->main_pid = 0; @@ -3238,23 +3269,21 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { assert_not_reached("Uh, control process died at wrong time."); } } - } + } else /* Neither control nor main PID? If so, don't notify about anything */ + notify_dbus = false; /* Notify clients about changed exit status */ - unit_add_to_dbus_queue(u); + if (notify_dbus) + unit_add_to_dbus_queue(u); - /* We got one SIGCHLD for the service, let's watch all - * processes that are now running of the service, and watch - * that. Among the PIDs we then watch will be children - * reassigned to us, which hopefully allows us to identify - * when all children are gone */ + /* If we get a SIGCHLD event for one of the processes we were interested in, then we look for others to watch, + * under the assumption that we'll sooner or later get a SIGCHLD for them, as the original process we watched + * was probably the parent of them, and they are hence now our children. */ unit_tidy_watch_pids(u, s->main_pid, s->control_pid); unit_watch_all_pids(u); - /* If the PID set is empty now, then let's finish this off - (On unified we use proper notifications) */ - if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) == 0 && set_isempty(u->pids)) - unit_add_to_cgroup_empty_queue(u); + /* If the PID set is empty now, then let's check if the cgroup is empty too and finish off the unit. */ + unit_synthesize_cgroup_empty_event(u); } static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) { @@ -3364,10 +3393,14 @@ static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void watchdog_usec = service_get_watchdog_usec(s); - log_unit_error(UNIT(s), "Watchdog timeout (limit %s)!", - format_timespan(t, sizeof(t), watchdog_usec, 1)); + if (UNIT(s)->manager->service_watchdogs) { + log_unit_error(UNIT(s), "Watchdog timeout (limit %s)!", + format_timespan(t, sizeof(t), watchdog_usec, 1)); - service_enter_signal(s, SERVICE_STOP_SIGABRT, SERVICE_FAILURE_WATCHDOG); + service_enter_signal(s, SERVICE_STOP_SIGABRT, SERVICE_FAILURE_WATCHDOG); + } else + log_unit_warning(UNIT(s), "Watchdog disabled! Ignoring watchdog timeout (limit %s)!", + format_timespan(t, sizeof(t), watchdog_usec, 1)); return 0; } @@ -3406,37 +3439,55 @@ static bool service_notify_message_authorized(Service *s, pid_t pid, char **tags return true; } -static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds) { +static void service_notify_message( + Unit *u, + const struct ucred *ucred, + char **tags, + FDSet *fds) { + Service *s = SERVICE(u); bool notify_dbus = false; const char *e; char **i; + int r; assert(u); + assert(ucred); - if (!service_notify_message_authorized(SERVICE(u), pid, tags, fds)) + if (!service_notify_message_authorized(SERVICE(u), ucred->pid, tags, fds)) return; - if (log_get_max_level() >= LOG_DEBUG) { + if (DEBUG_LOGGING) { _cleanup_free_ char *cc = NULL; cc = strv_join(tags, ", "); - log_unit_debug(u, "Got notification message from PID "PID_FMT" (%s)", pid, isempty(cc) ? "n/a" : cc); + log_unit_debug(u, "Got notification message from PID "PID_FMT" (%s)", ucred->pid, isempty(cc) ? "n/a" : cc); } /* Interpret MAINPID= */ e = strv_find_startswith(tags, "MAINPID="); if (e && IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) { - if (parse_pid(e, &pid) < 0) - log_unit_warning(u, "Failed to parse MAINPID= field in notification message: %s", e); - else if (pid == s->control_pid) - log_unit_warning(u, "A control process cannot also be the main process"); - else if (pid == getpid_cached() || pid == 1) - log_unit_warning(u, "Service manager can't be main process, ignoring sd_notify() MAINPID= field"); - else if (pid != s->main_pid) { - service_set_main_pid(s, pid); - unit_watch_pid(UNIT(s), pid); - notify_dbus = true; + pid_t new_main_pid; + + if (parse_pid(e, &new_main_pid) < 0) + log_unit_warning(u, "Failed to parse MAINPID= field in notification message, ignoring: %s", e); + else if (!s->main_pid_known || new_main_pid != s->main_pid) { + + r = service_is_suitable_main_pid(s, new_main_pid, LOG_WARNING); + if (r == 0) { + /* The new main PID is a bit suspicous, which is OK if the sender is privileged. */ + + if (ucred->uid == 0) { + log_unit_debug(u, "New main PID "PID_FMT" does not belong to service, but we'll accept it as the request to change it came from a privileged process.", new_main_pid); + r = 1; + } else + log_unit_debug(u, "New main PID "PID_FMT" does not belong to service, refusing.", new_main_pid); + } + if (r > 0) { + service_set_main_pid(s, new_main_pid); + unit_watch_pid(UNIT(s), new_main_pid); + notify_dbus = true; + } } } @@ -3735,6 +3786,32 @@ static int service_control_pid(Unit *u) { return s->control_pid; } +static bool service_needs_console(Unit *u) { + Service *s = SERVICE(u); + + assert(s); + + /* We provide our own implementation of this here, instead of relying of the generic implementation + * unit_needs_console() provides, since we want to return false if we are in SERVICE_EXITED state. */ + + if (!exec_context_may_touch_console(&s->exec_context)) + return false; + + return IN_SET(s->state, + SERVICE_START_PRE, + SERVICE_START, + SERVICE_START_POST, + SERVICE_RUNNING, + SERVICE_RELOAD, + SERVICE_STOP, + SERVICE_STOP_SIGABRT, + SERVICE_STOP_SIGTERM, + SERVICE_STOP_SIGKILL, + SERVICE_STOP_POST, + SERVICE_FINAL_SIGTERM, + SERVICE_FINAL_SIGKILL); +} + static const char* const service_restart_table[_SERVICE_RESTART_MAX] = { [SERVICE_RESTART_NO] = "no", [SERVICE_RESTART_ON_SUCCESS] = "on-success", @@ -3850,6 +3927,7 @@ const UnitVTable service_vtable = { .bus_commit_properties = bus_service_commit_properties, .get_timeout = service_get_timeout, + .needs_console = service_needs_console, .can_transient = true, .status_message_formats = { diff --git a/src/core/shutdown.c b/src/core/shutdown.c index aca89d13d1..cc31b33f1c 100644 --- a/src/core/shutdown.c +++ b/src/core/shutdown.c @@ -31,10 +31,11 @@ #include <unistd.h> #include "alloc-util.h" +#include "async.h" #include "cgroup-util.h" -#include "fd-util.h" #include "def.h" #include "exec-util.h" +#include "fd-util.h" #include "fileio.h" #include "killall.h" #include "log.h" @@ -57,6 +58,7 @@ static char* arg_verb; static uint8_t arg_exit_code; +static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC; static int parse_argv(int argc, char *argv[]) { enum { @@ -65,6 +67,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_LOG_COLOR, ARG_LOG_LOCATION, ARG_EXIT_CODE, + ARG_TIMEOUT, }; static const struct option options[] = { @@ -73,6 +76,7 @@ static int parse_argv(int argc, char *argv[]) { { "log-color", optional_argument, NULL, ARG_LOG_COLOR }, { "log-location", optional_argument, NULL, ARG_LOG_LOCATION }, { "exit-code", required_argument, NULL, ARG_EXIT_CODE }, + { "timeout", required_argument, NULL, ARG_TIMEOUT }, {} }; @@ -128,6 +132,13 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_TIMEOUT: + r = parse_sec(optarg, &arg_timeout); + if (r < 0) + log_error("Failed to parse shutdown timeout %s, ignoring", optarg); + + break; + case '\001': if (!arg_verb) arg_verb = optarg; @@ -211,26 +222,20 @@ static bool sync_making_progress(unsigned long long *prev_dirty) { } static void sync_with_progress(void) { + unsigned long long dirty = ULONG_LONG_MAX; unsigned checks; pid_t pid; int r; - unsigned long long dirty = ULONG_LONG_MAX; BLOCK_SIGNALS(SIGCHLD); - /* Due to the possiblity of the sync operation hanging, we fork - * a child process and monitor the progress. If the timeout - * lapses, the assumption is that that particular sync stalled. */ - pid = fork(); - if (pid < 0) { - log_error_errno(errno, "Failed to fork: %m"); - return; - } + /* Due to the possiblity of the sync operation hanging, we fork a child process and monitor the progress. If + * the timeout lapses, the assumption is that that particular sync stalled. */ - if (pid == 0) { - /* Start the sync operation here in the child */ - sync(); - _exit(EXIT_SUCCESS); + r = asynchronous_sync(&pid); + if (r < 0) { + log_error_errno(r, "Failed to fork sync(): %m"); + return; } log_info("Syncing filesystems and block devices."); @@ -279,7 +284,7 @@ int main(int argc, char *argv[]) { /* journald will die if not gone yet. The log target defaults * to console, but may have been changed by command line options. */ - log_close_console(); /* force reopen of /dev/console */ + log_set_prohibit_ipc(true); log_open(); umask(0022); @@ -328,11 +333,13 @@ int main(int argc, char *argv[]) { if (!in_container) sync_with_progress(); + disable_coredumps(); + log_info("Sending SIGTERM to remaining processes..."); - broadcast_signal(SIGTERM, true, true); + broadcast_signal(SIGTERM, true, true, arg_timeout); log_info("Sending SIGKILL to remaining processes..."); - broadcast_signal(SIGKILL, true, false); + broadcast_signal(SIGKILL, true, false, arg_timeout); need_umount = !in_container; need_swapoff = !in_container; @@ -488,15 +495,10 @@ int main(int argc, char *argv[]) { if (!in_container) { /* We cheat and exec kexec to avoid doing all its work */ - pid_t pid; - log_info("Rebooting with kexec."); - pid = fork(); - if (pid < 0) - log_error_errno(errno, "Failed to fork: %m"); - else if (pid == 0) { - + r = safe_fork("(sd-kexec)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_WAIT, NULL); + if (r == 0) { const char * const args[] = { KEXEC, "-e", NULL }; @@ -505,8 +507,9 @@ int main(int argc, char *argv[]) { execv(args[0], (char * const *) args); _exit(EXIT_FAILURE); - } else - wait_for_terminate_and_warn("kexec", pid, true); + } + + /* If we are still running, then the kexec can't have worked, let's fall through */ } cmd = RB_AUTOBOOT; @@ -548,7 +551,7 @@ int main(int argc, char *argv[]) { * CAP_SYS_BOOT just exit, this will kill our * container for good. */ log_info("Exiting container."); - exit(0); + exit(EXIT_SUCCESS); } r = log_error_errno(errno, "Failed to invoke reboot(): %m"); diff --git a/src/core/slice.c b/src/core/slice.c index 5ab1e6f898..fef47b04fe 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -59,30 +59,24 @@ static void slice_set_state(Slice *t, SliceState state) { } static int slice_add_parent_slice(Slice *s) { - char *a, *dash; - Unit *parent; + Unit *u = UNIT(s), *parent; + _cleanup_free_ char *a = NULL; int r; assert(s); - if (UNIT_ISSET(UNIT(s)->slice)) + if (UNIT_ISSET(u->slice)) return 0; - if (unit_has_name(UNIT(s), SPECIAL_ROOT_SLICE)) - return 0; - - a = strdupa(UNIT(s)->id); - dash = strrchr(a, '-'); - if (dash) - strcpy(dash, ".slice"); - else - a = (char*) SPECIAL_ROOT_SLICE; + r = slice_build_parent_slice(u->id, &a); + if (r <= 0) /* 0 means root slice */ + return r; - r = manager_load_unit(UNIT(s)->manager, a, NULL, NULL, &parent); + r = manager_load_unit(u->manager, a, NULL, NULL, &parent); if (r < 0) return r; - unit_ref_set(&UNIT(s)->slice, parent); + unit_ref_set(&u->slice, parent); return 0; } diff --git a/src/core/socket.c b/src/core/socket.c index 7e3630ada7..74cdebbe81 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -53,6 +53,7 @@ #include "signal-util.h" #include "smack-util.h" #include "socket.h" +#include "socket-protocol-list.h" #include "special.h" #include "string-table.h" #include "string-util.h" @@ -655,7 +656,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { SocketExecCommand c; Socket *s = SOCKET(u); SocketPort *p; - const char *prefix2; + const char *prefix2, *str; assert(s); assert(f); @@ -680,7 +681,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { "%sTCPCongestion: %s\n" "%sRemoveOnStop: %s\n" "%sWritable: %s\n" - "%sFDName: %s\n" + "%sFileDescriptorName: %s\n" "%sSELinuxContextFromNet: %s\n", prefix, socket_state_to_string(s->state), prefix, socket_result_to_string(s->result), @@ -715,10 +716,12 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sAccepted: %u\n" "%sNConnections: %u\n" - "%sMaxConnections: %u\n", + "%sMaxConnections: %u\n" + "%sMaxConnectionsPerSource: %u\n", prefix, s->n_accepted, prefix, s->n_connections, - prefix, s->max_connections); + prefix, s->max_connections, + prefix, s->max_connections_per_source); if (s->priority >= 0) fprintf(f, @@ -843,6 +846,24 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->trigger_limit.interval, USEC_PER_SEC), prefix, s->trigger_limit.burst); + str = socket_protocol_to_name(s->socket_protocol); + if (str) + fprintf(f, "%sSocketProtocol: %s\n", prefix, str); + + if (!strv_isempty(s->symlinks)) { + char **q; + + fprintf(f, "%sSymlinks:", prefix); + STRV_FOREACH(q, s->symlinks) + fprintf(f, " %s", *q); + + fprintf(f, "\n"); + } + + fprintf(f, + "%sTimeoutSec: %s\n", + prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->timeout_usec, USEC_PER_SEC)); + exec_context_dump(&s->exec_context, f, prefix); kill_context_dump(&s->kill_context, f, prefix); @@ -1506,7 +1527,7 @@ static int socket_address_listen_in_cgroup( if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pair) < 0) return log_unit_error_errno(UNIT(s), errno, "Failed to create communication channel: %m"); - r = unit_fork_helper_process(UNIT(s), &pid); + r = unit_fork_helper_process(UNIT(s), "(sd-listen)", &pid); if (r < 0) return log_unit_error_errno(UNIT(s), r, "Failed to fork off listener stub process: %m"); if (r == 0) { @@ -1533,7 +1554,7 @@ static int socket_address_listen_in_cgroup( fd = receive_one_fd(pair[0], 0); /* We synchronously wait for the helper, as it shouldn't be slow */ - r = wait_for_terminate_and_warn("listen-cgroup-helper", pid, false); + r = wait_for_terminate_and_check("(sd-listen)", pid, WAIT_LOG_ABNORMAL); if (r < 0) { safe_close(fd); return r; @@ -1923,7 +1944,7 @@ static int socket_chown(Socket *s, pid_t *_pid) { /* We have to resolve the user names out-of-process, hence * let's fork here. It's messy, but well, what can we do? */ - r = unit_fork_helper_process(UNIT(s), &pid); + r = unit_fork_helper_process(UNIT(s), "(sd-chown)", &pid); if (r < 0) return r; if (r == 0) { @@ -2780,6 +2801,23 @@ const char* socket_port_type_to_string(SocketPort *p) { } } +SocketType socket_port_type_from_string(const char *s) { + assert(s); + + if (STR_IN_SET(s, "Stream", "Datagram", "SequentialPacket", "Netlink")) + return SOCKET_SOCKET; + else if (streq(s, "Special")) + return SOCKET_SPECIAL; + else if (streq(s, "MessageQueue")) + return SOCKET_MQUEUE; + else if (streq(s, "FIFO")) + return SOCKET_FIFO; + else if (streq(s, "USBFunction")) + return SOCKET_USB_FUNCTION; + else + return _SOCKET_TYPE_INVALID; +} + _pure_ static bool socket_check_gc(Unit *u) { Socket *s = SOCKET(u); @@ -2833,7 +2871,7 @@ static int socket_accept_in_cgroup(Socket *s, SocketPort *p, int fd) { if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pair) < 0) return log_unit_error_errno(UNIT(s), errno, "Failed to create communication channel: %m"); - r = unit_fork_helper_process(UNIT(s), &pid); + r = unit_fork_helper_process(UNIT(s), "(sd-accept)", &pid); if (r < 0) return log_unit_error_errno(UNIT(s), r, "Failed to fork off accept stub process: %m"); if (r == 0) { @@ -2860,7 +2898,7 @@ static int socket_accept_in_cgroup(Socket *s, SocketPort *p, int fd) { cfd = receive_one_fd(pair[0], 0); /* We synchronously wait for the helper, as it shouldn't be slow */ - r = wait_for_terminate_and_warn("accept-cgroup-helper", pid, false); + r = wait_for_terminate_and_check("(sd-accept)", pid, WAIT_LOG_ABNORMAL); if (r < 0) { safe_close(cfd); return r; @@ -3212,10 +3250,7 @@ char *socket_fdname(Socket *s) { * didn't specify anything specifically, use the socket unit's * name as fallback. */ - if (s->fdname) - return s->fdname; - - return UNIT(s)->id; + return s->fdname ?: UNIT(s)->id; } static int socket_control_pid(Unit *u) { @@ -3227,11 +3262,11 @@ static int socket_control_pid(Unit *u) { } static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = { - [SOCKET_EXEC_START_PRE] = "StartPre", - [SOCKET_EXEC_START_CHOWN] = "StartChown", - [SOCKET_EXEC_START_POST] = "StartPost", - [SOCKET_EXEC_STOP_PRE] = "StopPre", - [SOCKET_EXEC_STOP_POST] = "StopPost" + [SOCKET_EXEC_START_PRE] = "ExecStartPre", + [SOCKET_EXEC_START_CHOWN] = "ExecStartChown", + [SOCKET_EXEC_START_POST] = "ExecStartPost", + [SOCKET_EXEC_STOP_PRE] = "ExecStopPre", + [SOCKET_EXEC_STOP_POST] = "ExecStopPost" }; DEFINE_STRING_TABLE_LOOKUP(socket_exec_command, SocketExecCommand); @@ -3264,6 +3299,8 @@ const UnitVTable socket_vtable = { "Install\0", .private_section = "Socket", + .can_transient = true, + .init = socket_init, .done = socket_done, .load = socket_load, diff --git a/src/core/socket.h b/src/core/socket.h index e9e560e57e..9c528fb39c 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -43,8 +43,8 @@ typedef enum SocketType { SOCKET_SPECIAL, SOCKET_MQUEUE, SOCKET_USB_FUNCTION, - _SOCKET_FIFO_MAX, - _SOCKET_FIFO_INVALID = -1 + _SOCKET_TYPE_MAX, + _SOCKET_TYPE_INVALID = -1 } SocketType; typedef enum SocketResult { @@ -194,3 +194,4 @@ const char* socket_result_to_string(SocketResult i) _const_; SocketResult socket_result_from_string(const char *s) _pure_; const char* socket_port_type_to_string(SocketPort *p) _pure_; +SocketType socket_port_type_from_string(const char *p) _pure_; diff --git a/src/core/swap.c b/src/core/swap.c index 849ccbdd43..70097ff2ba 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -1309,7 +1309,7 @@ static void swap_enumerate(Manager *m) { /* Dispatch this before we dispatch SIGCHLD, so that * we always get the events from /proc/swaps before * the SIGCHLD of /sbin/swapon. */ - r = sd_event_source_set_priority(m->swap_event_source, -10); + r = sd_event_source_set_priority(m->swap_event_source, SD_EVENT_PRIORITY_NORMAL-10); if (r < 0) { log_error_errno(r, "Failed to change /proc/swaps priority: %m"); goto fail; diff --git a/src/core/timer.c b/src/core/timer.c index 03935eea94..133cbb974d 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -431,6 +431,7 @@ static void timer_enter_waiting(Timer *t, bool initial) { if (base <= 0) continue; + base = MAX(base, t->last_trigger.monotonic); break; @@ -443,6 +444,7 @@ static void timer_enter_waiting(Timer *t, bool initial) { if (base <= 0) continue; + base = MAX(base, t->last_trigger.monotonic); break; diff --git a/src/core/umount.c b/src/core/umount.c index 7f8ddb99ee..731436af27 100644 --- a/src/core/umount.c +++ b/src/core/umount.c @@ -28,6 +28,7 @@ #include "libudev.h" #include "alloc-util.h" +#include "blockdev-util.h" #include "def.h" #include "escape.h" #include "fd-util.h" @@ -35,12 +36,13 @@ #include "linux-3.13/dm-ioctl.h" #include "list.h" #include "mount-setup.h" +#include "mount-util.h" #include "path-util.h" +#include "process-util.h" #include "signal-util.h" #include "string-util.h" #include "udev-util.h" #include "umount.h" -#include "mount-util.h" #include "util.h" #include "virt.h" @@ -388,11 +390,10 @@ static int remount_with_timeout(MountPoint *m, char *options, int *n_failed) { * fork a child process and set a timeout. If the timeout * lapses, the assumption is that that particular remount * failed. */ - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork: %m"); - - if (pid == 0) { + r = safe_fork("(sd-remount)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid); + if (r < 0) + return r; + if (r == 0) { log_info("Remounting '%s' read-only in with options '%s'.", m->path, options); /* Start the mount operation here in the child */ @@ -423,11 +424,10 @@ static int umount_with_timeout(MountPoint *m, bool *changed) { * fork a child process and set a timeout. If the timeout * lapses, the assumption is that that particular umount * failed. */ - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork: %m"); - - if (pid == 0) { + r = safe_fork("(sd-umount)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid); + if (r < 0) + return r; + if (r == 0) { log_info("Unmounting '%s'.", m->path); /* Start the mount operation here in the child Using MNT_FORCE diff --git a/src/core/unit.c b/src/core/unit.c index 7af8425707..932f05baa2 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -21,6 +21,7 @@ #include <errno.h> #include <stdlib.h> #include <string.h> +#include <sys/prctl.h> #include <sys/stat.h> #include <unistd.h> @@ -629,6 +630,9 @@ void unit_free(Unit *u) { if (u->in_cgroup_empty_queue) LIST_REMOVE(cgroup_empty_queue, u->manager->cgroup_empty_queue, u); + if (u->on_console) + manager_unref_console(u->manager); + unit_release_cgroup(u); if (!MANAGER_IS_RELOADING(u->manager)) @@ -2304,6 +2308,23 @@ finish: } +static void unit_update_on_console(Unit *u) { + bool b; + + assert(u); + + b = unit_needs_console(u); + if (u->on_console == b) + return; + + u->on_console = b; + if (b) + manager_ref_console(u->manager); + else + manager_unref_console(u->manager); + +} + void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) { Manager *m; bool unexpected; @@ -2344,24 +2365,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su unit_unlink_state_files(u); } - /* Note that this doesn't apply to RemainAfterExit services exiting - * successfully, since there's no change of state in that case. Which is - * why it is handled in service_set_state() */ - if (UNIT_IS_INACTIVE_OR_FAILED(os) != UNIT_IS_INACTIVE_OR_FAILED(ns)) { - ExecContext *ec; - - 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--; - - 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++; - } - } + unit_update_on_console(u); if (u->job) { unexpected = false; @@ -2533,44 +2537,97 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su } int unit_watch_pid(Unit *u, pid_t pid) { - int q, r; + int r; assert(u); - assert(pid >= 1); + assert(pid_is_valid(pid)); - /* Watch a specific PID. We only support one or two units - * watching each PID for now, not more. */ + /* Watch a specific PID */ r = set_ensure_allocated(&u->pids, NULL); if (r < 0) return r; - r = hashmap_ensure_allocated(&u->manager->watch_pids1, NULL); + r = hashmap_ensure_allocated(&u->manager->watch_pids, NULL); if (r < 0) return r; - r = hashmap_put(u->manager->watch_pids1, PID_TO_PTR(pid), u); - if (r == -EEXIST) { - r = hashmap_ensure_allocated(&u->manager->watch_pids2, NULL); - if (r < 0) - return r; + /* First try, let's add the unit keyed by "pid". */ + r = hashmap_put(u->manager->watch_pids, PID_TO_PTR(pid), u); + if (r == -EEXIST) { + Unit **array; + bool found = false; + size_t n = 0; - r = hashmap_put(u->manager->watch_pids2, PID_TO_PTR(pid), u); - } + /* OK, the "pid" key is already assigned to a different unit. Let's see if the "-pid" key (which points + * to an array of Units rather than just a Unit), lists us already. */ - q = set_put(u->pids, PID_TO_PTR(pid)); - if (q < 0) - return q; + array = hashmap_get(u->manager->watch_pids, PID_TO_PTR(-pid)); + if (array) + for (; array[n]; n++) + if (array[n] == u) + found = true; - return r; + if (found) /* Found it already? if so, do nothing */ + r = 0; + else { + Unit **new_array; + + /* Allocate a new array */ + new_array = new(Unit*, n + 2); + if (!new_array) + return -ENOMEM; + + memcpy_safe(new_array, array, sizeof(Unit*) * n); + new_array[n] = u; + new_array[n+1] = NULL; + + /* Add or replace the old array */ + r = hashmap_replace(u->manager->watch_pids, PID_TO_PTR(-pid), new_array); + if (r < 0) { + free(new_array); + return r; + } + + free(array); + } + } else if (r < 0) + return r; + + r = set_put(u->pids, PID_TO_PTR(pid)); + if (r < 0) + return r; + + return 0; } void unit_unwatch_pid(Unit *u, pid_t pid) { + Unit **array; + assert(u); - assert(pid >= 1); + assert(pid_is_valid(pid)); + + /* First let's drop the unit in case it's keyed as "pid". */ + (void) hashmap_remove_value(u->manager->watch_pids, PID_TO_PTR(pid), u); + + /* Then, let's also drop the unit, in case it's in the array keyed by -pid */ + array = hashmap_get(u->manager->watch_pids, PID_TO_PTR(-pid)); + if (array) { + size_t n, m = 0; + + /* Let's iterate through the array, dropping our own entry */ + for (n = 0; array[n]; n++) + if (array[n] != u) + array[m++] = array[n]; + array[m] = NULL; + + if (m == 0) { + /* The array is now empty, remove the entire entry */ + assert(hashmap_remove(u->manager->watch_pids, PID_TO_PTR(-pid)) == array); + free(array); + } + } - (void) hashmap_remove_value(u->manager->watch_pids1, PID_TO_PTR(pid), u); - (void) hashmap_remove_value(u->manager->watch_pids2, PID_TO_PTR(pid), u); (void) set_remove(u->pids, PID_TO_PTR(pid)); } @@ -3041,7 +3098,7 @@ int unit_install_bus_match(Unit *u, sd_bus *bus, const char *name) { "member='NameOwnerChanged'," "arg0='", name, "'"); - return sd_bus_add_match(bus, &u->match_bus_slot, match, signal_name_owner_changed, u); + return sd_bus_add_match_async(bus, &u->match_bus_slot, match, signal_name_owner_changed, NULL, u); } int unit_watch_bus_name(Unit *u, const char *name) { @@ -4704,25 +4761,29 @@ void unit_warn_if_dir_nonempty(Unit *u, const char* where) { NULL); } -int unit_fail_if_symlink(Unit *u, const char* where) { +int unit_fail_if_noncanonical(Unit *u, const char* where) { + _cleanup_free_ char *canonical_where; int r; assert(u); assert(where); - r = is_symlink(where); + r = chase_symlinks(where, NULL, CHASE_NONEXISTENT, &canonical_where); if (r < 0) { - log_unit_debug_errno(u, r, "Failed to check symlink %s, ignoring: %m", where); + log_unit_debug_errno(u, r, "Failed to check %s for symlinks, ignoring: %m", where); return 0; } - if (r == 0) + + /* We will happily ignore a trailing slash (or any redundant slashes) */ + if (path_equal(where, canonical_where)) return 0; + /* No need to mention "." or "..", they would already have been rejected by unit_name_from_path() */ log_struct(LOG_ERR, "MESSAGE_ID=" SD_MESSAGE_OVERMOUNTING_STR, LOG_UNIT_ID(u), LOG_UNIT_INVOCATION_ID(u), - LOG_UNIT_MESSAGE(u, "Mount on symlink %s not allowed.", where), + LOG_UNIT_MESSAGE(u, "Mount path %s is not canonical (contains a symlink).", where), "WHERE=%s", where, NULL); @@ -4968,8 +5029,7 @@ void unit_set_exec_params(Unit *u, ExecParameters *p) { SET_FLAG(p->flags, EXEC_CGROUP_DELEGATE, UNIT_CGROUP_BOOL(u, delegate)); } -int unit_fork_helper_process(Unit *u, pid_t *ret) { - pid_t pid; +int unit_fork_helper_process(Unit *u, const char *name, pid_t *ret) { int r; assert(u); @@ -4980,32 +5040,24 @@ int unit_fork_helper_process(Unit *u, pid_t *ret) { (void) unit_realize_cgroup(u); - pid = fork(); - if (pid < 0) - return -errno; - - if (pid == 0) { + r = safe_fork(name, FORK_REOPEN_LOG, ret); + if (r != 0) + return r; - (void) default_signals(SIGNALS_CRASH_HANDLER, SIGNALS_IGNORE, -1); - (void) ignore_signals(SIGPIPE, -1); + (void) default_signals(SIGNALS_CRASH_HANDLER, SIGNALS_IGNORE, -1); + (void) ignore_signals(SIGPIPE, -1); - log_close(); - log_open(); + (void) prctl(PR_SET_PDEATHSIG, SIGTERM); - if (u->cgroup_path) { - r = cg_attach_everywhere(u->manager->cgroup_supported, u->cgroup_path, 0, NULL, NULL); - if (r < 0) { - log_unit_error_errno(u, r, "Failed to join unit cgroup %s: %m", u->cgroup_path); - _exit(EXIT_CGROUP); - } + if (u->cgroup_path) { + r = cg_attach_everywhere(u->manager->cgroup_supported, u->cgroup_path, 0, NULL, NULL); + if (r < 0) { + log_unit_error_errno(u, r, "Failed to join unit cgroup %s: %m", u->cgroup_path); + _exit(EXIT_CGROUP); } - - *ret = getpid_cached(); - return 0; } - *ret = pid; - return 1; + return 0; } static void unit_update_dependency_mask(Unit *u, UnitDependency d, Unit *other, UnitDependencyInfo di) { @@ -5301,6 +5353,28 @@ void unit_warn_leftover_processes(Unit *u) { (void) cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, 0, 0, NULL, log_leftover, u); } +bool unit_needs_console(Unit *u) { + ExecContext *ec; + UnitActiveState state; + + assert(u); + + state = unit_active_state(u); + + if (UNIT_IS_INACTIVE_OR_FAILED(state)) + return false; + + if (UNIT_VTABLE(u)->needs_console) + return UNIT_VTABLE(u)->needs_console(u); + + /* If this unit type doesn't implement this call, let's use a generic fallback implementation: */ + ec = unit_get_exec_context(u); + if (!ec) + return false; + + return exec_context_may_touch_console(ec); +} + static const char* const collect_mode_table[_COLLECT_MODE_MAX] = { [COLLECT_INACTIVE] = "inactive", [COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed", diff --git a/src/core/unit.h b/src/core/unit.h index fdd82315ba..8c79d4ed2e 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -236,8 +236,10 @@ struct Unit { * process SIGCHLD for */ Set *pids; - /* Used in sigchld event invocation to avoid repeat events being invoked */ - uint64_t sigchldgen; + /* Used in SIGCHLD and sd_notify() message event invocation logic to avoid that we dispatch the same event + * multiple times on the same unit. */ + unsigned sigchldgen; + unsigned notifygen; /* Used during GC sweeps */ unsigned gc_marker; @@ -338,6 +340,7 @@ struct Unit { bool sent_dbus_new_signal:1; bool in_audit:1; + bool on_console:1; bool cgroup_realized:1; bool cgroup_members_mask_valid:1; @@ -506,7 +509,7 @@ struct UnitVTable { void (*notify_cgroup_empty)(Unit *u); /* Called whenever a process of this unit sends us a message */ - void (*notify_message)(Unit *u, pid_t pid, char **tags, FDSet *fds); + void (*notify_message)(Unit *u, const struct ucred *ucred, char **tags, FDSet *fds); /* Called whenever a name this Unit registered for comes or goes away. */ void (*bus_name_owner_change)(Unit *u, const char *name, const char *old_owner, const char *new_owner); @@ -539,6 +542,9 @@ struct UnitVTable { /* Returns the main PID if there is any defined, or 0. */ pid_t (*control_pid)(Unit *u); + /* Returns true if the unit currently needs access to the console */ + bool (*needs_console)(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 @@ -760,7 +766,7 @@ 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_fail_if_noncanonical(Unit *u, const char* where); int unit_start_limit_test(Unit *u); @@ -782,7 +788,7 @@ bool unit_shall_confirm_spawn(Unit *u); void unit_set_exec_params(Unit *s, ExecParameters *p); -int unit_fork_helper_process(Unit *u, pid_t *ret); +int unit_fork_helper_process(Unit *u, const char *name, pid_t *ret); void unit_remove_dependencies(Unit *u, UnitDependencyMask mask); @@ -793,6 +799,8 @@ int unit_prepare_exec(Unit *u); void unit_warn_leftover_processes(Unit *u); +bool unit_needs_console(Unit *u); + /* Macros which append UNIT= or USER_UNIT= to the message */ #define log_unit_full(unit, level, error, ...) \ diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index e6063cc980..fdcea22f56 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -1126,7 +1126,7 @@ static int gather_pid_metadata( /* If this is PID 1 disable coredump collection, we'll unlikely be able to process it later on. */ if (is_pid1_crash((const char**) context)) { log_notice("Due to PID 1 having crashed coredump collection will now be turned off."); - (void) write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0); + disable_coredumps(); } set_iovec_field(iovec, n_iovec, "COREDUMP_UNIT=", context[CONTEXT_UNIT]); diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index 0d420b2190..96e4a3e7e2 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -885,7 +885,6 @@ static int run_gdb(sd_journal *j) { _cleanup_free_ char *exe = NULL, *path = NULL; bool unlink_path = false; const char *data; - siginfo_t st; size_t len; pid_t pid; int r; @@ -928,28 +927,16 @@ static int run_gdb(sd_journal *j) { /* Don't interfere with gdb and its handling of SIGINT. */ (void) ignore_signals(SIGINT, -1); - pid = fork(); - if (pid < 0) { - r = log_error_errno(errno, "Failed to fork(): %m"); + r = safe_fork("(gdb)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid); + if (r < 0) goto finish; - } - if (pid == 0) { - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - + if (r == 0) { execlp("gdb", "gdb", exe, path, NULL); - log_error_errno(errno, "Failed to invoke gdb: %m"); - _exit(1); - } - - r = wait_for_terminate(pid, &st); - if (r < 0) { - log_error_errno(r, "Failed to wait for gdb: %m"); - goto finish; + _exit(EXIT_FAILURE); } - r = st.si_code == CLD_EXITED ? st.si_status : 255; + r = wait_for_terminate_and_check("gdb", pid, WAIT_LOG_ABNORMAL); finish: (void) default_signals(SIGINT, -1); @@ -1061,7 +1048,7 @@ int main(int argc, char *argv[]) { if (r < 0) goto end; - if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) { + if (DEBUG_LOGGING) { _cleanup_free_ char *filter; filter = journal_make_match_string(j); diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c index 7e61332e52..acde2a6a32 100644 --- a/src/cryptsetup/cryptsetup-generator.c +++ b/src/cryptsetup/cryptsetup-generator.c @@ -61,7 +61,7 @@ static int create_disk( const char *password, const char *options) { - _cleanup_free_ char *p = NULL, *n = NULL, *d = NULL, *u = NULL, *e = NULL, + _cleanup_free_ char *n = NULL, *d = NULL, *u = NULL, *e = NULL, *filtered = NULL, *u_escaped = NULL, *password_escaped = NULL, *filtered_escaped = NULL, *name_escaped = NULL; _cleanup_fclose_ FILE *f = NULL; const char *dmname; @@ -90,18 +90,14 @@ static int create_disk( if (!e) return log_oom(); - r = unit_name_build("systemd-cryptsetup", e, ".service", &n); - if (r < 0) - return log_error_errno(r, "Failed to generate unit name: %m"); - - p = strjoin(arg_dest, "/", n); - if (!p) - return log_oom(); - u = fstab_node_to_udev_node(device); if (!u) return log_oom(); + r = unit_name_build("systemd-cryptsetup", e, ".service", &n); + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + u_escaped = specifier_escape(u); if (!u_escaped) return log_oom(); @@ -110,18 +106,17 @@ static int create_disk( if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); - password_escaped = specifier_escape(password); - if (!password_escaped) - return log_oom(); - - f = fopen(p, "wxe"); - if (!f) - return log_error_errno(errno, "Failed to create unit file %s: %m", p); + if (password) { + password_escaped = specifier_escape(password); + if (!password_escaped) + return log_oom(); + } - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + r = generator_open_unit_file(arg_dest, NULL, n, &f); + if (r < 0) + return r; fprintf(f, - "# Automatically generated by systemd-cryptsetup-generator\n\n" "[Unit]\n" "Description=Cryptography Setup for %%I\n" "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n" @@ -183,9 +178,11 @@ static int create_disk( if (r < 0) return r; - filtered_escaped = specifier_escape(filtered); - if (!filtered_escaped) - return log_oom(); + if (filtered) { + filtered_escaped = specifier_escape(filtered); + if (!filtered_escaped) + return log_oom(); + } fprintf(f, "\n[Service]\n" @@ -210,7 +207,7 @@ static int create_disk( r = fflush_and_check(f); if (r < 0) - return log_error_errno(r, "Failed to write file %s: %m", p); + return log_error_errno(r, "Failed to write unit file %s: %m", n); if (!noauto) { r = generator_add_symlink(arg_dest, d, "wants", n); @@ -475,7 +472,8 @@ int main(int argc, char *argv[]) { if (argc > 1) arg_dest = argv[1]; - log_set_target(LOG_TARGET_SAFE); + log_set_prohibit_ipc(true); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 21c51022ef..7255ff418c 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -47,7 +47,7 @@ static char *arg_cipher = NULL; static unsigned arg_key_size = 0; static int arg_key_slot = CRYPT_ANY_SLOT; static unsigned arg_keyfile_size = 0; -static unsigned arg_keyfile_offset = 0; +static uint64_t arg_keyfile_offset = 0; static char *arg_hash = NULL; static char *arg_header = NULL; static unsigned arg_tries = 3; @@ -131,13 +131,22 @@ static int parse_one_option(const char *option) { } } else if ((val = startswith(option, "keyfile-offset="))) { + uint64_t off; - r = safe_atou(val, &arg_keyfile_offset); + r = safe_atou64(val, &off); if (r < 0) { log_error_errno(r, "Failed to parse %s, ignoring: %m", option); return 0; } + if ((size_t) off != off) { + /* https://gitlab.com/cryptsetup/cryptsetup/issues/359 */ + log_error("keyfile-offset= value would truncated to %zu, ignoring.", (size_t) off); + return 0; + } + + arg_keyfile_offset = off; + } else if ((val = startswith(option, "hash="))) { r = free_and_strdup(&arg_hash, val); if (r < 0) diff --git a/src/debug-generator/debug-generator.c b/src/debug-generator/debug-generator.c index 604faa0d18..61c890d05a 100644 --- a/src/debug-generator/debug-generator.c +++ b/src/debug-generator/debug-generator.c @@ -165,7 +165,8 @@ int main(int argc, char *argv[]) { if (argc > 1) arg_dest = argv[2]; - log_set_target(LOG_TARGET_SAFE); + log_set_prohibit_ipc(true); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); diff --git a/src/delta/delta.c b/src/delta/delta.c index d286881698..645b0b2278 100644 --- a/src/delta/delta.c +++ b/src/delta/delta.c @@ -161,8 +161,8 @@ static int notify_override_unchanged(const char *f) { static int found_override(const char *top, const char *bottom) { _cleanup_free_ char *dest = NULL; - int k; pid_t pid; + int r; assert(top); assert(bottom); @@ -170,40 +170,35 @@ static int found_override(const char *top, const char *bottom) { if (null_or_empty_path(top) > 0) return notify_override_masked(top, bottom); - k = readlink_malloc(top, &dest); - if (k >= 0) { + r = readlink_malloc(top, &dest); + if (r >= 0) { if (equivalent(dest, bottom) > 0) return notify_override_equivalent(top, bottom); else return notify_override_redirected(top, bottom); } - k = notify_override_overridden(top, bottom); + r = notify_override_overridden(top, bottom); if (!arg_diff) - return k; + return r; putchar('\n'); fflush(stdout); - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork off diff: %m"); - else if (pid == 0) { - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - + r = safe_fork("(diff)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid); + if (r < 0) + return r; + if (r == 0) { execlp("diff", "diff", "-us", "--", bottom, top, NULL); log_error_errno(errno, "Failed to execute diff: %m"); _exit(EXIT_FAILURE); } - wait_for_terminate_and_warn("diff", pid, false); + (void) wait_for_terminate_and_check("diff", pid, WAIT_LOG_ABNORMAL); putchar('\n'); - return k; + return r; } static int enumerate_dir_d( @@ -392,19 +387,28 @@ static int enumerate_dir( return 0; } -static int should_skip_prefix(const char* p) { +static bool should_skip_path(const char *prefix, const char *suffix) { #if HAVE_SPLIT_USR - int r; _cleanup_free_ char *target = NULL; + const char *p; + char *dirname; - r = chase_symlinks(p, NULL, 0, &target); - if (r < 0) - return r; + dirname = strjoina(prefix, "/", suffix); - return !streq(p, target) && nulstr_contains(prefixes, target); -#else - return 0; + if (chase_symlinks(dirname, NULL, 0, &target) < 0) + return false; + + NULSTR_FOREACH(p, prefixes) { + if (path_startswith(dirname, p)) + continue; + + if (path_equal(target, strjoina(p, "/", suffix))) { + log_debug("%s redirects to %s, skipping.", dirname, target); + return true; + } + } #endif + return false; } static int process_suffix(const char *suffix, const char *onlyprefix) { @@ -434,14 +438,8 @@ static int process_suffix(const char *suffix, const char *onlyprefix) { NULSTR_FOREACH(p, prefixes) { _cleanup_free_ char *t = NULL; - int skip; - skip = should_skip_prefix(p); - if (skip < 0) { - r = skip; - goto finish; - } - if (skip) + if (should_skip_path(p, suffix)) continue; t = strjoin(p, "/", suffix); @@ -520,19 +518,12 @@ static int process_suffix_chop(const char *arg) { /* Strip prefix from the suffix */ NULSTR_FOREACH(p, prefixes) { const char *suffix; - int skip; - - skip = should_skip_prefix(p); - if (skip < 0) - return skip; - if (skip) - continue; suffix = startswith(arg, p); if (suffix) { suffix += strspn(suffix, "/"); if (*suffix) - return process_suffix(suffix, NULL); + return process_suffix(suffix, p); else return process_suffixes(arg); } diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 207ddeb70f..262e520d56 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -23,6 +23,21 @@ #include <shadow.h> #include <unistd.h> +#ifdef HAVE_CRYPT_H +/* libxcrypt is a replacement for glibc's libcrypt, and libcrypt might be + * removed from glibc at some point. As part of the removal, defines for + * crypt(3) are dropped from unistd.h, and we must include crypt.h instead. + * + * Newer versions of glibc (v2.0+) already ship crypt.h with a definition + * of crypt(3) as well, so we simply include it if it is present. MariaDB, + * MySQL, PostgreSQL, Perl and some other wide-spread packages do it the + * same way since ages without any problems. + */ +# include <crypt.h> +#endif + +#include "sd-id128.h" + #include "alloc-util.h" #include "ask-password-api.h" #include "copy.h" diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c index 0091e388dc..97d824aca4 100644 --- a/src/fsck/fsck.c +++ b/src/fsck/fsck.c @@ -284,9 +284,8 @@ int main(int argc, char *argv[]) { _cleanup_(sd_device_unrefp) sd_device *dev = NULL; const char *device, *type; bool root_directory; - siginfo_t status; struct stat st; - int r; + int r, exit_status; pid_t pid; if (argc > 2) { @@ -392,12 +391,10 @@ int main(int argc, char *argv[]) { } } - pid = fork(); - if (pid < 0) { - r = log_error_errno(errno, "fork(): %m"); + r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); + if (r < 0) goto finish; - } - if (pid == 0) { + if (r == 0) { char dash_c[STRLEN("-C") + DECIMAL_STR_MAX(int) + 1]; int progress_socket = -1; const char *cmdline[9]; @@ -405,10 +402,6 @@ int main(int argc, char *argv[]) { /* Child */ - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - /* Close the reading side of the progress pipe */ progress_pipe[0] = safe_close(progress_pipe[0]); @@ -455,38 +448,30 @@ int main(int argc, char *argv[]) { (void) process_progress(progress_pipe[0]); progress_pipe[0] = -1; - r = wait_for_terminate(pid, &status); - if (r < 0) { - log_error_errno(r, "waitid(): %m"); + exit_status = wait_for_terminate_and_check("fsck", pid, WAIT_LOG_ABNORMAL); + if (exit_status < 0) { + r = exit_status; goto finish; } + if (exit_status & ~1) { + log_error("fsck failed with exit status %i.", exit_status); - if (status.si_code != CLD_EXITED || (status.si_status & ~1)) { - - if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)) - log_error("fsck terminated by signal %s.", signal_to_string(status.si_status)); - else if (status.si_code == CLD_EXITED) - log_error("fsck failed with error code %i.", status.si_status); - else - log_error("fsck failed due to unknown reason."); - - r = -EINVAL; - - if (status.si_code == CLD_EXITED && (status.si_status & FSCK_SYSTEM_SHOULD_REBOOT) && root_directory) + if ((exit_status & FSCK_SYSTEM_SHOULD_REBOOT) && root_directory) { /* System should be rebooted. */ start_target(SPECIAL_REBOOT_TARGET, "replace-irreversibly"); - else if (status.si_code == CLD_EXITED && (status.si_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED))) + r = -EINVAL; + } else if (exit_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED)) { /* Some other problem */ start_target(SPECIAL_EMERGENCY_TARGET, "replace"); - else { + r = -EINVAL; + } else { log_warning("Ignoring error."); r = 0; } - } else r = 0; - if (status.si_code == CLD_EXITED && (status.si_status & FSCK_ERROR_CORRECTED)) + if (exit_status & FSCK_ERROR_CORRECTED) (void) touch("/run/systemd/quotacheck"); finish: diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 22c4ae9861..f392f89099 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -102,7 +102,7 @@ static int add_swap( struct mntent *me, MountpointFlags flags) { - _cleanup_free_ char *name = NULL, *unit = NULL; + _cleanup_free_ char *name = NULL; _cleanup_fclose_ FILE *f = NULL; int r; @@ -123,19 +123,9 @@ static int add_swap( if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); - unit = strjoin(arg_dest, "/", name); - if (!unit) - return log_oom(); - - f = fopen(unit, "wxe"); - if (!f) - return log_error_errno(errno, - errno == EEXIST ? - "Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?" : - "Failed to create unit file %s: %m", - unit); - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + r = generator_open_unit_file(arg_dest, "/etc/fstab", name, &f); + if (r < 0) + return r; fputs("# Automatically generated by systemd-fstab-generator\n\n" "[Unit]\n" @@ -153,7 +143,7 @@ static int add_swap( r = fflush_and_check(f); if (r < 0) - return log_error_errno(r, "Failed to write unit file %s: %m", unit); + return log_error_errno(r, "Failed to write unit file %s: %m", name); /* use what as where, to have a nicer error message */ r = generator_write_timeouts(arg_dest, what, what, me->mnt_opts, NULL); @@ -323,10 +313,9 @@ static int add_mount( _cleanup_free_ char *name = NULL, - *automount_name = NULL, *automount_unit = NULL, + *automount_name = NULL, *filtered = NULL, *where_escaped = NULL; - const char *unit; _cleanup_fclose_ FILE *f = NULL; int r; @@ -363,20 +352,11 @@ static int add_mount( if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); - unit = strjoina(dest, "/", name); - - f = fopen(unit, "wxe"); - if (!f) - return log_error_errno(errno, - errno == EEXIST ? - "Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?" : - "Failed to create unit file %s: %m", - unit); - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + r = generator_open_unit_file(dest, "/etc/fstab", name, &f); + if (r < 0) + return r; fprintf(f, - "# Automatically generated by systemd-fstab-generator\n\n" "[Unit]\n" "SourcePath=%s\n" "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n", @@ -461,7 +441,7 @@ static int add_mount( r = fflush_and_check(f); if (r < 0) - return log_error_errno(r, "Failed to write unit file %s: %m", unit); + return log_error_errno(r, "Failed to write unit file %s: %m", name); if (flags & MAKEFS) { r = generator_hook_up_mkfs(dest, what, where, fstype); @@ -487,19 +467,13 @@ static int add_mount( if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); - automount_unit = strjoin(dest, "/", automount_name); - if (!automount_unit) - return log_oom(); - fclose(f); - f = fopen(automount_unit, "wxe"); - if (!f) - return log_error_errno(errno, "Failed to create unit file %s: %m", automount_unit); - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + r = generator_open_unit_file(dest, "/etc/fstab", automount_name, &f); + if (r < 0) + return r; fprintf(f, - "# Automatically generated by systemd-fstab-generator\n\n" "[Unit]\n" "SourcePath=%s\n" "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n", @@ -534,7 +508,7 @@ static int add_mount( r = fflush_and_check(f); if (r < 0) - return log_error_errno(r, "Failed to write unit file %s: %m", automount_unit); + return log_error_errno(r, "Failed to write unit file %s: %m", automount_name); r = generator_add_symlink(dest, post, (flags & NOFAIL) ? "wants" : "requires", automount_name); @@ -917,7 +891,8 @@ int main(int argc, char *argv[]) { if (argc > 3) arg_dest_late = argv[3]; - log_set_target(LOG_TARGET_SAFE); + log_set_prohibit_ipc(true); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); diff --git a/src/fuzz/fuzz-dhcp-server.c b/src/fuzz/fuzz-dhcp-server.c new file mode 100644 index 0000000000..ba903c7158 --- /dev/null +++ b/src/fuzz/fuzz-dhcp-server.c @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/*** + Copyright 2018 Jonathan Rudenberg + + 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 "fuzz.h" + +#include "sd-dhcp-server.c" + +/* stub out network so that the server doesn't send */ +ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { + return len; +} + +ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) { + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL; + struct in_addr address = {.s_addr = htobe32(UINT32_C(10) << 24 | UINT32_C(1))}; + static const uint8_t chaddr[] = {3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3}; + uint8_t *client_id; + DHCPLease *lease; + int pool_offset; + + if (size < sizeof(DHCPMessage)) + return 0; + + assert_se(sd_dhcp_server_new(&server, 1) >= 0); + server->fd = open("/dev/null", O_RDWR|O_CLOEXEC|O_NOCTTY); + assert_se(server->fd >= 0); + assert_se(sd_dhcp_server_configure_pool(server, &address, 24, 0, 0) >= 0); + + /* add a lease to the pool to expose additional code paths */ + client_id = malloc(2); + assert_se(client_id); + client_id[0] = 2; + client_id[1] = 2; + lease = new0(DHCPLease, 1); + assert_se(lease); + lease->client_id.length = 2; + lease->client_id.data = client_id; + lease->address = htobe32(UINT32_C(10) << 24 | UINT32_C(2)); + lease->gateway = htobe32(UINT32_C(10) << 24 | UINT32_C(1)); + lease->expiration = UINT64_MAX; + memcpy(lease->chaddr, chaddr, 16); + pool_offset = get_pool_offset(server, lease->address); + server->bound_leases[pool_offset] = lease; + assert_se(hashmap_put(server->leases_by_client_id, &lease->client_id, lease) >= 0); + + (void) dhcp_server_handle_message(server, (DHCPMessage*)data, size); + + return 0; +} diff --git a/src/fuzz/fuzz-dns-packet.c b/src/fuzz/fuzz-dns-packet.c new file mode 100644 index 0000000000..0f25081b22 --- /dev/null +++ b/src/fuzz/fuzz-dns-packet.c @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/*** + Copyright 2018 Jonathan Rudenberg + + 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 "fuzz.h" +#include "resolved-dns-packet.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + + if (size > DNS_PACKET_SIZE_MAX) + return 0; + + assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX) >= 0); + p->size = 0; /* by default append starts after the header, undo that */ + assert_se(dns_packet_append_blob(p, data, size, NULL) >= 0); + if (size < DNS_PACKET_HEADER_SIZE) { + /* make sure we pad the packet back up to the minimum header size */ + assert_se(p->allocated >= DNS_PACKET_HEADER_SIZE); + memzero(DNS_PACKET_DATA(p) + size, DNS_PACKET_HEADER_SIZE - size); + p->size = DNS_PACKET_HEADER_SIZE; + } + (void) dns_packet_extract(p); + + return 0; +} diff --git a/src/fuzz/fuzz-dns-packet.options b/src/fuzz/fuzz-dns-packet.options new file mode 100644 index 0000000000..0824b19fab --- /dev/null +++ b/src/fuzz/fuzz-dns-packet.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 65535 diff --git a/src/fuzz/fuzz-dns-server.options b/src/fuzz/fuzz-dns-server.options new file mode 100644 index 0000000000..5c330e5cec --- /dev/null +++ b/src/fuzz/fuzz-dns-server.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 600 diff --git a/src/fuzz/fuzz-main.c b/src/fuzz/fuzz-main.c new file mode 100644 index 0000000000..45e46907e2 --- /dev/null +++ b/src/fuzz/fuzz-main.c @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/*** + Copyright 2018 Jonathan Rudenberg + + 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 "fileio.h" +#include "fuzz.h" + +/* This is a test driver for the systemd fuzzers that provides main function + * for regression testing outside of oss-fuzz (https://github.com/google/oss-fuzz) + * + * It reads files named on the command line and passes them one by one into the + * fuzzer that it is compiled into. */ + +int main(int argc, char **argv) { + int i, r; + size_t size; + char *name; + + log_set_max_level(LOG_DEBUG); + for (i = 1; i < argc; i++) { + _cleanup_free_ char *buf = NULL; + + name = argv[i]; + r = read_full_file(name, &buf, &size); + if (r < 0) { + log_error_errno(r, "Failed to open '%s': %m", name); + return EXIT_FAILURE; + } + printf("%s... ", name); + fflush(stdout); + (void) LLVMFuzzerTestOneInput((uint8_t*)buf, size); + printf("ok\n"); + } + return EXIT_SUCCESS; +} diff --git a/src/fuzz/fuzz.h b/src/fuzz/fuzz.h new file mode 100644 index 0000000000..5293c5762f --- /dev/null +++ b/src/fuzz/fuzz.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/*** + Copyright 2018 Jonathan Rudenberg + + 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 <stdint.h> + +/* The entry point into the fuzzer */ +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build new file mode 100644 index 0000000000..09a8c8a11d --- /dev/null +++ b/src/fuzz/meson.build @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# Copyright 2018 Jonathan Rudenberg +# +# 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/>. + +fuzzers += [ + [['src/fuzz/fuzz-dns-packet.c', + dns_type_headers], + [libsystemd_resolve_core, + libshared], + [libgcrypt, + libgpg_error, + libm]], + [['src/fuzz/fuzz-dhcp-server.c', + ], + [libsystemd_network, + libshared], + []] +] diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c index 3f62a81abc..f475aef66c 100644 --- a/src/getty-generator/getty-generator.c +++ b/src/getty-generator/getty-generator.c @@ -138,7 +138,8 @@ int main(int argc, char *argv[]) { if (argc > 1) arg_dest = argv[1]; - log_set_target(LOG_TARGET_SAFE); + log_set_prohibit_ipc(true); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index 9e8b956d5c..cbdbc5afe2 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -28,6 +28,7 @@ #include "alloc-util.h" #include "blkid-util.h" +#include "blockdev-util.h" #include "btrfs-util.h" #include "dirent-util.h" #include "dissect-image.h" @@ -712,7 +713,8 @@ int main(int argc, char *argv[]) { if (argc > 1) arg_dest = argv[3]; - log_set_target(LOG_TARGET_SAFE); + log_set_prohibit_ipc(true); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); diff --git a/src/hibernate-resume/hibernate-resume-generator.c b/src/hibernate-resume/hibernate-resume-generator.c index 01222db516..a81386ef63 100644 --- a/src/hibernate-resume/hibernate-resume-generator.c +++ b/src/hibernate-resume/hibernate-resume-generator.c @@ -86,7 +86,8 @@ int main(int argc, char *argv[]) { if (argc > 1) arg_dest = argv[1]; - log_set_target(LOG_TARGET_SAFE); + log_set_prohibit_ipc(true); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index 5feaa60c99..1c8c76934c 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -680,9 +680,9 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { if (r < 0) return log_error_errno(r, "Failed to register object: %m"); - r = sd_bus_request_name(bus, "org.freedesktop.hostname1", 0); + r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.hostname1", 0, NULL, NULL); if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); + return log_error_errno(r, "Failed to request name: %m"); r = sd_bus_attach_event(bus, event, 0); if (r < 0) diff --git a/src/import/curl-util.c b/src/import/curl-util.c index 7069c95a9f..62bbaa500d 100644 --- a/src/import/curl-util.c +++ b/src/import/curl-util.c @@ -21,6 +21,7 @@ #include "alloc-util.h" #include "curl-util.h" #include "fd-util.h" +#include "locale-util.h" #include "string-util.h" static void curl_glue_check_finished(CurlGlue *g) { @@ -414,8 +415,8 @@ int curl_header_strdup(const void *contents, size_t sz, const char *field, char } int curl_parse_http_time(const char *t, usec_t *ret) { + _cleanup_(freelocalep) locale_t loc = (locale_t) 0; const char *e; - locale_t loc; struct tm tm; time_t v; @@ -434,7 +435,6 @@ int curl_parse_http_time(const char *t, usec_t *ret) { if (!e || *e != 0) /* ANSI C */ e = strptime_l(t, "%a %b %d %H:%M:%S %Y", &tm, loc); - freelocale(loc); if (!e || *e != 0) return -EINVAL; diff --git a/src/import/export.c b/src/import/export.c index 753d1399f5..0f32b90051 100644 --- a/src/import/export.c +++ b/src/import/export.c @@ -21,6 +21,7 @@ #include <getopt.h> #include "sd-event.h" +#include "sd-id128.h" #include "alloc-util.h" #include "export-raw.h" diff --git a/src/import/import-common.c b/src/import/import-common.c index 2f989a171c..c24a0b0c86 100644 --- a/src/import/import-common.c +++ b/src/import/import-common.c @@ -27,6 +27,7 @@ #include "capability-util.h" #include "fd-util.h" #include "import-common.h" +#include "process-util.h" #include "signal-util.h" #include "util.h" @@ -82,11 +83,10 @@ int import_fork_tar_x(const char *path, pid_t *ret) { if (pipe2(pipefd, O_CLOEXEC) < 0) return log_error_errno(errno, "Failed to create pipe for tar: %m"); - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork off tar: %m"); - - if (pid == 0) { + r = safe_fork("(tar)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); + if (r < 0) + return r; + if (r == 0) { int null_fd; uint64_t retain = (1ULL << CAP_CHOWN) | @@ -98,10 +98,6 @@ int import_fork_tar_x(const char *path, pid_t *ret) { /* Child */ - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - pipefd[1] = safe_close(pipefd[1]); r = move_fd(pipefd[0], STDIN_FILENO, false); @@ -156,20 +152,15 @@ int import_fork_tar_c(const char *path, pid_t *ret) { if (pipe2(pipefd, O_CLOEXEC) < 0) return log_error_errno(errno, "Failed to create pipe for tar: %m"); - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork off tar: %m"); - - if (pid == 0) { + r = safe_fork("(tar)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); + if (r < 0) + return r; + if (r == 0) { int null_fd; uint64_t retain = (1ULL << CAP_DAC_OVERRIDE); /* Child */ - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - pipefd[0] = safe_close(pipefd[0]); r = move_fd(pipefd[1], STDOUT_FILENO, false); diff --git a/src/import/import-compress.c b/src/import/import-compress.c index cb5b9821c3..acb47fef14 100644 --- a/src/import/import-compress.c +++ b/src/import/import-compress.c @@ -70,7 +70,7 @@ int import_uncompress_detect(ImportCompress *c, const void *data, size_t size) { if (memcmp(data, xz_signature, sizeof(xz_signature)) == 0) { lzma_ret xzr; - xzr = lzma_stream_decoder(&c->xz, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK); + xzr = lzma_stream_decoder(&c->xz, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK | LZMA_CONCATENATED); if (xzr != LZMA_OK) return -EIO; diff --git a/src/import/import-tar.c b/src/import/import-tar.c index c5014499ac..09c7654adc 100644 --- a/src/import/import-tar.c +++ b/src/import/import-tar.c @@ -190,7 +190,7 @@ static int tar_import_finish(TarImport *i) { i->tar_fd = safe_close(i->tar_fd); if (i->tar_pid > 0) { - r = wait_for_terminate_and_warn("tar", i->tar_pid, true); + r = wait_for_terminate_and_check("tar", i->tar_pid, WAIT_LOG); i->tar_pid = 0; if (r < 0) return r; diff --git a/src/import/import.c b/src/import/import.c index cc454ee15b..454e64e3cb 100644 --- a/src/import/import.c +++ b/src/import/import.c @@ -21,6 +21,7 @@ #include <getopt.h> #include "sd-event.h" +#include "sd-id128.h" #include "alloc-util.h" #include "fd-util.h" diff --git a/src/import/importd.c b/src/import/importd.c index 9c7694c0ad..98ee1a2fab 100644 --- a/src/import/importd.c +++ b/src/import/importd.c @@ -371,10 +371,10 @@ static int transfer_start(Transfer *t) { if (pipe2(pipefd, O_CLOEXEC) < 0) return -errno; - t->pid = fork(); - if (t->pid < 0) - return -errno; - if (t->pid == 0) { + r = safe_fork("(sd-transfer)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &t->pid); + if (r < 0) + return r; + if (r == 0) { const char *cmd[] = { NULL, /* systemd-import, systemd-export or systemd-pull */ NULL, /* tar, raw */ @@ -393,10 +393,6 @@ static int transfer_start(Transfer *t) { /* Child */ - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - pipefd[0] = safe_close(pipefd[0]); if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) { @@ -1153,9 +1149,9 @@ static int manager_add_bus_objects(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to add transfer enumerator: %m"); - r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0); + r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.import1", 0, NULL, NULL); if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); + return log_error_errno(r, "Failed to request name: %m"); r = sd_bus_attach_event(m->bus, m->event, 0); if (r < 0) diff --git a/src/import/pull-common.c b/src/import/pull-common.c index c2a3a6aa8b..ecdcbd2dc2 100644 --- a/src/import/pull-common.c +++ b/src/import/pull-common.c @@ -463,10 +463,10 @@ int pull_verify(PullJob *main_job, gpg_home_created = true; - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork off gpg: %m"); - if (pid == 0) { + r = safe_fork("(gpg)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); + if (r < 0) + return r; + if (r == 0) { const char *cmd[] = { "gpg", "--no-options", @@ -487,10 +487,6 @@ int pull_verify(PullJob *main_job, /* Child */ - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - gpg_pipe[1] = safe_close(gpg_pipe[1]); r = move_fd(gpg_pipe[0], STDIN_FILENO, false); @@ -546,11 +542,11 @@ int pull_verify(PullJob *main_job, gpg_pipe[1] = safe_close(gpg_pipe[1]); - r = wait_for_terminate_and_warn("gpg", pid, true); + r = wait_for_terminate_and_check("gpg", pid, WAIT_LOG_ABNORMAL); pid = 0; if (r < 0) goto finish; - if (r > 0) { + if (r != EXIT_SUCCESS) { log_error("DOWNLOAD INVALID: Signature verification failed."); r = -EBADMSG; } else { diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index ed91511245..6ee63bdad5 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -333,11 +333,11 @@ static void tar_pull_job_on_finished(PullJob *j) { goto finish; if (i->tar_pid > 0) { - r = wait_for_terminate_and_warn("tar", i->tar_pid, true); + r = wait_for_terminate_and_check("tar", i->tar_pid, WAIT_LOG); i->tar_pid = 0; if (r < 0) goto finish; - if (r > 0) { + if (r != EXIT_SUCCESS) { r = -EIO; goto finish; } diff --git a/src/import/pull.c b/src/import/pull.c index 46e0fd5acb..325f7e3d50 100644 --- a/src/import/pull.c +++ b/src/import/pull.c @@ -21,6 +21,7 @@ #include <getopt.h> #include "sd-event.h" +#include "sd-id128.h" #include "alloc-util.h" #include "hostname-util.h" diff --git a/src/initctl/initctl.c b/src/initctl/initctl.c index 5488999727..c1af13d15b 100644 --- a/src/initctl/initctl.c +++ b/src/initctl/initctl.c @@ -38,6 +38,7 @@ #include "log.h" #include "special.h" #include "util.h" +#include "process-util.h" #define SERVER_FD_MAX 16 #define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)) diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index 82c70cfbe3..5a437ce031 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -226,7 +226,8 @@ static ssize_t request_reader_entries( return MHD_CONTENT_READER_END_WITH_ERROR; } - r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH, NULL, NULL); + r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH, + NULL, NULL, NULL); if (r < 0) { log_error_errno(r, "Failed to serialize item: %m"); return MHD_CONTENT_READER_END_WITH_ERROR; diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c index e44989e1ba..66d5369a54 100644 --- a/src/journal-remote/journal-remote.c +++ b/src/journal-remote/journal-remote.c @@ -43,6 +43,7 @@ #include "journald-native.h" #include "macro.h" #include "parse-util.h" +#include "process-util.h" #include "signal-util.h" #include "socket-util.h" #include "stat-util.h" @@ -81,27 +82,20 @@ static bool arg_trust_all = false; **********************************************************************/ static int spawn_child(const char* child, char** argv) { - int fd[2]; - pid_t parent_pid, child_pid; - int r; + pid_t child_pid; + int fd[2], r; if (pipe(fd) < 0) return log_error_errno(errno, "Failed to create pager pipe: %m"); - parent_pid = getpid_cached(); - - child_pid = fork(); - if (child_pid < 0) { - r = log_error_errno(errno, "Failed to fork: %m"); + r = safe_fork("(remote)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &child_pid); + if (r < 0) { safe_close_pair(fd); return r; } /* In the child */ - if (child_pid == 0) { - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); + if (r == 0) { r = dup2(fd[1], STDOUT_FILENO); if (r < 0) { @@ -111,15 +105,6 @@ static int spawn_child(const char* child, char** argv) { safe_close_pair(fd); - /* Make sure the child goes away when the parent dies */ - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) - _exit(EXIT_FAILURE); - - /* Check whether our parent died before we were able - * to set the death signal */ - if (getppid() != parent_pid) - _exit(EXIT_SUCCESS); - execvp(child, argv); log_error_errno(errno, "Failed to exec child %s: %m", child); _exit(EXIT_FAILURE); diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c index 69718aae87..0b74ca98a7 100644 --- a/src/journal-remote/journal-upload.c +++ b/src/journal-remote/journal-upload.c @@ -37,6 +37,7 @@ #include "log.h" #include "mkdir.h" #include "parse-util.h" +#include "process-util.h" #include "sigbus.h" #include "signal-util.h" #include "string-util.h" @@ -248,7 +249,7 @@ int start_upload(Uploader *u, easy_setopt(curl, CURLOPT_HTTPHEADER, u->header, LOG_ERR, return -EXFULL); - if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) + if (DEBUG_LOGGING) /* enable verbose for easier tracing */ easy_setopt(curl, CURLOPT_VERBOSE, 1L, LOG_WARNING, ); diff --git a/src/journal/generate-audit_type-list.sh b/src/journal/generate-audit_type-list.sh index 18cbe0599c..2445a02668 100755 --- a/src/journal/generate-audit_type-list.sh +++ b/src/journal/generate-audit_type-list.sh @@ -1,4 +1,5 @@ -#!/bin/sh -eu +#!/bin/sh +set -eu cpp="$1" shift diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 7fef403391..3353b3a0d8 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -147,6 +147,8 @@ static void journal_file_set_offline_internal(JournalFile *f) { static void * journal_file_set_offline_thread(void *arg) { JournalFile *f = arg; + (void) pthread_setname_np(pthread_self(), "journal-offline"); + journal_file_set_offline_internal(f); return NULL; @@ -245,11 +247,25 @@ int journal_file_set_offline(JournalFile *f, bool wait) { if (wait) /* Without using a thread if waiting. */ journal_file_set_offline_internal(f); else { + sigset_t ss, saved_ss; + int k; + + if (sigfillset(&ss) < 0) + return -errno; + + r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss); + if (r > 0) + return -r; + r = pthread_create(&f->offline_thread, NULL, journal_file_set_offline_thread, f); + + k = pthread_sigmask(SIG_SETMASK, &saved_ss, NULL); if (r > 0) { f->offline_state = OFFLINE_JOINED; return -r; } + if (k > 0) + return -k; } return 0; @@ -3237,11 +3253,8 @@ int journal_file_open( if (!IN_SET((flags & O_ACCMODE), O_RDONLY, O_RDWR)) return -EINVAL; - if (fname) { - if (!endswith(fname, ".journal") && - !endswith(fname, ".journal~")) - return -EINVAL; - } + if (fname && (flags & O_CREAT) && !endswith(fname, ".journal")) + return -EINVAL; f = new0(JournalFile, 1); if (!f) diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c index a78aa07032..73329ba024 100644 --- a/src/journal/journal-send.c +++ b/src/journal/journal-send.c @@ -416,10 +416,9 @@ _public_ int sd_journal_stream_fd(const char *identifier, int priority, int leve if (shutdown(fd, SHUT_RD) < 0) return -errno; - fd_inc_sndbuf(fd, SNDBUF_SIZE); + (void) fd_inc_sndbuf(fd, SNDBUF_SIZE); - if (!identifier) - identifier = ""; + identifier = strempty(identifier); l = strlen(identifier); header = alloca(l + 1 + 1 + 2 + 2 + 2 + 2 + 2); diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 956d85aff2..17782688d9 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -34,6 +34,11 @@ #include <sys/stat.h> #include <unistd.h> +#if HAVE_PCRE2 +# define PCRE2_CODE_UNIT_WIDTH 8 +# include <pcre2.h> +#endif + #include "sd-bus.h" #include "sd-journal.h" @@ -76,6 +81,34 @@ #define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE) +#if HAVE_PCRE2 +DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_match_data*, pcre2_match_data_free); +DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_code*, pcre2_code_free); + +static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out) { + int errorcode, r; + PCRE2_SIZE erroroffset; + pcre2_code *p; + + p = pcre2_compile((PCRE2_SPTR8) pattern, + PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL); + if (!p) { + unsigned char buf[LINE_MAX]; + + r = pcre2_get_error_message(errorcode, buf, sizeof buf); + + log_error("Bad pattern \"%s\": %s", + pattern, + r < 0 ? "unknown error" : (char*) buf); + return -EINVAL; + } + + *out = p; + return 0; +} + +#endif + enum { /* Special values for arg_lines */ ARG_LINES_DEFAULT = -2, @@ -126,6 +159,12 @@ static uint64_t arg_vacuum_n_files = 0; static usec_t arg_vacuum_time = 0; static char **arg_output_fields = NULL; +#if HAVE_PCRE2 +static const char *arg_pattern = NULL; +static pcre2_code *arg_compiled_pattern = NULL; +static int arg_case_sensitive = -1; /* -1 means be smart */ +#endif + static enum { ACTION_SHOW, ACTION_NEW_ID128, @@ -280,67 +319,69 @@ static void help(void) { printf("%s [OPTIONS...] [MATCHES...]\n\n" "Query the journal.\n\n" "Options:\n" - " --system Show the system journal\n" - " --user Show the user journal for the current user\n" - " -M --machine=CONTAINER Operate on local container\n" - " -S --since=DATE Show entries not older than the specified date\n" - " -U --until=DATE Show entries not newer than the specified date\n" - " -c --cursor=CURSOR Show entries starting at the specified cursor\n" - " --after-cursor=CURSOR Show entries after the specified cursor\n" - " --show-cursor Print the cursor after all the entries\n" - " -b --boot[=ID] Show current boot or the specified boot\n" - " --list-boots Show terse information about recorded boots\n" - " -k --dmesg Show kernel message log from the current boot\n" - " -u --unit=UNIT Show logs from the specified unit\n" - " --user-unit=UNIT Show logs from the specified user unit\n" - " -t --identifier=STRING Show entries with the specified syslog identifier\n" - " -p --priority=RANGE Show entries with the specified priority\n" - " -e --pager-end Immediately jump to the end in the pager\n" - " -f --follow Follow the journal\n" - " -n --lines[=INTEGER] Number of journal entries to show\n" - " --no-tail Show all lines, even in follow mode\n" - " -r --reverse Show the newest entries first\n" - " -o --output=STRING Change journal output mode (short, short-precise,\n" - " short-iso, short-iso-precise, short-full,\n" - " short-monotonic, short-unix, verbose, export,\n" - " json, json-pretty, json-sse, cat)\n" - " --output-fields=LIST Select fields to print in verbose/export/json modes\n" - " --utc Express time in Coordinated Universal Time (UTC)\n" - " -x --catalog Add message explanations where available\n" - " --no-full Ellipsize fields\n" - " -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" - " --root=ROOT Operate on files below a root directory\n" + " --system Show the system journal\n" + " --user Show the user journal for the current user\n" + " -M --machine=CONTAINER Operate on local container\n" + " -S --since=DATE Show entries not older than the specified date\n" + " -U --until=DATE Show entries not newer than the specified date\n" + " -c --cursor=CURSOR Show entries starting at the specified cursor\n" + " --after-cursor=CURSOR Show entries after the specified cursor\n" + " --show-cursor Print the cursor after all the entries\n" + " -b --boot[=ID] Show current boot or the specified boot\n" + " --list-boots Show terse information about recorded boots\n" + " -k --dmesg Show kernel message log from the current boot\n" + " -u --unit=UNIT Show logs from the specified unit\n" + " --user-unit=UNIT Show logs from the specified user unit\n" + " -t --identifier=STRING Show entries with the specified syslog identifier\n" + " -p --priority=RANGE Show entries with the specified priority\n" + " -g --grep=PATTERN Show entries with MESSSAGE matching PATTERN\n" + " --case-sensitive[=BOOL] Force case sensitive or insenstive matching\n" + " -e --pager-end Immediately jump to the end in the pager\n" + " -f --follow Follow the journal\n" + " -n --lines[=INTEGER] Number of journal entries to show\n" + " --no-tail Show all lines, even in follow mode\n" + " -r --reverse Show the newest entries first\n" + " -o --output=STRING Change journal output mode (short, short-precise,\n" + " short-iso, short-iso-precise, short-full,\n" + " short-monotonic, short-unix, verbose, export,\n" + " json, json-pretty, json-sse, cat)\n" + " --output-fields=LIST Select fields to print in verbose/export/json modes\n" + " --utc Express time in Coordinated Universal Time (UTC)\n" + " -x --catalog Add message explanations where available\n" + " --no-full Ellipsize fields\n" + " -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" + " --root=ROOT Operate on files below a root directory\n" #if HAVE_GCRYPT - " --interval=TIME Time interval for changing the FSS sealing key\n" - " --verify-key=KEY Specify FSS verification key\n" - " --force Override of the FSS key pair with --setup-keys\n" + " --interval=TIME Time interval for changing the FSS sealing key\n" + " --verify-key=KEY Specify FSS verification key\n" + " --force Override of the FSS key pair with --setup-keys\n" #endif "\nCommands:\n" - " -h --help Show this help text\n" - " --version Show package version\n" - " -N --fields List all field names currently used\n" - " -F --field=FIELD List all values that a specified field takes\n" - " --disk-usage Show total disk usage of all journal files\n" - " --vacuum-size=BYTES Reduce disk usage below specified size\n" - " --vacuum-files=INT Leave only the specified number of journal files\n" - " --vacuum-time=TIME Remove journal files older than specified time\n" - " --verify Verify journal file consistency\n" - " --sync Synchronize unwritten journal messages to disk\n" - " --flush Flush all journal data from /run into /var\n" - " --rotate Request immediate rotation of the journal files\n" - " --header Show journal header information\n" - " --list-catalog Show all message IDs in the catalog\n" - " --dump-catalog Show entries in the message catalog\n" - " --update-catalog Update the message catalog database\n" - " --new-id128 Generate a new 128-bit ID\n" + " -h --help Show this help text\n" + " --version Show package version\n" + " -N --fields List all field names currently used\n" + " -F --field=FIELD List all values that a specified field takes\n" + " --disk-usage Show total disk usage of all journal files\n" + " --vacuum-size=BYTES Reduce disk usage below specified size\n" + " --vacuum-files=INT Leave only the specified number of journal files\n" + " --vacuum-time=TIME Remove journal files older than specified time\n" + " --verify Verify journal file consistency\n" + " --sync Synchronize unwritten journal messages to disk\n" + " --flush Flush all journal data from /run into /var\n" + " --rotate Request immediate rotation of the journal files\n" + " --header Show journal header information\n" + " --list-catalog Show all message IDs in the catalog\n" + " --dump-catalog Show entries in the message catalog\n" + " --update-catalog Update the message catalog database\n" + " --new-id128 Generate a new 128-bit ID\n" #if HAVE_GCRYPT - " --setup-keys Generate a new FSS key pair\n" + " --setup-keys Generate a new FSS key pair\n" #endif , program_invocation_short_name); } @@ -372,6 +413,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_DUMP_CATALOG, ARG_UPDATE_CATALOG, ARG_FORCE, + ARG_CASE_SENSITIVE, ARG_UTC, ARG_SYNC, ARG_FLUSH, @@ -411,6 +453,8 @@ static int parse_argv(int argc, char *argv[]) { { "header", no_argument, NULL, ARG_HEADER }, { "identifier", required_argument, NULL, 't' }, { "priority", required_argument, NULL, 'p' }, + { "grep", required_argument, NULL, 'g' }, + { "case-sensitive", optional_argument, NULL, ARG_CASE_SENSITIVE }, { "setup-keys", no_argument, NULL, ARG_SETUP_KEYS }, { "interval", required_argument, NULL, ARG_INTERVAL }, { "verify", no_argument, NULL, ARG_VERIFY }, @@ -762,6 +806,27 @@ static int parse_argv(int argc, char *argv[]) { break; } +#if HAVE_PCRE2 + case 'g': + arg_pattern = optarg; + break; + + case ARG_CASE_SENSITIVE: + if (optarg) { + r = parse_boolean(optarg); + if (r < 0) + return log_error_errno(r, "Bad --case-sensitive= argument \"%s\": %m", optarg); + arg_case_sensitive = r; + } else + arg_case_sensitive = true; + + break; +#else + case 'g': + case ARG_CASE_SENSITIVE: + return log_error("Compiled without pattern matching support"); +#endif + case 'S': r = parse_timestamp(optarg, &arg_since); if (r < 0) { @@ -917,6 +982,42 @@ static int parse_argv(int argc, char *argv[]) { arg_system_units = strv_free(arg_system_units); } + +#if HAVE_PCRE2 + if (arg_pattern) { + unsigned flags; + + if (arg_case_sensitive >= 0) + flags = !arg_case_sensitive * PCRE2_CASELESS; + else { + _cleanup_(pcre2_match_data_freep) pcre2_match_data *md = NULL; + bool has_case; + _cleanup_(pcre2_code_freep) pcre2_code *cs = NULL; + + md = pcre2_match_data_create(1, NULL); + if (!md) + return log_oom(); + + r = pattern_compile("[[:upper:]]", 0, &cs); + if (r < 0) + return r; + + r = pcre2_match(cs, (PCRE2_SPTR8) arg_pattern, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL); + has_case = r >= 0; + + flags = !has_case * PCRE2_CASELESS; + } + + log_debug("Doing case %s matching based on %s", + flags & PCRE2_CASELESS ? "insensitive" : "sensitive", + arg_case_sensitive >= 0 ? "request" : "pattern casing"); + + r = pattern_compile(arg_pattern, flags, &arg_compiled_pattern); + if (r < 0) + return r; + } +#endif + return 1; } @@ -2257,7 +2358,7 @@ int main(int argc, char *argv[]) { if (r < 0) goto finish; - if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) { + if (DEBUG_LOGGING) { _cleanup_free_ char *filter; filter = journal_make_match_string(j); @@ -2415,6 +2516,7 @@ int main(int argc, char *argv[]) { for (;;) { while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) { int flags; + size_t highlight[2] = {}; if (need_seek) { if (!arg_reverse) @@ -2468,6 +2570,58 @@ int main(int argc, char *argv[]) { } } +#if HAVE_PCRE2 + if (arg_compiled_pattern) { + _cleanup_(pcre2_match_data_freep) pcre2_match_data *md = NULL; + const void *message; + size_t len; + PCRE2_SIZE *ovec; + + md = pcre2_match_data_create(1, NULL); + if (!md) + return log_oom(); + + r = sd_journal_get_data(j, "MESSAGE", &message, &len); + if (r < 0) { + if (r == -ENOENT) { + need_seek = true; + continue; + } + + log_error_errno(r, "Failed to get MESSAGE field: %m"); + goto finish; + } + + assert_se(message = startswith(message, "MESSAGE=")); + + r = pcre2_match(arg_compiled_pattern, + message, + len - strlen("MESSAGE="), + 0, /* start at offset 0 in the subject */ + 0, /* default options */ + md, + NULL); + if (r == PCRE2_ERROR_NOMATCH) { + need_seek = true; + continue; + } + if (r < 0) { + unsigned char buf[LINE_MAX]; + int r2; + + r2 = pcre2_get_error_message(r, buf, sizeof buf); + log_error("Pattern matching failed: %s", + r2 < 0 ? "unknown error" : (char*) buf); + r = -EINVAL; + goto finish; + } + + ovec = pcre2_get_ovector_pointer(md); + highlight[0] = ovec[0]; + highlight[1] = ovec[1]; + } +#endif + flags = arg_all * OUTPUT_SHOW_ALL | arg_full * OUTPUT_FULL_WIDTH | @@ -2476,7 +2630,8 @@ int main(int argc, char *argv[]) { arg_utc * OUTPUT_UTC | arg_no_hostname * OUTPUT_NO_HOSTNAME; - r = output_journal(stdout, j, arg_output, 0, flags, arg_output_fields, &ellipsized); + r = output_journal(stdout, j, arg_output, 0, flags, + arg_output_fields, highlight, &ellipsized); need_seek = true; if (r == -EADDRNOTAVAIL) break; @@ -2527,5 +2682,10 @@ finish: free(arg_root); free(arg_verify_key); +#if HAVE_PCRE2 + if (arg_compiled_pattern) + pcre2_code_free(arg_compiled_pattern); +#endif + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c index 65fb6ab63a..cee873215d 100644 --- a/src/journal/journald-native.c +++ b/src/journal/journald-native.c @@ -425,7 +425,7 @@ void server_process_native_file( * https://github.com/systemd/systemd/issues/1822 */ if (vfs.f_flag & ST_MANDLOCK) { - log_error("Received file descriptor from file system with mandatory locking enable, refusing."); + log_error("Received file descriptor from file system with mandatory locking enabled, refusing."); return; } diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 48375f9c3e..5cd58e8a77 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -133,7 +133,7 @@ static int determine_path_usage(Server *s, const char *path, uint64_t *ret_used, } static void cache_space_invalidate(JournalStorageSpace *space) { - memset(space, 0, sizeof(*space)); + zero(*space); } static int cache_space_refresh(Server *s, JournalStorage *storage) { @@ -241,6 +241,13 @@ void server_space_usage_message(Server *s, JournalStorage *storage) { NULL); } +static bool uid_for_system_journal(uid_t uid) { + + /* Returns true if the specified UID shall get its data stored in the system journal*/ + + return uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY; +} + static void server_add_acls(JournalFile *f, uid_t uid) { #if HAVE_ACL int r; @@ -248,7 +255,7 @@ static void server_add_acls(JournalFile *f, uid_t uid) { assert(f); #if HAVE_ACL - if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY) + if (uid_for_system_journal(uid)) return; r = add_acls_for_user(f->fd, uid); @@ -406,7 +413,7 @@ static JournalFile* find_journal(Server *s, uid_t uid) { if (s->runtime_journal) return s->runtime_journal; - if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY) + if (uid_for_system_journal(uid)) return s->system_journal; r = sd_id128_get_machine(&machine); @@ -457,13 +464,14 @@ static int do_rotate( return -EINVAL; r = journal_file_rotate(f, s->compress, seal, s->deferred_closes); - if (r < 0) + if (r < 0) { if (*f) - log_error_errno(r, "Failed to rotate %s: %m", (*f)->path); + return log_error_errno(r, "Failed to rotate %s: %m", (*f)->path); else - log_error_errno(r, "Failed to create new %s journal: %m", name); - else - server_add_acls(*f, uid); + return log_error_errno(r, "Failed to create new %s journal: %m", name); + } + + server_add_acls(*f, uid); return r; } @@ -1489,7 +1497,8 @@ static int server_open_hostname(Server *s) { assert(s); - s->hostname_fd = open("/proc/sys/kernel/hostname", O_RDONLY|O_CLOEXEC|O_NDELAY|O_NOCTTY); + s->hostname_fd = open("/proc/sys/kernel/hostname", + O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); if (s->hostname_fd < 0) return log_error_errno(errno, "Failed to open /proc/sys/kernel/hostname: %m"); diff --git a/src/journal/journald.c b/src/journal/journald.c index 6d670e2f4f..10a6955769 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -28,6 +28,7 @@ #include "journald-kmsg.h" #include "journald-server.h" #include "journald-syslog.h" +#include "process-util.h" #include "sigbus.h" int main(int argc, char *argv[]) { @@ -39,7 +40,8 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - log_set_target(LOG_TARGET_SAFE); + log_set_prohibit_ipc(true); + log_set_target(LOG_TARGET_AUTO); log_set_facility(LOG_SYSLOG); log_parse_environment(); log_open(); diff --git a/src/journal/meson.build b/src/journal/meson.build index edb2a1a30e..a23f6a712c 100644 --- a/src/journal/meson.build +++ b/src/journal/meson.build @@ -15,7 +15,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with systemd; If not, see <http://www.gnu.org/licenses/>. -journal_internal_sources = files(''' +journal_client_sources = files(''' audit-type.c audit-type.h catalog.c @@ -38,14 +38,12 @@ journal_internal_sources = files(''' '''.split()) if conf.get('HAVE_GCRYPT') == 1 - journal_internal_sources += files(''' + journal_client_sources += files(''' journal-authenticate.c journal-authenticate.h fsprg.c fsprg.h '''.split()) - - journal_internal_sources += gcrypt_util_sources endif ############################################################ @@ -71,7 +69,13 @@ audit_type_to_name = custom_target( command : [awk, '-f', '@INPUT0@', '@INPUT1@'], capture : true) -journal_internal_sources += [audit_type_to_name] +journal_client_sources += [audit_type_to_name] + +libjournal_client = static_library( + 'journal-client', + journal_client_sources, + include_directories : includes, + c_args : ['-fvisibility=default']) ############################################################ diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 8a1b161d8f..6da7bf8e81 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -48,6 +48,7 @@ #include "lookup3.h" #include "missing.h" #include "path-util.h" +#include "process-util.h" #include "replace-var.h" #include "stat-util.h" #include "stdio-util.h" diff --git a/src/journal/test-compress-benchmark.c b/src/journal/test-compress-benchmark.c index 409a876054..1f77197549 100644 --- a/src/journal/test-compress-benchmark.c +++ b/src/journal/test-compress-benchmark.c @@ -23,6 +23,7 @@ #include "env-util.h" #include "macro.h" #include "parse-util.h" +#include "process-util.h" #include "random-util.h" #include "string-util.h" #include "util.h" diff --git a/src/libsystemd-network/arp-util.c b/src/libsystemd-network/arp-util.c index 67409cc918..89e68c7ac7 100644 --- a/src/libsystemd-network/arp-util.c +++ b/src/libsystemd-network/arp-util.c @@ -24,6 +24,7 @@ #include "arp-util.h" #include "fd-util.h" +#include "unaligned.h" #include "util.h" int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_addr *eth_mac) { @@ -48,12 +49,12 @@ int arp_network_bind_raw_socket(int ifindex, be32_t address, const struct ether_ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0), /* protocol == reply ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ /* Sender Hardware Address must be different from our own */ - BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((uint32_t *) eth_mac))), /* A <- 4 bytes of client's MAC */ + BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_ne32(ð_mac->ether_addr_octet[0])),/* A <- 4 bytes of client's MAC */ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)), /* A <- 4 bytes of SHA */ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 6), /* A == 0 ? */ - BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((uint16_t *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */ + BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_ne16(ð_mac->ether_addr_octet[4])),/* A <- remainder of client's MAC */ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4), /* A <- remainder of SHA */ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h index ac5cc47efd..65c182f48b 100644 --- a/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/libsystemd-network/dhcp-lease-internal.h @@ -34,6 +34,8 @@ struct sd_dhcp_route { struct in_addr dst_addr; struct in_addr gw_addr; unsigned char dst_prefixlen; + + uint8_t option; }; struct sd_dhcp_raw_option { diff --git a/src/libsystemd-network/dhcp-network.c b/src/libsystemd-network/dhcp-network.c index 010090aef1..602bf08a60 100644 --- a/src/libsystemd-network/dhcp-network.c +++ b/src/libsystemd-network/dhcp-network.c @@ -32,6 +32,7 @@ #include "dhcp-internal.h" #include "fd-util.h" #include "socket-util.h" +#include "unaligned.h" static int _bind_raw_socket(int ifindex, union sockaddr_union *link, uint32_t xid, const uint8_t *mac_addr, @@ -70,13 +71,13 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.xid)), /* A <- client identifier */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, xid, 1, 0), /* client identifier == xid ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_IMM, htobe32(*((unsigned int *) eth_mac))), /* A <- 4 bytes of client's MAC */ + BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be32(ð_mac->ether_addr_octet[0])), /* A <- 4 bytes of client's MAC */ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)), /* A <- 4 bytes of MAC from dhcp.chaddr */ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_IMM, htobe16(*((unsigned short *) (((char *) eth_mac) + 4)))), /* A <- remainder of client's MAC */ + BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be16(ð_mac->ether_addr_octet[4])), /* A <- remainder of client's MAC */ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4), /* A <- remainder of MAC from dhcp.chaddr */ BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index cb5b359cbe..13844a86c6 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -29,25 +29,65 @@ #include "macro.h" #include "sparse-endian.h" +/* Common option header */ +typedef struct DHCP6Option { + be16_t code; + be16_t len; + uint8_t data[]; +} _packed_ DHCP6Option; + +/* Address option */ +struct iaaddr { + struct in6_addr address; + be32_t lifetime_preferred; + be32_t lifetime_valid; +} _packed_; + +/* Prefix Delegation Prefix option */ +struct iapdprefix { + be32_t lifetime_preferred; + be32_t lifetime_valid; + uint8_t prefixlen; + struct in6_addr address; +} _packed_; + typedef struct DHCP6Address DHCP6Address; struct DHCP6Address { LIST_FIELDS(DHCP6Address, addresses); - struct { - struct in6_addr address; - be32_t lifetime_preferred; - be32_t lifetime_valid; - } iaaddr _packed_; + union { + struct iaaddr iaaddr; + struct iapdprefix iapdprefix; + }; }; +/* Non-temporary Address option */ +struct ia_na { + be32_t id; + be32_t lifetime_t1; + be32_t lifetime_t2; +} _packed_; + +/* Prefix Delegation option */ +struct ia_pd { + be32_t id; + be32_t lifetime_t1; + be32_t lifetime_t2; +} _packed_; + +/* Temporary Address option */ +struct ia_ta { + be32_t id; +} _packed_; + struct DHCP6IA { uint16_t type; - struct { - be32_t id; - be32_t lifetime_t1; - be32_t lifetime_t2; - } _packed_; + union { + struct ia_na ia_na; + struct ia_pd ia_pd; + struct ia_ta ia_ta; + }; sd_event_source *timeout_t1; sd_event_source *timeout_t2; @@ -62,11 +102,12 @@ typedef struct DHCP6IA DHCP6IA; int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, size_t optlen, const void *optval); int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia); +int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd); int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn); int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen, uint8_t **optvalue); -int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, - DHCP6IA *ia); +int dhcp6_option_parse_status(DHCP6Option *option); +int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia); int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, struct in6_addr **addrs, size_t count, size_t *allocated); diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index a3f00d4a5d..45e0e82427 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -36,8 +36,10 @@ struct sd_dhcp6_lease { bool rapid_commit; DHCP6IA ia; + DHCP6IA pd; DHCP6Address *addr_iter; + DHCP6Address *prefix_iter; struct in6_addr *dns; size_t dns_count; diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index f346bda5fc..df96ad739d 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -26,6 +26,7 @@ #include "alloc-util.h" #include "dhcp6-internal.h" +#include "dhcp6-lease-internal.h" #include "dhcp6-protocol.h" #include "dns-domain.h" #include "sparse-endian.h" @@ -33,14 +34,27 @@ #include "unaligned.h" #include "util.h" -#define DHCP6_OPTION_IA_NA_LEN 12 -#define DHCP6_OPTION_IA_TA_LEN 4 +typedef struct DHCP6StatusOption { + struct DHCP6Option option; + be16_t status; + char msg[]; +} _packed_ DHCP6StatusOption; -typedef struct DHCP6Option { - be16_t code; - be16_t len; - uint8_t data[]; -} _packed_ DHCP6Option; +typedef struct DHCP6AddressOption { + struct DHCP6Option option; + struct iaaddr iaaddr; + uint8_t options[]; +} _packed_ DHCP6AddressOption; + +typedef struct DHCP6PDPrefixOption { + struct DHCP6Option option; + struct iapdprefix iapdprefix; + uint8_t options[]; +} _packed_ DHCP6PDPrefixOption; + +#define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na)) +#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd)) +#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta)) static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) { @@ -83,7 +97,7 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { uint16_t len; uint8_t *ia_hdr; - size_t ia_buflen, ia_addrlen = 0; + size_t iaid_offset, ia_buflen, ia_addrlen = 0; DHCP6Address *addr; int r; @@ -92,10 +106,12 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { switch (ia->type) { case SD_DHCP6_OPTION_IA_NA: len = DHCP6_OPTION_IA_NA_LEN; + iaid_offset = offsetof(DHCP6IA, ia_na); break; case SD_DHCP6_OPTION_IA_TA: len = DHCP6_OPTION_IA_TA_LEN; + iaid_offset = offsetof(DHCP6IA, ia_ta); break; default: @@ -111,7 +127,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { *buf += sizeof(DHCP6Option); *buflen -= sizeof(DHCP6Option); - memcpy(*buf, &ia->id, len); + memcpy(*buf, (char*) ia + iaid_offset, len); *buf += len; *buflen -= len; @@ -164,6 +180,42 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { return r; } +int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd) { + DHCP6Option *option = (DHCP6Option *)buf; + size_t i = sizeof(*option) + sizeof(pd->ia_pd); + DHCP6Address *prefix; + + assert_return(buf, -EINVAL); + assert_return(pd, -EINVAL); + assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL); + + if (len < i) + return -ENOBUFS; + + option->code = htobe16(SD_DHCP6_OPTION_IA_PD); + + memcpy(&option->data, &pd->ia_pd, sizeof(pd->ia_pd)); + + LIST_FOREACH(addresses, prefix, pd->addresses) { + DHCP6PDPrefixOption *prefix_opt; + + if (len < i + sizeof(*prefix_opt)) + return -ENOBUFS; + + prefix_opt = (DHCP6PDPrefixOption *)&buf[i]; + prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX); + prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix)); + + memcpy(&prefix_opt->iapdprefix, &prefix->iapdprefix, + sizeof(struct iapdprefix)); + + i += sizeof(*prefix_opt); + } + + option->len = htobe16(i - sizeof(*option)); + + return i; +} static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) { DHCP6Option *option = (DHCP6Option*) *buf; @@ -210,35 +262,147 @@ int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, return 0; } -int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, - DHCP6IA *ia) { +int dhcp6_option_parse_status(DHCP6Option *option) { + DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option; + + if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*statusopt)) + return -ENOBUFS; + + return be16toh(statusopt->status); +} + +static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia, + uint32_t *lifetime_valid) { + DHCP6AddressOption *addr_option = (DHCP6AddressOption *)option; + DHCP6Address *addr; + uint32_t lt_valid, lt_pref; int r; - uint16_t opt, status; - size_t optlen; + + if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*addr_option)) + return -ENOBUFS; + + lt_valid = be32toh(addr_option->iaaddr.lifetime_valid); + lt_pref = be32toh(addr_option->iaaddr.lifetime_preferred); + + if (lt_valid == 0 || lt_pref > lt_valid) { + log_dhcp6_client(client, "Valid lifetime of an IA address is zero or preferred lifetime %d > valid lifetime %d", + lt_pref, lt_valid); + + return 0; + } + + if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*addr_option)) { + r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options); + if (r != 0) + return r < 0 ? r: 0; + } + + addr = new0(DHCP6Address, 1); + if (!addr) + return -ENOMEM; + + LIST_INIT(addresses, addr); + memcpy(&addr->iaaddr, option->data, sizeof(addr->iaaddr)); + + LIST_PREPEND(addresses, ia->addresses, addr); + + *lifetime_valid = be32toh(addr->iaaddr.lifetime_valid); + + return 0; +} + +static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia, + uint32_t *lifetime_valid) { + DHCP6PDPrefixOption *pdprefix_option = (DHCP6PDPrefixOption *)option; + DHCP6Address *prefix; + uint32_t lt_valid, lt_pref; + int r; + + if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*pdprefix_option)) + return -ENOBUFS; + + lt_valid = be32toh(pdprefix_option->iapdprefix.lifetime_valid); + lt_pref = be32toh(pdprefix_option->iapdprefix.lifetime_preferred); + + if (lt_valid == 0 || lt_pref > lt_valid) { + log_dhcp6_client(client, "Valid lifetieme of a PD prefix is zero or preferred lifetime %d > valid lifetime %d", + lt_pref, lt_valid); + + return 0; + } + + if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*pdprefix_option)) { + r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options); + if (r != 0) + return r < 0 ? r: 0; + } + + prefix = new0(DHCP6Address, 1); + if (!prefix) + return -ENOMEM; + + LIST_INIT(addresses, prefix); + memcpy(&prefix->iapdprefix, option->data, sizeof(prefix->iapdprefix)); + + LIST_PREPEND(addresses, ia->addresses, prefix); + + *lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid); + + return 0; +} + +int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { + uint16_t iatype, optlen; + size_t i, len; + int r = 0, status; + uint16_t opt; size_t iaaddr_offset; - DHCP6Address *addr; - uint32_t lt_t1, lt_t2, lt_valid, lt_pref, lt_min = ~0; + uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX; assert_return(ia, -EINVAL); assert_return(!ia->addresses, -EINVAL); + iatype = be16toh(iaoption->code); + len = be16toh(iaoption->len); + switch (iatype) { case SD_DHCP6_OPTION_IA_NA: - if (*buflen < DHCP6_OPTION_IA_NA_LEN + sizeof(DHCP6Option) + - sizeof(addr->iaaddr)) { + if (len < DHCP6_OPTION_IA_NA_LEN) { r = -ENOBUFS; goto error; } iaaddr_offset = DHCP6_OPTION_IA_NA_LEN; - memcpy(&ia->id, *buf, iaaddr_offset); + memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na)); - lt_t1 = be32toh(ia->lifetime_t1); - lt_t2 = be32toh(ia->lifetime_t2); + lt_t1 = be32toh(ia->ia_na.lifetime_t1); + lt_t2 = be32toh(ia->ia_na.lifetime_t2); if (lt_t1 && lt_t2 && lt_t1 > lt_t2) { - log_dhcp6_client(client, "IA T1 %ds > T2 %ds", + log_dhcp6_client(client, "IA NA T1 %ds > T2 %ds", + lt_t1, lt_t2); + r = -EINVAL; + goto error; + } + + break; + + case SD_DHCP6_OPTION_IA_PD: + + if (len < sizeof(ia->ia_pd)) { + r = -ENOBUFS; + goto error; + } + + iaaddr_offset = sizeof(ia->ia_pd); + memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd)); + + lt_t1 = be32toh(ia->ia_pd.lifetime_t1); + lt_t2 = be32toh(ia->ia_pd.lifetime_t2); + + if (lt_t1 && lt_t2 && lt_t1 > lt_t2) { + log_dhcp6_client(client, "IA PD T1 %ds > T2 %ds", lt_t1, lt_t2); r = -EINVAL; goto error; @@ -247,17 +411,13 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, break; case SD_DHCP6_OPTION_IA_TA: - if (*buflen < DHCP6_OPTION_IA_TA_LEN + sizeof(DHCP6Option) + - sizeof(addr->iaaddr)) { + if (len < DHCP6_OPTION_IA_TA_LEN) { r = -ENOBUFS; goto error; } iaaddr_offset = DHCP6_OPTION_IA_TA_LEN; - memcpy(&ia->id, *buf, iaaddr_offset); - - ia->lifetime_t1 = 0; - ia->lifetime_t2 = 0; + memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta)); break; @@ -267,48 +427,63 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, } ia->type = iatype; + i = iaaddr_offset; - *buflen -= iaaddr_offset; - *buf += iaaddr_offset; + while (i < len) { + DHCP6Option *option = (DHCP6Option *)&iaoption->data[i]; - while ((r = option_parse_hdr(buf, buflen, &opt, &optlen)) >= 0) { + if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len)) { + r = -ENOBUFS; + goto error; + } + + opt = be16toh(option->code); + optlen = be16toh(option->len); switch (opt) { case SD_DHCP6_OPTION_IAADDR: - addr = new0(DHCP6Address, 1); - if (!addr) { - r = -ENOMEM; + if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) { + log_dhcp6_client(client, "IA Address option not in IA NA or TA option"); + r = -EINVAL; goto error; } - LIST_INIT(addresses, addr); + r = dhcp6_option_parse_address(option, ia, <_valid); + if (r < 0) + goto error; + + if (lt_valid < lt_min) + lt_min = lt_valid; - memcpy(&addr->iaaddr, *buf, sizeof(addr->iaaddr)); + break; - lt_valid = be32toh(addr->iaaddr.lifetime_valid); - lt_pref = be32toh(addr->iaaddr.lifetime_valid); + case SD_DHCP6_OPTION_IA_PD_PREFIX: - if (!lt_valid || lt_pref > lt_valid) { - log_dhcp6_client(client, "IA preferred %ds > valid %ds", - lt_pref, lt_valid); - free(addr); - } else { - LIST_PREPEND(addresses, ia->addresses, addr); - if (lt_valid < lt_min) - lt_min = lt_valid; + if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_PD)) { + log_dhcp6_client(client, "IA PD Prefix option not in IA PD option"); + r = -EINVAL; + goto error; } + r = dhcp6_option_parse_pdprefix(option, ia, <_valid); + if (r < 0) + goto error; + + if (lt_valid < lt_min) + lt_min = lt_valid; + break; case SD_DHCP6_OPTION_STATUS_CODE: - if (optlen < sizeof(status)) - break; - status = (*buf)[0] << 8 | (*buf)[1]; + status = dhcp6_option_parse_status(option); if (status) { log_dhcp6_client(client, "IA status %d", status); + + dhcp6_lease_free_ia(ia); + r = -EINVAL; goto error; } @@ -320,30 +495,41 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, break; } - *buflen -= optlen; - *buf += optlen; + i += sizeof(*option) + optlen; } - if (r == -ENOMSG) - r = 0; + switch(iatype) { + case SD_DHCP6_OPTION_IA_NA: + if (!ia->ia_na.lifetime_t1 && !ia->ia_na.lifetime_t2) { + lt_t1 = lt_min / 2; + lt_t2 = lt_min / 10 * 8; + ia->ia_na.lifetime_t1 = htobe32(lt_t1); + ia->ia_na.lifetime_t2 = htobe32(lt_t2); - if (!ia->lifetime_t1 && !ia->lifetime_t2) { - lt_t1 = lt_min / 2; - lt_t2 = lt_min / 10 * 8; - ia->lifetime_t1 = htobe32(lt_t1); - ia->lifetime_t2 = htobe32(lt_t2); + log_dhcp6_client(client, "Computed IA NA T1 %ds and T2 %ds as both were zero", + lt_t1, lt_t2); + } - log_dhcp6_client(client, "Computed IA T1 %ds and T2 %ds as both were zero", - lt_t1, lt_t2); - } + break; - if (*buflen) - r = -ENOMSG; + case SD_DHCP6_OPTION_IA_PD: + if (!ia->ia_pd.lifetime_t1 && !ia->ia_pd.lifetime_t2) { + lt_t1 = lt_min / 2; + lt_t2 = lt_min / 10 * 8; + ia->ia_pd.lifetime_t1 = htobe32(lt_t1); + ia->ia_pd.lifetime_t2 = htobe32(lt_t2); -error: - *buf += *buflen; - *buflen = 0; + log_dhcp6_client(client, "Computed IA PD T1 %ds and T2 %ds as both were zero", + lt_t1, lt_t2); + } + break; + + default: + break; + } + +error: return r; } diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h index 7bbf183996..5f7e809ba1 100644 --- a/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libsystemd-network/dhcp6-protocol.h @@ -34,6 +34,7 @@ struct DHCP6Message { } _packed_; be32_t transaction_id; }; + uint8_t options[]; } _packed_; typedef struct DHCP6Message DHCP6Message; diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index e48b7d22dd..94386e4860 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -22,6 +22,7 @@ #include <linux/if.h> #include <netinet/ether.h> +#include "sd-id128.h" #include "sd-ndisc.h" #include "alloc-util.h" @@ -116,7 +117,8 @@ bool net_match_config(const struct ether_addr *match_mac, char * const *match_names, Condition *match_host, Condition *match_virt, - Condition *match_kernel, + Condition *match_kernel_cmdline, + Condition *match_kernel_version, Condition *match_arch, const struct ether_addr *dev_mac, const char *dev_path, @@ -131,7 +133,10 @@ bool net_match_config(const struct ether_addr *match_mac, if (match_virt && condition_test(match_virt) <= 0) return false; - if (match_kernel && condition_test(match_kernel) <= 0) + if (match_kernel_cmdline && condition_test(match_kernel_cmdline) <= 0) + return false; + + if (match_kernel_version && condition_test(match_kernel_version) <= 0) return false; if (match_arch && condition_test(match_arch) <= 0) diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h index a54adac602..4e69f1a598 100644 --- a/src/libsystemd-network/network-internal.h +++ b/src/libsystemd-network/network-internal.h @@ -37,7 +37,8 @@ bool net_match_config(const struct ether_addr *match_mac, char * const *match_name, Condition *match_host, Condition *match_virt, - Condition *match_kernel, + Condition *match_kernel_cmdline, + Condition *match_kernel_version, Condition *match_arch, const struct ether_addr *dev_mac, const char *dev_path, diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h index 441939b717..837e7f2603 100644 --- a/src/libsystemd-network/radv-internal.h +++ b/src/libsystemd-network/radv-internal.h @@ -93,6 +93,9 @@ struct sd_radv_prefix { } _packed_ opt; LIST_FIELDS(struct sd_radv_prefix, prefix); + + usec_t valid_until; + usec_t preferred_until; }; #define log_radv_full(level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "RADV: " fmt, ##__VA_ARGS__) diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index 78b8e058b4..2e88e39878 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -471,6 +471,7 @@ static int lease_parse_routes( struct sd_dhcp_route *route = *routes + *routes_size; int r; + route->option = SD_DHCP_OPTION_STATIC_ROUTE; r = in4_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen); if (r < 0) { log_debug("Failed to determine destination prefix length from class based IP, ignoring"); @@ -514,6 +515,7 @@ static int lease_parse_classless_routes( return -ENOMEM; route = *routes + *routes_size; + route->option = SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE; dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1); route->dst_prefixlen = *option; diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index 63fb355e85..907b72391b 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -28,6 +28,7 @@ #include "dhcp-server-internal.h" #include "fd-util.h" #include "in-addr-util.h" +#include "sd-id128.h" #include "siphash24.h" #include "string-util.h" #include "unaligned.h" diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 1c12e5430f..ec3484383b 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -54,6 +54,8 @@ struct sd_dhcp6_client { size_t mac_addr_len; uint16_t arp_type; DHCP6IA ia_na; + DHCP6IA ia_pd; + bool prefix_delegation; be32_t transaction_id; usec_t transaction_start; struct sd_dhcp6_lease *lease; @@ -230,7 +232,8 @@ 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); + client->ia_na.ia_na.id = htobe32(iaid); + client->ia_pd.ia_pd.id = htobe32(iaid); return 0; } @@ -279,6 +282,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) case SD_DHCP6_OPTION_DOMAIN_LIST: case SD_DHCP6_OPTION_SNTP_SERVERS: case SD_DHCP6_OPTION_NTP_SERVER: + case SD_DHCP6_OPTION_RAPID_COMMIT: break; default: @@ -298,6 +302,14 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) return 0; } +int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, bool delegation) { + assert_return(client, -EINVAL); + + client->prefix_delegation = delegation; + + return 0; +} + int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) { assert_return(client, -EINVAL); @@ -336,8 +348,6 @@ static int client_reset(sd_dhcp6_client *client) { client->receive_message = sd_event_source_unref(client->receive_message); - client->fd = safe_close(client->fd); - client->transaction_id = 0; client->transaction_start = 0; @@ -413,6 +423,15 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } + if (client->prefix_delegation) { + r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd); + if (r < 0) + return r; + + opt += r; + optlen -= r; + } + break; case DHCP6_STATE_REQUEST: @@ -439,6 +458,15 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } + if (client->prefix_delegation) { + r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd); + if (r < 0) + return r; + + opt += r; + optlen -= r; + } + break; case DHCP6_STATE_REBIND: @@ -454,6 +482,15 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } + if (client->prefix_delegation) { + r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd); + if (r < 0) + return r; + + opt += r; + optlen -= r; + } + break; case DHCP6_STATE_STOPPED: @@ -709,16 +746,20 @@ error: static int client_ensure_iaid(sd_dhcp6_client *client) { int r; + be32_t iaid; assert(client); - if (client->ia_na.id) + if (client->ia_na.ia_na.id) return 0; - r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->ia_na.id); + r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &iaid); if (r < 0) return r; + client->ia_na.ia_na.id = iaid; + client->ia_pd.ia_pd.id = iaid; + return 0; } @@ -727,23 +768,35 @@ static int client_parse_message( DHCP6Message *message, size_t len, sd_dhcp6_lease *lease) { + size_t pos = 0; int r; - uint8_t *optval, *option, *id = NULL; - uint16_t optcode, status; - size_t optlen, id_len; bool clientid = false; - be32_t iaid_lease; + uint8_t *id = NULL; + size_t id_len; + uint32_t lt_t1 = ~0, lt_t2 = ~0; assert(client); assert(message); assert(len >= sizeof(DHCP6Message)); assert(lease); - option = (uint8_t *)message + sizeof(DHCP6Message); len -= sizeof(DHCP6Message); - while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen, - &optval)) >= 0) { + while (pos < len) { + DHCP6Option *option = (DHCP6Option *)&message->options[pos]; + uint16_t optcode, optlen; + int status; + uint8_t *optval; + be32_t iaid_lease; + + if (len < offsetof(DHCP6Option, data) || + len < offsetof(DHCP6Option, data) + be16toh(option->len)) + return -ENOBUFS; + + optcode = be16toh(option->code); + optlen = be16toh(option->len); + optval = option->data; + switch (optcode) { case SD_DHCP6_OPTION_CLIENTID: if (clientid) { @@ -781,21 +834,21 @@ static int client_parse_message( if (optlen != 1) return -EINVAL; - r = dhcp6_lease_set_preference(lease, *optval); + r = dhcp6_lease_set_preference(lease, optval[0]); if (r < 0) return r; break; case SD_DHCP6_OPTION_STATUS_CODE: - if (optlen < 2) - return -EINVAL; - - status = optval[0] << 8 | optval[1]; + status = dhcp6_option_parse_status(option); if (status) { log_dhcp6_client(client, "%s Status %s", dhcp6_message_type_to_string(message->type), dhcp6_message_status_to_string(status)); + dhcp6_lease_free_ia(&lease->ia); + dhcp6_lease_free_ia(&lease->pd); + return -EINVAL; } @@ -808,8 +861,35 @@ static int client_parse_message( break; } - r = dhcp6_option_parse_ia(&optval, &optlen, optcode, - &lease->ia); + r = dhcp6_option_parse_ia(option, &lease->ia); + if (r < 0 && r != -ENOMSG) + return r; + + r = dhcp6_lease_get_iaid(lease, &iaid_lease); + if (r < 0) + return r; + + if (client->ia_na.ia_na.id != iaid_lease) { + log_dhcp6_client(client, "%s has wrong IAID for IA NA", + dhcp6_message_type_to_string(message->type)); + return -EINVAL; + } + + if (lease->ia.addresses) { + lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1)); + lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t1)); + } + + break; + + case SD_DHCP6_OPTION_IA_PD: + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + log_dhcp6_client(client, "Information request ignoring IA PD option"); + + break; + } + + r = dhcp6_option_parse_ia(option, &lease->pd); if (r < 0 && r != -ENOMSG) return r; @@ -817,12 +897,17 @@ static int client_parse_message( if (r < 0) return r; - if (client->ia_na.id != iaid_lease) { - log_dhcp6_client(client, "%s has wrong IAID", + if (client->ia_pd.ia_pd.id != iaid_lease) { + log_dhcp6_client(client, "%s has wrong IAID for IA PD", dhcp6_message_type_to_string(message->type)); return -EINVAL; } + if (lease->pd.addresses) { + lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1)); + lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2)); + } + break; case SD_DHCP6_OPTION_RAPID_COMMIT: @@ -861,12 +946,10 @@ static int client_parse_message( break; } + pos += sizeof(*option) + optlen; } - if (r == -ENOMSG) - r = 0; - - if (r < 0 || !clientid) { + if (!clientid) { log_dhcp6_client(client, "%s has incomplete options", dhcp6_message_type_to_string(message->type)); return -EINVAL; @@ -877,6 +960,17 @@ static int client_parse_message( if (r < 0) log_dhcp6_client(client, "%s has no server id", dhcp6_message_type_to_string(message->type)); + return r; + } + + if (lease->ia.addresses) { + lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1); + lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2); + } + + if (lease->pd.addresses) { + lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1); + lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2); } return r; @@ -1092,6 +1186,24 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { if (r < 0) return r; + if (!client->receive_message) { + r = sd_event_add_io(client->event, &client->receive_message, + client->fd, EPOLLIN, client_receive_message, + client); + if (r < 0) + goto error; + + r = sd_event_source_set_priority(client->receive_message, + client->event_priority); + if (r < 0) + goto error; + + r = sd_event_source_set_description(client->receive_message, + "dhcp6-receive-message"); + if (r < 0) + goto error; + } + switch (state) { case DHCP6_STATE_STOPPED: if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { @@ -1117,17 +1229,17 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { case DHCP6_STATE_BOUND: - if (client->lease->ia.lifetime_t1 == 0xffffffff || - client->lease->ia.lifetime_t2 == 0xffffffff) { + if (client->lease->ia.ia_na.lifetime_t1 == 0xffffffff || + client->lease->ia.ia_na.lifetime_t2 == 0xffffffff) { log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x", - be32toh(client->lease->ia.lifetime_t1), - be32toh(client->lease->ia.lifetime_t2)); + be32toh(client->lease->ia.ia_na.lifetime_t1), + be32toh(client->lease->ia.ia_na.lifetime_t2)); return 0; } - timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC); + timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t1) * USEC_PER_SEC); log_dhcp6_client(client, "T1 expires in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC)); @@ -1138,18 +1250,18 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { 10 * USEC_PER_SEC, client_timeout_t1, client); if (r < 0) - return r; + goto error; r = sd_event_source_set_priority(client->lease->ia.timeout_t1, client->event_priority); if (r < 0) - return r; + goto error; r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout"); if (r < 0) - return r; + goto error; - timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC); + timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t2) * USEC_PER_SEC); log_dhcp6_client(client, "T2 expires in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC)); @@ -1160,16 +1272,16 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { 10 * USEC_PER_SEC, client_timeout_t2, client); if (r < 0) - return r; + goto error; r = sd_event_source_set_priority(client->lease->ia.timeout_t2, client->event_priority); if (r < 0) - return r; + goto error; r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout"); if (r < 0) - return r; + goto error; client->state = state; @@ -1183,18 +1295,22 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { clock_boottime_or_monotonic(), 0, 0, client_timeout_resend, client); if (r < 0) - return r; + goto error; r = sd_event_source_set_priority(client->timeout_resend, client->event_priority); if (r < 0) - return r; + goto error; r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout"); if (r < 0) - return r; + goto error; return 0; + + error: + client_reset(client); + return r; } int sd_dhcp6_client_stop(sd_dhcp6_client *client) { @@ -1202,6 +1318,8 @@ int sd_dhcp6_client_stop(sd_dhcp6_client *client) { client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP); + client->fd = safe_close(client->fd); + return 0; } @@ -1235,32 +1353,18 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { if (r < 0) return r; - r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address); - 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; - - r = sd_event_add_io(client->event, &client->receive_message, - client->fd, EPOLLIN, client_receive_message, - client); - if (r < 0) - goto error; + if (client->fd < 0) { + r = dhcp6_network_bind_udp_socket(client->ifindex, &client->local_address); + if (r < 0) { + _cleanup_free_ char *p = NULL; - r = sd_event_source_set_priority(client->receive_message, - client->event_priority); - if (r < 0) - goto error; + (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)); + } - r = sd_event_source_set_description(client->receive_message, - "dhcp6-receive-message"); - if (r < 0) - goto error; + client->fd = r; + } if (client->information_request) state = DHCP6_STATE_INFORMATION_REQUEST; @@ -1270,10 +1374,6 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { "Managed"); return client_start(client, state); - -error: - client_reset(client); - return r; } int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) { @@ -1333,6 +1433,8 @@ sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) { client_reset(client); + client->fd = safe_close(client->fd); + sd_dhcp6_client_detach_event(client); free(client->req_opts); @@ -1352,6 +1454,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) { client->n_ref = 1; client->ia_na.type = SD_DHCP6_OPTION_IA_NA; + client->ia_pd.type = SD_DHCP6_OPTION_IA_PD; client->ifindex = -1; client->fd = -1; diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 6f604e072f..1f3a782f8c 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -49,7 +49,7 @@ int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) { valid = t; } - t = be32toh(ia->lifetime_t2); + t = be32toh(ia->ia_na.lifetime_t2); if (t > valid) return -EINVAL; @@ -144,7 +144,7 @@ int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) { assert_return(lease, -EINVAL); assert_return(iaid, -EINVAL); - *iaid = lease->ia.id; + *iaid = lease->ia.ia_na.id; return 0; } @@ -176,6 +176,37 @@ void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) { lease->addr_iter = lease->ia.addresses; } +int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix, + uint8_t *prefix_len, + uint32_t *lifetime_preferred, + uint32_t *lifetime_valid) { + assert_return(lease, -EINVAL); + assert_return(prefix, -EINVAL); + assert_return(prefix_len, -EINVAL); + assert_return(lifetime_preferred, -EINVAL); + assert_return(lifetime_valid, -EINVAL); + + if (!lease->prefix_iter) + return -ENOMSG; + + memcpy(prefix, &lease->prefix_iter->iapdprefix.address, + sizeof(struct in6_addr)); + *prefix_len = lease->prefix_iter->iapdprefix.prefixlen; + *lifetime_preferred = + be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred); + *lifetime_valid = + be32toh(lease->prefix_iter->iapdprefix.lifetime_valid); + + lease->prefix_iter = lease->prefix_iter->addresses_next; + + return 0; +} + +void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) { + if (lease) + lease->prefix_iter = lease->pd.addresses; +} + int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { int r; @@ -382,6 +413,7 @@ sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) { free(lease->serverid); dhcp6_lease_free_ia(&lease->ia); + dhcp6_lease_free_ia(&lease->pd); free(lease->dns); diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c index 23e2f5211d..f3d09eb30a 100644 --- a/src/libsystemd-network/sd-ipv4ll.c +++ b/src/libsystemd-network/sd-ipv4ll.c @@ -25,6 +25,7 @@ #include <stdlib.h> #include <string.h> +#include "sd-id128.h" #include "sd-ipv4acd.h" #include "sd-ipv4ll.h" diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index 46704acdef..f30d6164ea 100644 --- a/src/libsystemd-network/sd-radv.c +++ b/src/libsystemd-network/sd-radv.c @@ -21,7 +21,6 @@ #include <netinet/icmp6.h> #include <netinet/in.h> #include <arpa/inet.h> -#include <linux/in6.h> #include "sd-radv.h" @@ -169,6 +168,12 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, .msg_namelen = sizeof(dst_addr), .msg_iov = iov, }; + usec_t time_now; + int r; + + r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now); + if (r < 0) + return r; if (dst && !in_addr_is_null(AF_INET6, (union in_addr_union*) dst)) dst_addr.sin6_addr = *dst; @@ -198,6 +203,18 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, } LIST_FOREACH(prefix, p, ra->prefixes) { + if (p->valid_until) { + + if (time_now > p->valid_until) + p->opt.valid_lifetime = 0; + else + p->opt.valid_lifetime = htobe32((p->valid_until - time_now) / USEC_PER_SEC); + + if (time_now > p->preferred_until) + p->opt.preferred_lifetime = 0; + else + p->opt.preferred_lifetime = htobe32((p->preferred_until - time_now) / USEC_PER_SEC); + } iov[msg.msg_iovlen].iov_base = &p->opt; iov[msg.msg_iovlen].iov_len = sizeof(p->opt); msg.msg_iovlen++; @@ -446,9 +463,6 @@ _public_ int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) { assert_return(ra, -EINVAL); assert_return(mtu >= 1280, -EINVAL); - if (ra->state != SD_RADV_STATE_IDLE) - return -EBUSY; - ra->mtu = mtu; return 0; @@ -518,9 +532,13 @@ _public_ int sd_radv_set_preference(sd_radv *ra, unsigned preference) { return r; } -_public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { +_public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, bool dynamic) { sd_radv_prefix *cur; + int r; _cleanup_free_ char *addr_p = NULL; + char time_string_preferred[FORMAT_TIMESPAN_MAX]; + char time_string_valid[FORMAT_TIMESPAN_MAX]; + usec_t time_now, valid, preferred, valid_until, preferred_until; assert_return(ra, -EINVAL); @@ -528,7 +546,6 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { return -EINVAL; LIST_FOREACH(prefix, cur, ra->prefixes) { - int r; r = in_addr_prefix_intersect(AF_INET6, (union in_addr_union*) &cur->opt.in6_addr, @@ -539,12 +556,15 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { _cleanup_free_ char *addr_cur = NULL; (void) in_addr_to_string(AF_INET6, - (union in_addr_union*) &cur->opt.in6_addr, - &addr_cur); - (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &p->opt.in6_addr, &addr_p); + if (dynamic && cur->opt.prefixlen == p->opt.prefixlen) + goto update; + + (void) in_addr_to_string(AF_INET6, + (union in_addr_union*) &cur->opt.in6_addr, + &addr_cur); log_radv("IPv6 prefix %s/%u already configured, ignoring %s/%u", addr_cur, cur->opt.prefixlen, addr_p, p->opt.prefixlen); @@ -560,11 +580,69 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { ra->n_prefixes++; (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &p->opt.in6_addr, &addr_p); - log_radv("Added prefix %s/%d", addr_p, p->opt.prefixlen); + + if (!dynamic) { + log_radv("Added prefix %s/%d", addr_p, p->opt.prefixlen); + return 0; + } + + cur = p; + + update: + r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now); + if (r < 0) + return r; + + valid = be32toh(p->opt.valid_lifetime) * USEC_PER_SEC; + valid_until = usec_add(valid, time_now); + if (valid_until == USEC_INFINITY) + return -EOVERFLOW; + + preferred = be32toh(p->opt.preferred_lifetime) * USEC_PER_SEC; + preferred_until = usec_add(preferred, time_now); + if (preferred_until == USEC_INFINITY) + return -EOVERFLOW; + + cur->valid_until = valid_until; + cur->preferred_until = preferred_until; + + log_radv("%s prefix %s/%u preferred %s valid %s", + cur? "Updated": "Added", + addr_p, p->opt.prefixlen, + format_timespan(time_string_preferred, FORMAT_TIMESPAN_MAX, + preferred, USEC_PER_SEC), + format_timespan(time_string_valid, FORMAT_TIMESPAN_MAX, + valid, USEC_PER_SEC)); return 0; } +_public_ sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra, + struct in6_addr *prefix, + uint8_t prefixlen) { + sd_radv_prefix *cur, *next; + + assert_return(ra, NULL); + assert_return(prefix, NULL); + + LIST_FOREACH_SAFE(prefix, cur, next, ra->prefixes) { + if (prefixlen != cur->opt.prefixlen) + continue; + + if (!in_addr_equal(AF_INET6, + (union in_addr_union *)prefix, + (union in_addr_union *)&cur->opt.in6_addr)) + continue; + + LIST_REMOVE(prefix, ra->prefixes, cur); + ra->n_prefixes--; + + break; + } + + return cur; +} + _public_ int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime, const struct in6_addr *dns, size_t n_dns) { _cleanup_free_ struct sd_radv_opt_dns *opt_rdnss = NULL; diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index a0418ecdb9..aae49fa467 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -156,6 +156,138 @@ static int test_option(sd_event *e) { return 0; } +static int test_option_status(sd_event *e) { + uint8_t option1[] = { + /* IA NA */ + 0x00, 0x03, 0x00, 0x12, 0x1a, 0x1d, 0x1a, 0x1d, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, + /* status option */ + 0x00, 0x0d, 0x00, 0x02, 0x00, 0x01, + }; + static const uint8_t option2[] = { + /* IA NA */ + 0x00, 0x03, 0x00, 0x2e, 0x1a, 0x1d, 0x1a, 0x1d, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, + /* IA Addr */ + 0x00, 0x05, 0x00, 0x1e, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x0c, 0x0d, + /* status option */ + 0x00, 0x0d, 0x00, 0x02, 0x00, 0x01, + }; + static const uint8_t option3[] = { + /* IA NA */ + 0x00, 0x03, 0x00, 0x34, 0x1a, 0x1d, 0x1a, 0x1d, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, + /* IA Addr */ + 0x00, 0x05, 0x00, 0x24, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x0c, 0x0d, + /* status option */ + 0x00, 0x0d, 0x00, 0x08, 0x00, 0x00, 'f', 'o', + 'o', 'b', 'a', 'r', + }; + static const uint8_t option4[] = { + /* IA PD */ + 0x00, 0x19, 0x00, 0x2f, 0x1a, 0x1d, 0x1a, 0x1d, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, + /* IA PD Prefix */ + 0x00, 0x1a, 0x00, 0x1f, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x80, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, + 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + /* status option */ + 0x00, 0x0d, 0x00, 0x02, 0x00, 0x00, + }; + static const uint8_t option5[] = { + /* IA PD */ + 0x00, 0x19, 0x00, 0x52, 0x1a, 0x1d, 0x1a, 0x1d, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, + /* IA PD Prefix #1 */ + 0x00, 0x1a, 0x00, 0x1f, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x80, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, + 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + /* status option */ + 0x00, 0x0d, 0x00, 0x02, 0x00, 0x00, + /* IA PD Prefix #2 */ + 0x00, 0x1a, 0x00, 0x1f, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x80, 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x0l, 0xd0, + 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + 0x00, 0x0d, 0x00, 0x02, 0x00, 0x00, + }; + DHCP6Option *option; + DHCP6IA ia, pd; + int r = 0; + + if (verbose) + printf("* %s\n", __FUNCTION__); + + zero(ia); + option = (DHCP6Option *)option1; + assert_se(sizeof(option1) == sizeof(DHCP6Option) + be16toh(option->len)); + + r = dhcp6_option_parse_ia(option, &ia); + assert_se(r == -EINVAL); + assert_se(ia.addresses == NULL); + + option->len = htobe16(17); + r = dhcp6_option_parse_ia(option, &ia); + assert_se(r == -ENOBUFS); + assert_se(ia.addresses == NULL); + + option->len = htobe16(sizeof(DHCP6Option)); + r = dhcp6_option_parse_ia(option, &ia); + assert_se(r == -ENOBUFS); + assert_se(ia.addresses == NULL); + + zero(ia); + option = (DHCP6Option *)option2; + assert_se(sizeof(option2) == sizeof(DHCP6Option) + be16toh(option->len)); + + r = dhcp6_option_parse_ia(option, &ia); + assert_se(r >= 0); + assert_se(ia.addresses == NULL); + + zero(ia); + option = (DHCP6Option *)option3; + assert_se(sizeof(option3) == sizeof(DHCP6Option) + be16toh(option->len)); + + r = dhcp6_option_parse_ia(option, &ia); + assert_se(r >= 0); + assert_se(ia.addresses != NULL); + dhcp6_lease_free_ia(&ia); + + zero(pd); + option = (DHCP6Option *)option4; + assert_se(sizeof(option4) == sizeof(DHCP6Option) + be16toh(option->len)); + + r = dhcp6_option_parse_ia(option, &pd); + assert_se(r == 0); + assert_se(pd.addresses != NULL); + assert_se(memcmp(&pd.ia_pd.id, &option4[4], 4) == 0); + assert_se(memcmp(&pd.ia_pd.lifetime_t1, &option4[8], 4) == 0); + assert_se(memcmp(&pd.ia_pd.lifetime_t2, &option4[12], 4) == 0); + dhcp6_lease_free_ia(&pd); + + zero(pd); + option = (DHCP6Option *)option5; + assert_se(sizeof(option5) == sizeof(DHCP6Option) + be16toh(option->len)); + + r = dhcp6_option_parse_ia(option, &pd); + assert_se(r == 0); + assert_se(pd.addresses != NULL); + dhcp6_lease_free_ia(&pd); + + return 0; +} + static uint8_t msg_advertise[198] = { 0x02, 0x0f, 0xb4, 0xe5, 0x00, 0x01, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, 0xf3, 0x30, @@ -217,14 +349,13 @@ static uint8_t fqdn_wire[16] = { static int test_advertise_option(sd_event *e) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; DHCP6Message *advertise = (DHCP6Message *)msg_advertise; - uint8_t *optval, *opt = msg_advertise + sizeof(DHCP6Message); - uint16_t optcode; - size_t optlen, len = sizeof(msg_advertise) - sizeof(DHCP6Message); + size_t len = sizeof(msg_advertise) - sizeof(DHCP6Message), pos = 0; be32_t val; uint8_t preference = 255; struct in6_addr addr; uint32_t lt_pref, lt_valid; int r; + uint8_t *opt; bool opt_clientid = false; struct in6_addr *addrs; char **domains; @@ -232,14 +363,19 @@ static int test_advertise_option(sd_event *e) { if (verbose) printf("* %s\n", __FUNCTION__); + assert_se(len >= sizeof(DHCP6Message)); + assert_se(dhcp6_lease_new(&lease) >= 0); assert_se(advertise->type == DHCP6_ADVERTISE); assert_se((be32toh(advertise->transaction_id) & 0x00ffffff) == 0x0fb4e5); - while ((r = dhcp6_option_parse(&opt, &len, &optcode, &optlen, - &optval)) >= 0) { + while (pos < len) { + DHCP6Option *option = (DHCP6Option *)&advertise->options[pos]; + const uint16_t optcode = be16toh(option->code); + const uint16_t optlen = be16toh(option->len); + uint8_t *optval = option->data; switch(optcode) { case SD_DHCP6_OPTION_CLIENTID: @@ -261,9 +397,7 @@ static int test_advertise_option(sd_event *e) { val = htobe32(120); assert_se(!memcmp(optval + 8, &val, sizeof(val))); - assert_se(dhcp6_option_parse_ia(&optval, &optlen, - optcode, - &lease->ia) >= 0); + assert_se(dhcp6_option_parse_ia(option, &lease->ia) >= 0); break; @@ -309,11 +443,11 @@ static int test_advertise_option(sd_event *e) { default: break; } - } + pos += sizeof(*option) + optlen; + } - assert_se(r == -ENOMSG); - + assert_se(pos == len); assert_se(opt_clientid); sd_dhcp6_lease_reset_address_iter(lease); @@ -415,15 +549,11 @@ static int test_client_send_reply(DHCP6Message *request) { return 0; } -static int test_client_verify_request(DHCP6Message *request, uint8_t *option, - size_t len) { +static int test_client_verify_request(DHCP6Message *request, size_t len) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - uint8_t *optval; - uint16_t optcode; - size_t optlen; + size_t pos = 0; bool found_clientid = false, found_iana = false, found_serverid = false, found_elapsed_time = false, found_fqdn = false; - int r; struct in6_addr addr; be32_t val; uint32_t lt_pref, lt_valid; @@ -432,8 +562,14 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option, assert_se(dhcp6_lease_new(&lease) >= 0); - while ((r = dhcp6_option_parse(&option, &len, - &optcode, &optlen, &optval)) >= 0) { + len -= sizeof(DHCP6Message); + + while (pos < len) { + DHCP6Option *option = (DHCP6Option *)&request->options[pos]; + uint16_t optcode = be16toh(option->code); + uint16_t optlen = be16toh(option->len); + uint8_t *optval = option->data; + switch(optcode) { case SD_DHCP6_OPTION_CLIENTID: assert_se(!found_clientid); @@ -458,8 +594,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option, val = htobe32(120); assert_se(!memcmp(optval + 8, &val, sizeof(val))); - assert_se(!dhcp6_option_parse_ia(&optval, &optlen, - optcode, &lease->ia)); + assert_se(!dhcp6_option_parse_ia(option, &lease->ia)); break; @@ -489,9 +624,10 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option, assert_se(!memcmp(optval + 1, fqdn_wire, sizeof(fqdn_wire))); break; } + + pos += sizeof(*option) + optlen; } - assert_se(r == -ENOMSG); assert_se(found_clientid && found_iana && found_serverid && found_elapsed_time); @@ -526,19 +662,21 @@ static int test_client_send_advertise(DHCP6Message *solicit) { return 0; } -static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option, - size_t len) { - uint8_t *optval; - uint16_t optcode; - size_t optlen; +static int test_client_verify_solicit(DHCP6Message *solicit, size_t len) { bool found_clientid = false, found_iana = false, found_elapsed_time = false, found_fqdn = false; - int r; + size_t pos = 0; assert_se(solicit->type == DHCP6_SOLICIT); - while ((r = dhcp6_option_parse(&option, &len, - &optcode, &optlen, &optval)) >= 0) { + len -= sizeof(DHCP6Message); + + while (pos < len) { + DHCP6Option *option = (DHCP6Option *)&solicit->options[pos]; + uint16_t optcode = be16toh(option->code); + uint16_t optlen = be16toh(option->len); + uint8_t *optval = option->data; + switch(optcode) { case SD_DHCP6_OPTION_CLIENTID: assert_se(!found_clientid); @@ -578,9 +716,11 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option, break; } + + pos += sizeof(*option) + optlen; } - assert_se(r == -ENOMSG); + assert_se(pos == len); assert_se(found_clientid && found_iana && found_elapsed_time); return 0; @@ -623,17 +763,15 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event, assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0); assert_se(sd_dhcp6_client_start(client) >= 0); + } static int test_client_verify_information_request(DHCP6Message *information_request, - uint8_t *option, size_t len) { + size_t len) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - uint8_t *optval; - uint16_t optcode; - size_t optlen; + size_t pos = 0; bool found_clientid = false, found_elapsed_time = false; - int r; struct in6_addr addr; uint32_t lt_pref, lt_valid; @@ -641,8 +779,14 @@ static int test_client_verify_information_request(DHCP6Message *information_requ assert_se(dhcp6_lease_new(&lease) >= 0); - while ((r = dhcp6_option_parse(&option, &len, - &optcode, &optlen, &optval)) >= 0) { + len -= sizeof(DHCP6Message); + + while (pos < len) { + DHCP6Option *option = (DHCP6Option *)&information_request->options[pos]; + uint16_t optcode = be16toh(option->code); + uint16_t optlen = be16toh(option->len); + uint8_t *optval = option->data; + switch(optcode) { case SD_DHCP6_OPTION_CLIENTID: assert_se(!found_clientid); @@ -671,9 +815,11 @@ static int test_client_verify_information_request(DHCP6Message *information_requ break; } + + pos += sizeof(*option) + optlen; } - assert_se(r == -ENOMSG); + assert_se(pos == len); assert_se(found_clientid && found_elapsed_time); sd_dhcp6_lease_reset_address_iter(lease); @@ -689,7 +835,6 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, struct in6_addr mcast = IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; DHCP6Message *message; - uint8_t *option; assert_se(s == test_dhcp_fd[0]); assert_se(server_address); @@ -699,21 +844,19 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast)); message = (DHCP6Message *)packet; - option = (uint8_t *)(message + 1); - len -= sizeof(DHCP6Message); assert_se(message->transaction_id & 0x00ffffff); if (test_client_message_num == 0) { - test_client_verify_information_request(message, option, len); + test_client_verify_information_request(message, len); test_client_send_reply(message); test_client_message_num++; } else if (test_client_message_num == 1) { - test_client_verify_solicit(message, option, len); + test_client_verify_solicit(message, len); test_client_send_advertise(message); test_client_message_num++; } else if (test_client_message_num == 2) { - test_client_verify_request(message, option, len); + test_client_verify_request(message, len); test_client_send_reply(message); test_client_message_num++; } @@ -789,6 +932,7 @@ int main(int argc, char *argv[]) { test_client_basic(e); test_option(e); + test_option_status(e); test_advertise_option(e); test_client_solicit(e); diff --git a/src/libsystemd-network/test-lldp.c b/src/libsystemd-network/test-lldp.c index c62689373f..b91797cb66 100644 --- a/src/libsystemd-network/test-lldp.c +++ b/src/libsystemd-network/test-lldp.c @@ -20,6 +20,7 @@ ***/ #include <arpa/inet.h> +#include <errno.h> #include <net/ethernet.h> #include <stdio.h> #include <string.h> diff --git a/src/libsystemd-network/test-ndisc-ra.c b/src/libsystemd-network/test-ndisc-ra.c index c1a8d5a00d..1fc8ca9eba 100644 --- a/src/libsystemd-network/test-ndisc-ra.c +++ b/src/libsystemd-network/test-ndisc-ra.c @@ -342,8 +342,8 @@ static void test_ra(void) { if (prefix[i].preferred) assert_se(sd_radv_prefix_set_preferred_lifetime(p, prefix[i].preferred) >= 0); - assert_se((sd_radv_add_prefix(ra, p) >= 0) == prefix[i].succesful); - assert_se(sd_radv_add_prefix(ra, p) < 0); + assert_se((sd_radv_add_prefix(ra, p, false) >= 0) == prefix[i].succesful); + assert_se(sd_radv_add_prefix(ra, p, false) < 0); p = sd_radv_prefix_unref(p); assert_se(!p); diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index 1a29b03e85..00aeefbe19 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -530,3 +530,22 @@ global: sd_bus_message_new; sd_bus_message_seal; } LIBSYSTEMD_234; + +LIBSYSTEMD_237 { +global: + sd_bus_set_watch_bind; + sd_bus_get_watch_bind; + sd_bus_request_name_async; + sd_bus_release_name_async; + sd_bus_add_match_async; + sd_bus_match_signal; + sd_bus_match_signal_async; + sd_bus_is_ready; + sd_bus_set_connected_signal; + sd_bus_get_connected_signal; + sd_bus_set_sender; + sd_bus_get_sender; + sd_bus_message_set_sender; + sd_event_source_get_io_fd_own; + sd_event_source_set_io_fd_own; +} LIBSYSTEMD_236; diff --git a/src/libsystemd/meson.build b/src/libsystemd/meson.build index 4abf50b111..706e090762 100644 --- a/src/libsystemd/meson.build +++ b/src/libsystemd/meson.build @@ -17,9 +17,7 @@ sd_login_c = files('sd-login/sd-login.c') -libsystemd_internal_sources = files(''' - sd-bus/bus-bloom.c - sd-bus/bus-bloom.h +libsystemd_sources = files(''' sd-bus/bus-common-errors.c sd-bus/bus-common-errors.h sd-bus/bus-container.c @@ -74,6 +72,7 @@ libsystemd_internal_sources = files(''' sd-id128/id128-util.c sd-id128/id128-util.h sd-id128/sd-id128.c + sd-netlink/generic-netlink.c sd-netlink/local-addresses.c sd-netlink/local-addresses.h sd-netlink/netlink-internal.h @@ -93,14 +92,15 @@ libsystemd_internal_sources = files(''' sd-utf8/sd-utf8.c '''.split()) + sd_login_c -libsystemd_internal = static_library( +libsystemd_static = static_library( 'systemd', - libsystemd_internal_sources, + libsystemd_sources, install : false, include_directories : includes, link_with : libbasic, dependencies : [threads, - librt]) + librt], + c_args : ['-fvisibility=default']) libsystemd_sym = 'src/libsystemd/libsystemd.sym' diff --git a/src/libsystemd/sd-bus/bus-bloom.c b/src/libsystemd/sd-bus/bus-bloom.c deleted file mode 100644 index ebda6516e2..0000000000 --- a/src/libsystemd/sd-bus/bus-bloom.c +++ /dev/null @@ -1,157 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -/*** - 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 "bus-bloom.h" -#include "siphash24.h" -#include "util.h" - -static inline void set_bit(uint64_t filter[], unsigned long b) { - filter[b >> 6] |= 1ULL << (b & 63); -} - -static const sd_id128_t hash_keys[] = { - SD_ID128_ARRAY(b9,66,0b,f0,46,70,47,c1,88,75,c4,9c,54,b9,bd,15), - SD_ID128_ARRAY(aa,a1,54,a2,e0,71,4b,39,bf,e1,dd,2e,9f,c5,4a,3b), - SD_ID128_ARRAY(63,fd,ae,be,cd,82,48,12,a1,6e,41,26,cb,fa,a0,c8), - SD_ID128_ARRAY(23,be,45,29,32,d2,46,2d,82,03,52,28,fe,37,17,f5), - SD_ID128_ARRAY(56,3b,bf,ee,5a,4f,43,39,af,aa,94,08,df,f0,fc,10), - SD_ID128_ARRAY(31,80,c8,73,c7,ea,46,d3,aa,25,75,0f,9e,4c,09,29), - SD_ID128_ARRAY(7d,f7,18,4b,7b,a4,44,d5,85,3c,06,e0,65,53,96,6d), - SD_ID128_ARRAY(f2,77,e9,6f,93,b5,4e,71,9a,0c,34,88,39,25,bf,35), -}; - -static void bloom_add_data( - uint64_t filter[], /* The filter bits */ - size_t size, /* Size of the filter in bytes */ - unsigned k, /* Number of hash functions */ - const void *data, /* Data to hash */ - size_t n) { /* Size of data to hash in bytes */ - - uint64_t h; - uint64_t m; - unsigned w, i, c = 0; - unsigned hash_index; - - assert(size > 0); - assert(k > 0); - - /* Determine bits in filter */ - m = size * 8; - - /* Determine how many bytes we need to generate a bit index 0..m for this filter */ - w = (u64log2(m) + 7) / 8; - - assert(w <= sizeof(uint64_t)); - - /* Make sure we have enough hash keys to generate m * k bits - * of hash value. Note that SipHash24 generates 64 bits of - * hash value for each 128 bits of hash key. */ - assert(k * w <= ELEMENTSOF(hash_keys) * 8); - - for (i = 0, hash_index = 0; i < k; i++) { - uint64_t p = 0; - unsigned d; - - for (d = 0; d < w; d++) { - if (c <= 0) { - h = siphash24(data, n, hash_keys[hash_index++].bytes); - c += 8; - } - - p = (p << 8ULL) | (uint64_t) ((uint8_t *)&h)[8 - c]; - c--; - } - - p &= m - 1; - set_bit(filter, p); - } - - /* log_debug("bloom: adding <%.*s>", (int) n, (char*) data); */ -} - -void bloom_add_pair(uint64_t filter[], size_t size, unsigned k, const char *a, const char *b) { - size_t n; - char *c; - - assert(filter); - assert(a); - assert(b); - - n = strlen(a) + 1 + strlen(b); - c = alloca(n + 1); - strcpy(stpcpy(stpcpy(c, a), ":"), b); - - bloom_add_data(filter, size, k, c, n); -} - -void bloom_add_prefixes(uint64_t filter[], size_t size, unsigned k, const char *a, const char *b, char sep) { - size_t n; - char *c, *p; - - assert(filter); - assert(a); - assert(b); - - n = strlen(a) + 1 + strlen(b); - c = alloca(n + 1); - - p = stpcpy(stpcpy(c, a), ":"); - strcpy(p, b); - - bloom_add_data(filter, size, k, c, n); - - for (;;) { - char *e; - - e = strrchr(p, sep); - if (!e) - break; - - *(e + 1) = 0; - bloom_add_data(filter, size, k, c, e - c + 1); - - if (e == p) - break; - - *e = 0; - bloom_add_data(filter, size, k, c, e - c); - } -} - -bool bloom_validate_parameters(size_t size, unsigned k) { - uint64_t m; - unsigned w; - - if (size <= 0) - return false; - - if (k <= 0) - return false; - - m = size * 8; - w = (u64log2(m) + 7) / 8; - if (w > sizeof(uint64_t)) - return false; - - if (k * w > ELEMENTSOF(hash_keys) * 8) - return false; - - return true; -} diff --git a/src/libsystemd/sd-bus/bus-container.c b/src/libsystemd/sd-bus/bus-container.c index 8f6d34838e..16156d8823 100644 --- a/src/libsystemd/sd-bus/bus-container.c +++ b/src/libsystemd/sd-bus/bus-container.c @@ -31,9 +31,8 @@ int bus_container_connect_socket(sd_bus *b) { _cleanup_close_pair_ int pair[2] = { -1, -1 }; _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1; - pid_t child; - siginfo_t si; int r, error_buf = 0; + pid_t child; ssize_t n; assert(b); @@ -62,11 +61,10 @@ int bus_container_connect_socket(sd_bus *b) { if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) return -errno; - child = fork(); - if (child < 0) - return -errno; - - if (child == 0) { + r = safe_fork("(sd-buscntr)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child); + if (r < 0) + return r; + if (r == 0) { pid_t grandchild; pair[0] = safe_close(pair[0]); @@ -82,11 +80,10 @@ int bus_container_connect_socket(sd_bus *b) { * comes from a process from within the container, and * not outside of it */ - grandchild = fork(); - if (grandchild < 0) + r = safe_fork("(sd-buscntr2)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &grandchild); + if (r < 0) _exit(EXIT_FAILURE); - - if (grandchild == 0) { + if (r == 0) { r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size); if (r < 0) { @@ -99,21 +96,20 @@ int bus_container_connect_socket(sd_bus *b) { _exit(EXIT_SUCCESS); } - r = wait_for_terminate(grandchild, &si); + r = wait_for_terminate_and_check("(sd-buscntr2)", grandchild, 0); if (r < 0) _exit(EXIT_FAILURE); - if (si.si_code != CLD_EXITED) - _exit(EXIT_FAILURE); - - _exit(si.si_status); + _exit(r); } pair[1] = safe_close(pair[1]); - r = wait_for_terminate(child, &si); + r = wait_for_terminate_and_check("(sd-buscntr)", child, 0); if (r < 0) return r; + if (r != EXIT_SUCCESS) + return -EPROTO; n = read(pair[0], &error_buf, sizeof(error_buf)); if (n < 0) @@ -133,11 +129,5 @@ int bus_container_connect_socket(sd_bus *b) { return -error_buf; } - if (si.si_code != CLD_EXITED) - return -EIO; - - if (si.si_status != EXIT_SUCCESS) - return -EIO; - return bus_socket_start_auth(b); } diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c index 12478e7cc6..9dd5274bf6 100644 --- a/src/libsystemd/sd-bus/bus-control.c +++ b/src/libsystemd/sd-bus/bus-control.c @@ -28,12 +28,12 @@ #include "sd-bus.h" #include "alloc-util.h" -#include "bus-bloom.h" #include "bus-control.h" #include "bus-internal.h" #include "bus-message.h" #include "bus-util.h" #include "capability-util.h" +#include "process-util.h" #include "stdio-util.h" #include "string-util.h" #include "strv.h" @@ -43,6 +43,7 @@ _public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) { int r; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(unique, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -57,13 +58,31 @@ _public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) { return 0; } -static int bus_request_name_dbus1(sd_bus *bus, const char *name, uint64_t flags) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - uint32_t ret, param = 0; - int r; +static int validate_request_name_parameters( + sd_bus *bus, + const char *name, + uint64_t flags, + uint32_t *ret_param) { + + uint32_t param = 0; assert(bus); assert(name); + assert(ret_param); + + assert_return(!(flags & ~(SD_BUS_NAME_ALLOW_REPLACEMENT|SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_QUEUE)), -EINVAL); + assert_return(service_name_is_valid(name), -EINVAL); + assert_return(name[0] != ':', -EINVAL); + + if (!bus->bus_client) + return -EINVAL; + + /* Don't allow requesting the special driver and local names */ + if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local")) + return -EINVAL; + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; if (flags & SD_BUS_NAME_ALLOW_REPLACEMENT) param |= BUS_NAME_ALLOW_REPLACEMENT; @@ -72,6 +91,29 @@ static int bus_request_name_dbus1(sd_bus *bus, const char *name, uint64_t flags) if (!(flags & SD_BUS_NAME_QUEUE)) param |= BUS_NAME_DO_NOT_QUEUE; + *ret_param = param; + + return 0; +} + +_public_ int sd_bus_request_name( + sd_bus *bus, + const char *name, + uint64_t flags) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + uint32_t ret, param = 0; + int r; + + assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); + assert_return(name, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + r = validate_request_name_parameters(bus, name, flags, ¶m); + if (r < 0) + return r; + r = sd_bus_call_method( bus, "org.freedesktop.DBus", @@ -90,46 +132,145 @@ static int bus_request_name_dbus1(sd_bus *bus, const char *name, uint64_t flags) if (r < 0) return r; - if (ret == BUS_NAME_ALREADY_OWNER) + switch (ret) { + + case BUS_NAME_ALREADY_OWNER: return -EALREADY; - else if (ret == BUS_NAME_EXISTS) + + case BUS_NAME_EXISTS: return -EEXIST; - else if (ret == BUS_NAME_IN_QUEUE) + + case BUS_NAME_IN_QUEUE: return 0; - else if (ret == BUS_NAME_PRIMARY_OWNER) + + case BUS_NAME_PRIMARY_OWNER: return 1; + } return -EIO; } -_public_ int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) { +static int default_request_name_handler( + sd_bus_message *m, + void *userdata, + sd_bus_error *ret_error) { + + uint32_t ret; + int r; + + assert(m); + + if (sd_bus_message_is_method_error(m, NULL)) { + log_debug_errno(sd_bus_message_get_errno(m), + "Unable to request name, failing connection: %s", + sd_bus_message_get_error(m)->message); + + bus_enter_closing(sd_bus_message_get_bus(m)); + return 1; + } + + r = sd_bus_message_read(m, "u", &ret); + if (r < 0) + return r; + + switch (ret) { + + case BUS_NAME_ALREADY_OWNER: + log_debug("Already owner of requested service name, ignoring."); + return 1; + + case BUS_NAME_IN_QUEUE: + log_debug("In queue for requested service name."); + return 1; + + case BUS_NAME_PRIMARY_OWNER: + log_debug("Successfully acquired requested service name."); + return 1; + + case BUS_NAME_EXISTS: + log_debug("Requested service name already owned, failing connection."); + bus_enter_closing(sd_bus_message_get_bus(m)); + return 1; + } + + log_debug("Unexpected response from RequestName(), failing connection."); + bus_enter_closing(sd_bus_message_get_bus(m)); + return 1; +} + +_public_ int sd_bus_request_name_async( + sd_bus *bus, + sd_bus_slot **ret_slot, + const char *name, + uint64_t flags, + sd_bus_message_handler_t callback, + void *userdata) { + + uint32_t param = 0; + int r; + assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(name, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); - assert_return(!(flags & ~(SD_BUS_NAME_ALLOW_REPLACEMENT|SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_QUEUE)), -EINVAL); + + r = validate_request_name_parameters(bus, name, flags, ¶m); + if (r < 0) + return r; + + return sd_bus_call_method_async( + bus, + ret_slot, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "RequestName", + callback ?: default_request_name_handler, + userdata, + "su", + name, + param); +} + +static int validate_release_name_parameters( + sd_bus *bus, + const char *name) { + + assert(bus); + assert(name); + assert_return(service_name_is_valid(name), -EINVAL); assert_return(name[0] != ':', -EINVAL); if (!bus->bus_client) return -EINVAL; - /* Don't allow requesting the special driver and local names */ + /* Don't allow releasing the special driver and local names */ if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local")) return -EINVAL; if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; - return bus_request_name_dbus1(bus, name, flags); + return 0; } -static int bus_release_name_dbus1(sd_bus *bus, const char *name) { +_public_ int sd_bus_release_name( + sd_bus *bus, + const char *name) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; uint32_t ret; int r; - assert(bus); - assert(name); + assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); + assert_return(name, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + r = validate_release_name_parameters(bus, name); + if (r < 0) + return r; r = sd_bus_call_method( bus, @@ -147,41 +288,112 @@ static int bus_release_name_dbus1(sd_bus *bus, const char *name) { r = sd_bus_message_read(reply, "u", &ret); if (r < 0) return r; - if (ret == BUS_NAME_NON_EXISTENT) + + switch (ret) { + + case BUS_NAME_NON_EXISTENT: return -ESRCH; - if (ret == BUS_NAME_NOT_OWNER) + + case BUS_NAME_NOT_OWNER: return -EADDRINUSE; - if (ret == BUS_NAME_RELEASED) + + case BUS_NAME_RELEASED: return 0; + } + + return -EIO; +} + +static int default_release_name_handler( + sd_bus_message *m, + void *userdata, + sd_bus_error *ret_error) { + + uint32_t ret; + int r; + + assert(m); + + if (sd_bus_message_is_method_error(m, NULL)) { + log_debug_errno(sd_bus_message_get_errno(m), + "Unable to release name, failing connection: %s", + sd_bus_message_get_error(m)->message); + + bus_enter_closing(sd_bus_message_get_bus(m)); + return 1; + } - return -EINVAL; + r = sd_bus_message_read(m, "u", &ret); + if (r < 0) + return r; + + switch (ret) { + + case BUS_NAME_NON_EXISTENT: + log_debug("Name asked to release is not taken currently, ignoring."); + return 1; + + case BUS_NAME_NOT_OWNER: + log_debug("Name asked to release is owned by somebody else, ignoring."); + return 1; + + case BUS_NAME_RELEASED: + log_debug("Name successfully released."); + return 1; + } + + log_debug("Unexpected response from ReleaseName(), failing connection."); + bus_enter_closing(sd_bus_message_get_bus(m)); + return 1; } -_public_ int sd_bus_release_name(sd_bus *bus, const char *name) { +_public_ int sd_bus_release_name_async( + sd_bus *bus, + sd_bus_slot **ret_slot, + const char *name, + sd_bus_message_handler_t callback, + void *userdata) { + + int r; + assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(name, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); - assert_return(service_name_is_valid(name), -EINVAL); - assert_return(name[0] != ':', -EINVAL); - if (!bus->bus_client) - return -EINVAL; - - /* Don't allow releasing the special driver and local names */ - if (STR_IN_SET(name, "org.freedesktop.DBus", "org.freedesktop.DBus.Local")) - return -EINVAL; - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; + r = validate_release_name_parameters(bus, name); + if (r < 0) + return r; - return bus_release_name_dbus1(bus, name); + return sd_bus_call_method_async( + bus, + ret_slot, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "ReleaseName", + callback ?: default_release_name_handler, + userdata, + "s", + name); } -static int bus_list_names_dbus1(sd_bus *bus, char ***acquired, char ***activatable) { +_public_ int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_strv_free_ char **x = NULL, **y = NULL; int r; + assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); + assert_return(acquired || activatable, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!bus->bus_client) + return -EINVAL; + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + if (acquired) { r = sd_bus_call_method( bus, @@ -231,21 +443,7 @@ static int bus_list_names_dbus1(sd_bus *bus, char ***acquired, char ***activatab return 0; } -_public_ int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable) { - assert_return(bus, -EINVAL); - assert_return(acquired || activatable, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!bus->bus_client) - return -EINVAL; - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - return bus_list_names_dbus1(bus, acquired, activatable); -} - -static int bus_get_name_creds_dbus1( +_public_ int sd_bus_get_name_creds( sd_bus *bus, const char *name, uint64_t mask, @@ -257,6 +455,31 @@ static int bus_get_name_creds_dbus1( pid_t pid = 0; int r; + assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); + assert_return(name, -EINVAL); + assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); + assert_return(mask == 0 || creds, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + assert_return(service_name_is_valid(name), -EINVAL); + + if (!bus->bus_client) + return -EINVAL; + + /* Turn off augmenting if this isn't a local connection. If the connection is not local, then /proc is not + * going to match. */ + if (!bus->is_local) + mask &= ~SD_BUS_CREDS_AUGMENT; + + if (streq(name, "org.freedesktop.DBus.Local")) + return -EINVAL; + + if (streq(name, "org.freedesktop.DBus")) + return sd_bus_get_owner_creds(bus, mask, creds); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + /* Only query the owner if the caller wants to know it or if * the caller just wants to check whether a name exists */ if ((mask & SD_BUS_CREDS_UNIQUE_NAME) || mask == 0) { @@ -519,51 +742,29 @@ static int bus_get_name_creds_dbus1( return 0; } -_public_ int sd_bus_get_name_creds( - sd_bus *bus, - const char *name, - uint64_t mask, - sd_bus_creds **creds) { +_public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; + bool do_label, do_groups; + pid_t pid = 0; + int r; assert_return(bus, -EINVAL); - assert_return(name, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); - assert_return(mask == 0 || creds, -EINVAL); + assert_return(ret, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); - assert_return(service_name_is_valid(name), -EINVAL); - - if (!bus->bus_client) - return -EINVAL; - - /* Turn off augmenting if this isn't a local connection. If the connection is not local, then /proc is not - * going to match. */ - if (!bus->is_local) - mask &= ~SD_BUS_CREDS_AUGMENT; - - if (streq(name, "org.freedesktop.DBus.Local")) - return -EINVAL; - - if (streq(name, "org.freedesktop.DBus")) - return sd_bus_get_owner_creds(bus, mask, creds); if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; - return bus_get_name_creds_dbus1(bus, name, mask, creds); -} - -static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *c = NULL; - pid_t pid = 0; - bool do_label; - int r; - - assert(bus); + if (!bus->is_local) + mask &= ~SD_BUS_CREDS_AUGMENT; do_label = bus->label && (mask & SD_BUS_CREDS_SELINUX_CONTEXT); + do_groups = bus->n_groups != (size_t) -1 && (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS); /* Avoid allocating anything if we have no chance of returning useful data */ - if (!bus->ucred_valid && !do_label) + if (!bus->ucred_valid && !do_label && !do_groups) return -ENODATA; c = bus_creds_new(); @@ -571,17 +772,17 @@ static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds ** return -ENOMEM; if (bus->ucred_valid) { - if (bus->ucred.pid > 0) { + if (pid_is_valid(bus->ucred.pid)) { pid = c->pid = bus->ucred.pid; c->mask |= SD_BUS_CREDS_PID & mask; } - if (bus->ucred.uid != UID_INVALID) { + if (uid_is_valid(bus->ucred.uid)) { c->euid = bus->ucred.uid; c->mask |= SD_BUS_CREDS_EUID & mask; } - if (bus->ucred.gid != GID_INVALID) { + if (gid_is_valid(bus->ucred.gid)) { c->egid = bus->ucred.gid; c->mask |= SD_BUS_CREDS_EGID & mask; } @@ -595,6 +796,16 @@ static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds ** c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; } + if (do_groups) { + c->supplementary_gids = newdup(gid_t, bus->groups, bus->n_groups); + if (!c->supplementary_gids) + return -ENOMEM; + + c->n_supplementary_gids = bus->n_groups; + + c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; + } + r = bus_creds_add_more(c, mask, pid, 0); if (r < 0) return r; @@ -604,36 +815,23 @@ static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds ** return 0; } -_public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { - assert_return(bus, -EINVAL); - assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -EOPNOTSUPP); - assert_return(ret, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; - - if (!bus->is_local) - mask &= ~SD_BUS_CREDS_AUGMENT; - - return bus_get_owner_creds_dbus1(bus, mask, ret); -} - -#define internal_match(bus, m) \ - ((bus)->hello_flags & KDBUS_HELLO_MONITOR \ +#define append_eavesdrop(bus, m) \ + ((bus)->is_monitor \ ? (isempty(m) ? "eavesdrop='true'" : strjoina((m), ",eavesdrop='true'")) \ : (m)) -static int bus_add_match_internal_dbus1( +int bus_add_match_internal( sd_bus *bus, const char *match) { const char *e; assert(bus); - assert(match); - e = internal_match(bus, match); + if (!bus->bus_client) + return -EINVAL; + + e = append_eavesdrop(bus, match); return sd_bus_call_method( bus, @@ -646,22 +844,36 @@ static int bus_add_match_internal_dbus1( "s", e); } - -int bus_add_match_internal( +int bus_add_match_internal_async( sd_bus *bus, + sd_bus_slot **ret_slot, const char *match, - struct bus_match_component *components, - unsigned n_components) { + sd_bus_message_handler_t callback, + void *userdata) { + + const char *e; assert(bus); if (!bus->bus_client) return -EINVAL; - return bus_add_match_internal_dbus1(bus, match); + e = append_eavesdrop(bus, match); + + return sd_bus_call_method_async( + bus, + ret_slot, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "AddMatch", + callback, + userdata, + "s", + e); } -static int bus_remove_match_internal_dbus1( +int bus_remove_match_internal( sd_bus *bus, const char *match) { @@ -670,10 +882,16 @@ static int bus_remove_match_internal_dbus1( assert(bus); assert(match); - e = internal_match(bus, match); + if (!bus->bus_client) + return -EINVAL; - return sd_bus_call_method( + e = append_eavesdrop(bus, match); + + /* Fire and forget */ + + return sd_bus_call_method_async( bus, + NULL, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", @@ -684,24 +902,13 @@ static int bus_remove_match_internal_dbus1( e); } -int bus_remove_match_internal( - sd_bus *bus, - const char *match) { - - assert(bus); - - if (!bus->bus_client) - return -EINVAL; - - return bus_remove_match_internal_dbus1(bus, match); -} - _public_ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL; const char *mid; int r; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(name, -EINVAL); assert_return(machine, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); diff --git a/src/libsystemd/sd-bus/bus-control.h b/src/libsystemd/sd-bus/bus-control.h index c9d434c607..3d9acebaf6 100644 --- a/src/libsystemd/sd-bus/bus-control.h +++ b/src/libsystemd/sd-bus/bus-control.h @@ -22,7 +22,7 @@ #include "sd-bus.h" -#include "bus-match.h" +int bus_add_match_internal(sd_bus *bus, const char *match); +int bus_add_match_internal_async(sd_bus *bus, sd_bus_slot **ret, const char *match, sd_bus_message_handler_t callback, void *userdata); -int bus_add_match_internal(sd_bus *bus, const char *match, struct bus_match_component *components, unsigned n_components); int bus_remove_match_internal(sd_bus *bus, const char *match); diff --git a/src/libsystemd/sd-bus/bus-convenience.c b/src/libsystemd/sd-bus/bus-convenience.c index 9d3b596429..8da6640ca0 100644 --- a/src/libsystemd/sd-bus/bus-convenience.c +++ b/src/libsystemd/sd-bus/bus-convenience.c @@ -36,6 +36,7 @@ _public_ int sd_bus_emit_signal( int r; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(!bus_pid_changed(bus), -ECHILD); if (!BUS_IS_OPEN(bus->state)) @@ -73,6 +74,7 @@ _public_ int sd_bus_call_method_async( int r; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(!bus_pid_changed(bus), -ECHILD); if (!BUS_IS_OPEN(bus->state)) @@ -615,3 +617,71 @@ _public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) return 0; } + +#define make_expression(sender, path, interface, member) \ + strjoina( \ + "type='signal'", \ + sender ? ",sender='" : "", \ + sender ?: "", \ + sender ? "'" : "", \ + path ? ",path='" : "", \ + path ?: "", \ + path ? "'" : "", \ + interface ? ",interface='" : "", \ + interface ?: "", \ + interface ? "'" : "", \ + member ? ",member='" : "", \ + member ?: "", \ + member ? "'" : "" \ + ) + +_public_ int sd_bus_match_signal( + sd_bus *bus, + sd_bus_slot **ret, + const char *sender, + const char *path, + const char *interface, + const char *member, + sd_bus_message_handler_t callback, + void *userdata) { + + const char *expression; + + assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); + assert_return(!bus_pid_changed(bus), -ECHILD); + assert_return(!sender || service_name_is_valid(sender), -EINVAL); + assert_return(!path || object_path_is_valid(path), -EINVAL); + assert_return(!interface || interface_name_is_valid(interface), -EINVAL); + assert_return(!member || member_name_is_valid(member), -EINVAL); + + expression = make_expression(sender, path, interface, member); + + return sd_bus_add_match(bus, ret, expression, callback, userdata); +} + +_public_ int sd_bus_match_signal_async( + sd_bus *bus, + sd_bus_slot **ret, + const char *sender, + const char *path, + const char *interface, + const char *member, + sd_bus_message_handler_t callback, + sd_bus_message_handler_t install_callback, + void *userdata) { + + const char *expression; + + assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); + assert_return(!bus_pid_changed(bus), -ECHILD); + assert_return(!sender || service_name_is_valid(sender), -EINVAL); + assert_return(!path || object_path_is_valid(path), -EINVAL); + assert_return(!interface || interface_name_is_valid(interface), -EINVAL); + assert_return(!member || member_name_is_valid(member), -EINVAL); + + expression = make_expression(sender, path, interface, member); + + return sd_bus_add_match_async(bus, ret, expression, callback, install_callback, userdata); +} diff --git a/src/libsystemd/sd-bus/bus-gvariant.c b/src/libsystemd/sd-bus/bus-gvariant.c index 6a990a02c0..e6ab984d1f 100644 --- a/src/libsystemd/sd-bus/bus-gvariant.c +++ b/src/libsystemd/sd-bus/bus-gvariant.c @@ -18,6 +18,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> +#include <string.h> + +#include "sd-bus.h" + #include "bus-gvariant.h" #include "bus-signature.h" #include "bus-type.h" diff --git a/src/libsystemd/sd-bus/bus-internal.c b/src/libsystemd/sd-bus/bus-internal.c index 3c381b0ffe..05a022fbf3 100644 --- a/src/libsystemd/sd-bus/bus-internal.c +++ b/src/libsystemd/sd-bus/bus-internal.c @@ -362,13 +362,18 @@ int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error) { } else return r; - log_debug("Failed to process message [type=%s sender=%s path=%s interface=%s member=%s signature=%s]: %s", + log_debug("Failed to process message type=%s sender=%s destination=%s path=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " signature=%s error-name=%s error-message=%s: %s", bus_message_type_to_string(m->header->type), - strna(m->sender), - strna(m->path), - strna(m->interface), - strna(m->member), + strna(sd_bus_message_get_sender(m)), + strna(sd_bus_message_get_destination(m)), + strna(sd_bus_message_get_path(m)), + strna(sd_bus_message_get_interface(m)), + strna(sd_bus_message_get_member(m)), + BUS_MESSAGE_COOKIE(m), + m->reply_cookie, strna(m->root_container.signature), + strna(m->error.name), + strna(m->error.message), bus_error_message(error, r)); return 1; diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h index 378c408ea3..1b55cdafed 100644 --- a/src/libsystemd/sd-bus/bus-internal.h +++ b/src/libsystemd/sd-bus/bus-internal.h @@ -38,7 +38,7 @@ struct reply_callback { sd_bus_message_handler_t callback; - usec_t timeout; + usec_t timeout_usec; /* this is a relative timeout until we reach the BUS_HELLO state, and an absolute one right after */ uint64_t cookie; unsigned prioq_idx; }; @@ -53,6 +53,9 @@ struct filter_callback { struct match_callback { sd_bus_message_handler_t callback; + sd_bus_message_handler_t install_callback; + + sd_bus_slot *install_slot; /* The AddMatch() call */ unsigned last_iteration; @@ -157,12 +160,14 @@ struct sd_bus_slot { enum bus_state { BUS_UNSET, - BUS_OPENING, - BUS_AUTHENTICATING, - BUS_HELLO, + BUS_WATCH_BIND, /* waiting for the socket to appear via inotify */ + BUS_OPENING, /* the kernel's connect() is still not ready */ + BUS_AUTHENTICATING, /* we are currently in the "SASL" authorization phase of dbus */ + BUS_HELLO, /* we are waiting for the Hello() response */ BUS_RUNNING, BUS_CLOSING, - BUS_CLOSED + BUS_CLOSED, + _BUS_STATE_MAX, }; static inline bool BUS_IS_OPEN(enum bus_state state) { @@ -188,6 +193,7 @@ struct sd_bus { enum bus_state state; int input_fd, output_fd; + int inotify_fd; int message_version; int message_endian; @@ -210,6 +216,11 @@ struct sd_bus { bool exited:1; bool exit_triggered:1; bool is_local:1; + bool watch_bind:1; + bool is_monitor:1; + bool accept_fd:1; + bool attach_timestamp:1; + bool connected_signal:1; int use_memfd; @@ -261,6 +272,8 @@ struct sd_bus { struct ucred ucred; char *label; + gid_t *groups; + size_t n_groups; uint64_t creds_mask; @@ -284,13 +297,11 @@ struct sd_bus { pid_t original_pid; - uint64_t hello_flags; - uint64_t attach_flags; - sd_event_source *input_io_event_source; sd_event_source *output_io_event_source; sd_event_source *time_event_source; sd_event_source *quit_event_source; + sd_event_source *inotify_event_source; sd_event *event; int event_priority; @@ -305,11 +316,15 @@ struct sd_bus { char *cgroup_root; char *description; + char *patch_sender; sd_bus_track *track_queue; LIST_HEAD(sd_bus_slot, slots); LIST_HEAD(sd_bus_track, tracks); + + int *inotify_watches; + size_t n_inotify_watches; }; /* For method calls we time-out at 25s, like in the D-Bus reference implementation */ @@ -353,6 +368,8 @@ const char *bus_message_type_to_string(uint8_t u) _pure_; #define error_name_is_valid interface_name_is_valid +sd_bus *bus_resolve(sd_bus *bus); + int bus_ensure_running(sd_bus *bus); int bus_start_running(sd_bus *bus); int bus_next_address(sd_bus *bus); @@ -365,6 +382,12 @@ bool bus_pid_changed(sd_bus *bus); char *bus_address_escape(const char *v); +int bus_attach_io_events(sd_bus *b); +int bus_attach_inotify_event(sd_bus *b); + +void bus_close_inotify_fd(sd_bus *b); +void bus_close_io_fds(sd_bus *b); + #define OBJECT_PATH_FOREACH_PREFIX(prefix, path) \ for (char *_slash = ({ strcpy((prefix), (path)); streq((prefix), "/") ? NULL : strrchr((prefix), '/'); }) ; \ _slash && !(_slash[(_slash) == (prefix)] = 0); \ @@ -381,8 +404,6 @@ int bus_set_address_user(sd_bus *bus); int bus_set_address_system_remote(sd_bus *b, const char *host); int bus_set_address_system_machine(sd_bus *b, const char *machine); -int bus_remove_match_by_string(sd_bus *bus, const char *match, sd_bus_message_handler_t callback, void *userdata); - int bus_get_root_path(sd_bus *bus); int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error); @@ -393,64 +414,6 @@ int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error); return sd_bus_error_set_errno(error, r); \ } while (false) -/** - * enum kdbus_attach_flags - flags for metadata attachments - * @KDBUS_ATTACH_TIMESTAMP: Timestamp - * @KDBUS_ATTACH_CREDS: Credentials - * @KDBUS_ATTACH_PIDS: PIDs - * @KDBUS_ATTACH_AUXGROUPS: Auxiliary groups - * @KDBUS_ATTACH_NAMES: Well-known names - * @KDBUS_ATTACH_TID_COMM: The "comm" process identifier of the TID - * @KDBUS_ATTACH_PID_COMM: The "comm" process identifier of the PID - * @KDBUS_ATTACH_EXE: The path of the executable - * @KDBUS_ATTACH_CMDLINE: The process command line - * @KDBUS_ATTACH_CGROUP: The croup membership - * @KDBUS_ATTACH_CAPS: The process capabilities - * @KDBUS_ATTACH_SECLABEL: The security label - * @KDBUS_ATTACH_AUDIT: The audit IDs - * @KDBUS_ATTACH_CONN_DESCRIPTION: The human-readable connection name - * @_KDBUS_ATTACH_ALL: All of the above - * @_KDBUS_ATTACH_ANY: Wildcard match to enable any kind of - * metatdata. - */ -enum kdbus_attach_flags { - KDBUS_ATTACH_TIMESTAMP = 1ULL << 0, - KDBUS_ATTACH_CREDS = 1ULL << 1, - KDBUS_ATTACH_PIDS = 1ULL << 2, - KDBUS_ATTACH_AUXGROUPS = 1ULL << 3, - KDBUS_ATTACH_NAMES = 1ULL << 4, - KDBUS_ATTACH_TID_COMM = 1ULL << 5, - KDBUS_ATTACH_PID_COMM = 1ULL << 6, - KDBUS_ATTACH_EXE = 1ULL << 7, - KDBUS_ATTACH_CMDLINE = 1ULL << 8, - KDBUS_ATTACH_CGROUP = 1ULL << 9, - KDBUS_ATTACH_CAPS = 1ULL << 10, - KDBUS_ATTACH_SECLABEL = 1ULL << 11, - KDBUS_ATTACH_AUDIT = 1ULL << 12, - KDBUS_ATTACH_CONN_DESCRIPTION = 1ULL << 13, - _KDBUS_ATTACH_ALL = (1ULL << 14) - 1, - _KDBUS_ATTACH_ANY = ~0ULL -}; +void bus_enter_closing(sd_bus *bus); -/** - * enum kdbus_hello_flags - flags for struct kdbus_cmd_hello - * @KDBUS_HELLO_ACCEPT_FD: The connection allows the reception of - * any passed file descriptors - * @KDBUS_HELLO_ACTIVATOR: Special-purpose connection which registers - * a well-know name for a process to be started - * when traffic arrives - * @KDBUS_HELLO_POLICY_HOLDER: Special-purpose connection which registers - * policy entries for a name. The provided name - * is not activated and not registered with the - * name database, it only allows unprivileged - * connections to acquire a name, talk or discover - * a service - * @KDBUS_HELLO_MONITOR: Special-purpose connection to monitor - * bus traffic - */ -enum kdbus_hello_flags { - KDBUS_HELLO_ACCEPT_FD = 1ULL << 0, - KDBUS_HELLO_ACTIVATOR = 1ULL << 1, - KDBUS_HELLO_POLICY_HOLDER = 1ULL << 2, - KDBUS_HELLO_MONITOR = 1ULL << 3, -}; +void bus_set_state(sd_bus *bus, enum bus_state state); diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c index c6179b4d95..b27b9d7d86 100644 --- a/src/libsystemd/sd-bus/bus-kernel.c +++ b/src/libsystemd/sd-bus/bus-kernel.c @@ -66,49 +66,3 @@ void bus_flush_memfd(sd_bus *b) { for (i = 0; i < b->n_memfd_cache; i++) close_and_munmap(b->memfd_cache[i].fd, b->memfd_cache[i].address, b->memfd_cache[i].mapped); } - -uint64_t attach_flags_to_kdbus(uint64_t mask) { - uint64_t m = 0; - - if (mask & (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)) - m |= KDBUS_ATTACH_CREDS; - - if (mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID|SD_BUS_CREDS_PPID)) - m |= KDBUS_ATTACH_PIDS; - - if (mask & SD_BUS_CREDS_COMM) - m |= KDBUS_ATTACH_PID_COMM; - - if (mask & SD_BUS_CREDS_TID_COMM) - m |= KDBUS_ATTACH_TID_COMM; - - if (mask & SD_BUS_CREDS_EXE) - m |= KDBUS_ATTACH_EXE; - - if (mask & SD_BUS_CREDS_CMDLINE) - m |= KDBUS_ATTACH_CMDLINE; - - if (mask & (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)) - m |= KDBUS_ATTACH_CGROUP; - - if (mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS)) - m |= KDBUS_ATTACH_CAPS; - - if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) - m |= KDBUS_ATTACH_SECLABEL; - - if (mask & (SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID)) - m |= KDBUS_ATTACH_AUDIT; - - if (mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) - m |= KDBUS_ATTACH_NAMES; - - if (mask & SD_BUS_CREDS_DESCRIPTION) - m |= KDBUS_ATTACH_CONN_DESCRIPTION; - - if (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) - m |= KDBUS_ATTACH_AUXGROUPS; - - return m; -} diff --git a/src/libsystemd/sd-bus/bus-kernel.h b/src/libsystemd/sd-bus/bus-kernel.h index d9f80935fe..fa78e5c80d 100644 --- a/src/libsystemd/sd-bus/bus-kernel.h +++ b/src/libsystemd/sd-bus/bus-kernel.h @@ -41,5 +41,3 @@ struct memfd_cache { void close_and_munmap(int fd, void *address, size_t size); void bus_flush_memfd(sd_bus *bus); - -uint64_t attach_flags_to_kdbus(uint64_t sd_bus_flags); diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c index 219cff1f6e..95a87da08b 100644 --- a/src/libsystemd/sd-bus/bus-message.c +++ b/src/libsystemd/sd-bus/bus-message.c @@ -127,7 +127,6 @@ static void message_free(sd_bus_message *m) { if (m->iovec != m->iovec_fixed) free(m->iovec); - m->destination_ptr = mfree(m->destination_ptr); message_reset_containers(m); free(m->root_container.signature); free(m->root_container.offsets); @@ -1321,7 +1320,9 @@ static void *message_extend_body( m->n_body_parts <= 0 || m->body_end->sealed || (padding != ALIGN_TO(m->body_end->size, align) - m->body_end->size) || - (force_inline && m->body_end->size > MEMFD_MIN_SIZE); /* if this must be an inlined extension, let's create a new part if the previous part is large enough to be inlined */ + (force_inline && m->body_end->size > MEMFD_MIN_SIZE); + /* If this must be an inlined extension, let's create a new part if + * the previous part is large enough to be inlined. */ if (add_new_part) { if (padding > 0) { @@ -1368,7 +1369,7 @@ static void *message_extend_body( } } else /* Return something that is not NULL and is aligned */ - p = (uint8_t *) NULL + align; + p = (uint8_t*) align; m->body_size = end_body; message_extend_containers(m, added); @@ -4779,7 +4780,7 @@ _public_ int sd_bus_message_read_array( if (sz == 0) /* Zero length array, let's return some aligned * pointer that is not NULL */ - p = (uint8_t*) NULL + align; + p = (uint8_t*) align; else { r = message_peek_body(m, &m->rindex, align, sz, &p); if (r < 0) @@ -5488,6 +5489,15 @@ _public_ int sd_bus_message_set_destination(sd_bus_message *m, const char *desti return message_append_field_string(m, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, destination, &m->destination); } +_public_ int sd_bus_message_set_sender(sd_bus_message *m, const char *sender) { + assert_return(m, -EINVAL); + assert_return(sender, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(!m->sender, -EEXIST); + + return message_append_field_string(m, BUS_MESSAGE_HEADER_SENDER, SD_BUS_TYPE_STRING, sender, &m->sender); +} + int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz) { size_t total; void *p, *e; diff --git a/src/libsystemd/sd-bus/bus-message.h b/src/libsystemd/sd-bus/bus-message.h index 1e4b20926d..88998700d6 100644 --- a/src/libsystemd/sd-bus/bus-message.h +++ b/src/libsystemd/sd-bus/bus-message.h @@ -136,10 +136,6 @@ struct sd_bus_message { usec_t timeout; - char sender_buffer[3 + DECIMAL_STR_MAX(uint64_t) + 1]; - char destination_buffer[3 + DECIMAL_STR_MAX(uint64_t) + 1]; - char *destination_ptr; - size_t header_offsets[_BUS_MESSAGE_HEADER_MAX]; unsigned n_header_offsets; }; diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c index 121197bbcb..6e00255b20 100644 --- a/src/libsystemd/sd-bus/bus-objects.c +++ b/src/libsystemd/sd-bus/bus-objects.c @@ -1369,7 +1369,7 @@ int bus_process_object(sd_bus *bus, sd_bus_message *m) { assert(bus); assert(m); - if (bus->hello_flags & KDBUS_HELLO_MONITOR) + if (bus->is_monitor) return 0; if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL) @@ -1547,6 +1547,7 @@ static int bus_add_object( int r; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(object_path_is_valid(path), -EINVAL); assert_return(callback, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -1650,6 +1651,7 @@ static int add_object_vtable_internal( int r; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(object_path_is_valid(path), -EINVAL); assert_return(interface_name_is_valid(interface), -EINVAL); assert_return(vtable, -EINVAL); @@ -1859,6 +1861,7 @@ _public_ int sd_bus_add_node_enumerator( int r; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(object_path_is_valid(path), -EINVAL); assert_return(callback, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -2110,6 +2113,7 @@ _public_ int sd_bus_emit_properties_changed_strv( int r; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(object_path_is_valid(path), -EINVAL); assert_return(interface_name_is_valid(interface), -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -2156,6 +2160,7 @@ _public_ int sd_bus_emit_properties_changed( char **names; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(object_path_is_valid(path), -EINVAL); assert_return(interface_name_is_valid(interface), -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -2340,6 +2345,7 @@ _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) { */ assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(object_path_is_valid(path), -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -2510,6 +2516,7 @@ _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) { */ assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(object_path_is_valid(path), -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -2663,6 +2670,7 @@ _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, ch int r; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(object_path_is_valid(path), -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -2729,6 +2737,7 @@ _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const c char **interfaces; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(object_path_is_valid(path), -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -2746,6 +2755,7 @@ _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, int r; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(object_path_is_valid(path), -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -2780,6 +2790,7 @@ _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char **interfaces; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(object_path_is_valid(path), -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -2797,6 +2808,7 @@ _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const ch int r; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(object_path_is_valid(path), -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); diff --git a/src/libsystemd/sd-bus/bus-signature.c b/src/libsystemd/sd-bus/bus-signature.c index d16461f4ae..f3cd9bd0fa 100644 --- a/src/libsystemd/sd-bus/bus-signature.c +++ b/src/libsystemd/sd-bus/bus-signature.c @@ -20,6 +20,8 @@ #include <util.h> +#include "sd-bus.h" + #include "bus-signature.h" #include "bus-type.h" diff --git a/src/libsystemd/sd-bus/bus-slot.c b/src/libsystemd/sd-bus/bus-slot.c index 756761c3ed..9a56371715 100644 --- a/src/libsystemd/sd-bus/bus-slot.c +++ b/src/libsystemd/sd-bus/bus-slot.c @@ -81,7 +81,7 @@ void bus_slot_disconnect(sd_bus_slot *slot) { if (slot->reply_callback.cookie != 0) ordered_hashmap_remove(slot->bus->reply_callbacks, &slot->reply_callback.cookie); - if (slot->reply_callback.timeout != 0) + if (slot->reply_callback.timeout_usec != 0) prioq_remove(slot->bus->reply_callbacks_prioq, &slot->reply_callback, &slot->reply_callback.prioq_idx); break; @@ -94,12 +94,17 @@ void bus_slot_disconnect(sd_bus_slot *slot) { case BUS_MATCH_CALLBACK: if (slot->match_added) - bus_remove_match_internal(slot->bus, slot->match_callback.match_string); + (void) bus_remove_match_internal(slot->bus, slot->match_callback.match_string); + + if (slot->match_callback.install_slot) { + bus_slot_disconnect(slot->match_callback.install_slot); + slot->match_callback.install_slot = sd_bus_slot_unref(slot->match_callback.install_slot); + } slot->bus->match_callbacks_modified = true; bus_match_remove(&slot->bus->match_callbacks, &slot->match_callback); - free(slot->match_callback.match_string); + slot->match_callback.match_string = mfree(slot->match_callback.match_string); break; @@ -174,7 +179,7 @@ void bus_slot_disconnect(sd_bus_slot *slot) { } } - free(slot->node_vtable.interface); + slot->node_vtable.interface = mfree(slot->node_vtable.interface); if (slot->node_vtable.node) { LIST_REMOVE(vtables, slot->node_vtable.node->vtables, &slot->node_vtable); diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c index 07a9c8affd..2fe86b61c4 100644 --- a/src/libsystemd/sd-bus/bus-socket.c +++ b/src/libsystemd/sd-bus/bus-socket.c @@ -32,9 +32,13 @@ #include "bus-socket.h" #include "fd-util.h" #include "format-util.h" +#include "fs-util.h" #include "hexdecoct.h" +#include "io-util.h" #include "macro.h" #include "missing.h" +#include "path-util.h" +#include "process-util.h" #include "selinux-util.h" #include "signal-util.h" #include "stdio-util.h" @@ -188,7 +192,7 @@ static int bus_socket_auth_verify_client(sd_bus *b) { if (!e) return 0; - if (b->hello_flags & KDBUS_HELLO_ACCEPT_FD) { + if (b->accept_fd) { f = memmem(e + 2, b->rbuffer_size - (e - (char*) b->rbuffer) - 2, "\r\n", 2); if (!f) return 0; @@ -475,7 +479,7 @@ static int bus_socket_auth_verify_server(sd_bus *b) { r = bus_socket_auth_write_ok(b); } } else if (line_equals(line, l, "NEGOTIATE_UNIX_FD")) { - if (b->auth == _BUS_AUTH_INVALID || !(b->hello_flags & KDBUS_HELLO_ACCEPT_FD)) + if (b->auth == _BUS_AUTH_INVALID || !b->accept_fd) r = bus_socket_auth_write(b, "ERROR\r\n"); else { b->can_fds = true; @@ -592,8 +596,8 @@ void bus_socket_setup(sd_bus *b) { assert(b); /* Increase the buffers to 8 MB */ - fd_inc_rcvbuf(b->input_fd, SNDBUF_SIZE); - fd_inc_sndbuf(b->output_fd, SNDBUF_SIZE); + (void) fd_inc_rcvbuf(b->input_fd, SNDBUF_SIZE); + (void) fd_inc_sndbuf(b->output_fd, SNDBUF_SIZE); b->message_version = 1; b->message_endian = 0; @@ -603,16 +607,24 @@ static void bus_get_peercred(sd_bus *b) { int r; assert(b); + assert(!b->ucred_valid); + assert(!b->label); + assert(b->n_groups == (size_t) -1); /* Get the peer for socketpair() sockets */ b->ucred_valid = getpeercred(b->input_fd, &b->ucred) >= 0; /* Get the SELinux context of the peer */ - if (mac_selinux_use()) { - r = getpeersec(b->input_fd, &b->label); - if (r < 0 && r != -EOPNOTSUPP) - log_debug_errno(r, "Failed to determine peer security context: %m"); - } + r = getpeersec(b->input_fd, &b->label); + if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT)) + log_debug_errno(r, "Failed to determine peer security context: %m"); + + /* Get the list of auxiliary groups of the peer */ + r = getpeergroups(b->input_fd, &b->groups); + if (r >= 0) + b->n_groups = (size_t) r; + else if (!IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT)) + log_debug_errno(r, "Failed to determine peer's group list: %m"); } static int bus_socket_start_auth_client(sd_bus *b) { @@ -641,7 +653,7 @@ static int bus_socket_start_auth_client(sd_bus *b) { if (!b->auth_buffer) return -ENOMEM; - if (b->hello_flags & KDBUS_HELLO_ACCEPT_FD) + if (b->accept_fd) auth_suffix = "\r\nNEGOTIATE_UNIX_FD\r\nBEGIN\r\n"; else auth_suffix = "\r\nBEGIN\r\n"; @@ -661,15 +673,15 @@ int bus_socket_start_auth(sd_bus *b) { bus_get_peercred(b); - b->state = BUS_AUTHENTICATING; + bus_set_state(b, BUS_AUTHENTICATING); b->auth_timeout = now(CLOCK_MONOTONIC) + BUS_AUTH_TIMEOUT; if (sd_is_socket(b->input_fd, AF_UNIX, 0, 0) <= 0) - b->hello_flags &= ~KDBUS_HELLO_ACCEPT_FD; + b->accept_fd = false; if (b->output_fd != b->input_fd) if (sd_is_socket(b->output_fd, AF_UNIX, 0, 0) <= 0) - b->hello_flags &= ~KDBUS_HELLO_ACCEPT_FD; + b->accept_fd = false; if (b->is_server) return bus_socket_read_auth(b); @@ -677,30 +689,249 @@ int bus_socket_start_auth(sd_bus *b) { return bus_socket_start_auth_client(b); } +static int bus_socket_inotify_setup(sd_bus *b) { + _cleanup_free_ int *new_watches = NULL; + _cleanup_free_ char *absolute = NULL; + size_t n_allocated = 0, n = 0, done = 0, i; + unsigned max_follow = 32; + const char *p; + int wd, r; + + assert(b); + assert(b->watch_bind); + assert(b->sockaddr.sa.sa_family == AF_UNIX); + assert(b->sockaddr.un.sun_path[0] != 0); + + /* Sets up an inotify fd in case watch_bind is enabled: wait until the configured AF_UNIX file system socket + * appears before connecting to it. The implemented is pretty simplistic: we just subscribe to relevant changes + * to all prefix components of the path, and every time we get an event for that we try to reconnect again, + * without actually caring what precisely the event we got told us. If we still can't connect we re-subscribe + * to all relevant changes of anything in the path, so that our watches include any possibly newly created path + * components. */ + + if (b->inotify_fd < 0) { + b->inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (b->inotify_fd < 0) + return -errno; + } + + /* Make sure the path is NUL terminated */ + p = strndupa(b->sockaddr.un.sun_path, sizeof(b->sockaddr.un.sun_path)); + + /* Make sure the path is absolute */ + r = path_make_absolute_cwd(p, &absolute); + if (r < 0) + goto fail; + + /* Watch all parent directories, and don't mind any prefix that doesn't exist yet. For the innermost directory + * that exists we want to know when files are created or moved into it. For all parents of it we just care if + * they are removed or renamed. */ + + if (!GREEDY_REALLOC(new_watches, n_allocated, n + 1)) { + r = -ENOMEM; + goto fail; + } + + /* Start with the top-level directory, which is a bit simpler than the rest, since it can't be a symlink, and + * always exists */ + wd = inotify_add_watch(b->inotify_fd, "/", IN_CREATE|IN_MOVED_TO); + if (wd < 0) { + r = log_debug_errno(errno, "Failed to add inotify watch on /: %m"); + goto fail; + } else + new_watches[n++] = wd; + + for (;;) { + _cleanup_free_ char *component = NULL, *prefix = NULL, *destination = NULL; + size_t n_slashes, n_component; + char *c = NULL; + + n_slashes = strspn(absolute + done, "/"); + n_component = n_slashes + strcspn(absolute + done + n_slashes, "/"); + + if (n_component == 0) /* The end */ + break; + + component = strndup(absolute + done, n_component); + if (!component) { + r = -ENOMEM; + goto fail; + } + + /* A trailing slash? That's a directory, and not a socket then */ + if (path_equal(component, "/")) { + r = -EISDIR; + goto fail; + } + + /* A single dot? Let's eat this up */ + if (path_equal(component, "/.")) { + done += n_component; + continue; + } + + prefix = strndup(absolute, done + n_component); + if (!prefix) { + r = -ENOMEM; + goto fail; + } + + if (!GREEDY_REALLOC(new_watches, n_allocated, n + 1)) { + r = -ENOMEM; + goto fail; + } + + wd = inotify_add_watch(b->inotify_fd, prefix, IN_DELETE_SELF|IN_MOVE_SELF|IN_ATTRIB|IN_CREATE|IN_MOVED_TO|IN_DONT_FOLLOW); + log_debug("Added inotify watch for %s on bus %s: %i", prefix, strna(b->description), wd); + + if (wd < 0) { + if (IN_SET(errno, ENOENT, ELOOP)) + break; /* This component doesn't exist yet, or the path contains a cyclic symlink right now */ + + r = log_debug_errno(errno, "Failed to add inotify watch on %s: %m", isempty(prefix) ? "/" : prefix); + goto fail; + } else + new_watches[n++] = wd; + + /* Check if this is possibly a symlink. If so, let's follow it and watch it too. */ + r = readlink_malloc(prefix, &destination); + if (r == -EINVAL) { /* not a symlink */ + done += n_component; + continue; + } + if (r < 0) + goto fail; + + if (isempty(destination)) { /* Empty symlink target? Yuck! */ + r = -EINVAL; + goto fail; + } + + if (max_follow <= 0) { /* Let's make sure we don't follow symlinks forever */ + r = -ELOOP; + goto fail; + } + + if (path_is_absolute(destination)) { + /* For absolute symlinks we build the new path and start anew */ + c = strjoin(destination, absolute + done + n_component); + done = 0; + } else { + _cleanup_free_ char *t = NULL; + + /* For relative symlinks we replace the last component, and try again */ + t = strndup(absolute, done); + if (!t) + return -ENOMEM; + + c = strjoin(t, "/", destination, absolute + done + n_component); + } + if (!c) { + r = -ENOMEM; + goto fail; + } + + free(absolute); + absolute = c; + + max_follow--; + } + + /* And now, let's remove all watches from the previous iteration we don't need anymore */ + for (i = 0; i < b->n_inotify_watches; i++) { + bool found = false; + size_t j; + + for (j = 0; j < n; j++) + if (new_watches[j] == b->inotify_watches[i]) { + found = true; + break; + } + + if (found) + continue; + + (void) inotify_rm_watch(b->inotify_fd, b->inotify_watches[i]); + } + + free_and_replace(b->inotify_watches, new_watches); + b->n_inotify_watches = n; + + return 0; + +fail: + bus_close_inotify_fd(b); + return r; +} + int bus_socket_connect(sd_bus *b) { + bool inotify_done = false; int r; assert(b); - assert(b->input_fd < 0); - assert(b->output_fd < 0); - assert(b->sockaddr.sa.sa_family != AF_UNSPEC); - b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (b->input_fd < 0) - return -errno; + for (;;) { + assert(b->input_fd < 0); + assert(b->output_fd < 0); + assert(b->sockaddr.sa.sa_family != AF_UNSPEC); - b->output_fd = b->input_fd; + b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (b->input_fd < 0) + return -errno; - bus_socket_setup(b); + b->output_fd = b->input_fd; + bus_socket_setup(b); - r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size); - if (r < 0) { - if (errno == EINPROGRESS) - return 1; + if (connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size) < 0) { + if (errno == EINPROGRESS) { - return -errno; + /* If we have any inotify watches open, close them now, we don't need them anymore, as + * we have successfully initiated a connection */ + bus_close_inotify_fd(b); + + /* Note that very likely we are already in BUS_OPENING state here, as we enter it when + * we start parsing the address string. The only reason we set the state explicitly + * here, is to undo BUS_WATCH_BIND, in case we did the inotify magic. */ + bus_set_state(b, BUS_OPENING); + return 1; + } + + if (IN_SET(errno, ENOENT, ECONNREFUSED) && /* ENOENT → unix socket doesn't exist at all; ECONNREFUSED → unix socket stale */ + b->watch_bind && + b->sockaddr.sa.sa_family == AF_UNIX && + b->sockaddr.un.sun_path[0] != 0) { + + /* This connection attempt failed, let's release the socket for now, and start with a + * fresh one when reconnecting. */ + bus_close_io_fds(b); + + if (inotify_done) { + /* inotify set up already, don't do it again, just return now, and remember + * that we are waiting for inotify events now. */ + bus_set_state(b, BUS_WATCH_BIND); + return 1; + } + + /* This is a file system socket, and the inotify logic is enabled. Let's create the necessary inotify fd. */ + r = bus_socket_inotify_setup(b); + if (r < 0) + return r; + + /* Let's now try to connect a second time, because in theory there's otherwise a race + * here: the socket might have been created in the time between our first connect() and + * the time we set up the inotify logic. But let's remember that we set up inotify now, + * so that we don't do the connect() more than twice. */ + inotify_done = true; + + } else + return -errno; + } else + break; } + /* Yay, established, we don't need no inotify anymore! */ + bus_close_inotify_fd(b); + return bus_socket_start_auth(b); } @@ -717,29 +948,24 @@ int bus_socket_exec(sd_bus *b) { if (r < 0) return -errno; - pid = fork(); - if (pid < 0) { + r = safe_fork_full("(sd-busexec)", s+1, 1, FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, &pid); + if (r < 0) { safe_close_pair(s); - return -errno; + return r; } - if (pid == 0) { + if (r == 0) { /* Child */ - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - close_all_fds(s+1, 1); - assert_se(dup3(s[1], STDIN_FILENO, 0) == STDIN_FILENO); assert_se(dup3(s[1], STDOUT_FILENO, 0) == STDOUT_FILENO); if (!IN_SET(s[1], STDIN_FILENO, STDOUT_FILENO)) safe_close(s[1]); - fd_cloexec(STDIN_FILENO, false); - fd_cloexec(STDOUT_FILENO, false); - fd_nonblock(STDIN_FILENO, false); - fd_nonblock(STDOUT_FILENO, false); + (void) fd_cloexec(STDIN_FILENO, false); + (void) fd_cloexec(STDOUT_FILENO, false); + (void) fd_nonblock(STDIN_FILENO, false); + (void) fd_nonblock(STDOUT_FILENO, false); if (b->exec_argv) execvp(b->exec_path, b->exec_argv); @@ -1063,3 +1289,34 @@ int bus_socket_process_authenticating(sd_bus *b) { return bus_socket_read_auth(b); } + +int bus_socket_process_watch_bind(sd_bus *b) { + int r, q; + + assert(b); + assert(b->state == BUS_WATCH_BIND); + assert(b->inotify_fd >= 0); + + r = flush_fd(b->inotify_fd); + if (r <= 0) + return r; + + log_debug("Got inotify event on bus %s.", strna(b->description)); + + /* We flushed events out of the inotify fd. In that case, maybe the socket is valid now? Let's try to connect + * to it again */ + + r = bus_socket_connect(b); + if (r < 0) + return r; + + q = bus_attach_io_events(b); + if (q < 0) + return q; + + q = bus_attach_inotify_event(b); + if (q < 0) + return q; + + return r; +} diff --git a/src/libsystemd/sd-bus/bus-socket.h b/src/libsystemd/sd-bus/bus-socket.h index 915a283f5a..c180562f98 100644 --- a/src/libsystemd/sd-bus/bus-socket.h +++ b/src/libsystemd/sd-bus/bus-socket.h @@ -34,5 +34,6 @@ int bus_socket_read_message(sd_bus *bus); int bus_socket_process_opening(sd_bus *b); int bus_socket_process_authenticating(sd_bus *b); +int bus_socket_process_watch_bind(sd_bus *b); bool bus_socket_auth_needs_write(sd_bus *b); diff --git a/src/libsystemd/sd-bus/bus-track.c b/src/libsystemd/sd-bus/bus-track.c index ab22d6e4de..5482d39a01 100644 --- a/src/libsystemd/sd-bus/bus-track.c +++ b/src/libsystemd/sd-bus/bus-track.c @@ -48,25 +48,13 @@ struct sd_bus_track { LIST_FIELDS(sd_bus_track, tracks); }; -#define MATCH_PREFIX \ - "type='signal'," \ - "sender='org.freedesktop.DBus'," \ - "path='/org/freedesktop/DBus'," \ - "interface='org.freedesktop.DBus'," \ - "member='NameOwnerChanged'," \ - "arg0='" - -#define MATCH_SUFFIX \ - "'" - -#define MATCH_FOR_NAME(name) \ - ({ \ - char *_x; \ - size_t _l = strlen(name); \ - _x = alloca(STRLEN(MATCH_PREFIX)+_l+STRLEN(MATCH_SUFFIX)+1); \ - strcpy(stpcpy(stpcpy(_x, MATCH_PREFIX), name), MATCH_SUFFIX); \ - _x; \ - }) +#define MATCH_FOR_NAME(name) \ + strjoina("type='signal'," \ + "sender='org.freedesktop.DBus'," \ + "path='/org/freedesktop/DBus'," \ + "interface='org.freedesktop.DBus'," \ + "member='NameOwnerChanged'," \ + "arg0='", name, "'") static struct track_item* track_item_free(struct track_item *i) { @@ -148,6 +136,7 @@ _public_ int sd_bus_track_new( sd_bus_track *t; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(track, -EINVAL); if (!bus->bus_client) @@ -259,9 +248,7 @@ _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) { bus_track_remove_from_queue(track); /* don't dispatch this while we work in it */ - track->n_adding++; /* make sure we aren't dispatched while we synchronously add this match */ - r = sd_bus_add_match(track->bus, &n->slot, match, on_name_owner_changed, track); - track->n_adding--; + r = sd_bus_add_match_async(track->bus, &n->slot, match, on_name_owner_changed, NULL, track); if (r < 0) { bus_track_add_to_queue(track); return r; diff --git a/src/libsystemd/sd-bus/bus-type.c b/src/libsystemd/sd-bus/bus-type.c index fe486f441d..980b35d8ea 100644 --- a/src/libsystemd/sd-bus/bus-type.c +++ b/src/libsystemd/sd-bus/bus-type.c @@ -18,6 +18,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> + +#include "sd-bus.h" + #include "bus-type.h" bool bus_type_is_valid(char c) { diff --git a/src/libsystemd/sd-bus/bus-type.h b/src/libsystemd/sd-bus/bus-type.h index ae272b1e6a..834f09777a 100644 --- a/src/libsystemd/sd-bus/bus-type.h +++ b/src/libsystemd/sd-bus/bus-type.h @@ -22,8 +22,6 @@ #include <stdbool.h> -#include "sd-bus.h" - #include "macro.h" bool bus_type_is_valid(char c) _const_; diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index 5fb15a7aaf..7e7ebb27a7 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -50,6 +50,7 @@ #include "macro.h" #include "missing.h" #include "parse-util.h" +#include "process-util.h" #include "string-util.h" #include "strv.h" #include "util.h" @@ -57,7 +58,7 @@ #define log_debug_bus_message(m) \ do { \ sd_bus_message *_mm = (m); \ - log_debug("Got message type=%s sender=%s destination=%s object=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " error-name=%s error-message=%s", \ + log_debug("Got message type=%s sender=%s destination=%s path=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " signature=%s error-name=%s error-message=%s", \ bus_message_type_to_string(_mm->header->type), \ strna(sd_bus_message_get_sender(_mm)), \ strna(sd_bus_message_get_destination(_mm)), \ @@ -66,28 +67,97 @@ strna(sd_bus_message_get_member(_mm)), \ BUS_MESSAGE_COOKIE(_mm), \ _mm->reply_cookie, \ + strna(_mm->root_container.signature), \ strna(_mm->error.name), \ strna(_mm->error.message)); \ } while (false) static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec); -static int attach_io_events(sd_bus *b); -static void detach_io_events(sd_bus *b); +static void bus_detach_io_events(sd_bus *b); +static void bus_detach_inotify_event(sd_bus *b); static thread_local sd_bus *default_system_bus = NULL; static thread_local sd_bus *default_user_bus = NULL; static thread_local sd_bus *default_starter_bus = NULL; -static void bus_close_fds(sd_bus *b) { +static sd_bus **bus_choose_default(int (**bus_open)(sd_bus **)) { + const char *e; + + /* Let's try our best to reuse another cached connection. If + * the starter bus type is set, connect via our normal + * connection logic, ignoring $DBUS_STARTER_ADDRESS, so that + * we can share the connection with the user/system default + * bus. */ + + e = secure_getenv("DBUS_STARTER_BUS_TYPE"); + if (e) { + if (streq(e, "system")) { + if (bus_open) + *bus_open = sd_bus_open_system; + return &default_system_bus; + } else if (STR_IN_SET(e, "user", "session")) { + if (bus_open) + *bus_open = sd_bus_open_user; + return &default_user_bus; + } + } + + /* No type is specified, so we have not other option than to + * use the starter address if it is set. */ + e = secure_getenv("DBUS_STARTER_ADDRESS"); + if (e) { + if (bus_open) + *bus_open = sd_bus_open; + return &default_starter_bus; + } + + /* Finally, if nothing is set use the cached connection for + * the right scope */ + + if (cg_pid_get_owner_uid(0, NULL) >= 0) { + if (bus_open) + *bus_open = sd_bus_open_user; + return &default_user_bus; + } else { + if (bus_open) + *bus_open = sd_bus_open_system; + return &default_system_bus; + } +} + +sd_bus *bus_resolve(sd_bus *bus) { + switch ((uintptr_t) bus) { + case (uintptr_t) SD_BUS_DEFAULT: + return *(bus_choose_default(NULL)); + case (uintptr_t) SD_BUS_DEFAULT_USER: + return default_user_bus; + case (uintptr_t) SD_BUS_DEFAULT_SYSTEM: + return default_system_bus; + default: + return bus; + } +} + +void bus_close_io_fds(sd_bus *b) { assert(b); - detach_io_events(b); + bus_detach_io_events(b); if (b->input_fd != b->output_fd) safe_close(b->output_fd); b->output_fd = b->input_fd = safe_close(b->input_fd); } +void bus_close_inotify_fd(sd_bus *b) { + assert(b); + + bus_detach_inotify_event(b); + + b->inotify_fd = safe_close(b->inotify_fd); + b->inotify_watches = mfree(b->inotify_watches); + b->n_inotify_watches = 0; +} + static void bus_reset_queues(sd_bus *b) { assert(b); @@ -131,9 +201,11 @@ static void bus_free(sd_bus *b) { if (b->default_bus_ptr) *b->default_bus_ptr = NULL; - bus_close_fds(b); + bus_close_io_fds(b); + bus_close_inotify_fd(b); free(b->label); + free(b->groups); free(b->rbuffer); free(b->unique_name); free(b->auth_buffer); @@ -141,6 +213,7 @@ static void bus_free(sd_bus *b) { free(b->machine); free(b->cgroup_root); free(b->description); + free(b->patch_sender); free(b->exec_path); strv_free(b->exec_argv); @@ -180,11 +253,12 @@ _public_ int sd_bus_new(sd_bus **ret) { r->n_ref = REFCNT_INIT; r->input_fd = r->output_fd = -1; + r->inotify_fd = -1; r->message_version = 1; r->creds_mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME; - r->hello_flags |= KDBUS_HELLO_ACCEPT_FD; - r->attach_flags |= KDBUS_ATTACH_NAMES; + r->accept_fd = true; r->original_pid = getpid_cached(); + r->n_groups = (size_t) -1; assert_se(pthread_mutex_init(&r->memfd_cache_mutex, NULL) == 0); @@ -203,6 +277,7 @@ _public_ int sd_bus_set_address(sd_bus *bus, const char *address) { char *a; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(address, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -219,6 +294,7 @@ _public_ int sd_bus_set_address(sd_bus *bus, const char *address) { _public_ int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(input_fd >= 0, -EBADF); assert_return(output_fd >= 0, -EBADF); @@ -233,6 +309,7 @@ _public_ int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]) char *p, **a; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(path, -EINVAL); assert_return(!strv_isempty(argv), -EINVAL); @@ -259,7 +336,9 @@ _public_ int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]) _public_ int sd_bus_set_bus_client(sd_bus *bus, int b) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(bus->state == BUS_UNSET, -EPERM); + assert_return(!bus->patch_sender, -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); bus->bus_client = !!b; @@ -268,43 +347,40 @@ _public_ int sd_bus_set_bus_client(sd_bus *bus, int b) { _public_ int sd_bus_set_monitor(sd_bus *bus, int b) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); - SET_FLAG(bus->hello_flags, KDBUS_HELLO_MONITOR, b); + bus->is_monitor = b; return 0; } _public_ int sd_bus_negotiate_fds(sd_bus *bus, int b) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); - SET_FLAG(bus->hello_flags, KDBUS_HELLO_ACCEPT_FD, b); + bus->accept_fd = b; return 0; } _public_ int sd_bus_negotiate_timestamp(sd_bus *bus, int b) { - uint64_t new_flags; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(!IN_SET(bus->state, BUS_CLOSING, BUS_CLOSED), -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); - new_flags = bus->attach_flags; - SET_FLAG(new_flags, KDBUS_ATTACH_TIMESTAMP, b); - - if (bus->attach_flags == new_flags) - return 0; - - bus->attach_flags = new_flags; + /* This is not actually supported by any of our transports these days, but we do honour it for synthetic + * replies, and maybe one day classic D-Bus learns this too */ + bus->attach_timestamp = b; return 0; } _public_ int sd_bus_negotiate_creds(sd_bus *bus, int b, uint64_t mask) { - uint64_t new_flags; - assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(mask <= _SD_BUS_CREDS_ALL, -EINVAL); assert_return(!IN_SET(bus->state, BUS_CLOSING, BUS_CLOSED), -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -314,18 +390,12 @@ _public_ int sd_bus_negotiate_creds(sd_bus *bus, int b, uint64_t mask) { /* 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; - /* Make sure we don't lose the timestamp flag */ - new_flags = (bus->attach_flags & KDBUS_ATTACH_TIMESTAMP) | attach_flags_to_kdbus(bus->creds_mask); - if (bus->attach_flags == new_flags) - return 0; - - bus->attach_flags = new_flags; - return 0; } _public_ int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(b || sd_id128_equal(server_id, SD_ID128_NULL), -EINVAL); assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -337,6 +407,7 @@ _public_ int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id) { _public_ int sd_bus_set_anonymous(sd_bus *bus, int b) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -346,6 +417,7 @@ _public_ int sd_bus_set_anonymous(sd_bus *bus, int b) { _public_ int sd_bus_set_trusted(sd_bus *bus, int b) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -355,6 +427,7 @@ _public_ int sd_bus_set_trusted(sd_bus *bus, int b) { _public_ int sd_bus_set_description(sd_bus *bus, const char *description) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -363,6 +436,7 @@ _public_ int sd_bus_set_description(sd_bus *bus, const char *description) { _public_ int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(!bus_pid_changed(bus), -ECHILD); bus->allow_interactive_authorization = !!b; @@ -371,11 +445,115 @@ _public_ int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b) { _public_ int sd_bus_get_allow_interactive_authorization(sd_bus *bus) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(!bus_pid_changed(bus), -ECHILD); return bus->allow_interactive_authorization; } +_public_ int sd_bus_set_watch_bind(sd_bus *bus, int b) { + assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); + assert_return(bus->state == BUS_UNSET, -EPERM); + assert_return(!bus_pid_changed(bus), -ECHILD); + + bus->watch_bind = b; + return 0; +} + +_public_ int sd_bus_get_watch_bind(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->watch_bind; +} + +_public_ int sd_bus_set_connected_signal(sd_bus *bus, int b) { + assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); + assert_return(bus->state == BUS_UNSET, -EPERM); + assert_return(!bus_pid_changed(bus), -ECHILD); + + bus->connected_signal = b; + return 0; +} + +_public_ int sd_bus_get_connected_signal(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->connected_signal; +} + +static int synthesize_connected_signal(sd_bus *bus) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r; + + assert(bus); + + /* If enabled, synthesizes a local "Connected" signal mirroring the local "Disconnected" signal. This is called + * whenever we fully established a connection, i.e. after the authorization phase, and after receiving the + * Hello() reply. Or in other words, whenver we enter BUS_RUNNING state. + * + * This is useful so that clients can start doing stuff whenver the connection is fully established in a way + * that works independently from whether we connected to a full bus or just a direct connection. */ + + if (!bus->connected_signal) + return 0; + + r = sd_bus_message_new_signal( + bus, + &m, + "/org/freedesktop/DBus/Local", + "org.freedesktop.DBus.Local", + "Connected"); + if (r < 0) + return r; + + bus_message_set_sender_local(bus, m); + + r = bus_seal_synthetic_message(bus, m); + if (r < 0) + return r; + + r = bus_rqueue_make_room(bus); + if (r < 0) + return r; + + /* Insert at the very front */ + memmove(bus->rqueue + 1, bus->rqueue, sizeof(sd_bus_message*) * bus->rqueue_size); + bus->rqueue[0] = m; + m = NULL; + bus->rqueue_size++; + + return 0; +} + +void bus_set_state(sd_bus *bus, enum bus_state state) { + + static const char * const table[_BUS_STATE_MAX] = { + [BUS_UNSET] = "UNSET", + [BUS_WATCH_BIND] = "WATCH_BIND", + [BUS_OPENING] = "OPENING", + [BUS_AUTHENTICATING] = "AUTHENTICATING", + [BUS_HELLO] = "HELLO", + [BUS_RUNNING] = "RUNNING", + [BUS_CLOSING] = "CLOSING", + [BUS_CLOSED] = "CLOSED", + }; + + assert(bus); + assert(state < _BUS_STATE_MAX); + + if (state == bus->state) + return; + + log_debug("Bus %s: changing state %s → %s", strna(bus->description), table[bus->state], table[state]); + bus->state = state; +} + static int hello_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) { const char *s; sd_bus *bus; @@ -401,8 +579,13 @@ static int hello_callback(sd_bus_message *reply, void *userdata, sd_bus_error *e if (!bus->unique_name) return -ENOMEM; - if (bus->state == BUS_HELLO) - bus->state = BUS_RUNNING; + if (bus->state == BUS_HELLO) { + bus_set_state(bus, BUS_RUNNING); + + r = synthesize_connected_signal(bus); + if (r < 0) + return r; + } return 1; } @@ -430,14 +613,37 @@ static int bus_send_hello(sd_bus *bus) { } int bus_start_running(sd_bus *bus) { + struct reply_callback *c; + Iterator i; + usec_t n; + int r; + assert(bus); + assert(bus->state < BUS_HELLO); + + /* We start all method call timeouts when we enter BUS_HELLO or BUS_RUNNING mode. At this point let's convert + * all relative to absolute timestamps. Note that we do not reshuffle the reply callback priority queue since + * adding a fixed value to all entries should not alter the internal order. */ + + n = now(CLOCK_MONOTONIC); + ORDERED_HASHMAP_FOREACH(c, bus->reply_callbacks, i) { + if (c->timeout_usec == 0) + continue; + + c->timeout_usec = usec_add(n, c->timeout_usec); + } if (bus->bus_client) { - bus->state = BUS_HELLO; + bus_set_state(bus, BUS_HELLO); return 1; } - bus->state = BUS_RUNNING; + bus_set_state(bus, BUS_RUNNING); + + r = synthesize_connected_signal(bus); + if (r < 0) + return r; + return 1; } @@ -799,6 +1005,7 @@ static int parse_container_unix_address(sd_bus *b, const char **p, char **guid) b->nspid = 0; b->sockaddr.un.sun_family = AF_UNIX; + /* Note that we use the old /var/run prefix here, to increase compatibility with really old containers */ strncpy(b->sockaddr.un.sun_path, "/var/run/dbus/system_bus_socket", sizeof(b->sockaddr.un.sun_path)); b->sockaddr_size = SOCKADDR_UN_LEN(b->sockaddr.un); b->is_local = false; @@ -898,7 +1105,8 @@ static int bus_start_address(sd_bus *b) { assert(b); for (;;) { - bus_close_fds(b); + bus_close_io_fds(b); + bus_close_inotify_fd(b); /* If you provide multiple different bus-addresses, we * try all of them in order and use the first one that @@ -906,20 +1114,25 @@ static int bus_start_address(sd_bus *b) { if (b->exec_path) r = bus_socket_exec(b); - else if ((b->nspid > 0 || b->machine) && b->sockaddr.sa.sa_family != AF_UNSPEC) r = bus_container_connect_socket(b); - else if (b->sockaddr.sa.sa_family != AF_UNSPEC) r = bus_socket_connect(b); - else goto next; if (r >= 0) { - r = attach_io_events(b); - if (r >= 0) - return r; + int q; + + q = bus_attach_io_events(b); + if (q < 0) + return q; + + q = bus_attach_inotify_event(b); + if (q < 0) + return q; + + return r; } b->last_connect_error = -r; @@ -976,10 +1189,11 @@ _public_ int sd_bus_start(sd_bus *bus) { int r; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); - bus->state = BUS_OPENING; + bus_set_state(bus, BUS_OPENING); if (bus->is_server && bus->bus_client) return -EINVAL; @@ -1040,7 +1254,6 @@ _public_ int sd_bus_open(sd_bus **ret) { * be safe, and authenticate everything */ b->trusted = false; b->is_local = false; - b->attach_flags |= KDBUS_ATTACH_CAPS | KDBUS_ATTACH_CREDS; b->creds_mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_EFFECTIVE_CAPS; r = sd_bus_start(b); @@ -1086,7 +1299,6 @@ _public_ int sd_bus_open_system(sd_bus **ret) { /* Let's do per-method access control on the system bus. We * need the caller's UID and capability set for that. */ b->trusted = false; - b->attach_flags |= KDBUS_ATTACH_CAPS | KDBUS_ATTACH_CREDS; b->creds_mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_EFFECTIVE_CAPS; b->is_local = true; @@ -1120,7 +1332,7 @@ int bus_set_address_user(sd_bus *b) { if (!ee) return -ENOMEM; - if (asprintf(&s, UNIX_USER_BUS_ADDRESS_FMT, ee) < 0) + if (asprintf(&s, DEFAULT_USER_BUS_ADDRESS_FMT, ee) < 0) return -ENOMEM; b->address = s; @@ -1294,7 +1506,7 @@ _public_ void sd_bus_close(sd_bus *bus) { if (bus_pid_changed(bus)) return; - bus->state = BUS_CLOSED; + bus_set_state(bus, BUS_CLOSED); sd_bus_detach_event(bus); @@ -1302,7 +1514,8 @@ _public_ void sd_bus_close(sd_bus *bus) { * the bus object and the bus may be freed */ bus_reset_queues(bus); - bus_close_fds(bus); + bus_close_io_fds(bus); + bus_close_inotify_fd(bus); } _public_ sd_bus* sd_bus_flush_close_unref(sd_bus *bus) { @@ -1316,13 +1529,13 @@ _public_ sd_bus* sd_bus_flush_close_unref(sd_bus *bus) { return sd_bus_unref(bus); } -static void bus_enter_closing(sd_bus *bus) { +void bus_enter_closing(sd_bus *bus) { assert(bus); - if (!IN_SET(bus->state, BUS_OPENING, BUS_AUTHENTICATING, BUS_HELLO, BUS_RUNNING)) + if (!IN_SET(bus->state, BUS_WATCH_BIND, BUS_OPENING, BUS_AUTHENTICATING, BUS_HELLO, BUS_RUNNING)) return; - bus->state = BUS_CLOSING; + bus_set_state(bus, BUS_CLOSING); } _public_ sd_bus *sd_bus_ref(sd_bus *bus) { @@ -1352,23 +1565,33 @@ _public_ sd_bus *sd_bus_unref(sd_bus *bus) { _public_ int sd_bus_is_open(sd_bus *bus) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(!bus_pid_changed(bus), -ECHILD); return BUS_IS_OPEN(bus->state); } +_public_ int sd_bus_is_ready(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->state == BUS_RUNNING; +} + _public_ int sd_bus_can_send(sd_bus *bus, char type) { int r; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(bus->state != BUS_UNSET, -ENOTCONN); assert_return(!bus_pid_changed(bus), -ECHILD); - if (bus->hello_flags & KDBUS_HELLO_MONITOR) + if (bus->is_monitor) return 0; if (type == SD_BUS_TYPE_UNIX_FD) { - if (!(bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) + if (!bus->accept_fd) return 0; r = bus_ensure_running(bus); @@ -1385,6 +1608,7 @@ _public_ int sd_bus_get_bus_id(sd_bus *bus, sd_id128_t *id) { int r; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(id, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -1397,6 +1621,8 @@ _public_ int sd_bus_get_bus_id(sd_bus *bus, sd_id128_t *id) { } static int bus_seal_message(sd_bus *b, sd_bus_message *m, usec_t timeout) { + int r; + assert(b); assert(m); @@ -1411,6 +1637,12 @@ static int bus_seal_message(sd_bus *b, sd_bus_message *m, usec_t timeout) { if (timeout == 0) timeout = BUS_DEFAULT_TIMEOUT; + if (!m->sender && b->patch_sender) { + r = sd_bus_message_set_sender(m, b->patch_sender); + if (r < 0) + return r; + } + return sd_bus_message_seal(m, ++b->cookie, timeout); } @@ -1436,7 +1668,7 @@ int bus_seal_synthetic_message(sd_bus *b, sd_bus_message *m) { /* Fake some timestamps, if they were requested, and not * already initialized */ - if (b->attach_flags & KDBUS_ATTACH_TIMESTAMP) { + if (b->attach_timestamp) { if (m->realtime <= 0) m->realtime = now(CLOCK_REALTIME); @@ -1454,7 +1686,7 @@ int bus_seal_synthetic_message(sd_bus *b, sd_bus_message *m) { return sd_bus_message_seal(m, 0xFFFFFFFFULL, 0); } -static int bus_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call, size_t *idx) { +static int bus_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) { int r; assert(bus); @@ -1465,7 +1697,7 @@ static int bus_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call return r; if (*idx >= BUS_MESSAGE_SIZE(m)) - log_debug("Sent message type=%s sender=%s destination=%s object=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " error-name=%s error-message=%s", + log_debug("Sent message type=%s sender=%s destination=%s path=%s interface=%s member=%s cookie=%" PRIu64 " reply_cookie=%" PRIu64 " signature=%s error-name=%s error-message=%s", bus_message_type_to_string(m->header->type), strna(sd_bus_message_get_sender(m)), strna(sd_bus_message_get_destination(m)), @@ -1474,6 +1706,7 @@ static int bus_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call strna(sd_bus_message_get_member(m)), BUS_MESSAGE_COOKIE(m), m->reply_cookie, + strna(m->root_container.signature), strna(m->error.name), strna(m->error.message)); @@ -1488,7 +1721,7 @@ static int dispatch_wqueue(sd_bus *bus) { while (bus->wqueue_size > 0) { - r = bus_write_message(bus, bus->wqueue[0], false, &bus->windex); + r = bus_write_message(bus, bus->wqueue[0], &bus->windex); if (r < 0) return r; else if (r == 0) @@ -1567,7 +1800,7 @@ static int dispatch_rqueue(sd_bus *bus, bool hint_priority, int64_t priority, sd } } -static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie, bool hint_sync_call) { +_public_ int sd_bus_send(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = sd_bus_message_ref(_m); int r; @@ -1612,7 +1845,7 @@ static int bus_send_internal(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie, if (IN_SET(bus->state, BUS_RUNNING, BUS_HELLO) && bus->wqueue_size <= 0) { size_t idx = 0; - r = bus_write_message(bus, m, hint_sync_call, &idx); + r = bus_write_message(bus, m, &idx); if (r < 0) { if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { bus_enter_closing(bus); @@ -1652,10 +1885,6 @@ finish: return 1; } -_public_ int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie) { - return bus_send_internal(bus, m, cookie, false); -} - _public_ int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destination, uint64_t *cookie) { int r; @@ -1682,26 +1911,35 @@ _public_ int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destinat return sd_bus_send(bus, m, cookie); } -static usec_t calc_elapse(uint64_t usec) { +static usec_t calc_elapse(sd_bus *bus, uint64_t usec) { + assert(bus); + if (usec == (uint64_t) -1) return 0; - return now(CLOCK_MONOTONIC) + usec; + /* We start all timeouts the instant we enter BUS_HELLO/BUS_RUNNING state, so that the don't run in parallel + * with any connection setup states. Hence, if a method callback is started earlier than that we just store the + * relative timestamp, and afterwards the absolute one. */ + + if (IN_SET(bus->state, BUS_WATCH_BIND, BUS_OPENING, BUS_AUTHENTICATING)) + return usec; + else + return now(CLOCK_MONOTONIC) + usec; } static int timeout_compare(const void *a, const void *b) { const struct reply_callback *x = a, *y = b; - if (x->timeout != 0 && y->timeout == 0) + if (x->timeout_usec != 0 && y->timeout_usec == 0) return -1; - if (x->timeout == 0 && y->timeout != 0) + if (x->timeout_usec == 0 && y->timeout_usec != 0) return 1; - if (x->timeout < y->timeout) + if (x->timeout_usec < y->timeout_usec) return -1; - if (x->timeout > y->timeout) + if (x->timeout_usec > y->timeout_usec) return 1; return 0; @@ -1721,8 +1959,7 @@ _public_ int sd_bus_call_async( assert_return(m, -EINVAL); assert_return(m->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL); - assert_return(!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED), -EINVAL); - assert_return(callback, -EINVAL); + assert_return(!m->sealed || (!!callback == !(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)), -EINVAL); if (!bus) bus = m->bus; @@ -1732,6 +1969,10 @@ _public_ int sd_bus_call_async( if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; + /* If no callback is specified and there's no interest in a slot, then there's no reason to ask for a reply */ + if (!callback && !slot && !m->sealed) + m->header->flags |= BUS_MESSAGE_NO_REPLY_EXPECTED; + r = ordered_hashmap_ensure_allocated(&bus->reply_callbacks, &uint64_hash_ops); if (r < 0) return r; @@ -1748,29 +1989,31 @@ _public_ int sd_bus_call_async( if (r < 0) return r; - s = bus_slot_allocate(bus, !slot, BUS_REPLY_CALLBACK, sizeof(struct reply_callback), userdata); - if (!s) - return -ENOMEM; + if (slot || callback) { + s = bus_slot_allocate(bus, !slot, BUS_REPLY_CALLBACK, sizeof(struct reply_callback), userdata); + if (!s) + return -ENOMEM; - s->reply_callback.callback = callback; + s->reply_callback.callback = callback; - s->reply_callback.cookie = BUS_MESSAGE_COOKIE(m); - r = ordered_hashmap_put(bus->reply_callbacks, &s->reply_callback.cookie, &s->reply_callback); - if (r < 0) { - s->reply_callback.cookie = 0; - return r; - } - - s->reply_callback.timeout = calc_elapse(m->timeout); - if (s->reply_callback.timeout != 0) { - r = prioq_put(bus->reply_callbacks_prioq, &s->reply_callback, &s->reply_callback.prioq_idx); + s->reply_callback.cookie = BUS_MESSAGE_COOKIE(m); + r = ordered_hashmap_put(bus->reply_callbacks, &s->reply_callback.cookie, &s->reply_callback); if (r < 0) { - s->reply_callback.timeout = 0; + s->reply_callback.cookie = 0; return r; } + + s->reply_callback.timeout_usec = calc_elapse(bus, m->timeout); + if (s->reply_callback.timeout_usec != 0) { + r = prioq_put(bus->reply_callbacks_prioq, &s->reply_callback, &s->reply_callback.prioq_idx); + if (r < 0) { + s->reply_callback.timeout_usec = 0; + return r; + } + } } - r = sd_bus_send(bus, m, &s->reply_callback.cookie); + r = sd_bus_send(bus, m, s ? &s->reply_callback.cookie : NULL); if (r < 0) return r; @@ -1848,11 +2091,11 @@ _public_ int sd_bus_call( if (r < 0) goto fail; - r = bus_send_internal(bus, m, &cookie, true); + r = sd_bus_send(bus, m, &cookie); if (r < 0) goto fail; - timeout = calc_elapse(m->timeout); + timeout = calc_elapse(bus, m->timeout); for (;;) { usec_t left; @@ -1871,7 +2114,7 @@ _public_ int sd_bus_call( if (incoming->header->type == SD_BUS_MESSAGE_METHOD_RETURN) { - if (incoming->n_fds <= 0 || (bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) { + if (incoming->n_fds <= 0 || bus->accept_fd) { if (reply) *reply = incoming; else @@ -1966,35 +2209,63 @@ fail: _public_ int sd_bus_get_fd(sd_bus *bus) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(bus->input_fd == bus->output_fd, -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); - return bus->input_fd; + if (bus->state == BUS_CLOSED) + return -ENOTCONN; + + if (bus->inotify_fd >= 0) + return bus->inotify_fd; + + if (bus->input_fd >= 0) + return bus->input_fd; + + return -ENOTCONN; } _public_ int sd_bus_get_events(sd_bus *bus) { int flags = 0; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(!bus_pid_changed(bus), -ECHILD); - if (!BUS_IS_OPEN(bus->state) && bus->state != BUS_CLOSING) + switch (bus->state) { + + case BUS_UNSET: + case BUS_CLOSED: return -ENOTCONN; - if (bus->state == BUS_OPENING) + case BUS_WATCH_BIND: + flags |= POLLIN; + break; + + case BUS_OPENING: flags |= POLLOUT; - else if (bus->state == BUS_AUTHENTICATING) { + break; + case BUS_AUTHENTICATING: if (bus_socket_auth_needs_write(bus)) flags |= POLLOUT; flags |= POLLIN; + break; - } else if (IN_SET(bus->state, BUS_RUNNING, BUS_HELLO)) { + case BUS_RUNNING: + case BUS_HELLO: if (bus->rqueue_size <= 0) flags |= POLLIN; if (bus->wqueue_size > 0) flags |= POLLOUT; + break; + + case BUS_CLOSING: + break; + + default: + assert_not_reached("Unknown state"); } return flags; @@ -2004,6 +2275,7 @@ _public_ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) { struct reply_callback *c; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(timeout_usec, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -2015,39 +2287,45 @@ _public_ int sd_bus_get_timeout(sd_bus *bus, uint64_t *timeout_usec) { return 1; } - if (bus->state == BUS_CLOSING) { - *timeout_usec = 0; - return 1; - } + switch (bus->state) { - if (bus->state == BUS_AUTHENTICATING) { + case BUS_AUTHENTICATING: *timeout_usec = bus->auth_timeout; return 1; - } - if (!IN_SET(bus->state, BUS_RUNNING, BUS_HELLO)) { - *timeout_usec = (uint64_t) -1; - return 0; - } + case BUS_RUNNING: + case BUS_HELLO: + if (bus->rqueue_size > 0) { + *timeout_usec = 0; + return 1; + } + + c = prioq_peek(bus->reply_callbacks_prioq); + if (!c) { + *timeout_usec = (uint64_t) -1; + return 0; + } + + if (c->timeout_usec == 0) { + *timeout_usec = (uint64_t) -1; + return 0; + } + + *timeout_usec = c->timeout_usec; + return 1; - if (bus->rqueue_size > 0) { + case BUS_CLOSING: *timeout_usec = 0; return 1; - } - c = prioq_peek(bus->reply_callbacks_prioq); - if (!c) { + case BUS_WATCH_BIND: + case BUS_OPENING: *timeout_usec = (uint64_t) -1; return 0; - } - if (c->timeout == 0) { - *timeout_usec = (uint64_t) -1; - return 0; + default: + assert_not_reached("Unknown or unexpected stat"); } - - *timeout_usec = c->timeout; - return 1; } static int process_timeout(sd_bus *bus) { @@ -2055,17 +2333,19 @@ static int process_timeout(sd_bus *bus) { _cleanup_(sd_bus_message_unrefp) sd_bus_message* m = NULL; struct reply_callback *c; sd_bus_slot *slot; + bool is_hello; usec_t n; int r; assert(bus); + assert(IN_SET(bus->state, BUS_RUNNING, BUS_HELLO)); c = prioq_peek(bus->reply_callbacks_prioq); if (!c) return 0; n = now(CLOCK_MONOTONIC); - if (c->timeout > n) + if (c->timeout_usec > n) return 0; r = bus_message_new_synthetic_error( @@ -2081,7 +2361,7 @@ static int process_timeout(sd_bus *bus) { return r; assert_se(prioq_pop(bus->reply_callbacks_prioq) == c); - c->timeout = 0; + c->timeout_usec = 0; ordered_hashmap_remove(bus->reply_callbacks, &c->cookie); c->cookie = 0; @@ -2090,6 +2370,8 @@ static int process_timeout(sd_bus *bus) { bus->iteration_counter++; + is_hello = bus->state == BUS_HELLO && c->callback == hello_callback; + bus->current_message = m; bus->current_slot = sd_bus_slot_ref(slot); bus->current_handler = c->callback; @@ -2107,6 +2389,11 @@ static int process_timeout(sd_bus *bus) { sd_bus_slot_unref(slot); + /* When this is the hello message and it timed out, then make sure to propagate the error up, don't just log + * and ignore the callback handler's return value. */ + if (is_hello) + return r; + return bus_maybe_reply_error(m, r, &error_buffer); } @@ -2136,6 +2423,7 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) { _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; struct reply_callback *c; sd_bus_slot *slot; + bool is_hello; int r; assert(bus); @@ -2155,7 +2443,7 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) { slot = container_of(c, sd_bus_slot, reply_callback); - if (m->n_fds > 0 && !(bus->hello_flags & KDBUS_HELLO_ACCEPT_FD)) { + if (m->n_fds > 0 && !bus->accept_fd) { /* If the reply contained a file descriptor which we * didn't want we pass an error instead. */ @@ -2184,11 +2472,13 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) { return r; } - if (c->timeout != 0) { + if (c->timeout_usec != 0) { prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx); - c->timeout = 0; + c->timeout_usec = 0; } + is_hello = bus->state == BUS_HELLO && c->callback == hello_callback; + bus->current_slot = sd_bus_slot_ref(slot); bus->current_handler = c->callback; bus->current_userdata = slot->userdata; @@ -2204,6 +2494,11 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) { sd_bus_slot_unref(slot); + /* When this is the hello message and it failed, then make sure to propagate the error up, don't just log and + * ignore the callback handler's return value. */ + if (is_hello) + return r; + return bus_maybe_reply_error(m, r, &error_buffer); } @@ -2280,7 +2575,7 @@ static int process_builtin(sd_bus *bus, sd_bus_message *m) { assert(bus); assert(m); - if (bus->hello_flags & KDBUS_HELLO_MONITOR) + if (bus->is_monitor) return 0; if (bus->manual_peer_interface) @@ -2338,13 +2633,13 @@ static int process_fd_check(sd_bus *bus, sd_bus_message *m) { * delivered to us later even though we ourselves did not * negotiate it. */ - if (bus->hello_flags & KDBUS_HELLO_MONITOR) + if (bus->is_monitor) return 0; if (m->n_fds <= 0) return 0; - if (bus->hello_flags & KDBUS_HELLO_ACCEPT_FD) + if (bus->accept_fd) return 0; if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL) @@ -2515,9 +2810,9 @@ static int process_closing_reply_callback(sd_bus *bus, struct reply_callback *c) if (r < 0) return r; - if (c->timeout != 0) { + if (c->timeout_usec != 0) { prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx); - c->timeout = 0; + c->timeout_usec = 0; } ordered_hashmap_remove(bus->reply_callbacks, &c->cookie); @@ -2622,6 +2917,7 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit * means *ret is filled in with an unprocessed message. */ assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(!bus_pid_changed(bus), -ECHILD); /* We don't allow recursively invoking sd_bus_process(). */ @@ -2636,48 +2932,44 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit case BUS_CLOSED: return -ECONNRESET; + case BUS_WATCH_BIND: + r = bus_socket_process_watch_bind(bus); + break; + case BUS_OPENING: r = bus_socket_process_opening(bus); - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { - bus_enter_closing(bus); - r = 1; - } else if (r < 0) - return r; - if (ret) - *ret = NULL; - return r; + break; case BUS_AUTHENTICATING: r = bus_socket_process_authenticating(bus); - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { - bus_enter_closing(bus); - r = 1; - } else if (r < 0) - return r; - - if (ret) - *ret = NULL; - - return r; + break; case BUS_RUNNING: case BUS_HELLO: r = process_running(bus, hint_priority, priority, ret); - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { - bus_enter_closing(bus); - r = 1; - - if (ret) - *ret = NULL; - } + if (r >= 0) + return r; - return r; + /* This branch initializes *ret, hence we don't use the generic error checking below */ + break; case BUS_CLOSING: return process_closing(bus, ret); + + default: + assert_not_reached("Unknown state"); } - assert_not_reached("Unknown state"); + if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { + bus_enter_closing(bus); + r = 1; + } else if (r < 0) + return r; + + if (ret) + *ret = NULL; + + return r; } _public_ int sd_bus_process(sd_bus *bus, sd_bus_message **ret) { @@ -2690,7 +2982,7 @@ _public_ int sd_bus_process_priority(sd_bus *bus, int64_t priority, sd_bus_messa static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) { struct pollfd p[2] = {}; - int r, e, n; + int r, n; struct timespec ts; usec_t m = USEC_INFINITY; @@ -2702,45 +2994,52 @@ static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) { if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; - e = sd_bus_get_events(bus); - if (e < 0) - return e; - - if (need_more) - /* The caller really needs some more data, he doesn't - * care about what's already read, or any timeouts - * except its own. */ - e |= POLLIN; - else { - usec_t until; - /* The caller wants to process if there's something to - * process, but doesn't care otherwise */ - - r = sd_bus_get_timeout(bus, &until); - if (r < 0) - return r; - if (r > 0) { - usec_t nw; - nw = now(CLOCK_MONOTONIC); - m = until > nw ? until - nw : 0; - } - } + if (bus->state == BUS_WATCH_BIND) { + assert(bus->inotify_fd >= 0); - if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m)) - m = timeout_usec; - - p[0].fd = bus->input_fd; - if (bus->output_fd == bus->input_fd) { - p[0].events = e; + p[0].events = POLLIN; + p[0].fd = bus->inotify_fd; n = 1; } else { - p[0].events = e & POLLIN; - p[1].fd = bus->output_fd; - p[1].events = e & POLLOUT; - n = 2; + int e; + + e = sd_bus_get_events(bus); + if (e < 0) + return e; + + if (need_more) + /* The caller really needs some more data, he doesn't + * care about what's already read, or any timeouts + * except its own. */ + e |= POLLIN; + else { + usec_t until; + /* The caller wants to process if there's something to + * process, but doesn't care otherwise */ + + r = sd_bus_get_timeout(bus, &until); + if (r < 0) + return r; + if (r > 0) + m = usec_sub_unsigned(until, now(CLOCK_MONOTONIC)); + } + + p[0].fd = bus->input_fd; + if (bus->output_fd == bus->input_fd) { + p[0].events = e; + n = 1; + } else { + p[0].events = e & POLLIN; + p[1].fd = bus->output_fd; + p[1].events = e & POLLOUT; + n = 2; + } } - r = ppoll(p, n, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL); + if (timeout_usec != (uint64_t) -1 && (m == USEC_INFINITY || timeout_usec < m)) + m = timeout_usec; + + r = ppoll(p, n, m == USEC_INFINITY ? NULL : timespec_store(&ts, m), NULL); if (r < 0) return -errno; @@ -2750,6 +3049,7 @@ static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec) { _public_ int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(!bus_pid_changed(bus), -ECHILD); if (bus->state == BUS_CLOSING) @@ -2768,6 +3068,7 @@ _public_ int sd_bus_flush(sd_bus *bus) { int r; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(!bus_pid_changed(bus), -ECHILD); if (bus->state == BUS_CLOSING) @@ -2776,6 +3077,10 @@ _public_ int sd_bus_flush(sd_bus *bus) { if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; + /* We never were connected? Don't hang in inotify for good, as there's no timeout set for it */ + if (bus->state == BUS_WATCH_BIND) + return -EUNATCH; + r = bus_ensure_running(bus); if (r < 0) return r; @@ -2812,6 +3117,7 @@ _public_ int sd_bus_add_filter( sd_bus_slot *s; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(callback, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -2830,11 +3136,78 @@ _public_ int sd_bus_add_filter( return 0; } -_public_ int sd_bus_add_match( +static int add_match_callback( + sd_bus_message *m, + void *userdata, + sd_bus_error *ret_error) { + + sd_bus_slot *match_slot = userdata; + bool failed = false; + int r; + + assert(m); + assert(match_slot); + + sd_bus_slot_ref(match_slot); + + if (sd_bus_message_is_method_error(m, NULL)) { + log_debug_errno(sd_bus_message_get_errno(m), + "Unable to add match %s, failing connection: %s", + match_slot->match_callback.match_string, + sd_bus_message_get_error(m)->message); + + failed = true; + } else + log_debug("Match %s successfully installed.", match_slot->match_callback.match_string); + + if (match_slot->match_callback.install_callback) { + sd_bus *bus; + + bus = sd_bus_message_get_bus(m); + + /* This function has been called as slot handler, and we want to call another slot handler. Let's + * update the slot callback metadata temporarily with our own data, and then revert back to the old + * values. */ + + assert(bus->current_slot == match_slot->match_callback.install_slot); + assert(bus->current_handler == add_match_callback); + assert(bus->current_userdata == userdata); + + bus->current_slot = match_slot; + bus->current_handler = match_slot->match_callback.install_callback; + bus->current_userdata = match_slot->userdata; + + r = match_slot->match_callback.install_callback(m, match_slot->userdata, ret_error); + + bus->current_slot = match_slot->match_callback.install_slot; + bus->current_handler = add_match_callback; + bus->current_userdata = userdata; + + match_slot->match_callback.install_slot = sd_bus_slot_unref(match_slot->match_callback.install_slot); + } else { + if (failed) /* Generic failure handling: destroy the connection */ + bus_enter_closing(sd_bus_message_get_bus(m)); + + r = 1; + } + + if (failed && match_slot->floating) { + bus_slot_disconnect(match_slot); + sd_bus_slot_unref(match_slot); + } + + sd_bus_slot_unref(match_slot); + + return r; +} + +static int bus_add_match_full( sd_bus *bus, sd_bus_slot **slot, + bool asynchronous, const char *match, sd_bus_message_handler_t callback, + sd_bus_message_handler_t install_callback, void *userdata) { struct bus_match_component *components = NULL; @@ -2843,6 +3216,7 @@ _public_ int sd_bus_add_match( int r = 0; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(match, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -2857,18 +3231,17 @@ _public_ int sd_bus_add_match( } s->match_callback.callback = callback; + s->match_callback.install_callback = install_callback; if (bus->bus_client) { enum bus_match_scope scope; scope = bus_match_get_scope(components, n_components); - /* Do not install server-side matches for matches - * against the local service, interface or bus path. */ + /* Do not install server-side matches for matches against the local service, interface or bus path. */ if (scope != BUS_MATCH_LOCAL) { - /* We store the original match string, so that - * we can use it to remove the match again. */ + /* We store the original match string, so that we can use it to remove the match again. */ s->match_callback.match_string = strdup(match); if (!s->match_callback.match_string) { @@ -2876,7 +3249,14 @@ _public_ int sd_bus_add_match( goto finish; } - r = bus_add_match_internal(bus, s->match_callback.match_string, components, n_components); + if (asynchronous) + r = bus_add_match_internal_async(bus, + &s->match_callback.install_slot, + s->match_callback.match_string, + add_match_callback, + s); + else + r = bus_add_match_internal(bus, s->match_callback.match_string); if (r < 0) goto finish; @@ -2900,35 +3280,25 @@ finish: return r; } -int bus_remove_match_by_string( +_public_ int sd_bus_add_match( sd_bus *bus, + sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata) { - struct bus_match_component *components = NULL; - unsigned n_components = 0; - struct match_callback *c; - int r = 0; - - assert_return(bus, -EINVAL); - assert_return(match, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); - - r = bus_match_parse(match, &components, &n_components); - if (r < 0) - goto finish; - - r = bus_match_find(&bus->match_callbacks, components, n_components, NULL, NULL, &c); - if (r <= 0) - goto finish; - - sd_bus_slot_unref(container_of(c, sd_bus_slot, match_callback)); + return bus_add_match_full(bus, slot, false, match, callback, NULL, userdata); +} -finish: - bus_match_parse_free(components, n_components); +_public_ int sd_bus_add_match_async( + sd_bus *bus, + sd_bus_slot **slot, + const char *match, + sd_bus_message_handler_t callback, + sd_bus_message_handler_t install_callback, + void *userdata) { - return r; + return bus_add_match_full(bus, slot, true, match, callback, install_callback, userdata); } bool bus_pid_changed(sd_bus *bus) { @@ -2946,9 +3316,13 @@ static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userd assert(bus); + /* Note that this is called both on input_fd, output_fd as well as inotify_fd events */ + r = sd_bus_process(bus, NULL); - if (r < 0) - return r; + if (r < 0) { + log_debug_errno(r, "Processing of bus failed, closing down: %m"); + bus_enter_closing(bus); + } return 1; } @@ -2960,8 +3334,10 @@ static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) { assert(bus); r = sd_bus_process(bus, NULL); - if (r < 0) - return r; + if (r < 0) { + log_debug_errno(r, "Processing of bus failed, closing down: %m"); + bus_enter_closing(bus); + } return 1; } @@ -2975,38 +3351,45 @@ static int prepare_callback(sd_event_source *s, void *userdata) { assert(bus); e = sd_bus_get_events(bus); - if (e < 0) - return e; + if (e < 0) { + r = e; + goto fail; + } if (bus->output_fd != bus->input_fd) { r = sd_event_source_set_io_events(bus->input_io_event_source, e & POLLIN); if (r < 0) - return r; + goto fail; r = sd_event_source_set_io_events(bus->output_io_event_source, e & POLLOUT); - if (r < 0) - return r; - } else { + } else r = sd_event_source_set_io_events(bus->input_io_event_source, e); - if (r < 0) - return r; - } + if (r < 0) + goto fail; r = sd_bus_get_timeout(bus, &until); if (r < 0) - return r; + goto fail; if (r > 0) { int j; j = sd_event_source_set_time(bus->time_event_source, until); - if (j < 0) - return j; + if (j < 0) { + r = j; + goto fail; + } } r = sd_event_source_set_enabled(bus->time_event_source, r > 0); if (r < 0) - return r; + goto fail; + + return 1; + +fail: + log_debug_errno(r, "Preparing of bus events failed, closing down: %m"); + bus_enter_closing(bus); return 1; } @@ -3022,7 +3405,7 @@ static int quit_callback(sd_event_source *event, void *userdata) { return 1; } -static int attach_io_events(sd_bus *bus) { +int bus_attach_io_events(sd_bus *bus) { int r; assert(bus); @@ -3076,7 +3459,7 @@ static int attach_io_events(sd_bus *bus) { return 0; } -static void detach_io_events(sd_bus *bus) { +static void bus_detach_io_events(sd_bus *bus) { assert(bus); if (bus->input_io_event_source) { @@ -3090,10 +3473,49 @@ static void detach_io_events(sd_bus *bus) { } } +int bus_attach_inotify_event(sd_bus *bus) { + int r; + + assert(bus); + + if (bus->inotify_fd < 0) + return 0; + + if (!bus->event) + return 0; + + if (!bus->inotify_event_source) { + r = sd_event_add_io(bus->event, &bus->inotify_event_source, bus->inotify_fd, EPOLLIN, io_callback, bus); + if (r < 0) + return r; + + r = sd_event_source_set_priority(bus->inotify_event_source, bus->event_priority); + if (r < 0) + return r; + + r = sd_event_source_set_description(bus->inotify_event_source, "bus-inotify"); + } else + r = sd_event_source_set_io_fd(bus->inotify_event_source, bus->inotify_fd); + if (r < 0) + return r; + + return 0; +} + +static void bus_detach_inotify_event(sd_bus *bus) { + assert(bus); + + if (bus->inotify_event_source) { + sd_event_source_set_enabled(bus->inotify_event_source, SD_EVENT_OFF); + bus->inotify_event_source = sd_event_source_unref(bus->inotify_event_source); + } +} + _public_ int sd_bus_attach_event(sd_bus *bus, sd_event *event, int priority) { int r; assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(!bus->event, -EBUSY); assert(!bus->input_io_event_source); @@ -3130,7 +3552,11 @@ _public_ int sd_bus_attach_event(sd_bus *bus, sd_event *event, int priority) { if (r < 0) goto fail; - r = attach_io_events(bus); + r = bus_attach_io_events(bus); + if (r < 0) + goto fail; + + r = bus_attach_inotify_event(bus); if (r < 0) goto fail; @@ -3143,11 +3569,13 @@ fail: _public_ int sd_bus_detach_event(sd_bus *bus) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); if (!bus->event) return 0; - detach_io_events(bus); + bus_detach_io_events(bus); + bus_detach_inotify_event(bus); if (bus->time_event_source) { sd_event_source_set_enabled(bus->time_event_source, SD_EVENT_OFF); @@ -3230,39 +3658,11 @@ _public_ int sd_bus_default_user(sd_bus **ret) { } _public_ int sd_bus_default(sd_bus **ret) { + int (*bus_open)(sd_bus **) = NULL; + sd_bus **busp; - const char *e; - - /* Let's try our best to reuse another cached connection. If - * the starter bus type is set, connect via our normal - * connection logic, ignoring $DBUS_STARTER_ADDRESS, so that - * we can share the connection with the user/system default - * bus. */ - - e = secure_getenv("DBUS_STARTER_BUS_TYPE"); - if (e) { - if (streq(e, "system")) - return sd_bus_default_system(ret); - else if (STR_IN_SET(e, "user", "session")) - return sd_bus_default_user(ret); - } - - /* No type is specified, so we have not other option than to - * use the starter address if it is set. */ - - e = secure_getenv("DBUS_STARTER_ADDRESS"); - if (e) { - - return bus_default(sd_bus_open, &default_starter_bus, ret); - } - - /* Finally, if nothing is set use the cached connection for - * the right scope */ - - if (cg_pid_get_owner_uid(0, NULL) >= 0) - return sd_bus_default_user(ret); - else - return sd_bus_default_system(ret); + busp = bus_choose_default(&bus_open); + return bus_default(bus_open, busp, ret); } _public_ int sd_bus_get_tid(sd_bus *b, pid_t *tid) { @@ -3483,13 +3883,13 @@ _public_ int sd_bus_path_decode_many(const char *path, const char *path_template } va_end(list); - free(labels); - labels = NULL; + labels = mfree(labels); return 1; } _public_ int sd_bus_try_close(sd_bus *bus) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(!bus_pid_changed(bus), -ECHILD); return -EOPNOTSUPP; @@ -3497,6 +3897,7 @@ _public_ int sd_bus_try_close(sd_bus *bus) { _public_ int sd_bus_get_description(sd_bus *bus, const char **description) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(description, -EINVAL); assert_return(bus->description, -ENXIO); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -3525,6 +3926,7 @@ int bus_get_root_path(sd_bus *bus) { _public_ int sd_bus_get_scope(sd_bus *bus, const char **scope) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(scope, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -3544,6 +3946,7 @@ _public_ int sd_bus_get_scope(sd_bus *bus, const char **scope) { _public_ int sd_bus_get_address(sd_bus *bus, const char **address) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(address, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -3557,6 +3960,7 @@ _public_ int sd_bus_get_address(sd_bus *bus, const char **address) { _public_ int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *mask) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(mask, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); @@ -3566,6 +3970,7 @@ _public_ int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *mask) { _public_ int sd_bus_is_bus_client(sd_bus *bus) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(!bus_pid_changed(bus), -ECHILD); return bus->bus_client; @@ -3573,6 +3978,7 @@ _public_ int sd_bus_is_bus_client(sd_bus *bus) { _public_ int sd_bus_is_server(sd_bus *bus) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(!bus_pid_changed(bus), -ECHILD); return bus->is_server; @@ -3580,6 +3986,7 @@ _public_ int sd_bus_is_server(sd_bus *bus) { _public_ int sd_bus_is_anonymous(sd_bus *bus) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(!bus_pid_changed(bus), -ECHILD); return bus->anonymous_auth; @@ -3587,6 +3994,7 @@ _public_ int sd_bus_is_anonymous(sd_bus *bus) { _public_ int sd_bus_is_trusted(sd_bus *bus) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(!bus_pid_changed(bus), -ECHILD); return bus->trusted; @@ -3594,9 +4002,10 @@ _public_ int sd_bus_is_trusted(sd_bus *bus) { _public_ int sd_bus_is_monitor(sd_bus *bus) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); assert_return(!bus_pid_changed(bus), -ECHILD); - return !!(bus->hello_flags & KDBUS_HELLO_MONITOR); + return bus->is_monitor; } static void flush_close(sd_bus *bus) { @@ -3618,6 +4027,7 @@ _public_ void sd_bus_default_flush_close(void) { _public_ int sd_bus_set_exit_on_disconnect(sd_bus *bus, int b) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); /* Turns on exit-on-disconnect, and triggers it immediately if the bus connection was already * disconnected. Note that this is triggered exclusively on disconnections triggered by the server side, never @@ -3630,6 +4040,28 @@ _public_ int sd_bus_set_exit_on_disconnect(sd_bus *bus, int b) { _public_ int sd_bus_get_exit_on_disconnect(sd_bus *bus) { assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); return bus->exit_on_disconnect; } + +_public_ int sd_bus_set_sender(sd_bus *bus, const char *sender) { + assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); + assert_return(!bus->bus_client, -EPERM); + assert_return(!sender || service_name_is_valid(sender), -EINVAL); + + return free_and_strdup(&bus->patch_sender, sender); +} + +_public_ int sd_bus_get_sender(sd_bus *bus, const char **ret) { + assert_return(bus, -EINVAL); + assert_return(bus = bus_resolve(bus), -ENOPKG); + assert_return(ret, -EINVAL); + + if (!bus->patch_sender) + return -ENODATA; + + *ret = bus->patch_sender; + return 0; +} diff --git a/src/libsystemd/sd-bus/test-bus-benchmark.c b/src/libsystemd/sd-bus/test-bus-benchmark.c index a466cddd75..bfd0f39372 100644 --- a/src/libsystemd/sd-bus/test-bus-benchmark.c +++ b/src/libsystemd/sd-bus/test-bus-benchmark.c @@ -319,7 +319,7 @@ int main(int argc, char *argv[]) { break; } - _exit(0); + _exit(EXIT_SUCCESS); } CPU_ZERO(&cpuset); diff --git a/src/libsystemd/sd-bus/test-bus-chat.c b/src/libsystemd/sd-bus/test-bus-chat.c index 1b2efb9bb4..bd6721946a 100644 --- a/src/libsystemd/sd-bus/test-bus-chat.c +++ b/src/libsystemd/sd-bus/test-bus-chat.c @@ -102,9 +102,9 @@ static int server_init(sd_bus **_bus) { goto fail; } - r = sd_bus_add_match(bus, NULL, "type='signal',interface='foo.bar',member='Notify'", match_callback, NULL); + r = sd_bus_match_signal(bus, NULL, NULL, NULL, "foo.bar", "Notify", match_callback, NULL); if (r < 0) { - log_error_errno(r, "Failed to add match: %m"); + log_error_errno(r, "Failed to request match: %m"); goto fail; } diff --git a/src/libsystemd/sd-bus/test-bus-track.c b/src/libsystemd/sd-bus/test-bus-track.c index 320e8347f4..94c9d09de3 100644 --- a/src/libsystemd/sd-bus/test-bus-track.c +++ b/src/libsystemd/sd-bus/test-bus-track.c @@ -18,6 +18,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> +#include <sys/socket.h> + #include "sd-bus.h" #include "macro.h" diff --git a/src/libsystemd/sd-bus/test-bus-watch-bind.c b/src/libsystemd/sd-bus/test-bus-watch-bind.c new file mode 100644 index 0000000000..aef5ba9486 --- /dev/null +++ b/src/libsystemd/sd-bus/test-bus-watch-bind.c @@ -0,0 +1,239 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +/*** + This file is part of systemd. + + Copyright 2017 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 "sd-bus.h" +#include "sd-event.h" +#include "sd-id128.h" + +#include "alloc-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" +#include "mkdir.h" +#include "path-util.h" +#include "random-util.h" +#include "rm-rf.h" +#include "socket-util.h" +#include "string-util.h" + +static int method_foobar(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { + log_info("Got Foobar() call."); + + assert_se(sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), 0) >= 0); + return sd_bus_reply_method_return(m, NULL); +} + +static int method_exit(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { + log_info("Got Exit() call"); + assert_se(sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), 1) >= 0); + return sd_bus_reply_method_return(m, NULL); +} + +static const sd_bus_vtable vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_METHOD("Foobar", NULL, NULL, method_foobar, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("Exit", NULL, NULL, method_exit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_VTABLE_END, +}; + +static void* thread_server(void *p) { + _cleanup_free_ char *suffixed = NULL, *suffixed2 = NULL, *d = NULL; + _cleanup_close_ int fd = -1; + union sockaddr_union u = { + .un.sun_family = AF_UNIX, + }; + const char *path = p; + + log_debug("Initializing server"); + + /* Let's play some games, by slowly creating the socket directory, and renaming it in the middle */ + (void) usleep(100 * USEC_PER_MSEC); + + assert_se(mkdir_parents(path, 0755) >= 0); + (void) usleep(100 * USEC_PER_MSEC); + + d = dirname_malloc(path); + assert_se(d); + assert_se(asprintf(&suffixed, "%s.%" PRIx64, d, random_u64()) >= 0); + assert_se(rename(d, suffixed) >= 0); + (void) usleep(100 * USEC_PER_MSEC); + + assert_se(asprintf(&suffixed2, "%s.%" PRIx64, d, random_u64()) >= 0); + assert_se(symlink(suffixed2, d) >= 0); + (void) usleep(100 * USEC_PER_MSEC); + + assert_se(symlink(basename(suffixed), suffixed2) >= 0); + (void) usleep(100 * USEC_PER_MSEC); + + strncpy(u.un.sun_path, path, sizeof(u.un.sun_path)); + + fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); + assert_se(fd >= 0); + + assert_se(bind(fd, &u.sa, SOCKADDR_UN_LEN(u.un)) >= 0); + usleep(100 * USEC_PER_MSEC); + + assert_se(listen(fd, SOMAXCONN) >= 0); + usleep(100 * USEC_PER_MSEC); + + assert_se(touch(path) >= 0); + usleep(100 * USEC_PER_MSEC); + + log_debug("Initialized server"); + + for (;;) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_(sd_event_unrefp) sd_event *event = NULL; + sd_id128_t id; + int bus_fd, code; + + assert_se(sd_id128_randomize(&id) >= 0); + + assert_se(sd_event_new(&event) >= 0); + + bus_fd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); + assert_se(bus_fd >= 0); + + log_debug("Accepted server connection"); + + assert_se(sd_bus_new(&bus) >= 0); + assert_se(sd_bus_set_description(bus, "server") >= 0); + assert_se(sd_bus_set_fd(bus, bus_fd, bus_fd) >= 0); + assert_se(sd_bus_set_server(bus, true, id) >= 0); + /* assert_se(sd_bus_set_anonymous(bus, true) >= 0); */ + + assert_se(sd_bus_attach_event(bus, event, 0) >= 0); + + assert_se(sd_bus_add_object_vtable(bus, NULL, "/foo", "foo.TestInterface", vtable, NULL) >= 0); + + assert_se(sd_bus_start(bus) >= 0); + + assert_se(sd_event_loop(event) >= 0); + + assert_se(sd_event_get_exit_code(event, &code) >= 0); + + if (code > 0) + break; + } + + log_debug("Server done"); + + return NULL; +} + +static void* thread_client1(void *p) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + const char *path = p, *t; + int r; + + log_debug("Initializing client1"); + + assert_se(sd_bus_new(&bus) >= 0); + assert_se(sd_bus_set_description(bus, "client1") >= 0); + + t = strjoina("unix:path=", path); + assert_se(sd_bus_set_address(bus, t) >= 0); + assert_se(sd_bus_set_watch_bind(bus, true) >= 0); + assert_se(sd_bus_start(bus) >= 0); + + r = sd_bus_call_method(bus, "foo.bar", "/foo", "foo.TestInterface", "Foobar", &error, NULL, NULL); + assert_se(r >= 0); + + log_debug("Client1 done"); + + return NULL; +} + +static int client2_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { + assert_se(sd_bus_message_is_method_error(m, NULL) == 0); + assert_se(sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), 0) >= 0); + return 0; +} + +static void* thread_client2(void *p) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_(sd_event_unrefp) sd_event *event = NULL; + const char *path = p, *t; + + log_debug("Initializing client2"); + + assert_se(sd_event_new(&event) >= 0); + assert_se(sd_bus_new(&bus) >= 0); + assert_se(sd_bus_set_description(bus, "client2") >= 0); + + t = strjoina("unix:path=", path); + assert_se(sd_bus_set_address(bus, t) >= 0); + assert_se(sd_bus_set_watch_bind(bus, true) >= 0); + assert_se(sd_bus_attach_event(bus, event, 0) >= 0); + assert_se(sd_bus_start(bus) >= 0); + + assert_se(sd_bus_call_method_async(bus, NULL, "foo.bar", "/foo", "foo.TestInterface", "Foobar", client2_callback, NULL, NULL) >= 0); + + assert_se(sd_event_loop(event) >= 0); + + log_debug("Client2 done"); + + return NULL; +} + +static void request_exit(const char *path) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + const char *t; + + assert_se(sd_bus_new(&bus) >= 0); + + t = strjoina("unix:path=", path); + assert_se(sd_bus_set_address(bus, t) >= 0); + assert_se(sd_bus_set_watch_bind(bus, true) >= 0); + assert_se(sd_bus_set_description(bus, "request-exit") >= 0); + assert_se(sd_bus_start(bus) >= 0); + + assert_se(sd_bus_call_method(bus, "foo.bar", "/foo", "foo.TestInterface", "Exit", NULL, NULL, NULL) >= 0); +} + +int main(int argc, char *argv[]) { + _cleanup_(rm_rf_physical_and_freep) char *d = NULL; + pthread_t server, client1, client2; + char *path; + + log_set_max_level(LOG_DEBUG); + + /* We use /dev/shm here rather than /tmp, since some weird distros might set up /tmp as some weird fs that + * doesn't support inotify properly. */ + assert_se(mkdtemp_malloc("/dev/shm/systemd-watch-bind-XXXXXX", &d) >= 0); + + path = strjoina(d, "/this/is/a/socket"); + + assert_se(pthread_create(&server, NULL, thread_server, path) == 0); + assert_se(pthread_create(&client1, NULL, thread_client1, path) == 0); + assert_se(pthread_create(&client2, NULL, thread_client2, path) == 0); + + assert_se(pthread_join(client1, NULL) == 0); + assert_se(pthread_join(client2, NULL) == 0); + + request_exit(path); + + assert_se(pthread_join(server, NULL) == 0); + + return 0; +} diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c index 64a74929bf..1334498ca4 100644 --- a/src/libsystemd/sd-daemon/sd-daemon.c +++ b/src/libsystemd/sd-daemon/sd-daemon.c @@ -39,6 +39,7 @@ #include "fs-util.h" #include "parse-util.h" #include "path-util.h" +#include "process-util.h" #include "socket-util.h" #include "strv.h" #include "util.h" @@ -306,17 +307,13 @@ _public_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint return 0; if (port > 0) { - if (sockaddr.sa.sa_family == AF_INET) { - if (l < sizeof(struct sockaddr_in)) - return -EINVAL; + unsigned sa_port; - return htobe16(port) == sockaddr.in.sin_port; - } else { - if (l < sizeof(struct sockaddr_in6)) - return -EINVAL; + r = sockaddr_port(&sockaddr.sa, &sa_port); + if (r < 0) + return r; - return htobe16(port) == sockaddr.in6.sin6_port; - } + return port == sa_port; } return 1; @@ -459,7 +456,13 @@ _public_ int sd_is_mq(int fd, const char *path) { return 1; } -_public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds) { +_public_ int sd_pid_notify_with_fds( + pid_t pid, + int unset_environment, + const char *state, + const int *fds, + unsigned n_fds) { + union sockaddr_union sockaddr = { .sa.sa_family = AF_UNIX, }; @@ -474,7 +477,7 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char _cleanup_close_ int fd = -1; struct cmsghdr *cmsg = NULL; const char *e; - bool have_pid; + bool send_ucred; int r; if (!state) { @@ -508,7 +511,7 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char goto finish; } - fd_inc_sndbuf(fd, SNDBUF_SIZE); + (void) fd_inc_sndbuf(fd, SNDBUF_SIZE); iovec.iov_len = strlen(state); @@ -518,13 +521,16 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char msghdr.msg_namelen = SOCKADDR_UN_LEN(sockaddr.un); - have_pid = pid != 0 && pid != getpid_cached(); + send_ucred = + (pid != 0 && pid != getpid_cached()) || + getuid() != geteuid() || + getgid() != getegid(); - if (n_fds > 0 || have_pid) { + if (n_fds > 0 || send_ucred) { /* 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); + (send_ucred ? CMSG_SPACE(sizeof(struct ucred)) : 0); msghdr.msg_control = alloca0(msghdr.msg_controllen); @@ -536,11 +542,11 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds); - if (have_pid) + if (send_ucred) assert_se(cmsg = CMSG_NXTHDR(&msghdr, cmsg)); } - if (have_pid) { + if (send_ucred) { struct ucred *ucred; cmsg->cmsg_level = SOL_SOCKET; @@ -548,7 +554,7 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); ucred = (struct ucred*) CMSG_DATA(cmsg); - ucred->pid = pid; + ucred->pid = pid != 0 ? pid : getpid_cached(); ucred->uid = getuid(); ucred->gid = getgid(); } @@ -561,7 +567,7 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char } /* If that failed, try with our own ucred instead */ - if (have_pid) { + if (send_ucred) { msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred)); if (msghdr.msg_controllen == 0) msghdr.msg_control = NULL; diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index a96d7445ce..1297dfa911 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -166,25 +166,38 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) { } if (verify) { - r = readlink_and_canonicalize(_syspath, NULL, &syspath); + r = chase_symlinks(_syspath, NULL, 0, &syspath); if (r == -ENOENT) /* the device does not exist (any more?) */ return -ENODEV; - else if (r == -EINVAL) { - /* not a symlink */ - syspath = canonicalize_file_name(_syspath); - if (!syspath) { - if (errno == ENOENT) - /* the device does not exist (any more?) */ - return -ENODEV; - - return log_debug_errno(errno, "sd-device: could not canonicalize '%s': %m", _syspath); - } - } else if (r < 0) { + else if (r < 0) { log_debug_errno(r, "sd-device: could not get target of '%s': %m", _syspath); return r; } + if (!path_startswith(syspath, "/sys")) { + _cleanup_free_ char *real_sys = NULL, *new_syspath = NULL; + char *p; + + /* /sys is a symlink to somewhere sysfs is mounted on? In that case, we convert the path to real sysfs to "/sys". */ + r = chase_symlinks("/sys", NULL, 0, &real_sys); + if (r < 0) + return log_debug_errno(r, "sd-device: could not chase symlink /sys: %m"); + + p = path_startswith(syspath, real_sys); + if (!p) { + log_debug("sd-device: canonicalized path '%s' does not starts with sysfs mount point '%s'", syspath, real_sys); + return -ENODEV; + } + + new_syspath = strjoin("/sys/", p); + if (!new_syspath) + return log_oom(); + + free_and_replace(syspath, new_syspath); + path_kill_slashes(syspath); + } + if (path_startswith(syspath, "/sys/devices/")) { char *path; diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index a5f3e854b1..cb9b3a4545 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -122,6 +122,7 @@ struct sd_event_source { uint32_t events; uint32_t revents; bool registered:1; + bool owned:1; } io; struct { sd_event_time_handler_t callback; @@ -240,8 +241,14 @@ struct sd_event { unsigned delays[sizeof(usec_t) * 8]; }; +static thread_local sd_event *default_event = NULL; + static void source_disconnect(sd_event_source *s); +static sd_event *event_resolve(sd_event *e) { + return e == SD_EVENT_DEFAULT ? default_event : e; +} + static int pending_prioq_compare(const void *a, const void *b) { const sd_event_source *x = a, *y = b; @@ -883,6 +890,10 @@ static void source_free(sd_event_source *s) { assert(s); source_disconnect(s); + + if (s->type == SOURCE_IO && s->io.owned) + safe_close(s->io.fd); + free(s->description); free(s); } @@ -967,6 +978,7 @@ _public_ int sd_event_add_io( int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(fd >= 0, -EBADF); assert_return(!(events & ~(EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET)), -EINVAL); assert_return(callback, -EINVAL); @@ -1067,6 +1079,7 @@ _public_ int sd_event_add_time( int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(accuracy != (uint64_t) -1, -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); @@ -1148,6 +1161,7 @@ _public_ int sd_event_add_signal( int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(SIGNAL_VALID(sig), -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); @@ -1207,6 +1221,7 @@ _public_ int sd_event_add_child( int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(pid > 1, -EINVAL); assert_return(!(options & ~(WEXITED|WSTOPPED|WCONTINUED)), -EINVAL); assert_return(options != 0, -EINVAL); @@ -1264,6 +1279,7 @@ _public_ int sd_event_add_defer( int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(callback, -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); @@ -1298,6 +1314,7 @@ _public_ int sd_event_add_post( int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(callback, -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); @@ -1336,6 +1353,7 @@ _public_ int sd_event_add_exit( int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(callback, -EINVAL); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); @@ -1481,6 +1499,21 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) { return 0; } +_public_ int sd_event_source_get_io_fd_own(sd_event_source *s) { + assert_return(s, -EINVAL); + assert_return(s->type == SOURCE_IO, -EDOM); + + return s->io.owned; +} + +_public_ int sd_event_source_set_io_fd_own(sd_event_source *s, int own) { + assert_return(s, -EINVAL); + assert_return(s->type == SOURCE_IO, -EDOM); + + s->io.owned = own; + return 0; +} + _public_ int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events) { assert_return(s, -EINVAL); assert_return(events, -EINVAL); @@ -2449,6 +2482,7 @@ _public_ int sd_event_prepare(sd_event *e) { int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(!event_pid_changed(e), -ECHILD); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); @@ -2506,6 +2540,7 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { int r, m, i; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(!event_pid_changed(e), -ECHILD); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(e->state == SD_EVENT_ARMED, -EBUSY); @@ -2612,6 +2647,7 @@ _public_ int sd_event_dispatch(sd_event *e) { int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(!event_pid_changed(e), -ECHILD); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(e->state == SD_EVENT_PENDING, -EBUSY); @@ -2653,6 +2689,7 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) { int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(!event_pid_changed(e), -ECHILD); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); @@ -2697,6 +2734,7 @@ _public_ int sd_event_loop(sd_event *e) { int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(!event_pid_changed(e), -ECHILD); assert_return(e->state == SD_EVENT_INITIAL, -EBUSY); @@ -2718,6 +2756,7 @@ finish: _public_ int sd_event_get_fd(sd_event *e) { assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(!event_pid_changed(e), -ECHILD); return e->epoll_fd; @@ -2725,6 +2764,7 @@ _public_ int sd_event_get_fd(sd_event *e) { _public_ int sd_event_get_state(sd_event *e) { assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(!event_pid_changed(e), -ECHILD); return e->state; @@ -2732,6 +2772,7 @@ _public_ int sd_event_get_state(sd_event *e) { _public_ int sd_event_get_exit_code(sd_event *e, int *code) { assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(code, -EINVAL); assert_return(!event_pid_changed(e), -ECHILD); @@ -2744,6 +2785,7 @@ _public_ int sd_event_get_exit_code(sd_event *e, int *code) { _public_ int sd_event_exit(sd_event *e, int code) { assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(e->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(e), -ECHILD); @@ -2755,6 +2797,7 @@ _public_ int sd_event_exit(sd_event *e, int code) { _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) { assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(usec, -EINVAL); assert_return(!event_pid_changed(e), -ECHILD); @@ -2779,8 +2822,6 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) { } _public_ int sd_event_default(sd_event **ret) { - - static thread_local sd_event *default_event = NULL; sd_event *e = NULL; int r; @@ -2806,6 +2847,7 @@ _public_ int sd_event_default(sd_event **ret) { _public_ int sd_event_get_tid(sd_event *e, pid_t *tid) { assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(tid, -EINVAL); assert_return(!event_pid_changed(e), -ECHILD); @@ -2821,6 +2863,7 @@ _public_ int sd_event_set_watchdog(sd_event *e, int b) { int r; assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(!event_pid_changed(e), -ECHILD); if (e->watchdog == !!b) @@ -2871,6 +2914,7 @@ fail: _public_ int sd_event_get_watchdog(sd_event *e) { assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(!event_pid_changed(e), -ECHILD); return e->watchdog; @@ -2878,6 +2922,7 @@ _public_ int sd_event_get_watchdog(sd_event *e) { _public_ int sd_event_get_iteration(sd_event *e, uint64_t *ret) { assert_return(e, -EINVAL); + assert_return(e = event_resolve(e), -ENOPKG); assert_return(!event_pid_changed(e), -ECHILD); *ret = e->iteration; diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c index 7f32838c6f..9873ae4a58 100644 --- a/src/libsystemd/sd-event/test-event.c +++ b/src/libsystemd/sd-event/test-event.c @@ -27,6 +27,7 @@ #include "macro.h" #include "signal-util.h" #include "util.h" +#include "process-util.h" static int prepare_handler(sd_event_source *s, void *userdata) { log_info("preparing %c", PTR_TO_INT(userdata)); @@ -97,7 +98,7 @@ static int signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, assert_se(pid >= 0); if (pid == 0) - _exit(0); + _exit(EXIT_SUCCESS); assert_se(sd_event_add_child(sd_event_source_get_event(s), &p, pid, WEXITED, child_handler, INT_TO_PTR('f')) >= 0); assert_se(sd_event_source_set_enabled(p, SD_EVENT_ONESHOT) >= 0); diff --git a/src/libsystemd/sd-id128/id128-util.c b/src/libsystemd/sd-id128/id128-util.c index 5541e8d47e..a6e38578b1 100644 --- a/src/libsystemd/sd-id128/id128-util.c +++ b/src/libsystemd/sd-id128/id128-util.c @@ -18,6 +18,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <fcntl.h> #include <unistd.h> diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c index e8adaa6823..69572d1a51 100644 --- a/src/libsystemd/sd-login/sd-login.c +++ b/src/libsystemd/sd-login/sd-login.c @@ -1061,10 +1061,15 @@ _public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) { } _public_ int sd_login_monitor_flush(sd_login_monitor *m) { + int r; assert_return(m, -EINVAL); - return flush_fd(MONITOR_TO_FD(m)); + r = flush_fd(MONITOR_TO_FD(m)); + if (r < 0) + return r; + + return 0; } _public_ int sd_login_monitor_get_fd(sd_login_monitor *m) { diff --git a/src/libsystemd/sd-netlink/generic-netlink.c b/src/libsystemd/sd-netlink/generic-netlink.c new file mode 100644 index 0000000000..771658d9ae --- /dev/null +++ b/src/libsystemd/sd-netlink/generic-netlink.c @@ -0,0 +1,97 @@ +#include <linux/genetlink.h> + +#include "sd-netlink.h" +#include "netlink-internal.h" +#include "alloc-util.h" + +typedef struct { + const char* name; + uint8_t version; +} genl_family; + +static const genl_family genl_families[] = { + [SD_GENL_ID_CTRL] = { .name = "", .version = 1 }, + [SD_GENL_WIREGUARD] = { .name = "wireguard", .version = 1 }, +}; + +int sd_genl_socket_open(sd_netlink **ret) { + return netlink_open_family(ret, NETLINK_GENERIC); +} +static int lookup_id(sd_netlink *nl, sd_genl_family family, uint16_t *id); + +static int genl_message_new(sd_netlink *nl, sd_genl_family family, uint16_t nlmsg_type, uint8_t cmd, sd_netlink_message **ret) { + int r; + struct genlmsghdr *genl; + const NLType *genl_cmd_type, *nl_type; + const NLTypeSystem *type_system; + size_t size; + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + + assert_return(nl->protocol == NETLINK_GENERIC, -EINVAL); + + r = type_system_get_type(&genl_family_type_system_root, &genl_cmd_type, family); + if (r < 0) + return r; + + r = message_new_empty(nl, &m); + if (r < 0) + return r; + + size = NLMSG_SPACE(sizeof(struct genlmsghdr)); + m->hdr = malloc0(size); + if (!m->hdr) + return -ENOMEM; + + m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + + type_get_type_system(genl_cmd_type, &type_system); + + r = type_system_get_type(type_system, &nl_type, cmd); + if (r < 0) + return r; + + m->hdr->nlmsg_len = size; + m->hdr->nlmsg_type = nlmsg_type; + + type_get_type_system(nl_type, &m->containers[0].type_system); + genl = NLMSG_DATA(m->hdr); + genl->cmd = cmd; + genl->version = genl_families[family].version; + + *ret = m; + m = NULL; + + return 0; +} + +int sd_genl_message_new(sd_netlink *nl, sd_genl_family family, uint8_t cmd, sd_netlink_message **ret) { + int r; + uint16_t id = GENL_ID_CTRL; + + if (family != SD_GENL_ID_CTRL) { + r = lookup_id(nl, family, &id); + if (r < 0) + return r; + } + + return genl_message_new(nl, family, id, cmd, ret); +} + +static int lookup_id(sd_netlink *nl, sd_genl_family family, uint16_t *id) { + int r; + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; + + r = sd_genl_message_new(nl, SD_GENL_ID_CTRL, CTRL_CMD_GETFAMILY, &req); + if (r < 0) + return r; + + r = sd_netlink_message_append_string(req, CTRL_ATTR_FAMILY_NAME, genl_families[family].name); + if (r < 0) + return r; + + r = sd_netlink_call(nl, req, 0, &reply); + if (r < 0) + return r; + + return sd_netlink_message_read_u16(reply, CTRL_ATTR_FAMILY_ID, id); +} diff --git a/src/libsystemd/sd-netlink/local-addresses.c b/src/libsystemd/sd-netlink/local-addresses.c index 23acec4061..81e55b6d9f 100644 --- a/src/libsystemd/sd-netlink/local-addresses.c +++ b/src/libsystemd/sd-netlink/local-addresses.c @@ -225,6 +225,8 @@ int local_gateways(sd_netlink *context, int ifindex, int af, struct local_addres continue; r = sd_netlink_message_read_u32(m, RTA_OIF, &ifi); + if (r == -ENODATA) /* Not all routes have an RTA_OIF attribute (for example nexthop ones) */ + continue; if (r < 0) return r; if (ifindex > 0 && (int) ifi != ifindex) diff --git a/src/libsystemd/sd-netlink/netlink-internal.h b/src/libsystemd/sd-netlink/netlink-internal.h index f045ff67ca..dc553d708c 100644 --- a/src/libsystemd/sd-netlink/netlink-internal.h +++ b/src/libsystemd/sd-netlink/netlink-internal.h @@ -62,6 +62,8 @@ struct sd_netlink { struct sockaddr_nl nl; } sockaddr; + int protocol; + Hashmap *broadcast_group_refs; bool broadcast_group_dont_leave:1; /* until we can rely on 4.2 */ @@ -111,6 +113,8 @@ struct sd_netlink_message { sd_netlink *rtnl; + int protocol; + struct nlmsghdr *hdr; struct netlink_container containers[RTNL_CONTAINER_DEPTH]; unsigned n_containers; /* number of containers */ @@ -123,6 +127,8 @@ struct sd_netlink_message { int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type); int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret); +int netlink_open_family(sd_netlink **ret, int family); + int socket_open(int family); int socket_bind(sd_netlink *nl); int socket_broadcast_group_ref(sd_netlink *nl, unsigned group); diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c index c88754540e..af3d13edcd 100644 --- a/src/libsystemd/sd-netlink/netlink-message.c +++ b/src/libsystemd/sd-netlink/netlink-message.c @@ -55,7 +55,7 @@ int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret) { return -ENOMEM; m->n_ref = REFCNT_INIT; - + m->protocol = rtnl->protocol; m->sealed = false; *ret = m; @@ -66,10 +66,15 @@ int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret) { int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; const NLType *nl_type; + const NLTypeSystem *type_system_root; size_t size; int r; - r = type_system_get_type(&type_system_root, &nl_type, type); + assert_return(rtnl, -EINVAL); + + type_system_root = type_system_get_root(rtnl->protocol); + + r = type_system_get_type(type_system_root, &nl_type, type); if (r < 0) return r; @@ -111,9 +116,10 @@ int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) { } sd_netlink_message *sd_netlink_message_ref(sd_netlink_message *m) { - if (m) - assert_se(REFCNT_INC(m->n_ref) >= 2); + if (!m) + return NULL; + assert_se(REFCNT_INC(m->n_ref) >= 2); return m; } @@ -186,6 +192,10 @@ static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *da /* get the new message size (with padding at the end) */ message_length = offset + RTA_ALIGN(rta_length); + /* buffer should be smaller than both one page or 8K to be accepted by the kernel */ + if (message_length > MIN(page_size(), 8192UL)) + return -ENOBUFS; + /* realloc to fit the new attribute */ new_hdr = realloc(m->hdr, message_length); if (!new_hdr) @@ -490,7 +500,7 @@ int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned shor if (r < 0) return r; - /* do we evere need non-null size */ + /* do we ever need non-null size */ r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0); if (r < 0) return r; @@ -500,14 +510,53 @@ int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned shor return 0; } - int sd_netlink_message_close_container(sd_netlink_message *m) { assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); assert_return(m->n_containers > 0, -EINVAL); m->containers[m->n_containers].type_system = NULL; + m->containers[m->n_containers].offset = 0; + m->n_containers--; + + return 0; +} + +int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type) { + int r; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(m->n_containers > 0, -EINVAL); + + r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0); + if (r < 0) + return r; + + m->containers[m->n_containers].offset = r; + m->n_containers++; + m->containers[m->n_containers].type_system = m->containers[m->n_containers - 1].type_system; + + return 0; +} + +int sd_netlink_message_cancel_array(sd_netlink_message *m) { + unsigned i; + uint32_t rta_len; + + assert_return(m, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(m->n_containers > 1, -EINVAL); + + rta_len = GET_CONTAINER(m, (m->n_containers - 1))->rta_len; + + for (i = 0; i < m->n_containers; i++) + GET_CONTAINER(m, i)->rta_len -= rta_len; + + m->hdr->nlmsg_len -= rta_len; + m->n_containers--; + m->containers[m->n_containers].type_system = NULL; return 0; } @@ -519,13 +568,14 @@ static int netlink_message_read_internal(sd_netlink_message *m, unsigned short t assert_return(m, -EINVAL); assert_return(m->sealed, -EPERM); assert_return(data, -EINVAL); + assert(m->n_containers < RTNL_CONTAINER_DEPTH); assert(m->containers[m->n_containers].attributes); assert(type < m->containers[m->n_containers].n_attributes); attribute = &m->containers[m->n_containers].attributes[type]; - if (!attribute->offset) + if (attribute->offset == 0) return -ENODATA; rta = (struct rtattr*)((uint8_t *) m->hdr + attribute->offset); @@ -745,7 +795,7 @@ static int netlink_container_parse(sd_netlink_message *m, if (type >= count) continue; - if (attributes[type].offset) + if (attributes[type].offset != 0) log_debug("rtnl: message parse - overwriting repeated attribute"); attributes[type].offset = (uint8_t *) rta - (uint8_t *) m->hdr; @@ -899,6 +949,7 @@ int sd_netlink_message_get_errno(sd_netlink_message *m) { int sd_netlink_message_rewind(sd_netlink_message *m) { const NLType *nl_type; + const NLTypeSystem *type_system_root; uint16_t type; size_t size; unsigned i; @@ -910,6 +961,8 @@ int sd_netlink_message_rewind(sd_netlink_message *m) { if (!m->sealed) rtnl_message_seal(m); + type_system_root = type_system_get_root(m->protocol); + for (i = 1; i <= m->n_containers; i++) m->containers[i].attributes = mfree(m->containers[i].attributes); @@ -921,7 +974,7 @@ int sd_netlink_message_rewind(sd_netlink_message *m) { assert(m->hdr); - r = type_system_get_type(&type_system_root, &nl_type, m->hdr->nlmsg_type); + r = type_system_get_type(type_system_root, &nl_type, m->hdr->nlmsg_type); if (r < 0) return r; diff --git a/src/libsystemd/sd-netlink/netlink-socket.c b/src/libsystemd/sd-netlink/netlink-socket.c index 22be94382a..e08248c9f6 100644 --- a/src/libsystemd/sd-netlink/netlink-socket.c +++ b/src/libsystemd/sd-netlink/netlink-socket.c @@ -269,13 +269,13 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool }; struct cmsghdr *cmsg; uint32_t group = 0; - int r; + ssize_t n; assert(fd >= 0); assert(iov); - r = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0)); - if (r < 0) { + n = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0)); + if (n < 0) { /* no data */ if (errno == ENOBUFS) log_debug("rtnl: kernel receive buffer overrun"); @@ -291,8 +291,8 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool if (peek) { /* drop the message */ - r = recvmsg(fd, &msg, 0); - if (r < 0) + n = recvmsg(fd, &msg, 0); + if (n < 0) return IN_SET(errno, EAGAIN, EINTR) ? 0 : -errno; } @@ -313,7 +313,7 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool if (_group) *_group = group; - return r; + return (int) n; } /* On success, the number of bytes received is returned and *ret points to the received message @@ -330,17 +330,20 @@ int socket_read_message(sd_netlink *rtnl) { size_t len; int r; unsigned i = 0; + const NLTypeSystem *type_system_root; assert(rtnl); assert(rtnl->rbuffer); assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr)); + type_system_root = type_system_get_root(rtnl->protocol); + /* read nothing, just get the pending message size */ r = socket_recv_message(rtnl->fd, &iov, NULL, true); if (r <= 0) return r; else - len = (size_t)r; + len = (size_t) r; /* make room for the pending message */ if (!greedy_realloc((void **)&rtnl->rbuffer, @@ -356,7 +359,7 @@ int socket_read_message(sd_netlink *rtnl) { if (r <= 0) return r; else - len = (size_t)r; + len = (size_t) r; if (len > rtnl->rbuffer_allocated) /* message did not fit in read buffer */ @@ -396,7 +399,7 @@ int socket_read_message(sd_netlink *rtnl) { } /* check that we support this message type */ - r = type_system_get_type(&type_system_root, &nl_type, new_msg->nlmsg_type); + r = type_system_get_type(type_system_root, &nl_type, new_msg->nlmsg_type); if (r < 0) { if (r == -EOPNOTSUPP) log_debug("sd-netlink: ignored message with unknown type: %i", @@ -433,7 +436,7 @@ int socket_read_message(sd_netlink *rtnl) { m = NULL; } - if (len) + if (len > 0) log_debug("sd-netlink: discarding %zu bytes of incoming message", len); if (!first) @@ -459,9 +462,9 @@ int socket_read_message(sd_netlink *rtnl) { } else { /* we only got a partial multi-part message, push it on the partial read queue */ - if (i < rtnl->rqueue_partial_size) { + if (i < rtnl->rqueue_partial_size) rtnl->rqueue_partial[i] = first; - } else { + else { r = rtnl_rqueue_partial_make_room(rtnl); if (r < 0) return r; diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c index f8be296d37..0ee7d6f0dc 100644 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ b/src/libsystemd/sd-netlink/netlink-types.c @@ -18,25 +18,22 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <netinet/in.h> #include <stdint.h> #include <sys/socket.h> #include <linux/netlink.h> #include <linux/rtnetlink.h> +#include <linux/genetlink.h> +#include <linux/ip.h> +#include <linux/if.h> #include <linux/can/netlink.h> #include <linux/fib_rules.h> -#include <linux/in6.h> -#include <linux/veth.h> -#include <linux/if_bridge.h> #include <linux/if_addr.h> #include <linux/if_addrlabel.h> -#include <linux/if.h> -#include <linux/ip.h> -#include <linux/if_addr.h> #include <linux/if_bridge.h> #include <linux/if_link.h> #include <linux/if_tunnel.h> -#include <linux/fib_rules.h> - +#include <linux/veth.h> #if HAVE_VXCAN_INFO_PEER #include <linux/can/vxcan.h> #endif @@ -44,8 +41,10 @@ #include "macro.h" #include "missing.h" #include "netlink-types.h" +#include "sd-netlink.h" #include "string-table.h" #include "util.h" +#include "wireguard-netlink.h" /* Maximum ARP IP target defined in kernel */ #define BOND_MAX_ARP_TARGETS 16 @@ -102,6 +101,7 @@ static const NLType rtnl_link_info_data_vxcan_types[] = { static const NLType rtnl_link_info_data_ipvlan_types[] = { [IFLA_IPVLAN_MODE] = { .type = NETLINK_TYPE_U16 }, + [IFLA_IPVLAN_FLAGS] = { .type = NETLINK_TYPE_U16 }, }; static const NLType rtnl_link_info_data_macvlan_types[] = { @@ -337,7 +337,7 @@ static const char* const nl_union_link_info_data_table[] = { [NL_UNION_LINK_INFO_DATA_VCAN] = "vcan", [NL_UNION_LINK_INFO_DATA_GENEVE] = "geneve", [NL_UNION_LINK_INFO_DATA_VXCAN] = "vxcan", - + [NL_UNION_LINK_INFO_DATA_WIREGUARD] = "wireguard", }; DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData); @@ -552,6 +552,8 @@ static const NLType rtnl_route_metrics_types[] = { [RTAX_INITCWND] = { .type = NETLINK_TYPE_U32 }, [RTAX_FEATURES] = { .type = NETLINK_TYPE_U32 }, [RTAX_RTO_MIN] = { .type = NETLINK_TYPE_U32 }, + [RTAX_INITRWND] = { .type = NETLINK_TYPE_U32 }, + [RTAX_QUICKACK] = { .type = NETLINK_TYPE_U32 }, }; static const NLTypeSystem rtnl_route_metrics_type_system = { @@ -663,11 +665,98 @@ static const NLType rtnl_types[] = { [RTM_GETRULE] = { .type = NETLINK_TYPE_NESTED, .type_system = &rtnl_routing_policy_rule_type_system, .size = sizeof(struct rtmsg) }, }; -const NLTypeSystem type_system_root = { +const NLTypeSystem rtnl_type_system_root = { .count = ELEMENTSOF(rtnl_types), .types = rtnl_types, }; +static const NLType genl_wireguard_allowedip_types[] = { + [WGALLOWEDIP_A_FAMILY] = { .type = NETLINK_TYPE_U16 }, + [WGALLOWEDIP_A_IPADDR] = { .type = NETLINK_TYPE_IN_ADDR }, + [WGALLOWEDIP_A_CIDR_MASK] = { .type = NETLINK_TYPE_U8 }, +}; + +static const NLTypeSystem genl_wireguard_allowedip_type_system = { + .count = ELEMENTSOF(genl_wireguard_allowedip_types), + .types = genl_wireguard_allowedip_types, +}; + +static const NLType genl_wireguard_peer_types[] = { + [WGPEER_A_PUBLIC_KEY] = { .size = WG_KEY_LEN }, + [WGPEER_A_FLAGS] = { .type = NETLINK_TYPE_U32 }, + [WGPEER_A_PRESHARED_KEY] = { .size = WG_KEY_LEN }, + [WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = { .type = NETLINK_TYPE_U32 }, + [WGPEER_A_ENDPOINT] = { /* either size of sockaddr_in or sockaddr_in6 depending on address family */ }, + [WGPEER_A_ALLOWEDIPS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_allowedip_type_system }, +}; + +static const NLTypeSystem genl_wireguard_peer_type_system = { + .count = ELEMENTSOF(genl_wireguard_peer_types), + .types = genl_wireguard_peer_types, +}; + +static const NLType genl_wireguard_set_device_types[] = { + [WGDEVICE_A_IFINDEX] = { .type = NETLINK_TYPE_U32 }, + [WGDEVICE_A_IFNAME] = { .type = NETLINK_TYPE_STRING }, + [WGDEVICE_A_FLAGS] = { .type = NETLINK_TYPE_U32 }, + [WGDEVICE_A_PRIVATE_KEY] = { .size = WG_KEY_LEN }, + [WGDEVICE_A_LISTEN_PORT] = { .type = NETLINK_TYPE_U16 }, + [WGDEVICE_A_FWMARK] = { .type = NETLINK_TYPE_U32 }, + [WGDEVICE_A_PEERS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_peer_type_system }, +}; + +static const NLTypeSystem genl_wireguard_set_device_type_system = { + .count = ELEMENTSOF(genl_wireguard_set_device_types), + .types = genl_wireguard_set_device_types, +}; + +static const NLType genl_wireguard_cmds[] = { + [WG_CMD_SET_DEVICE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_set_device_type_system }, +}; + +static const NLTypeSystem genl_wireguard_type_system = { + .count = ELEMENTSOF(genl_wireguard_cmds), + .types = genl_wireguard_cmds, +}; + +static const NLType genl_get_family_types[] = { + [CTRL_ATTR_FAMILY_NAME] = { .type = NETLINK_TYPE_STRING }, + [CTRL_ATTR_FAMILY_ID] = { .type = NETLINK_TYPE_U16 }, +}; + +static const NLTypeSystem genl_get_family_type_system = { + .count = ELEMENTSOF(genl_get_family_types), + .types = genl_get_family_types, +}; + +static const NLType genl_ctrl_id_ctrl_cmds[] = { + [CTRL_CMD_GETFAMILY] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_get_family_type_system }, +}; + +static const NLTypeSystem genl_ctrl_id_ctrl_type_system = { + .count = ELEMENTSOF(genl_ctrl_id_ctrl_cmds), + .types = genl_ctrl_id_ctrl_cmds, +}; + +static const NLType genl_families[] = { + [SD_GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_id_ctrl_type_system }, + [SD_GENL_WIREGUARD] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_type_system }, +}; + +const NLTypeSystem genl_family_type_system_root = { + .count = ELEMENTSOF(genl_families), + .types = genl_families, +}; + +static const NLType genl_types[] = { + [GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_get_family_type_system, .size = sizeof(struct genlmsghdr) }, +}; + +const NLTypeSystem genl_type_system_root = { + .count = ELEMENTSOF(genl_types), + .types = genl_types, +}; + uint16_t type_get_type(const NLType *type) { assert(type); return type->type; @@ -701,6 +790,15 @@ uint16_t type_system_get_count(const NLTypeSystem *type_system) { return type_system->count; } +const NLTypeSystem *type_system_get_root(int protocol) { + switch (protocol) { + case NETLINK_GENERIC: + return &genl_type_system_root; + default: /* NETLINK_ROUTE: */ + return &rtnl_type_system_root; + } +} + int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type) { const NLType *nl_type; diff --git a/src/libsystemd/sd-netlink/netlink-types.h b/src/libsystemd/sd-netlink/netlink-types.h index 57b46339f9..ea7f8d5e6c 100644 --- a/src/libsystemd/sd-netlink/netlink-types.h +++ b/src/libsystemd/sd-netlink/netlink-types.h @@ -54,13 +54,16 @@ struct NLTypeSystemUnion { const NLTypeSystem *type_systems; }; -extern const NLTypeSystem type_system_root; +extern const NLTypeSystem rtnl_type_system_root; +extern const NLTypeSystem genl_type_system_root; +extern const NLTypeSystem genl_family_type_system_root; uint16_t type_get_type(const NLType *type); size_t type_get_size(const NLType *type); void type_get_type_system(const NLType *type, const NLTypeSystem **ret); void type_get_type_system_union(const NLType *type, const NLTypeSystemUnion **ret); +const NLTypeSystem* type_system_get_root(int protocol); uint16_t type_system_get_count(const NLTypeSystem *type_system); int type_system_get_type(const NLTypeSystem *type_system, const NLType **ret, uint16_t type); int type_system_get_type_system(const NLTypeSystem *type_system, const NLTypeSystem **ret, uint16_t type); @@ -91,6 +94,7 @@ typedef enum NLUnionLinkInfoData { NL_UNION_LINK_INFO_DATA_VCAN, NL_UNION_LINK_INFO_DATA_GENEVE, NL_UNION_LINK_INFO_DATA_VXCAN, + NL_UNION_LINK_INFO_DATA_WIREGUARD, _NL_UNION_LINK_INFO_DATA_MAX, _NL_UNION_LINK_INFO_DATA_INVALID = -1 } NLUnionLinkInfoData; diff --git a/src/libsystemd/sd-netlink/netlink-util.c b/src/libsystemd/sd-netlink/netlink-util.c index b32fad271a..7680b30e70 100644 --- a/src/libsystemd/sd-netlink/netlink-util.c +++ b/src/libsystemd/sd-netlink/netlink-util.c @@ -98,13 +98,13 @@ int rtnl_set_link_properties(sd_netlink **rtnl, int ifindex, const char *alias, return 0; } -int rtnl_message_new_synthetic_error(int error, uint32_t serial, sd_netlink_message **ret) { +int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret) { struct nlmsgerr *err; int r; assert(error <= 0); - r = message_new(NULL, ret, NLMSG_ERROR); + r = message_new(rtnl, ret, NLMSG_ERROR); if (r < 0) return r; diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h index c804f5514c..795e4dc15c 100644 --- a/src/libsystemd/sd-netlink/netlink-util.h +++ b/src/libsystemd/sd-netlink/netlink-util.h @@ -24,7 +24,7 @@ #include "util.h" -int rtnl_message_new_synthetic_error(int error, uint32_t serial, sd_netlink_message **ret); +int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret); uint32_t rtnl_message_get_serial(sd_netlink_message *m); void rtnl_message_seal(sd_netlink_message *m); diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c index 924b0c954f..116e287bb6 100644 --- a/src/libsystemd/sd-netlink/sd-netlink.c +++ b/src/libsystemd/sd-netlink/sd-netlink.c @@ -30,6 +30,7 @@ #include "missing.h" #include "netlink-internal.h" #include "netlink-util.h" +#include "process-util.h" #include "socket-util.h" #include "util.h" @@ -46,6 +47,7 @@ static int sd_netlink_new(sd_netlink **ret) { rtnl->fd = -1; rtnl->sockaddr.nl.nl_family = AF_NETLINK; rtnl->original_pid = getpid_cached(); + rtnl->protocol = -1; LIST_HEAD_INIT(rtnl->match_callbacks); @@ -106,6 +108,8 @@ static bool rtnl_pid_changed(sd_netlink *rtnl) { int sd_netlink_open_fd(sd_netlink **ret, int fd) { _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; int r; + int protocol; + socklen_t l; assert_return(ret, -EINVAL); assert_return(fd >= 0, -EBADF); @@ -114,11 +118,18 @@ int sd_netlink_open_fd(sd_netlink **ret, int fd) { if (r < 0) return r; + l = sizeof(protocol); + r = getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &protocol, &l); + if (r < 0) + return r; + rtnl->fd = fd; + rtnl->protocol = protocol; r = socket_bind(rtnl); if (r < 0) { rtnl->fd = -1; /* on failure, the caller remains owner of the fd, hence don't close it here */ + rtnl->protocol = -1; return r; } @@ -128,11 +139,11 @@ int sd_netlink_open_fd(sd_netlink **ret, int fd) { return 0; } -int sd_netlink_open(sd_netlink **ret) { +int netlink_open_family(sd_netlink **ret, int family) { _cleanup_close_ int fd = -1; int r; - fd = socket_open(NETLINK_ROUTE); + fd = socket_open(family); if (fd < 0) return fd; @@ -145,6 +156,10 @@ int sd_netlink_open(sd_netlink **ret) { return 0; } +int sd_netlink_open(sd_netlink **ret) { + return netlink_open_family(ret, NETLINK_ROUTE); +} + int sd_netlink_inc_rcvbuf(sd_netlink *rtnl, size_t size) { assert_return(rtnl, -EINVAL); assert_return(!rtnl_pid_changed(rtnl), -ECHILD); @@ -309,7 +324,7 @@ static int process_timeout(sd_netlink *rtnl) { if (c->timeout > n) return 0; - r = rtnl_message_new_synthetic_error(-ETIMEDOUT, c->serial, &m); + r = rtnl_message_new_synthetic_error(rtnl, -ETIMEDOUT, c->serial, &m); if (r < 0) return r; diff --git a/src/libsystemd/sd-netlink/test-local-addresses.c b/src/libsystemd/sd-netlink/test-local-addresses.c index bb195b9de9..bf11042221 100644 --- a/src/libsystemd/sd-netlink/test-local-addresses.c +++ b/src/libsystemd/sd-netlink/test-local-addresses.c @@ -38,6 +38,10 @@ int main(int argc, char *argv[]) { struct local_address *a; int n; + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + a = NULL; n = local_addresses(NULL, 0, AF_UNSPEC, &a); assert_se(n >= 0); diff --git a/src/libsystemd/sd-netlink/test-netlink.c b/src/libsystemd/sd-netlink/test-netlink.c index 73e5af0060..9ccc8ea607 100644 --- a/src/libsystemd/sd-netlink/test-netlink.c +++ b/src/libsystemd/sd-netlink/test-netlink.c @@ -143,13 +143,13 @@ static void test_address_get(sd_netlink *rtnl, int ifindex) { } -static void test_route(void) { +static void test_route(sd_netlink *rtnl) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req; struct in_addr addr, addr_data; uint32_t index = 2, u32_data; int r; - r = sd_rtnl_message_new_route(NULL, &req, RTM_NEWROUTE, AF_INET, RTPROT_STATIC); + r = sd_rtnl_message_new_route(rtnl, &req, RTM_NEWROUTE, AF_INET, RTPROT_STATIC); if (r < 0) { log_error_errno(r, "Could not create RTM_NEWROUTE message: %m"); return; @@ -291,13 +291,13 @@ static void test_pipe(int ifindex) { assert_se((rtnl = sd_netlink_unref(rtnl)) == NULL); } -static void test_container(void) { +static void test_container(sd_netlink *rtnl) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; uint16_t u16_data; uint32_t u32_data; const char *string_data; - assert_se(sd_rtnl_message_new_link(NULL, &m, RTM_NEWLINK, 0) >= 0); + assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0) >= 0); assert_se(sd_netlink_message_open_container(m, IFLA_LINKINFO) >= 0); assert_se(sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "vlan") >= 0); @@ -369,10 +369,10 @@ static void test_get_addresses(sd_netlink *rtnl) { } } -static void test_message(void) { +static void test_message(sd_netlink *rtnl) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - assert_se(rtnl_message_new_synthetic_error(-ETIMEDOUT, 1, &m) >= 0); + assert_se(rtnl_message_new_synthetic_error(rtnl, -ETIMEDOUT, 1, &m) >= 0); assert_se(sd_netlink_message_get_errno(m) == -ETIMEDOUT); } @@ -384,19 +384,19 @@ int main(void) { int if_loopback; uint16_t type; - test_message(); - test_match(); test_multiple(); - test_route(); - - test_container(); - assert_se(sd_netlink_open(&rtnl) >= 0); assert_se(rtnl); + test_route(rtnl); + + test_message(rtnl); + + test_container(rtnl); + if_loopback = (int) if_nametoindex("lo"); assert_se(if_loopback > 0); diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c index be3748e3ce..787642a7fb 100644 --- a/src/libsystemd/sd-resolve/sd-resolve.c +++ b/src/libsystemd/sd-resolve/sd-resolve.c @@ -40,6 +40,7 @@ #include "missing.h" #include "socket-util.h" #include "util.h" +#include "process-util.h" #define WORKERS_MIN 1U #define WORKERS_MAX 16U @@ -398,14 +399,9 @@ static int handle_request(int out_fd, const Packet *packet, size_t length) { static void* thread_worker(void *p) { sd_resolve *resolve = p; - sigset_t fullset; - - /* No signals in this thread please */ - assert_se(sigfillset(&fullset) == 0); - assert_se(pthread_sigmask(SIG_BLOCK, &fullset, NULL) == 0); /* Assign a pretty name to this thread */ - (void) prctl(PR_SET_NAME, (unsigned long) "sd-resolve"); + (void) pthread_setname_np(pthread_self(), "sd-resolve"); while (!resolve->dead) { union { @@ -437,8 +433,18 @@ static void* thread_worker(void *p) { } static int start_threads(sd_resolve *resolve, unsigned extra) { + sigset_t ss, saved_ss; unsigned n; - int r; + int r, k; + + if (sigfillset(&ss) < 0) + return -errno; + + /* No signals in forked off threads please. We set the mask before forking, so that the threads never exist + * with a different mask than a fully blocked one */ + r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss); + if (r > 0) + return -r; n = resolve->n_outstanding + extra; n = CLAMP(n, WORKERS_MIN, WORKERS_MAX); @@ -446,13 +452,22 @@ static int start_threads(sd_resolve *resolve, unsigned extra) { while (resolve->n_valid_workers < n) { r = pthread_create(&resolve->workers[resolve->n_valid_workers], NULL, thread_worker, resolve); - if (r != 0) - return -r; + if (r > 0) { + r = -r; + goto finish; + } resolve->n_valid_workers++; } - return 0; + r = 0; + +finish: + k = pthread_sigmask(SIG_SETMASK, &saved_ss, NULL); + if (k > 0 && r >= 0) + r = -k; + + return r; } static bool resolve_pid_changed(sd_resolve *r) { diff --git a/src/libsystemd/sd-resolve/test-resolve.c b/src/libsystemd/sd-resolve/test-resolve.c index 752eb15228..b728dee9dd 100644 --- a/src/libsystemd/sd-resolve/test-resolve.c +++ b/src/libsystemd/sd-resolve/test-resolve.c @@ -89,7 +89,9 @@ int main(int argc, char *argv[]) { assert_se(sd_resolve_default(&resolve) >= 0); /* Test a floating resolver query */ - sd_resolve_getaddrinfo(resolve, NULL, "redhat.com", "http", NULL, getaddrinfo_handler, NULL); + r = sd_resolve_getaddrinfo(resolve, NULL, "redhat.com", "http", NULL, getaddrinfo_handler, NULL); + if (r < 0) + log_error_errno(r, "sd_resolve_getaddrinfo(): %m"); /* Make a name -> address query */ r = sd_resolve_getaddrinfo(resolve, &q1, argc >= 2 ? argv[1] : "www.heise.de", NULL, &hints, getaddrinfo_handler, NULL); diff --git a/src/libudev/libudev-queue.c b/src/libudev/libudev-queue.c index b941afb773..85ceb263a3 100644 --- a/src/libudev/libudev-queue.c +++ b/src/libudev/libudev-queue.c @@ -268,8 +268,16 @@ _public_ int udev_queue_get_fd(struct udev_queue *udev_queue) { * Returns: the result of clearing the watch for queue changes. */ _public_ int udev_queue_flush(struct udev_queue *udev_queue) { + int r; + + assert(udev_queue); + if (udev_queue->fd < 0) return -EINVAL; - return flush_fd(udev_queue->fd); + r = flush_fd(udev_queue->fd); + if (r < 0) + return r; + + return 0; } diff --git a/src/libudev/meson.build b/src/libudev/meson.build index 30d6721b60..c381352ce8 100644 --- a/src/libudev/meson.build +++ b/src/libudev/meson.build @@ -31,21 +31,8 @@ libudev_sources = files(''' ############################################################ -libudev_sym = 'libudev.sym' -libudev_sym_path = '@0@/@1@'.format(meson.current_source_dir(), libudev_sym) -libudev = shared_library( - 'udev', - libudev_sources, - version : libudev_version, - include_directories : includes, - link_args : ['-shared', - '-Wl,--version-script=' + libudev_sym_path], - link_with : [libbasic, - libsystemd_internal], - dependencies : [threads], - link_depends : libudev_sym, - install : true, - install_dir : rootlibdir) +libudev_sym = files('libudev.sym') +libudev_sym_path = meson.current_source_dir() + '/libudev.sym' install_headers('libudev.h') libudev_h_path = '@0@/libudev.h'.format(meson.current_source_dir()) diff --git a/src/locale/localectl.c b/src/locale/localectl.c index f09fe42626..af39e431f5 100644 --- a/src/locale/localectl.c +++ b/src/locale/localectl.c @@ -595,7 +595,7 @@ static int localectl_main(sd_bus *bus, int argc, char *argv[]) { } int main(int argc, char*argv[]) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + sd_bus *bus = NULL; int r; setlocale(LC_ALL, ""); @@ -615,6 +615,9 @@ int main(int argc, char*argv[]) { r = localectl_main(bus, argc, argv); finish: + /* make sure we terminate the bus connection first, and then close the + * pager, see issue #3543 for the details. */ + sd_bus_flush_close_unref(bus); pager_close(); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/src/locale/localed.c b/src/locale/localed.c index 3e3f03e046..02f5e8c656 100644 --- a/src/locale/localed.c +++ b/src/locale/localed.c @@ -652,9 +652,9 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { if (r < 0) return log_error_errno(r, "Failed to register object: %m"); - r = sd_bus_request_name(bus, "org.freedesktop.locale1", 0); + r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.locale1", 0, NULL, NULL); if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); + return log_error_errno(r, "Failed to request name: %m"); r = sd_bus_attach_event(bus, event, 0); if (r < 0) diff --git a/src/login/73-seat-late.rules.in b/src/login/73-seat-late.rules.m4 index d2546c8ee9..4db8d4dd4c 100644 --- a/src/login/73-seat-late.rules.in +++ b/src/login/73-seat-late.rules.m4 @@ -13,7 +13,8 @@ ENV{ID_SEAT}=="", ENV{ID_AUTOSEAT}=="1", ENV{ID_FOR_SEAT}!="", ENV{ID_SEAT}="sea ENV{ID_SEAT}=="", IMPORT{parent}="ID_SEAT" ENV{ID_SEAT}!="", TAG+="$env{ID_SEAT}" - -TAG=="uaccess", ENV{MAJOR}!="", RUN{builtin}+="uaccess" +m4_ifdef(`HAVE_ACL',`` +TAG=="uaccess", ENV{MAJOR}!="", RUN{builtin}+="uaccess"'' +)m4_dnl LABEL="seat_late_end" diff --git a/src/login/inhibit.c b/src/login/inhibit.c index 7b9e3f0f6e..22657f9eda 100644 --- a/src/login/inhibit.c +++ b/src/login/inhibit.c @@ -266,26 +266,17 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - pid = fork(); - if (pid < 0) { - log_error_errno(errno, "Failed to fork: %m"); + r = safe_fork("(inhibit)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid); + if (r < 0) return EXIT_FAILURE; - } - - if (pid == 0) { + if (r == 0) { /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - close_all_fds(NULL, 0); - execvp(argv[optind], argv + optind); log_error_errno(errno, "Failed to execute %s: %m", argv[optind]); _exit(EXIT_FAILURE); } - r = wait_for_terminate_and_warn(argv[optind], pid, true); + r = wait_for_terminate_and_check(argv[optind], pid, WAIT_LOG); return r < 0 ? EXIT_FAILURE : r; } diff --git a/src/login/loginctl.c b/src/login/loginctl.c index dfcaff6195..c811ee6c5e 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -1584,7 +1584,7 @@ static int loginctl_main(int argc, char *argv[], sd_bus *bus) { } int main(int argc, char *argv[]) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + sd_bus *bus = NULL; int r; setlocale(LC_ALL, ""); @@ -1607,6 +1607,9 @@ int main(int argc, char *argv[]) { r = loginctl_main(argc, argv, bus); finish: + /* make sure we terminate the bus connection first, and then close the + * pager, see issue #3543 for the details. */ + sd_bus_flush_close_unref(bus); pager_close(); polkit_agent_close(); diff --git a/src/login/logind-core.c b/src/login/logind-core.c index adeba746f5..e338682f41 100644 --- a/src/login/logind-core.c +++ b/src/login/logind-core.c @@ -31,6 +31,7 @@ #include "fd-util.h" #include "logind.h" #include "parse-util.h" +#include "process-util.h" #include "strv.h" #include "terminal-util.h" #include "udev-util.h" diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c index 8a6487ea45..e14835292e 100644 --- a/src/login/logind-inhibit.c +++ b/src/login/logind-inhibit.c @@ -305,7 +305,7 @@ int inhibitor_create_fifo(Inhibitor *i) { /* Open reading side */ if (i->fifo_fd < 0) { - i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY); + i->fifo_fd = open(i->fifo_path, O_RDONLY|O_CLOEXEC|O_NONBLOCK); if (i->fifo_fd < 0) return -errno; } @@ -321,7 +321,7 @@ int inhibitor_create_fifo(Inhibitor *i) { } /* Open writing side */ - r = open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY); + r = open(i->fifo_path, O_WRONLY|O_CLOEXEC|O_NONBLOCK); if (r < 0) return -errno; diff --git a/src/login/logind-session.c b/src/login/logind-session.c index c4bde80c0c..92eb2943fe 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -962,7 +962,7 @@ int session_create_fifo(Session *s) { /* Open reading side */ if (s->fifo_fd < 0) { - s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY); + s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NONBLOCK); if (s->fifo_fd < 0) return -errno; @@ -981,7 +981,7 @@ int session_create_fifo(Session *s) { } /* Open writing side */ - r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY); + r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NONBLOCK); if (r < 0) return -errno; diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c index 9fca5ce0cd..d5d086cfe0 100644 --- a/src/login/logind-user-dbus.c +++ b/src/login/logind-user-dbus.c @@ -288,13 +288,13 @@ int user_object_find(sd_bus *bus, const char *path, const char *interface, void return 0; r = parse_uid(p, &uid); - } - if (r < 0) - return 0; + if (r < 0) + return 0; - user = hashmap_get(m->users, UID_TO_PTR(uid)); - if (!user) - return 0; + user = hashmap_get(m->users, UID_TO_PTR(uid)); + if (!user) + return 0; + } *found = user; return 1; diff --git a/src/login/logind-user.c b/src/login/logind-user.c index 94e250b94a..32b2045696 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -344,16 +344,13 @@ static int user_mkdir_runtime_path(User *u) { if (path_is_mount_point(u->runtime_path, NULL, 0) <= 0) { _cleanup_free_ char *t = NULL; - (void) mkdir_label(u->runtime_path, 0700); + r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu%s", + u->uid, u->gid, u->manager->runtime_dir_size, + mac_smack_use() ? ",smackfsroot=*" : ""); + if (r < 0) + return log_oom(); - if (mac_smack_use()) - r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size); - else - r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size); - if (r < 0) { - r = log_oom(); - goto fail; - } + (void) mkdir_label(u->runtime_path, 0700); r = mount("tmpfs", u->runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, t); if (r < 0) { @@ -461,7 +458,7 @@ int user_start(User *u) { u->stopping = false; if (!u->started) { - log_debug("New user %s logged in.", u->name); + log_debug("Starting services for new user %s.", u->name); /* Make XDG_RUNTIME_DIR */ r = user_mkdir_runtime_path(u); @@ -530,9 +527,7 @@ static int user_stop_service(User *u) { return r; } - free(u->service_job); - u->service_job = job; - + free_and_replace(u->service_job, job); return r; } diff --git a/src/login/logind.c b/src/login/logind.c index 49ca367e18..d15d4cec5b 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -29,17 +29,18 @@ #include "alloc-util.h" #include "bus-error.h" #include "bus-util.h" +#include "cgroup-util.h" #include "conf-parser.h" #include "def.h" #include "dirent-util.h" #include "fd-util.h" #include "format-util.h" #include "logind.h" +#include "process-util.h" #include "selinux-util.h" #include "signal-util.h" #include "strv.h" #include "udev-util.h" -#include "cgroup-util.h" static void manager_free(Manager *m); @@ -658,7 +659,6 @@ static int manager_reserve_vt(Manager *m) { } static int manager_connect_bus(Manager *m) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; assert(m); @@ -696,65 +696,65 @@ static int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to add user enumerator: %m"); - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='JobRemoved'," - "path='/org/freedesktop/systemd1'", - match_job_removed, m); + r = sd_bus_match_signal_async( + m->bus, + NULL, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "JobRemoved", + match_job_removed, NULL, m); if (r < 0) - return log_error_errno(r, "Failed to add match for JobRemoved: %m"); - - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='UnitRemoved'," - "path='/org/freedesktop/systemd1'", - match_unit_removed, m); + return log_error_errno(r, "Failed to request match for JobRemoved: %m"); + + r = sd_bus_match_signal_async( + m->bus, + NULL, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "UnitRemoved", + match_unit_removed, NULL, m); if (r < 0) - return log_error_errno(r, "Failed to add match for UnitRemoved: %m"); - - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.DBus.Properties'," - "member='PropertiesChanged'", - match_properties_changed, m); + return log_error_errno(r, "Failed to request match for UnitRemoved: %m"); + + r = sd_bus_match_signal_async( + m->bus, + NULL, + "org.freedesktop.systemd1", + NULL, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + match_properties_changed, NULL, m); if (r < 0) - return log_error_errno(r, "Failed to add match for PropertiesChanged: %m"); - - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='Reloading'," - "path='/org/freedesktop/systemd1'", - match_reloading, m); + return log_error_errno(r, "Failed to request match for PropertiesChanged: %m"); + + r = sd_bus_match_signal_async( + m->bus, + NULL, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "Reloading", + match_reloading, NULL, m); if (r < 0) - return log_error_errno(r, "Failed to add match for Reloading: %m"); + return log_error_errno(r, "Failed to request match for Reloading: %m"); - r = sd_bus_call_method( + r = sd_bus_call_method_async( m->bus, + NULL, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "Subscribe", - &error, - NULL, NULL); - if (r < 0) { - log_error("Failed to enable subscription: %s", bus_error_message(&error, r)); - return r; - } + NULL, NULL, + NULL); + if (r < 0) + return log_error_errno(r, "Failed to enable subscription: %m"); - r = sd_bus_request_name(m->bus, "org.freedesktop.login1", 0); + r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.login1", 0, NULL, NULL); if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); + return log_error_errno(r, "Failed to request name: %m"); r = sd_bus_attach_event(m->bus, m->event, SD_EVENT_PRIORITY_NORMAL); if (r < 0) diff --git a/src/login/meson.build b/src/login/meson.build index 33f9ed48cc..e8e4f7bd7d 100644 --- a/src/login/meson.build +++ b/src/login/meson.build @@ -97,19 +97,27 @@ if conf.get('ENABLE_LOGIND') == 1 install : install_polkit, install_dir : polkitpolicydir) - install_data('70-power-switch.rules', - '70-uaccess.rules', + install_data('70-power-switch.rules', install_dir : udevrulesdir) + + if conf.get('HAVE_ACL') == 1 + install_data('70-uaccess.rules', install_dir : udevrulesdir) + endif + + seat_rules = configure_file( + input : '71-seat.rules.in', + output : '71-seat.rules', + configuration : substs) + install_data(seat_rules, install_dir : udevrulesdir) - foreach file : ['71-seat.rules', - '73-seat-late.rules'] - gen = configure_file( - input : file + '.in', - output : file, - configuration : substs) - install_data(gen, - install_dir : udevrulesdir) - endforeach + custom_target( + '73-seat-late.rules', + input : '73-seat-late.rules.m4', + output: '73-seat-late.rules', + command : [m4, '-P'] + m4_defines + ['@INPUT@'], + capture : true, + install : true, + install_dir : udevrulesdir) custom_target( 'systemd-user', diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index 246bbddeee..1c3ba33e23 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -197,7 +197,7 @@ static int export_legacy_dbus_address( return PAM_SUCCESS; s = mfree(s); - if (asprintf(&s, UNIX_USER_BUS_ADDRESS_FMT, runtime) < 0) + if (asprintf(&s, DEFAULT_USER_BUS_ADDRESS_FMT, runtime) < 0) goto error; r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", s, 0); diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c index 10d1b06016..8ba1380c81 100644 --- a/src/machine/image-dbus.c +++ b/src/machine/image-dbus.c @@ -75,10 +75,10 @@ int bus_image_method_remove( 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) { + r = safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS, &child); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m"); + if (r == 0) { errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); r = image_remove(image); @@ -187,10 +187,10 @@ int bus_image_method_clone( 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) { + r = safe_fork("(imgclone)", FORK_RESET_SIGNALS, &child); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m"); + if (r == 0) { errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); r = image_clone(image, new_name, read_only); diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index 3761267b57..2d7806491b 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -228,7 +228,6 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd _cleanup_free_ char *us = NULL, *them = NULL; _cleanup_close_ int netns_fd = -1; const char *p; - siginfo_t si; pid_t child; r = readlink_malloc("/proc/self/ns/net", &us); @@ -250,11 +249,10 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd if (socketpair(AF_UNIX, SOCK_SEQPACKET, 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) { + r = safe_fork("(sd-addr)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m"); + if (r == 0) { _cleanup_free_ struct local_address *addresses = NULL; struct local_address *a; int i, n; @@ -338,10 +336,10 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd return r; } - r = wait_for_terminate(child, &si); + r = wait_for_terminate_and_check("(sd-addr)", child, 0); 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) + if (r != EXIT_SUCCESS) return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); break; } @@ -380,7 +378,6 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s _cleanup_close_ int mntns_fd = -1, root_fd = -1; _cleanup_close_pair_ int pair[2] = { -1, -1 }; _cleanup_fclose_ FILE *f = NULL; - siginfo_t si; pid_t child; r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd); @@ -390,11 +387,10 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s if (socketpair(AF_UNIX, SOCK_SEQPACKET, 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) { + r = safe_fork("(sd-osrel)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m"); + if (r == 0) { int fd = -1; pair[0] = safe_close(pair[0]); @@ -431,12 +427,12 @@ int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, s if (r < 0) return r; - r = wait_for_terminate(child, &si); + r = wait_for_terminate_and_check("(sd-osrel)", child, 0); 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_NOT_FOUND) + if (r == EXIT_NOT_FOUND) return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Machine does not contain OS release information"); - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + if (r != EXIT_SUCCESS) return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); break; @@ -613,7 +609,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu _cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL; sd_bus *container_bus = NULL; _cleanup_close_ int master = -1, slave = -1; - _cleanup_strv_free_ char **env = NULL, **args = NULL; + _cleanup_strv_free_ char **env = NULL, **args_wire = NULL, **args = NULL; Machine *m = userdata; const char *p, *unit, *user, *path, *description, *utmp_id; int r; @@ -625,22 +621,41 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu if (r < 0) return r; user = empty_to_null(user); - if (isempty(path)) - path = "/bin/sh"; - if (!path_is_absolute(path)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified path '%s' is not absolute", path); - - r = sd_bus_message_read_strv(message, &args); + r = sd_bus_message_read_strv(message, &args_wire); if (r < 0) return r; - if (strv_isempty(args)) { - args = strv_free(args); + if (isempty(path)) { + path = "/bin/sh"; - args = strv_new(path, NULL); + args = new0(char*, 3 + 1); if (!args) return -ENOMEM; - - args[0][0] = '-'; /* Tell /bin/sh that this shall be a login shell */ + args[0] = strdup("sh"); + if (!args[0]) + return -ENOMEM; + args[1] = strdup("-c"); + if (!args[1]) + return -ENOMEM; + r = asprintf(&args[2], + "shell=$(getent passwd %s 2>/dev/null | { IFS=: read _ _ _ _ _ _ x; echo \"$x\"; })\n"\ + "exec \"${shell:-/bin/sh}\" -l", /* -l is means --login */ + isempty(user) ? "root" : user); + if (r < 0) { + args[2] = NULL; + return -ENOMEM; + } + } else { + if (!path_is_absolute(path)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified path '%s' is not absolute", path); + args = args_wire; + args_wire = NULL; + if (strv_isempty(args)) { + args = strv_free(args); + + args = strv_new(path, NULL); + if (!args) + return -ENOMEM; + } } r = sd_bus_message_read_strv(message, &env); @@ -842,7 +857,6 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu const char *dest, *src; Machine *m = userdata; struct stat st; - siginfo_t si; pid_t child; uid_t uid; int r; @@ -931,7 +945,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu /* Second, we mount the source file or directory to a directory inside of our MS_SLAVE playground. */ mount_tmp = strjoina(mount_slave, "/mount"); if (S_ISDIR(st.st_mode)) - r = mkdir(mount_tmp, 0700) < 0 ? -errno : 0; + r = mkdir_errno_wrapper(mount_tmp, 0700); else r = touch(mount_tmp); if (r < 0) { @@ -997,13 +1011,12 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu goto finish; } - child = fork(); - if (child < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + r = safe_fork("(sd-bindmnt)", FORK_RESET_SIGNALS, &child); + if (r < 0) { + sd_bus_error_set_errnof(error, r, "Failed to fork(): %m"); goto finish; } - - if (child == 0) { + if (r == 0) { const char *mount_inside; int mntfd; const char *q; @@ -1049,17 +1062,12 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); - r = wait_for_terminate(child, &si); + r = wait_for_terminate_and_check("(sd-bindmnt)", child, 0); if (r < 0) { r = sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m"); goto finish; } - if (si.si_code != CLD_EXITED) { - r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); - goto finish; - } - if (si.si_status != EXIT_SUCCESS) { - + if (r != EXIT_SUCCESS) { if (read(errno_pipe_fd[0], &r, sizeof(r)) == sizeof(r)) r = sd_bus_error_set_errnof(error, r, "Failed to mount: %m"); else @@ -1172,11 +1180,10 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro 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) { + r = safe_fork("(sd-copy)", FORK_RESET_SIGNALS, &child); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m"); + if (r == 0) { int containerfd; const char *q; int mntfd; @@ -1272,7 +1279,6 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda 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); @@ -1282,11 +1288,10 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda 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) { + r = safe_fork("(sd-openroot)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m"); + if (r == 0) { _cleanup_close_ int dfd = -1; pair[0] = safe_close(pair[0]); @@ -1309,10 +1314,10 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda pair[1] = safe_close(pair[1]); - r = wait_for_terminate(child, &si); + r = wait_for_terminate_and_check("(sd-openroot)", child, 0); 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) + if (r != EXIT_SUCCESS) return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Child died abnormally."); fd = receive_one_fd(pair[0], MSG_DONTWAIT); diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index c435bb9b5a..75743ce6a6 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -1568,9 +1568,9 @@ static int login_machine(int argc, char *argv[], void *userdata) { "member='MachineRemoved'," "arg0='", machine, "'"); - r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward); + r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, &forward); if (r < 0) - return log_error_errno(r, "Failed to add machine removal match: %m"); + return log_error_errno(r, "Failed to request machine removal match: %m"); r = sd_bus_call_method( bus, @@ -1643,9 +1643,9 @@ static int shell_machine(int argc, char *argv[], void *userdata) { "member='MachineRemoved'," "arg0='", machine, "'"); - r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward); + r = sd_bus_add_match_async(bus, &slot, match, on_machine_removed, NULL, &forward); if (r < 0) - return log_error_errno(r, "Failed to add machine removal match: %m"); + return log_error_errno(r, "Failed to request machine removal match: %m"); r = sd_bus_message_new_method_call( bus, @@ -2087,28 +2087,27 @@ static int transfer_image_common(sd_bus *bus, sd_bus_message *m) { if (r < 0) return log_error_errno(r, "Failed to attach bus to event loop: %m"); - r = sd_bus_add_match( + r = sd_bus_match_signal_async( bus, &slot_job_removed, - "type='signal'," - "sender='org.freedesktop.import1'," - "interface='org.freedesktop.import1.Manager'," - "member='TransferRemoved'," - "path='/org/freedesktop/import1'", - match_transfer_removed, &path); + "org.freedesktop.import1", + "/org/freedesktop/import1", + "org.freedesktop.import1.Manager", + "TransferRemoved", + match_transfer_removed, NULL, &path); if (r < 0) - return log_error_errno(r, "Failed to install match: %m"); + return log_error_errno(r, "Failed to request match: %m"); - r = sd_bus_add_match( + r = sd_bus_match_signal_async( bus, &slot_log_message, - "type='signal'," - "sender='org.freedesktop.import1'," - "interface='org.freedesktop.import1.Transfer'," - "member='LogMessage'", - match_log_message, &path); + "org.freedesktop.import1", + NULL, + "org.freedesktop.import1.Transfer", + "LogMessage", + match_log_message, NULL, &path); if (r < 0) - return log_error_errno(r, "Failed to install match: %m"); + return log_error_errno(r, "Failed to request match: %m"); r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0) { @@ -3144,7 +3143,7 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) { } int main(int argc, char*argv[]) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + sd_bus *bus = NULL; int r; setlocale(LC_ALL, ""); @@ -3167,6 +3166,9 @@ int main(int argc, char*argv[]) { r = machinectl_main(argc, argv, bus); finish: + /* make sure we terminate the bus connection first, and then close the + * pager, see issue #3543 for the details. */ + sd_bus_flush_close_unref(bus); pager_close(); polkit_agent_close(); diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 330d6b3d6e..c5e59c4716 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -1078,11 +1078,10 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err return -errno; /* This might be a slow operation, run it asynchronously in a background process */ - child = fork(); - if (child < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); - - if (child == 0) { + r = safe_fork("(sd-clean)", FORK_RESET_SIGNALS, &child); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m"); + if (r == 0) { _cleanup_(image_hashmap_freep) Hashmap *images = NULL; bool success = true; Image *image; diff --git a/src/machine/machined.c b/src/machine/machined.c index d481020893..34b2024043 100644 --- a/src/machine/machined.c +++ b/src/machine/machined.c @@ -37,6 +37,7 @@ #include "machined.h" #include "process-util.h" #include "signal-util.h" +#include "special.h" Manager *manager_new(void) { Manager *m; @@ -112,7 +113,7 @@ static int manager_add_host_machine(Manager *m) { if (!rd) return log_oom(); - unit = strdup("-.slice"); + unit = strdup(SPECIAL_ROOT_SLICE); if (!unit) return log_oom(); @@ -185,7 +186,6 @@ int manager_enumerate_machines(Manager *m) { } static int manager_connect_bus(Manager *m) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; assert(m); @@ -215,70 +215,65 @@ static int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to add image enumerator: %m"); - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='JobRemoved'," - "path='/org/freedesktop/systemd1'", - match_job_removed, - m); + r = sd_bus_match_signal_async( + m->bus, + NULL, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "JobRemoved", + match_job_removed, NULL, m); if (r < 0) return log_error_errno(r, "Failed to add match for JobRemoved: %m"); - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='UnitRemoved'," - "path='/org/freedesktop/systemd1'", - match_unit_removed, - m); + r = sd_bus_match_signal_async( + m->bus, + NULL, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "UnitRemoved", + match_unit_removed, NULL, m); if (r < 0) - return log_error_errno(r, "Failed to add match for UnitRemoved: %m"); - - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.DBus.Properties'," - "member='PropertiesChanged'," - "arg0='org.freedesktop.systemd1.Unit'", - match_properties_changed, - m); + return log_error_errno(r, "Failed to request match for UnitRemoved: %m"); + + r = sd_bus_match_signal_async( + m->bus, + NULL, + "org.freedesktop.systemd1", + NULL, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + match_properties_changed, NULL, m); if (r < 0) - return log_error_errno(r, "Failed to add match for PropertiesChanged: %m"); - - r = sd_bus_add_match(m->bus, - NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Manager'," - "member='Reloading'," - "path='/org/freedesktop/systemd1'", - match_reloading, - m); + return log_error_errno(r, "Failed to request match for PropertiesChanged: %m"); + + r = sd_bus_match_signal_async( + m->bus, + NULL, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "Reloading", + match_reloading, NULL, m); if (r < 0) - return log_error_errno(r, "Failed to add match for Reloading: %m"); + return log_error_errno(r, "Failed to request match for Reloading: %m"); - r = sd_bus_call_method( + r = sd_bus_call_method_async( m->bus, + NULL, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "Subscribe", - &error, - NULL, NULL); - if (r < 0) { - log_error("Failed to enable subscription: %s", bus_error_message(&error, r)); - return r; - } + NULL, NULL, + NULL); + if (r < 0) + return log_error_errno(r, "Failed to enable subscription: %m"); - r = sd_bus_request_name(m->bus, "org.freedesktop.machine1", 0); + r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.machine1", 0, NULL, NULL); if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); + return log_error_errno(r, "Failed to request name: %m"); r = sd_bus_attach_event(m->bus, m->event, 0); if (r < 0) diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c index da3647e7e2..0cd9f07094 100644 --- a/src/mount/mount-tool.c +++ b/src/mount/mount-tool.c @@ -40,7 +40,9 @@ #include "stat-util.h" #include "strv.h" #include "udev-util.h" +#include "unit-def.h" #include "unit-name.h" +#include "user-util.h" #include "terminal-util.h" enum { @@ -69,6 +71,8 @@ static usec_t arg_timeout_idle = USEC_INFINITY; static bool arg_timeout_idle_set = false; static char **arg_automount_property = NULL; static int arg_bind_device = -1; +static uid_t arg_uid = UID_INVALID; +static gid_t arg_gid = GID_INVALID; static bool arg_fsck = true; static bool arg_aggressive_gc = false; @@ -89,6 +93,7 @@ static void help(void) { " --discover Discover mount device metadata\n" " -t --type=TYPE File system type\n" " -o --options=OPTIONS Mount options\n" + " --owner=USER Add uid= and gid= options for USER\n" " --fsck=no Don't run file system check before mount\n" " --description=TEXT Description for unit\n" " -p --property=NAME=VALUE Set mount unit property\n" @@ -116,6 +121,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_DISCOVER, ARG_MOUNT_TYPE, ARG_MOUNT_OPTIONS, + ARG_OWNER, ARG_FSCK, ARG_DESCRIPTION, ARG_TIMEOUT_IDLE, @@ -139,6 +145,7 @@ static int parse_argv(int argc, char *argv[]) { { "discover", no_argument, NULL, ARG_DISCOVER }, { "type", required_argument, NULL, 't' }, { "options", required_argument, NULL, 'o' }, + { "owner", required_argument, NULL, ARG_OWNER }, { "fsck", required_argument, NULL, ARG_FSCK }, { "description", required_argument, NULL, ARG_DESCRIPTION }, { "property", required_argument, NULL, 'p' }, @@ -220,6 +227,18 @@ static int parse_argv(int argc, char *argv[]) { return log_oom(); break; + case ARG_OWNER: { + const char *user = optarg; + + r = get_user_creds(&user, &arg_uid, &arg_gid, NULL, NULL); + if (r < 0) + return log_error_errno(r, + r == -EBADMSG ? "UID or GID of user %s are invalid." + : "Cannot use \"%s\" as owner: %m", + optarg); + break; + } + case ARG_FSCK: r = parse_boolean(optarg); if (r < 0) @@ -385,7 +404,7 @@ static int parse_argv(int argc, char *argv[]) { return 1; } -static int transient_unit_set_properties(sd_bus_message *m, char **properties) { +static int transient_unit_set_properties(sd_bus_message *m, UnitType t, char **properties) { int r; if (!isempty(arg_description)) { @@ -414,7 +433,7 @@ static int transient_unit_set_properties(sd_bus_message *m, char **properties) { return r; } - r = bus_append_unit_property_assignment_many(m, properties); + r = bus_append_unit_property_assignment_many(m, t, properties); if (r < 0) return r; @@ -422,11 +441,12 @@ static int transient_unit_set_properties(sd_bus_message *m, char **properties) { } static int transient_mount_set_properties(sd_bus_message *m) { + _cleanup_free_ char *options = NULL; int r; assert(m); - r = transient_unit_set_properties(m, arg_property); + r = transient_unit_set_properties(m, UNIT_MOUNT, arg_property); if (r < 0) return r; @@ -442,12 +462,25 @@ static int transient_mount_set_properties(sd_bus_message *m) { return r; } - if (arg_mount_options) { - r = sd_bus_message_append(m, "(sv)", "Options", "s", arg_mount_options); + /* Prepend uid=…,gid=… if arg_uid is set */ + if (arg_uid != UID_INVALID) { + r = asprintf(&options, + "uid=" UID_FMT ",gid=" GID_FMT "%s%s", + arg_uid, arg_gid, + arg_mount_options ? "," : "", arg_mount_options); if (r < 0) - return r; + return -ENOMEM; } + if (options || arg_mount_options) { + log_debug("Using mount options: %s", options ?: arg_mount_options); + + r = sd_bus_message_append(m, "(sv)", "Options", "s", options ?: arg_mount_options); + if (r < 0) + return r; + } else + log_debug("Not using any mount options"); + if (arg_fsck) { _cleanup_free_ char *fsck = NULL; @@ -471,7 +504,7 @@ static int transient_automount_set_properties(sd_bus_message *m) { assert(m); - r = transient_unit_set_properties(m, arg_automount_property); + r = transient_unit_set_properties(m, UNIT_AUTOMOUNT, arg_automount_property); if (r < 0) return r; @@ -1530,7 +1563,7 @@ finish: } int main(int argc, char* argv[]) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + sd_bus *bus = NULL; int r; log_parse_environment(); @@ -1604,6 +1637,23 @@ int main(int argc, char* argv[]) { } } + /* The kernel (properly) refuses mounting file systems with unknown uid=,gid= options, + * but not for all filesystem types. Let's try to catch the cases where the option + * would be used if the file system does not support it. It is also possible to + * autodetect the file system, but that's only possible with disk-based file systems + * which incidentally seem to be implemented more carefully and reject unknown options, + * so it's probably OK that we do the check only when the type is specified. + */ + if (arg_mount_type && + !streq(arg_mount_type, "auto") && + arg_uid != UID_INVALID && + !fstype_can_uid_gid(arg_mount_type)) { + log_error("File system type %s is not known to support uid=/gid=, refusing.", + arg_mount_type); + r = -EOPNOTSUPP; + goto finish; + } + switch (arg_action) { case ACTION_MOUNT: @@ -1620,6 +1670,9 @@ int main(int argc, char* argv[]) { } finish: + /* make sure we terminate the bus connection first, and then close the + * pager, see issue #3543 for the details. */ + bus = sd_bus_flush_close_unref(bus); pager_close(); free(arg_mount_what); diff --git a/src/network/meson.build b/src/network/meson.build index f97484eb26..e1ac0f13dc 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -46,6 +46,8 @@ sources = files(''' netdev/geneve.h netdev/vxcan.c netdev/vxcan.h + netdev/wireguard.c + netdev/wireguard.h networkd-address-label.c networkd-address-label.h networkd-address-pool.c @@ -151,7 +153,7 @@ if conf.get('ENABLE_NETWORKD') == 1 [['src/network/test-network.c'], [libnetworkd_core, - libudev_internal, + libudev_static, libsystemd_network, libshared], [threads]], @@ -166,7 +168,7 @@ if conf.get('ENABLE_NETWORKD') == 1 'src/network/test-network-tables.c', test_tables_h], [libnetworkd_core, - libudev_internal, + libudev_static, libudev_core, libsystemd_network, libshared], diff --git a/src/network/netdev/ipvlan.c b/src/network/netdev/ipvlan.c index df9487418b..856f5bd805 100644 --- a/src/network/netdev/ipvlan.c +++ b/src/network/netdev/ipvlan.c @@ -27,11 +27,21 @@ static const char* const ipvlan_mode_table[_NETDEV_IPVLAN_MODE_MAX] = { [NETDEV_IPVLAN_MODE_L2] = "L2", [NETDEV_IPVLAN_MODE_L3] = "L3", + [NETDEV_IPVLAN_MODE_L3S] = "L3S", }; DEFINE_STRING_TABLE_LOOKUP(ipvlan_mode, IPVlanMode); DEFINE_CONFIG_PARSE_ENUM(config_parse_ipvlan_mode, ipvlan_mode, IPVlanMode, "Failed to parse ipvlan mode"); +static const char* const ipvlan_flags_table[_NETDEV_IPVLAN_FLAGS_MAX] = { + [NETDEV_IPVLAN_FLAGS_BRIGDE] = "bridge", + [NETDEV_IPVLAN_FLAGS_PRIVATE] = "private", + [NETDEV_IPVLAN_FLAGS_VEPA] = "vepa", +}; + +DEFINE_STRING_TABLE_LOOKUP(ipvlan_flags, IPVlanFlags); +DEFINE_CONFIG_PARSE_ENUM(config_parse_ipvlan_flags, ipvlan_flags, IPVlanFlags, "Failed to parse ipvlan flags"); + static int netdev_ipvlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *req) { IPVlan *m; int r; @@ -50,6 +60,12 @@ static int netdev_ipvlan_fill_message_create(NetDev *netdev, Link *link, sd_netl return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPVLAN_MODE attribute: %m"); } + if (m->flags != _NETDEV_IPVLAN_FLAGS_INVALID) { + r = sd_netlink_message_append_u16(req, IFLA_IPVLAN_FLAGS, m->flags); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPVLAN_FLAGS attribute: %m"); + } + return 0; } @@ -63,6 +79,7 @@ static void ipvlan_init(NetDev *n) { assert(m); m->mode = _NETDEV_IPVLAN_MODE_INVALID; + m->flags = _NETDEV_IPVLAN_FLAGS_INVALID; } const NetDevVTable ipvlan_vtable = { diff --git a/src/network/netdev/ipvlan.h b/src/network/netdev/ipvlan.h index cb43db4348..b6bc1ac739 100644 --- a/src/network/netdev/ipvlan.h +++ b/src/network/netdev/ipvlan.h @@ -20,20 +20,33 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <linux/if_link.h> + #include "missing.h" #include "netdev/netdev.h" + typedef enum IPVlanMode { NETDEV_IPVLAN_MODE_L2 = IPVLAN_MODE_L2, NETDEV_IPVLAN_MODE_L3 = IPVLAN_MODE_L3, + NETDEV_IPVLAN_MODE_L3S = IPVLAN_MODE_L3S, _NETDEV_IPVLAN_MODE_MAX, _NETDEV_IPVLAN_MODE_INVALID = -1 } IPVlanMode; +typedef enum IPVlanFlags { + NETDEV_IPVLAN_FLAGS_BRIGDE, + NETDEV_IPVLAN_FLAGS_PRIVATE = IPVLAN_F_PRIVATE, + NETDEV_IPVLAN_FLAGS_VEPA = IPVLAN_F_VEPA, + _NETDEV_IPVLAN_FLAGS_MAX, + _NETDEV_IPVLAN_FLAGS_INVALID = -1 +} IPVlanFlags; + typedef struct IPVlan { NetDev meta; IPVlanMode mode; + IPVlanFlags flags; } IPVlan; DEFINE_NETDEV_CAST(IPVLAN, IPVlan); @@ -42,4 +55,8 @@ extern const NetDevVTable ipvlan_vtable; const char *ipvlan_mode_to_string(IPVlanMode d) _const_; IPVlanMode ipvlan_mode_from_string(const char *d) _pure_; +const char *ipvlan_flags_to_string(IPVlanFlags d) _const_; +IPVlanFlags ipvlan_flags_from_string(const char *d) _pure_; + int config_parse_ipvlan_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_ipvlan_flags(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index 628e6648b7..ba6268fa66 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -18,6 +18,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"") #include "netdev/vrf.h" #include "netdev/netdev.h" #include "netdev/vxcan.h" +#include "netdev/wireguard.h" #include "vlan-util.h" %} struct ConfigPerfItem; @@ -31,114 +32,125 @@ struct ConfigPerfItem; %struct-type %includes %% -Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, match_host) -Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, match_virt) -Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, match_kernel) -Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, match_arch) -NetDev.Description, config_parse_string, 0, offsetof(NetDev, description) -NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname) -NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind) -NetDev.MTUBytes, config_parse_iec_size, 0, offsetof(NetDev, mtu) -NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac) -VLAN.Id, config_parse_vlanid, 0, offsetof(VLan, id) -VLAN.GVRP, config_parse_tristate, 0, offsetof(VLan, gvrp) -VLAN.MVRP, config_parse_tristate, 0, offsetof(VLan, mvrp) -VLAN.LooseBinding, config_parse_tristate, 0, offsetof(VLan, loose_binding) -VLAN.ReorderHeader, config_parse_tristate, 0, offsetof(VLan, reorder_hdr) -MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) -MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) -IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode) -Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local) -Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote) -Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos) -Tunnel.TTL, config_parse_unsigned, 0, offsetof(Tunnel, ttl) -Tunnel.Key, config_parse_tunnel_key, 0, offsetof(Tunnel, key) -Tunnel.InputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, ikey) -Tunnel.OutputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, okey) -Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(Tunnel, pmtudisc) -Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode) -Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, offsetof(Tunnel, ipv6_flowlabel) -Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp) -Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit) -Tunnel.Independent, config_parse_bool, 0, offsetof(Tunnel, independent) -Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer) -Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer) -VXCAN.Peer, config_parse_ifname, 0, offsetof(VxCan, ifname_peer) -VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id) -VXLAN.Group, config_parse_vxlan_address, 0, offsetof(VxLan, remote) -VXLAN.Local, config_parse_vxlan_address, 0, offsetof(VxLan, local) -VXLAN.Remote, config_parse_vxlan_address, 0, offsetof(VxLan, remote) -VXLAN.TOS, config_parse_unsigned, 0, offsetof(VxLan, tos) -VXLAN.TTL, config_parse_unsigned, 0, offsetof(VxLan, ttl) -VXLAN.MacLearning, config_parse_bool, 0, offsetof(VxLan, learning) -VXLAN.ARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy) -VXLAN.ReduceARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy) -VXLAN.L2MissNotification, config_parse_bool, 0, offsetof(VxLan, l2miss) -VXLAN.L3MissNotification, config_parse_bool, 0, offsetof(VxLan, l3miss) -VXLAN.RouteShortCircuit, config_parse_bool, 0, offsetof(VxLan, route_short_circuit) -VXLAN.UDPCheckSum, config_parse_bool, 0, offsetof(VxLan, udpcsum) -VXLAN.UDPChecksum, config_parse_bool, 0, offsetof(VxLan, udpcsum) -VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx) -VXLAN.UDP6ZeroChecksumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx) -VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx) -VXLAN.UDP6ZeroChecksumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx) -VXLAN.RemoteChecksumTx, config_parse_bool, 0, offsetof(VxLan, remote_csum_tx) -VXLAN.RemoteChecksumRx, config_parse_bool, 0, offsetof(VxLan, remote_csum_rx) -VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing) -VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy) -VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb) -VXLAN.PortRange, config_parse_port_range, 0, 0 -VXLAN.DestinationPort, config_parse_ip_port, 0, offsetof(VxLan, dest_port) -VXLAN.FlowLabel, config_parse_flow_label, 0, 0 -GENEVE.Id, config_parse_geneve_vni, 0, offsetof(Geneve, id) -GENEVE.Remote, config_parse_geneve_address, 0, offsetof(Geneve, remote) -GENEVE.TOS, config_parse_uint8, 0, offsetof(Geneve, tos) -GENEVE.TTL, config_parse_uint8, 0, offsetof(Geneve, ttl) -GENEVE.UDPChecksum, config_parse_bool, 0, offsetof(Geneve, udpcsum) -GENEVE.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumrx) -GENEVE.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumtx) -GENEVE.DestinationPort, config_parse_ip_port, 0, offsetof(Geneve, dest_port) -GENEVE.FlowLabel, config_parse_geneve_flow_label, 0, 0 -Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) -Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) -Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) -Tun.User, config_parse_string, 0, offsetof(TunTap, user_name) -Tun.Group, config_parse_string, 0, offsetof(TunTap, group_name) -Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) -Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) -Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) -Tap.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr) -Tap.User, config_parse_string, 0, offsetof(TunTap, user_name) -Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name) -Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode) -Bond.TransmitHashPolicy, config_parse_bond_xmit_hash_policy, 0, offsetof(Bond, xmit_hash_policy) -Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0, offsetof(Bond, lacp_rate) -Bond.AdSelect, config_parse_bond_ad_select, 0, offsetof(Bond, ad_select) -Bond.FailOverMACPolicy, config_parse_bond_fail_over_mac, 0, offsetof(Bond, fail_over_mac) -Bond.ARPIPTargets, config_parse_arp_ip_target_address, 0, 0 -Bond.ARPValidate, config_parse_bond_arp_validate, 0, offsetof(Bond, arp_validate) -Bond.ARPAllTargets, config_parse_bond_arp_all_targets, 0, offsetof(Bond, arp_all_targets) -Bond.PrimaryReselectPolicy, config_parse_bond_primary_reselect, 0, offsetof(Bond, primary_reselect) -Bond.ResendIGMP, config_parse_unsigned, 0, offsetof(Bond, resend_igmp) -Bond.PacketsPerSlave, config_parse_unsigned, 0, offsetof(Bond, packets_per_slave) -Bond.GratuitousARP, config_parse_unsigned, 0, offsetof(Bond, num_grat_arp) -Bond.AllSlavesActive, config_parse_unsigned, 0, offsetof(Bond, all_slaves_active) -Bond.MinLinks, config_parse_unsigned, 0, offsetof(Bond, min_links) -Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon) -Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay) -Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay) -Bond.ARPIntervalSec, config_parse_sec, 0, offsetof(Bond, arp_interval) -Bond.LearnPacketIntervalSec, config_parse_sec, 0, offsetof(Bond, lp_interval) -Bridge.HelloTimeSec, config_parse_sec, 0, offsetof(Bridge, hello_time) -Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age) -Bridge.AgeingTimeSec, config_parse_sec, 0, offsetof(Bridge, ageing_time) -Bridge.ForwardDelaySec, config_parse_sec, 0, offsetof(Bridge, forward_delay) -Bridge.Priority, config_parse_uint16, 0, offsetof(Bridge, priority) -Bridge.GroupForwardMask, config_parse_uint16, 0, offsetof(Bridge, group_fwd_mask) -Bridge.DefaultPVID, config_parse_default_port_vlanid, 0, offsetof(Bridge, default_pvid) -Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier) -Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping) -Bridge.VLANFiltering, config_parse_tristate, 0, offsetof(Bridge, vlan_filtering) -Bridge.STP, config_parse_tristate, 0, offsetof(Bridge, stp) -VRF.TableId, config_parse_uint32, 0, offsetof(Vrf, table) /* deprecated */ -VRF.Table, config_parse_route_table, 0, offsetof(Vrf, table) +Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, match_host) +Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, match_virt) +Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, match_kernel_cmdline) +Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(NetDev, match_kernel_version) +Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, match_arch) +NetDev.Description, config_parse_string, 0, offsetof(NetDev, description) +NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname) +NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind) +NetDev.MTUBytes, config_parse_iec_size, 0, offsetof(NetDev, mtu) +NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac) +VLAN.Id, config_parse_vlanid, 0, offsetof(VLan, id) +VLAN.GVRP, config_parse_tristate, 0, offsetof(VLan, gvrp) +VLAN.MVRP, config_parse_tristate, 0, offsetof(VLan, mvrp) +VLAN.LooseBinding, config_parse_tristate, 0, offsetof(VLan, loose_binding) +VLAN.ReorderHeader, config_parse_tristate, 0, offsetof(VLan, reorder_hdr) +MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) +MACVTAP.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) +IPVLAN.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode) +IPVLAN.Flags, config_parse_ipvlan_flags, 0, offsetof(IPVlan, flags) +Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local) +Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote) +Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos) +Tunnel.TTL, config_parse_unsigned, 0, offsetof(Tunnel, ttl) +Tunnel.Key, config_parse_tunnel_key, 0, offsetof(Tunnel, key) +Tunnel.InputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, ikey) +Tunnel.OutputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, okey) +Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(Tunnel, pmtudisc) +Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode) +Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, offsetof(Tunnel, ipv6_flowlabel) +Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp) +Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit) +Tunnel.Independent, config_parse_bool, 0, offsetof(Tunnel, independent) +Tunnel.AllowLocalRemote, config_parse_tristate, 0, offsetof(Tunnel, allow_localremote) +Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer) +Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer) +VXCAN.Peer, config_parse_ifname, 0, offsetof(VxCan, ifname_peer) +VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id) +VXLAN.Group, config_parse_vxlan_address, 0, offsetof(VxLan, remote) +VXLAN.Local, config_parse_vxlan_address, 0, offsetof(VxLan, local) +VXLAN.Remote, config_parse_vxlan_address, 0, offsetof(VxLan, remote) +VXLAN.TOS, config_parse_unsigned, 0, offsetof(VxLan, tos) +VXLAN.TTL, config_parse_unsigned, 0, offsetof(VxLan, ttl) +VXLAN.MacLearning, config_parse_bool, 0, offsetof(VxLan, learning) +VXLAN.ARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy) +VXLAN.ReduceARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy) +VXLAN.L2MissNotification, config_parse_bool, 0, offsetof(VxLan, l2miss) +VXLAN.L3MissNotification, config_parse_bool, 0, offsetof(VxLan, l3miss) +VXLAN.RouteShortCircuit, config_parse_bool, 0, offsetof(VxLan, route_short_circuit) +VXLAN.UDPCheckSum, config_parse_bool, 0, offsetof(VxLan, udpcsum) +VXLAN.UDPChecksum, config_parse_bool, 0, offsetof(VxLan, udpcsum) +VXLAN.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx) +VXLAN.UDP6ZeroChecksumRx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumrx) +VXLAN.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx) +VXLAN.UDP6ZeroChecksumTx, config_parse_bool, 0, offsetof(VxLan, udp6zerocsumtx) +VXLAN.RemoteChecksumTx, config_parse_bool, 0, offsetof(VxLan, remote_csum_tx) +VXLAN.RemoteChecksumRx, config_parse_bool, 0, offsetof(VxLan, remote_csum_rx) +VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing) +VXLAN.GroupPolicyExtension, config_parse_bool, 0, offsetof(VxLan, group_policy) +VXLAN.MaximumFDBEntries, config_parse_unsigned, 0, offsetof(VxLan, max_fdb) +VXLAN.PortRange, config_parse_port_range, 0, 0 +VXLAN.DestinationPort, config_parse_ip_port, 0, offsetof(VxLan, dest_port) +VXLAN.FlowLabel, config_parse_flow_label, 0, 0 +GENEVE.Id, config_parse_geneve_vni, 0, offsetof(Geneve, id) +GENEVE.Remote, config_parse_geneve_address, 0, offsetof(Geneve, remote) +GENEVE.TOS, config_parse_uint8, 0, offsetof(Geneve, tos) +GENEVE.TTL, config_parse_uint8, 0, offsetof(Geneve, ttl) +GENEVE.UDPChecksum, config_parse_bool, 0, offsetof(Geneve, udpcsum) +GENEVE.UDP6ZeroCheckSumRx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumrx) +GENEVE.UDP6ZeroCheckSumTx, config_parse_bool, 0, offsetof(Geneve, udp6zerocsumtx) +GENEVE.DestinationPort, config_parse_ip_port, 0, offsetof(Geneve, dest_port) +GENEVE.FlowLabel, config_parse_geneve_flow_label, 0, 0 +Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) +Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) +Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) +Tun.User, config_parse_string, 0, offsetof(TunTap, user_name) +Tun.Group, config_parse_string, 0, offsetof(TunTap, group_name) +Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) +Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) +Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) +Tap.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr) +Tap.User, config_parse_string, 0, offsetof(TunTap, user_name) +Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name) +Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode) +Bond.TransmitHashPolicy, config_parse_bond_xmit_hash_policy, 0, offsetof(Bond, xmit_hash_policy) +Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0, offsetof(Bond, lacp_rate) +Bond.AdSelect, config_parse_bond_ad_select, 0, offsetof(Bond, ad_select) +Bond.FailOverMACPolicy, config_parse_bond_fail_over_mac, 0, offsetof(Bond, fail_over_mac) +Bond.ARPIPTargets, config_parse_arp_ip_target_address, 0, 0 +Bond.ARPValidate, config_parse_bond_arp_validate, 0, offsetof(Bond, arp_validate) +Bond.ARPAllTargets, config_parse_bond_arp_all_targets, 0, offsetof(Bond, arp_all_targets) +Bond.PrimaryReselectPolicy, config_parse_bond_primary_reselect, 0, offsetof(Bond, primary_reselect) +Bond.ResendIGMP, config_parse_unsigned, 0, offsetof(Bond, resend_igmp) +Bond.PacketsPerSlave, config_parse_unsigned, 0, offsetof(Bond, packets_per_slave) +Bond.GratuitousARP, config_parse_unsigned, 0, offsetof(Bond, num_grat_arp) +Bond.AllSlavesActive, config_parse_unsigned, 0, offsetof(Bond, all_slaves_active) +Bond.MinLinks, config_parse_unsigned, 0, offsetof(Bond, min_links) +Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon) +Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay) +Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay) +Bond.ARPIntervalSec, config_parse_sec, 0, offsetof(Bond, arp_interval) +Bond.LearnPacketIntervalSec, config_parse_sec, 0, offsetof(Bond, lp_interval) +Bridge.HelloTimeSec, config_parse_sec, 0, offsetof(Bridge, hello_time) +Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age) +Bridge.AgeingTimeSec, config_parse_sec, 0, offsetof(Bridge, ageing_time) +Bridge.ForwardDelaySec, config_parse_sec, 0, offsetof(Bridge, forward_delay) +Bridge.Priority, config_parse_uint16, 0, offsetof(Bridge, priority) +Bridge.GroupForwardMask, config_parse_uint16, 0, offsetof(Bridge, group_fwd_mask) +Bridge.DefaultPVID, config_parse_default_port_vlanid, 0, offsetof(Bridge, default_pvid) +Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier) +Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping) +Bridge.VLANFiltering, config_parse_tristate, 0, offsetof(Bridge, vlan_filtering) +Bridge.STP, config_parse_tristate, 0, offsetof(Bridge, stp) +VRF.TableId, config_parse_uint32, 0, offsetof(Vrf, table) /* deprecated */ +VRF.Table, config_parse_route_table, 0, offsetof(Vrf, table) +WireGuard.FwMark, config_parse_unsigned, 0, offsetof(Wireguard, fwmark) +WireGuard.ListenPort, config_parse_wireguard_listen_port, 0, offsetof(Wireguard, port) +WireGuard.PrivateKey, config_parse_wireguard_private_key, 0, 0 +WireGuardPeer.AllowedIPs, config_parse_wireguard_allowed_ips, 0, 0 +WireGuardPeer.Endpoint, config_parse_wireguard_endpoint, 0, 0 +WireGuardPeer.PublicKey, config_parse_wireguard_public_key, 0, 0 +WireGuardPeer.PresharedKey, config_parse_wireguard_preshared_key, 0, 0 +WireGuardPeer.PersistentKeepalive, config_parse_wireguard_keepalive, 0, 0 diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index 5530760e19..93648e1be0 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -49,6 +49,7 @@ #include "netdev/vrf.h" #include "netdev/vcan.h" #include "netdev/vxcan.h" +#include "netdev/wireguard.h" const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = { [NETDEV_KIND_BRIDGE] = &bridge_vtable, @@ -75,6 +76,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = { [NETDEV_KIND_VCAN] = &vcan_vtable, [NETDEV_KIND_GENEVE] = &geneve_vtable, [NETDEV_KIND_VXCAN] = &vxcan_vtable, + [NETDEV_KIND_WIREGUARD] = &wireguard_vtable, }; static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = { @@ -102,6 +104,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = { [NETDEV_KIND_VCAN] = "vcan", [NETDEV_KIND_GENEVE] = "geneve", [NETDEV_KIND_VXCAN] = "vxcan", + [NETDEV_KIND_WIREGUARD] = "wireguard", }; DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind); @@ -111,10 +114,10 @@ static void netdev_cancel_callbacks(NetDev *netdev) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; netdev_join_callback *callback; - if (!netdev) + if (!netdev || !netdev->manager) return; - rtnl_message_new_synthetic_error(-ENODEV, 0, &m); + rtnl_message_new_synthetic_error(netdev->manager->rtnl, -ENODEV, 0, &m); while ((callback = netdev->callbacks)) { if (m) { @@ -138,7 +141,7 @@ static void netdev_free(NetDev *netdev) { netdev_cancel_callbacks(netdev); - if (netdev->ifname) + if (netdev->ifname && netdev->manager) hashmap_remove(netdev->manager->netdevs, netdev->ifname); free(netdev->filename); @@ -149,10 +152,19 @@ static void netdev_free(NetDev *netdev) { condition_free_list(netdev->match_host); condition_free_list(netdev->match_virt); - condition_free_list(netdev->match_kernel); + condition_free_list(netdev->match_kernel_cmdline); + condition_free_list(netdev->match_kernel_version); condition_free_list(netdev->match_arch); - if (NETDEV_VTABLE(netdev) && + /* Invoke the per-kind done() destructor, but only if the state field is initialized. We conditionalize that + * because we parse .netdev files twice: once to determine the kind (with a short, minimal NetDev structure + * allocation, with no room for per-kind fields), and once to read the kind's properties (with a full, + * comprehensive NetDev structure allocation with enough space for whatever the specific kind needs). Now, in + * the first case we shouldn't try to destruct the per-kind NetDev fields on destruction, in the second case we + * should. We use the state field to discern the two cases: it's _NETDEV_STATE_INVALID on the first "raw" + * call. */ + if (netdev->state != _NETDEV_STATE_INVALID && + NETDEV_VTABLE(netdev) && NETDEV_VTABLE(netdev)->done) NETDEV_VTABLE(netdev)->done(netdev); @@ -321,7 +333,7 @@ int netdev_enslave(NetDev *netdev, Link *link, sd_netlink_message_handler_t call } else if (IN_SET(netdev->state, NETDEV_STATE_LINGER, NETDEV_STATE_FAILED)) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - r = rtnl_message_new_synthetic_error(-ENODEV, 0, &m); + r = rtnl_message_new_synthetic_error(netdev->manager->rtnl, -ENODEV, 0, &m); if (r >= 0) callback(netdev->manager->rtnl, m, link); } else { @@ -601,8 +613,7 @@ int netdev_join(NetDev *netdev, Link *link, sd_netlink_message_handler_t callbac } static int netdev_load_one(Manager *manager, const char *filename) { - _cleanup_netdev_unref_ NetDev *netdev = NULL; - _cleanup_free_ NetDev *netdev_raw = NULL; + _cleanup_netdev_unref_ NetDev *netdev_raw = NULL, *netdev = NULL; _cleanup_fclose_ FILE *file = NULL; const char *dropin_dirname; bool independent = false; @@ -615,8 +626,8 @@ static int netdev_load_one(Manager *manager, const char *filename) { if (!file) { if (errno == ENOENT) return 0; - else - return -errno; + + return -errno; } if (null_or_empty_fd(fileno(file))) { @@ -628,24 +639,23 @@ static int netdev_load_one(Manager *manager, const char *filename) { if (!netdev_raw) return log_oom(); + netdev_raw->n_ref = 1; netdev_raw->kind = _NETDEV_KIND_INVALID; - dropin_dirname = strjoina(basename(filename), ".d"); + netdev_raw->state = _NETDEV_STATE_INVALID; /* an invalid state means done() of the implementation won't be called on destruction */ + dropin_dirname = strjoina(basename(filename), ".d"); r = config_parse_many(filename, network_dirs, dropin_dirname, "Match\0NetDev\0", config_item_perf_lookup, network_netdev_gperf_lookup, - CONFIG_PARSE_WARN, netdev_raw); + CONFIG_PARSE_WARN|CONFIG_PARSE_RELAXED, netdev_raw); if (r < 0) return r; - r = fseek(file, 0, SEEK_SET); - if (r < 0) - return -errno; - /* skip out early if configuration does not match the environment */ if (net_match_config(NULL, NULL, NULL, NULL, NULL, netdev_raw->match_host, netdev_raw->match_virt, - netdev_raw->match_kernel, netdev_raw->match_arch, + netdev_raw->match_kernel_cmdline, netdev_raw->match_kernel_version, + netdev_raw->match_arch, NULL, NULL, NULL, NULL, NULL, NULL) <= 0) return 0; @@ -659,15 +669,18 @@ static int netdev_load_one(Manager *manager, const char *filename) { return 0; } + r = fseek(file, 0, SEEK_SET); + if (r < 0) + return -errno; + netdev = malloc0(NETDEV_VTABLE(netdev_raw)->object_size); if (!netdev) return log_oom(); netdev->n_ref = 1; netdev->manager = manager; - netdev->state = _NETDEV_STATE_INVALID; netdev->kind = netdev_raw->kind; - netdev->ifname = netdev_raw->ifname; + netdev->state = NETDEV_STATE_LOADING; /* we initialize the state here for the first time, so that done() will be called on destruction */ if (NETDEV_VTABLE(netdev)->init) NETDEV_VTABLE(netdev)->init(netdev); diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h index ec65251464..51b3ea7a8f 100644 --- a/src/network/netdev/netdev.h +++ b/src/network/netdev/netdev.h @@ -60,11 +60,13 @@ typedef enum NetDevKind { NETDEV_KIND_VCAN, NETDEV_KIND_GENEVE, NETDEV_KIND_VXCAN, + NETDEV_KIND_WIREGUARD, _NETDEV_KIND_MAX, _NETDEV_KIND_INVALID = -1 } NetDevKind; typedef enum NetDevState { + NETDEV_STATE_LOADING, NETDEV_STATE_FAILED, NETDEV_STATE_CREATING, NETDEV_STATE_READY, @@ -93,7 +95,8 @@ typedef struct NetDev { Condition *match_host; Condition *match_virt; - Condition *match_kernel; + Condition *match_kernel_cmdline; + Condition *match_kernel_version; Condition *match_arch; NetDevState state; @@ -143,12 +146,14 @@ typedef struct NetDevVTable { extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX]; -#define NETDEV_VTABLE(n) netdev_vtable[(n)->kind] +#define NETDEV_VTABLE(n) ((n)->kind != _NETDEV_KIND_INVALID ? netdev_vtable[(n)->kind] : NULL) /* For casting a netdev into the various netdev kinds */ #define DEFINE_NETDEV_CAST(UPPERCASE, MixedCase) \ static inline MixedCase* UPPERCASE(NetDev *n) { \ - if (_unlikely_(!n || n->kind != NETDEV_KIND_##UPPERCASE)) \ + if (_unlikely_(!n || \ + n->kind != NETDEV_KIND_##UPPERCASE) || \ + n->state == _NETDEV_STATE_INVALID) \ return NULL; \ \ return (MixedCase*) n; \ diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c index 8d6d54d567..a6a3c701f9 100644 --- a/src/network/netdev/tunnel.c +++ b/src/network/netdev/tunnel.c @@ -37,6 +37,7 @@ #define DEFAULT_TNL_HOP_LIMIT 64 #define IP6_FLOWINFO_FLOWLABEL htobe32(0x000FFFFF) +#define IP6_TNL_F_ALLOW_LOCAL_REMOTE 0x40 static const char* const ip6tnl_mode_table[_NETDEV_IP6_TNL_MODE_MAX] = { [NETDEV_IP6_TNL_MODE_IP6IP6] = "ip6ip6", @@ -60,7 +61,6 @@ static int netdev_ipip_fill_message_create(NetDev *netdev, Link *link, sd_netlin r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m"); - } r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &t->local.in); @@ -95,7 +95,6 @@ static int netdev_sit_fill_message_create(NetDev *netdev, Link *link, sd_netlink r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m"); - } r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &t->local.in); @@ -336,6 +335,9 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netl if (t->copy_dscp) t->flags |= IP6_TNL_F_RCV_DSCP_COPY; + if (t->allow_localremote != -1) + SET_FLAG(t->flags, IP6_TNL_F_ALLOW_LOCAL_REMOTE, t->allow_localremote); + if (t->encap_limit != IPV6_DEFAULT_TNL_ENCAP_LIMIT) { r = sd_netlink_message_append_u8(m, IFLA_IPTUN_ENCAP_LIMIT, t->encap_limit); if (r < 0) @@ -682,6 +684,7 @@ static void ip6tnl_init(NetDev *n) { t->encap_limit = IPV6_DEFAULT_TNL_ENCAP_LIMIT; t->ip6tnl_mode = _NETDEV_IP6_TNL_MODE_INVALID; t->ipv6_flowlabel = _NETDEV_IPV6_FLOWLABEL_INVALID; + t->allow_localremote = -1; } const NetDevVTable ipip_vtable = { diff --git a/src/network/netdev/tunnel.h b/src/network/netdev/tunnel.h index 67f8fe35c7..7ffafe9e98 100644 --- a/src/network/netdev/tunnel.h +++ b/src/network/netdev/tunnel.h @@ -45,6 +45,7 @@ typedef struct Tunnel { int family; int ipv6_flowlabel; + int allow_localremote; unsigned ttl; unsigned tos; diff --git a/src/network/netdev/tuntap.c b/src/network/netdev/tuntap.c index 4597a7feeb..4fc9b610af 100644 --- a/src/network/netdev/tuntap.c +++ b/src/network/netdev/tuntap.c @@ -18,6 +18,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <fcntl.h> #include <linux/if_tun.h> #include <net/if.h> diff --git a/src/network/netdev/veth.c b/src/network/netdev/veth.c index 9220b3200f..2a2f50e345 100644 --- a/src/network/netdev/veth.c +++ b/src/network/netdev/veth.c @@ -18,8 +18,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <net/if.h> +#include <errno.h> #include <linux/veth.h> +#include <net/if.h> #include "sd-netlink.h" diff --git a/src/network/netdev/vlan.c b/src/network/netdev/vlan.c index 3a0100d7e6..e7c0e7602a 100644 --- a/src/network/netdev/vlan.c +++ b/src/network/netdev/vlan.c @@ -18,6 +18,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <linux/if_vlan.h> #include <net/if.h> diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c new file mode 100644 index 0000000000..f1f4bab475 --- /dev/null +++ b/src/network/netdev/wireguard.c @@ -0,0 +1,721 @@ +/*** + This file is part of systemd. + + Copyright 2016-2017 Jörg Thalheim <joerg@thalheim.io> + Copyright 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include <sys/ioctl.h> +#include <net/if.h> + +#include "alloc-util.h" +#include "parse-util.h" +#include "fd-util.h" +#include "strv.h" +#include "hexdecoct.h" +#include "string-util.h" +#include "wireguard.h" +#include "networkd-link.h" +#include "networkd-util.h" +#include "networkd-manager.h" +#include "wireguard-netlink.h" + +static void resolve_endpoints(NetDev *netdev); + +static WireguardPeer *wireguard_peer_new(Wireguard *w, unsigned section) { + WireguardPeer *peer; + + assert(w); + + if (w->last_peer_section == section && w->peers) + return w->peers; + + peer = new0(WireguardPeer, 1); + if (!peer) + return NULL; + peer->flags = WGPEER_F_REPLACE_ALLOWEDIPS; + + LIST_PREPEND(peers, w->peers, peer); + w->last_peer_section = section; + + return peer; +} + +static int set_wireguard_interface(NetDev *netdev) { + int r; + unsigned int i, j; + WireguardPeer *peer, *peer_start; + WireguardIPmask *mask, *mask_start = NULL; + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; + Wireguard *w; + uint32_t serial; + + assert(netdev); + w = WIREGUARD(netdev); + assert(w); + + peer_start = w->peers; + + do { + message = sd_netlink_message_unref(message); + + r = sd_genl_message_new(netdev->manager->genl, SD_GENL_WIREGUARD, WG_CMD_SET_DEVICE, &message); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m"); + + r = sd_netlink_message_append_string(message, WGDEVICE_A_IFNAME, netdev->ifname); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append wireguard interface name: %m"); + + if (peer_start == w->peers) { + r = sd_netlink_message_append_data(message, WGDEVICE_A_PRIVATE_KEY, &w->private_key, WG_KEY_LEN); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append wireguard private key: %m"); + + r = sd_netlink_message_append_u16(message, WGDEVICE_A_LISTEN_PORT, w->port); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append wireguard port: %m"); + + r = sd_netlink_message_append_u32(message, WGDEVICE_A_FWMARK, w->fwmark); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append wireguard fwmark: %m"); + + r = sd_netlink_message_append_u32(message, WGDEVICE_A_FLAGS, w->flags); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append wireguard flags: %m"); + } + + r = sd_netlink_message_open_container(message, WGDEVICE_A_PEERS); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append wireguard peer attributes: %m"); + + i = 0; + + LIST_FOREACH(peers, peer, peer_start) { + r = sd_netlink_message_open_array(message, ++i); + if (r < 0) + break; + + r = sd_netlink_message_append_data(message, WGPEER_A_PUBLIC_KEY, &peer->public_key, sizeof(peer->public_key)); + if (r < 0) + break; + + if (!mask_start) { + r = sd_netlink_message_append_data(message, WGPEER_A_PRESHARED_KEY, &peer->preshared_key, WG_KEY_LEN); + if (r < 0) + break; + + r = sd_netlink_message_append_u32(message, WGPEER_A_FLAGS, peer->flags); + if (r < 0) + break; + + r = sd_netlink_message_append_u32(message, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval); + if (r < 0) + break; + + if (peer->endpoint.sa.sa_family == AF_INET) { + r = sd_netlink_message_append_data(message, WGPEER_A_ENDPOINT, &peer->endpoint.in, sizeof(peer->endpoint.in)); + if (r < 0) + break; + } else if (peer->endpoint.sa.sa_family == AF_INET6) { + r = sd_netlink_message_append_data(message, WGPEER_A_ENDPOINT, &peer->endpoint.in6, sizeof(peer->endpoint.in6)); + if (r < 0) + break; + } + + mask_start = peer->ipmasks; + } + + r = sd_netlink_message_open_container(message, WGPEER_A_ALLOWEDIPS); + if (r < 0) { + mask_start = NULL; + break; + } + j = 0; + LIST_FOREACH(ipmasks, mask, mask_start) { + r = sd_netlink_message_open_array(message, ++j); + if (r < 0) + break; + + r = sd_netlink_message_append_u16(message, WGALLOWEDIP_A_FAMILY, mask->family); + if (r < 0) + break; + + if (mask->family == AF_INET) { + r = sd_netlink_message_append_in_addr(message, WGALLOWEDIP_A_IPADDR, &mask->ip.in); + if (r < 0) + break; + } else if (mask->family == AF_INET6) { + r = sd_netlink_message_append_in6_addr(message, WGALLOWEDIP_A_IPADDR, &mask->ip.in6); + if (r < 0) + break; + } + + r = sd_netlink_message_append_u8(message, WGALLOWEDIP_A_CIDR_MASK, mask->cidr); + if (r < 0) + break; + + r = sd_netlink_message_close_container(message); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m"); + } + mask_start = mask; + if (mask_start) { + r = sd_netlink_message_cancel_array(message); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not cancel wireguard allowed ip message attribute: %m"); + } + r = sd_netlink_message_close_container(message); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not add wireguard allowed ip: %m"); + + r = sd_netlink_message_close_container(message); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not add wireguard peer: %m"); + } + + peer_start = peer; + if (peer_start && !mask_start) { + r = sd_netlink_message_cancel_array(message); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not cancel wireguard peers: %m"); + } + + r = sd_netlink_message_close_container(message); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not close wireguard container: %m"); + + r = sd_netlink_send(netdev->manager->genl, message, &serial); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not set wireguard device: %m"); + + } while (peer || mask_start); + + return 0; +} + +static WireguardEndpoint* wireguard_endpoint_free(WireguardEndpoint *e) { + if (!e) + return NULL; + netdev_unref(e->netdev); + e->host = mfree(e->host); + e->port = mfree(e->port); + return mfree(e); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(WireguardEndpoint*, wireguard_endpoint_free); + +static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) { + NetDev *netdev = userdata; + Wireguard *w; + + assert(netdev); + w = WIREGUARD(netdev); + assert(w); + + w->resolve_retry_event_source = sd_event_source_unref(w->resolve_retry_event_source); + + w->unresolved_endpoints = w->failed_endpoints; + w->failed_endpoints = NULL; + + resolve_endpoints(netdev); + + return 0; +} + +/* + * Given the number of retries this function will return will an exponential + * increasing time in milliseconds to wait starting at 200ms and capped at 25 seconds. + */ +static int exponential_backoff_milliseconds(unsigned n_retries) { + return (2 << MAX(n_retries, 7U)) * 100 * USEC_PER_MSEC; +} + +static int wireguard_resolve_handler(sd_resolve_query *q, + int ret, + const struct addrinfo *ai, + void *userdata) { + NetDev *netdev; + Wireguard *w; + _cleanup_(wireguard_endpoint_freep) WireguardEndpoint *e; + int r; + + assert(userdata); + e = userdata; + netdev = e->netdev; + + assert(netdev); + w = WIREGUARD(netdev); + assert(w); + + w->resolve_query = sd_resolve_query_unref(w->resolve_query); + + if (ret != 0) { + log_netdev_error(netdev, "Failed to resolve host '%s:%s': %s", e->host, e->port, gai_strerror(ret)); + LIST_PREPEND(endpoints, w->failed_endpoints, e); + e = NULL; + } else if ((ai->ai_family == AF_INET && ai->ai_addrlen == sizeof(struct sockaddr_in)) || + (ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6))) + memcpy(&e->peer->endpoint, ai->ai_addr, ai->ai_addrlen); + else + log_netdev_error(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint: %s:%s", e->host, e->port); + + if (w->unresolved_endpoints) { + resolve_endpoints(netdev); + return 0; + } + + set_wireguard_interface(netdev); + if (w->failed_endpoints) { + w->n_retries++; + r = sd_event_add_time(netdev->manager->event, + &w->resolve_retry_event_source, + CLOCK_MONOTONIC, + now(CLOCK_MONOTONIC) + exponential_backoff_milliseconds(w->n_retries), + 0, + on_resolve_retry, + netdev); + if (r < 0) + log_netdev_warning_errno(netdev, r, "Could not arm resolve retry handler: %m"); + } + + return 0; +} + +static void resolve_endpoints(NetDev *netdev) { + int r = 0; + Wireguard *w; + WireguardEndpoint *endpoint; + static const struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_DGRAM, + .ai_protocol = IPPROTO_UDP + }; + + assert(netdev); + w = WIREGUARD(netdev); + assert(w); + + LIST_FOREACH(endpoints, endpoint, w->unresolved_endpoints) { + r = sd_resolve_getaddrinfo(netdev->manager->resolve, + &w->resolve_query, + endpoint->host, + endpoint->port, + &hints, + wireguard_resolve_handler, + endpoint); + + if (r == -ENOBUFS) + break; + + LIST_REMOVE(endpoints, w->unresolved_endpoints, endpoint); + + if (r < 0) + log_netdev_error_errno(netdev, r, "Failed create resolver: %m"); + } +} + + +static int netdev_wireguard_post_create(NetDev *netdev, Link *link, sd_netlink_message *m) { + Wireguard *w; + + assert(netdev); + w = WIREGUARD(netdev); + assert(w); + + set_wireguard_interface(netdev); + resolve_endpoints(netdev); + return 0; +} + +int config_parse_wireguard_listen_port(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + uint16_t *s = data; + uint16_t port = 0; + int r; + + assert(rvalue); + assert(data); + + if (!streq(rvalue, "auto")) { + r = parse_ip_port(rvalue, &port); + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid port specification, ignoring assignment: %s", rvalue); + } + + *s = port; + + return 0; +} + +static int parse_wireguard_key(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_ void *key = NULL; + size_t len; + int r; + + assert(filename); + assert(rvalue); + assert(userdata); + + r = unbase64mem(rvalue, strlen(rvalue), &key, &len); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse wireguard key \"%s\", ignoring assignment: %m", rvalue); + return 0; + } + if (len != WG_KEY_LEN) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Wireguard key is too short, ignoring assignment: %s", rvalue); + return 0; + } + + memcpy(userdata, key, WG_KEY_LEN); + return true; +} + +int config_parse_wireguard_private_key(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) { + Wireguard *w; + + assert(data); + + w = WIREGUARD(data); + + assert(w); + + return parse_wireguard_key(unit, + filename, + line, + section, + section_line, + lvalue, + ltype, + rvalue, + data, + &w->private_key); + +} + +int config_parse_wireguard_preshared_key(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) { + Wireguard *w; + WireguardPeer *peer; + + assert(data); + + w = WIREGUARD(data); + + assert(w); + + peer = wireguard_peer_new(w, section_line); + if (!peer) + return log_oom(); + + return parse_wireguard_key(unit, + filename, + line, + section, + section_line, + lvalue, + ltype, + rvalue, + data, + peer->preshared_key); +} + + +int config_parse_wireguard_public_key(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) { + Wireguard *w; + WireguardPeer *peer; + + assert(data); + + w = WIREGUARD(data); + + assert(w); + + peer = wireguard_peer_new(w, section_line); + if (!peer) + return log_oom(); + + return parse_wireguard_key(unit, + filename, + line, + section, + section_line, + lvalue, + ltype, + rvalue, + data, + peer->public_key); +} + +int config_parse_wireguard_allowed_ips(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) { + union in_addr_union addr; + unsigned char prefixlen; + int r, family; + Wireguard *w; + WireguardPeer *peer; + WireguardIPmask *ipmask; + + assert(rvalue); + assert(data); + + w = WIREGUARD(data); + + peer = wireguard_peer_new(w, section_line); + if (!peer) + return log_oom(); + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&rvalue, &word, "," WHITESPACE, 0); + if (r == 0) + break; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to split allowed ips \"%s\" option: %m", rvalue); + break; + } + + r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Network address is invalid, ignoring assignment: %s", word); + return 0; + } + + ipmask = new0(WireguardIPmask, 1); + if (!ipmask) + return log_oom(); + ipmask->family = family; + ipmask->ip.in6 = addr.in6; + ipmask->cidr = prefixlen; + + LIST_PREPEND(ipmasks, peer->ipmasks, ipmask); + } + + return 0; +} + +int config_parse_wireguard_endpoint(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) { + Wireguard *w; + WireguardPeer *peer; + size_t len; + const char *begin, *end = NULL; + _cleanup_free_ char *host = NULL, *port = NULL; + _cleanup_(wireguard_endpoint_freep) WireguardEndpoint *endpoint = NULL; + + assert(data); + assert(rvalue); + + w = WIREGUARD(data); + + assert(w); + + peer = wireguard_peer_new(w, section_line); + if (!peer) + return log_oom(); + + endpoint = new0(WireguardEndpoint, 1); + if (!endpoint) + return log_oom(); + + if (rvalue[0] == '[') { + begin = &rvalue[1]; + end = strchr(rvalue, ']'); + if (!end) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find matching brace of endpoint, ignoring assignment: %s", rvalue); + return 0; + } + len = end - begin; + ++end; + if (*end != ':' || !*(end + 1)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find port of endpoint: %s", rvalue); + return 0; + } + ++end; + } else { + begin = rvalue; + end = strrchr(rvalue, ':'); + if (!end || !*(end + 1)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find port of endpoint: %s", rvalue); + return 0; + } + len = end - begin; + ++end; + } + + host = strndup(begin, len); + if (!host) + return log_oom(); + + port = strdup(end); + if (!port) + return log_oom(); + + endpoint->peer = peer; + endpoint->host = host; + endpoint->port = port; + endpoint->netdev = netdev_ref(data); + LIST_PREPEND(endpoints, w->unresolved_endpoints, endpoint); + + peer = NULL; + host = NULL; + port = NULL; + endpoint = NULL; + + return 0; +} + +int config_parse_wireguard_keepalive(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 r; + uint16_t keepalive = 0; + Wireguard *w; + WireguardPeer *peer; + + assert(rvalue); + assert(data); + + w = WIREGUARD(data); + + assert(w); + + peer = wireguard_peer_new(w, section_line); + if (!peer) + return log_oom(); + + if (streq(rvalue, "off")) + keepalive = 0; + else { + r = safe_atou16(rvalue, &keepalive); + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, r, "The persistent keepalive interval must be 0-65535. Ignore assignment: %s", rvalue); + } + + peer->persistent_keepalive_interval = keepalive; + return 0; +} + +static void wireguard_init(NetDev *netdev) { + Wireguard *w; + + assert(netdev); + + w = WIREGUARD(netdev); + + assert(w); + + w->flags = WGDEVICE_F_REPLACE_PEERS; +} + +static void wireguard_done(NetDev *netdev) { + Wireguard *w; + WireguardPeer *peer; + WireguardIPmask *mask; + + assert(netdev); + w = WIREGUARD(netdev); + assert(!w->unresolved_endpoints); + w->resolve_retry_event_source = sd_event_source_unref(w->resolve_retry_event_source); + + while ((peer = w->peers)) { + LIST_REMOVE(peers, w->peers, peer); + while ((mask = peer->ipmasks)) { + LIST_REMOVE(ipmasks, peer->ipmasks, mask); + free(mask); + } + free(peer); + } +} + +const NetDevVTable wireguard_vtable = { + .object_size = sizeof(Wireguard), + .sections = "Match\0NetDev\0WireGuard\0WireGuardPeer\0", + .post_create = netdev_wireguard_post_create, + .init = wireguard_init, + .done = wireguard_done, + .create_type = NETDEV_CREATE_INDEPENDENT, +}; diff --git a/src/network/netdev/wireguard.h b/src/network/netdev/wireguard.h new file mode 100644 index 0000000000..f788fa4221 --- /dev/null +++ b/src/network/netdev/wireguard.h @@ -0,0 +1,98 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2016 Jörg Thalheim <joerg@thalheim.io> + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +typedef struct Wireguard Wireguard; + +#include "netdev.h" +#include "sd-resolve.h" +#include "wireguard-netlink.h" +#include "socket-util.h" +#include "in-addr-util.h" + +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif + +typedef struct WireguardIPmask { + uint16_t family; + union in_addr_union ip; + uint8_t cidr; + + LIST_FIELDS(struct WireguardIPmask, ipmasks); +} WireguardIPmask; + +typedef struct WireguardPeer { + uint8_t public_key[WG_KEY_LEN]; + uint8_t preshared_key[WG_KEY_LEN]; + uint32_t flags; + + union sockaddr_union endpoint; + + uint16_t persistent_keepalive_interval; + + LIST_HEAD(WireguardIPmask, ipmasks); + LIST_FIELDS(struct WireguardPeer, peers); +} WireguardPeer; + +typedef struct WireguardEndpoint { + char *host; + char *port; + + NetDev *netdev; + WireguardPeer *peer; + + LIST_FIELDS(struct WireguardEndpoint, endpoints); +} WireguardEndpoint; + +struct Wireguard { + NetDev meta; + unsigned last_peer_section; + + char interface[IFNAMSIZ]; + uint32_t flags; + + uint8_t public_key[WG_KEY_LEN]; + uint8_t private_key[WG_KEY_LEN]; + uint32_t fwmark; + + uint16_t port; + + LIST_HEAD(WireguardPeer, peers); + size_t allocation_size; + sd_event_source *resolve_retry_event_source; + + LIST_HEAD(WireguardEndpoint, unresolved_endpoints); + LIST_HEAD(WireguardEndpoint, failed_endpoints); + unsigned n_retries; + sd_resolve_query *resolve_query; +}; + +DEFINE_NETDEV_CAST(WIREGUARD, Wireguard); +extern const NetDevVTable wireguard_vtable; + +int config_parse_wireguard_allowed_ips(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_wireguard_endpoint(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_wireguard_listen_port(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); + +int config_parse_wireguard_public_key(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_wireguard_private_key(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_wireguard_preshared_key(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_wireguard_keepalive(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index ff125e35de..ca5b54bdbf 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -984,251 +984,3 @@ bool address_is_ready(const Address *a) { else return !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)); } - -int config_parse_router_preference(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - Network *network = userdata; - - assert(filename); - assert(section); - assert(lvalue); - assert(rvalue); - assert(data); - - if (streq(rvalue, "high")) - network->router_preference = SD_NDISC_PREFERENCE_HIGH; - else if (STR_IN_SET(rvalue, "medium", "normal", "default")) - network->router_preference = SD_NDISC_PREFERENCE_MEDIUM; - else if (streq(rvalue, "low")) - network->router_preference = SD_NDISC_PREFERENCE_LOW; - else - log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router preference '%s' is invalid, ignoring assignment: %m", rvalue); - - return 0; -} - -void prefix_free(Prefix *prefix) { - if (!prefix) - return; - - if (prefix->network) { - LIST_REMOVE(prefixes, prefix->network->static_prefixes, prefix); - assert(prefix->network->n_static_prefixes > 0); - prefix->network->n_static_prefixes--; - - if (prefix->section) - hashmap_remove(prefix->network->prefixes_by_section, - prefix->section); - } - - prefix->radv_prefix = sd_radv_prefix_unref(prefix->radv_prefix); - - free(prefix); -} - -int prefix_new(Prefix **ret) { - _cleanup_prefix_free_ Prefix *prefix = NULL; - - prefix = new0(Prefix, 1); - if (!prefix) - return -ENOMEM; - - if (sd_radv_prefix_new(&prefix->radv_prefix) < 0) - return -ENOMEM; - - *ret = prefix; - prefix = NULL; - - return 0; -} - -int prefix_new_static(Network *network, const char *filename, - unsigned section_line, Prefix **ret) { - _cleanup_network_config_section_free_ NetworkConfigSection *n = NULL; - _cleanup_prefix_free_ Prefix *prefix = NULL; - int r; - - assert(network); - assert(ret); - assert(!!filename == (section_line > 0)); - - if (filename) { - r = network_config_section_new(filename, section_line, &n); - if (r < 0) - return r; - - if (section_line) { - prefix = hashmap_get(network->prefixes_by_section, n); - if (prefix) { - *ret = prefix; - prefix = NULL; - - return 0; - } - } - } - - r = prefix_new(&prefix); - if (r < 0) - return r; - - if (filename) { - prefix->section = n; - n = NULL; - - r = hashmap_put(network->prefixes_by_section, prefix->section, - prefix); - if (r < 0) - return r; - } - - prefix->network = network; - LIST_APPEND(prefixes, network->static_prefixes, prefix); - network->n_static_prefixes++; - - *ret = prefix; - prefix = NULL; - - return 0; -} - -int config_parse_prefix(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - Network *network = userdata; - _cleanup_prefix_free_ Prefix *p = NULL; - uint8_t prefixlen = 64; - union in_addr_union in6addr; - int r; - - assert(filename); - assert(section); - assert(lvalue); - assert(rvalue); - assert(data); - - r = prefix_new_static(network, filename, section_line, &p); - if (r < 0) - return r; - - r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Prefix is invalid, ignoring assignment: %s", rvalue); - return 0; - } - - if (sd_radv_prefix_set_prefix(p->radv_prefix, &in6addr.in6, prefixlen) < 0) - return -EADDRNOTAVAIL; - - log_syntax(unit, LOG_INFO, filename, line, r, "Found prefix %s", rvalue); - - p = NULL; - - return 0; -} - -int config_parse_prefix_flags(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - Network *network = userdata; - _cleanup_prefix_free_ Prefix *p = NULL; - int r, val; - - assert(filename); - assert(section); - assert(lvalue); - assert(rvalue); - assert(data); - - r = prefix_new_static(network, filename, section_line, &p); - if (r < 0) - return r; - - r = parse_boolean(rvalue); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address flag, ignoring: %s", rvalue); - return 0; - } - - val = r; - - if (streq(lvalue, "OnLink")) - r = sd_radv_prefix_set_onlink(p->radv_prefix, val); - else if (streq(lvalue, "AddressAutoconfiguration")) - r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, val); - if (r < 0) - return r; - - p = NULL; - - return 0; -} - -int config_parse_prefix_lifetime(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - Network *network = userdata; - _cleanup_prefix_free_ Prefix *p = NULL; - usec_t usec; - int r; - - assert(filename); - assert(section); - assert(lvalue); - assert(rvalue); - assert(data); - - r = prefix_new_static(network, filename, section_line, &p); - if (r < 0) - return r; - - r = parse_sec(rvalue, &usec); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue); - return 0; - } - - /* a value of 0xffffffff represents infinity */ - if (streq(lvalue, "PreferredLifetimeSec")) - r = sd_radv_prefix_set_preferred_lifetime(p->radv_prefix, - DIV_ROUND_UP(usec, USEC_PER_SEC)); - else if (streq(lvalue, "ValidLifetimeSec")) - r = sd_radv_prefix_set_valid_lifetime(p->radv_prefix, - DIV_ROUND_UP(usec, USEC_PER_SEC)); - if (r < 0) - return r; - - p = NULL; - - return 0; -} diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 3f5dffac4c..c2a241b571 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -26,7 +26,6 @@ #include "in-addr-util.h" typedef struct Address Address; -typedef struct Prefix Prefix; #include "networkd-link.h" #include "networkd-network.h" @@ -37,15 +36,6 @@ typedef struct Network Network; typedef struct Link Link; typedef struct NetworkConfigSection NetworkConfigSection; -struct Prefix { - Network *network; - NetworkConfigSection *section; - - sd_radv_prefix *radv_prefix; - - LIST_FIELDS(Prefix, prefixes); -}; - struct Address { Network *network; NetworkConfigSection *section; @@ -90,21 +80,9 @@ bool address_is_ready(const Address *a); DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free); #define _cleanup_address_free_ _cleanup_(address_freep) -int prefix_new(Prefix **ret); -void prefix_free(Prefix *prefix); -int prefix_new_static(Network *network, const char *filename, unsigned section, - Prefix **ret); - -DEFINE_TRIVIAL_CLEANUP_FUNC(Prefix*, prefix_free); -#define _cleanup_prefix_free_ _cleanup_(prefix_freep) - int config_parse_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_broadcast(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_label(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_lifetime(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_address_flags(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_address_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_router_preference(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_prefix(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_prefix_flags(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_prefix_lifetime(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 0b46deb009..ecb96cdb57 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -71,8 +71,9 @@ static int route_scope_from_address(const Route *route, const struct in_addr *se } static int link_set_dhcp_routes(Link *link) { - struct in_addr gateway, address; _cleanup_free_ sd_dhcp_route **static_routes = NULL; + bool classless_route = false, static_route = false; + struct in_addr gateway, address; int r, n, i; uint32_t table; @@ -102,8 +103,21 @@ static int link_set_dhcp_routes(Link *link) { log_link_debug_errno(link, n, "DHCP error: could not get routes: %m"); for (i = 0; i < n; i++) { + if (static_routes[i]->option == SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE) + classless_route = true; + + if (static_routes[i]->option == SD_DHCP_OPTION_STATIC_ROUTE) + static_route = true; + } + + for (i = 0; i < n; i++) { _cleanup_route_free_ Route *route = NULL; + /* if the DHCP server returns both a Classless Static Routes option and a Static Routes option, + the DHCP client MUST ignore the Static Routes option. */ + if (classless_route && static_routes[i]->option == SD_DHCP_OPTION_STATIC_ROUTE) + continue; + r = route_new(&route); if (r < 0) return log_link_error_errno(link, r, "Could not allocate route: %m"); @@ -132,7 +146,10 @@ static int link_set_dhcp_routes(Link *link) { /* According to RFC 3442: If the DHCP server returns both a Classless Static Routes option and a Router option, the DHCP client MUST ignore the Router option. */ - if (r >= 0 && link->dhcp4_messages <= 0) { + if (classless_route && static_route) + log_link_warning(link, "Classless static routes received from DHCP server: ignoring static-route option and router option"); + + if (r >= 0 && !classless_route) { _cleanup_route_free_ Route *route = NULL; _cleanup_route_free_ Route *route_gw = NULL; diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index a46a11bf16..234e0a4602 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -20,21 +20,249 @@ #include <netinet/ether.h> #include <linux/if.h> +#include "sd-radv.h" #include "sd-dhcp6-client.h" +#include "hashmap.h" #include "hostname-util.h" #include "network-internal.h" #include "networkd-link.h" #include "networkd-manager.h" +#include "siphash24.h" +#include "string-util.h" +#include "radv-internal.h" static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link); +static bool dhcp6_verify_link(Link *link) { + if (!link->network) { + log_link_info(link, "Link is not managed by us"); + return false; + } + + if (!IN_SET(link->network->router_prefix_delegation, + RADV_PREFIX_DELEGATION_DHCP6, + RADV_PREFIX_DELEGATION_BOTH)) { + log_link_debug(link, "Link does not request DHCPv6 prefix delegation"); + return false; + } + + return true; +} + +static bool dhcp6_enable_prefix_delegation(Link *dhcp6_link) { + Manager *manager; + Link *l; + Iterator i; + + assert(dhcp6_link); + + manager = dhcp6_link->manager; + assert(manager); + + HASHMAP_FOREACH(l, manager->links, i) { + if (l == dhcp6_link) + continue; + + if (!dhcp6_verify_link(l)) + continue; + + return true; + } + + return false; +} + static int dhcp6_lease_information_acquired(sd_dhcp6_client *client, Link *link) { return 0; } +static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix, + uint8_t prefix_len, + uint32_t lifetime_preferred, + uint32_t lifetime_valid) { + sd_radv *radv = link->radv; + int r; + _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL; + + r = sd_radv_prefix_new(&p); + if (r < 0) + return r; + + r = sd_radv_prefix_set_prefix(p, prefix, prefix_len); + if (r < 0) + return r; + + r = sd_radv_prefix_set_preferred_lifetime(p, lifetime_preferred); + if (r < 0) + return r; + + r = sd_radv_prefix_set_valid_lifetime(p, lifetime_valid); + if (r < 0) + return r; + + r = sd_radv_stop(radv); + if (r < 0) + return r; + + r = sd_radv_add_prefix(radv, p, true); + if (r < 0 && r != -EEXIST) + return r; + + r = manager_dhcp6_prefix_add(link->manager, &p->opt.in6_addr, link); + if (r < 0) + return r; + + return sd_radv_start(radv); +} + +static Network *dhcp6_reset_pd_prefix_network(Link *link) { + assert(link); + assert(link->manager); + assert(link->manager->networks); + + return link->manager->networks; +} + +static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i, + struct in6_addr *pd_prefix, + uint8_t pd_prefix_len, + uint32_t lifetime_preferred, + uint32_t lifetime_valid) { + Link *link; + Manager *manager = dhcp6_link->manager; + union in_addr_union prefix; + uint8_t n_prefixes, n_used = 0; + _cleanup_free_ char *buf = NULL; + int r; + + assert(manager); + assert(pd_prefix_len <= 64); + + prefix.in6 = *pd_prefix; + + r = in_addr_mask(AF_INET6, &prefix, pd_prefix_len); + if (r < 0) + return r; + + n_prefixes = 1 << (64 - pd_prefix_len); + + (void) in_addr_to_string(AF_INET6, &prefix, &buf); + log_link_debug(dhcp6_link, "Assigning up to %u prefixes from %s/%u", + n_prefixes, strnull(buf), pd_prefix_len); + + while (hashmap_iterate(manager->links, i, (void **)&link, NULL)) { + Link *assigned_link; + + if (n_used == n_prefixes) { + log_link_debug(dhcp6_link, "Assigned %u/%u prefixes from %s/%u", + n_used, n_prefixes, strnull(buf), pd_prefix_len); + + return -EAGAIN; + } + + if (link == dhcp6_link) + continue; + + if (!dhcp6_verify_link(link)) + continue; + + assigned_link = manager_dhcp6_prefix_get(manager, &prefix.in6); + if (assigned_link != NULL && assigned_link != link) + continue; + + r = dhcp6_pd_prefix_assign(link, &prefix.in6, 64, + lifetime_preferred, lifetime_valid); + if (r < 0) { + log_link_error_errno(link, r, "Unable to %s prefix %s/%u for link: %m", + assigned_link ? "update": "assign", + strnull(buf), pd_prefix_len); + + if (assigned_link == NULL) + continue; + + } else + log_link_debug(link, "Assigned prefix %u/%u %s/64 to link", + n_used + 1, n_prefixes, strnull(buf)); + + n_used++; + + r = in_addr_prefix_next(AF_INET6, &prefix, pd_prefix_len); + if (r < 0 && n_used < n_prefixes) + return r; + } + + if (n_used < n_prefixes) { + Route *route; + int n = n_used; + + r = route_new(&route); + if (r < 0) + return r; + + while (n < n_prefixes) { + route_update(route, &prefix, pd_prefix_len, NULL, NULL, + 0, 0, RTN_UNREACHABLE); + + r = route_configure(route, link, NULL); + if (r < 0) { + route_free(route); + return r; + } + + r = in_addr_prefix_next(AF_INET6, &prefix, pd_prefix_len); + if (r < 0) + return r; + } + } + + return n_used; +} + +static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) { + int r; + sd_dhcp6_lease *lease; + struct in6_addr pd_prefix; + uint8_t pd_prefix_len; + uint32_t lifetime_preferred, lifetime_valid; + _cleanup_free_ char *buf = NULL; + Iterator i = ITERATOR_FIRST; + + r = sd_dhcp6_client_get_lease(client, &lease); + if (r < 0) + return r; + + (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &pd_prefix, &buf); + + dhcp6_reset_pd_prefix_network(link); + sd_dhcp6_lease_reset_pd_prefix_iter(lease); + + while (sd_dhcp6_lease_get_pd(lease, &pd_prefix, &pd_prefix_len, + &lifetime_preferred, + &lifetime_valid) >= 0) { + + if (pd_prefix_len > 64) { + log_link_debug(link, "PD Prefix length > 64, ignoring prefix %s/%u", + strnull(buf), pd_prefix_len); + continue; + } + + r = dhcp6_pd_prefix_distribute(link, &i, &pd_prefix, + pd_prefix_len, + lifetime_preferred, + lifetime_valid); + if (r < 0 && r != -EAGAIN) + return r; + + if (r >= 0) + i = ITERATOR_FIRST; + } + + return 0; +} + static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { _cleanup_link_unref_ Link *link = userdata; @@ -139,6 +367,8 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { if (sd_dhcp6_client_get_lease(client, NULL) >= 0) log_link_warning(link, "DHCPv6 lease lost"); + (void) manager_dhcp6_prefix_remove_all(link->manager, link); + link->dhcp6_configured = false; break; @@ -149,6 +379,10 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { return; } + r = dhcp6_lease_pd_prefix_acquired(client, link); + if (r < 0) + log_link_debug(link, "DHCPv6 did not receive prefixes to delegate"); + _fallthrough_; case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST: r = dhcp6_lease_information_acquired(client, link); @@ -237,10 +471,11 @@ static int dhcp6_set_hostname(sd_dhcp6_client *client, Link *link) { int dhcp6_configure(Link *link) { sd_dhcp6_client *client = NULL; - int r; const DUID *duid; + int r; assert(link); + assert(link->network); if (link->dhcp6_client) return 0; @@ -279,10 +514,22 @@ int dhcp6_configure(Link *link) { if (r < 0) goto error; + if (link->network->rapid_commit) { + r = sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_RAPID_COMMIT); + if (r < 0) + goto error; + } + r = sd_dhcp6_client_set_callback(client, dhcp6_handler, link); if (r < 0) goto error; + if (dhcp6_enable_prefix_delegation(link)) { + r = sd_dhcp6_client_set_prefix_delegation(client, true); + if (r < 0) + goto error; + } + link->dhcp6_client = client; return 0; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 60ac980ad9..64c45080df 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -92,6 +92,9 @@ static bool link_ipv4ll_enabled(Link *link) { if (!link->network) return false; + if (streq_ptr(link->kind, "wireguard")) + return false; + return link->network->link_local & ADDRESS_FAMILY_IPV4; } @@ -107,6 +110,9 @@ static bool link_ipv6ll_enabled(Link *link) { if (!link->network) return false; + if (streq_ptr(link->kind, "wireguard")) + return false; + return link->network->link_local & ADDRESS_FAMILY_IPV6; } @@ -129,7 +135,7 @@ static bool link_radv_enabled(Link *link) { if (!link_ipv6ll_enabled(link)) return false; - return link->network->router_prefix_delegation; + return link->network->router_prefix_delegation != RADV_PREFIX_DELEGATION_NONE; } static bool link_lldp_rx_enabled(Link *link) { @@ -849,6 +855,8 @@ static int link_enter_set_routes(Link *link) { assert(link->network); assert(link->state == LINK_STATE_SETTING_ADDRESSES); + (void) link_set_routing_policy_rule(link); + link_set_state(link, LINK_STATE_SETTING_ROUTES); LIST_FOREACH(routes, rt, link->network->static_routes) { @@ -862,8 +870,6 @@ static int link_enter_set_routes(Link *link) { link->route_messages++; } - (void) link_set_routing_policy_rule(link); - if (link->route_messages == 0) { link->static_routes_configured = true; link_check_ready(link); @@ -3317,6 +3323,12 @@ int link_update(Link *link, sd_netlink_message *m) { if (r < 0) return log_link_warning_errno(link, r, "Could not update MAC for Router Advertisement: %m"); } + + if (link->ndisc) { + r = sd_ndisc_set_mac(link->ndisc, &link->mac); + if (r < 0) + return log_link_warning_errno(link, r, "Could not update MAC for ndisc: %m"); + } } } diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index cc17af9391..749b87f336 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -82,19 +82,6 @@ static int setup_default_address_pool(Manager *m) { return 0; } -static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) { - Manager *m = userdata; - - assert(s); - assert(m); - - m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source); - - manager_connect_bus(m); - - return 0; -} - static int manager_reset_all(Manager *m) { Link *link; Iterator i; @@ -116,6 +103,7 @@ static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_b int b, r; assert(message); + assert(m); r = sd_bus_message_read(message, "b", &b); if (r < 0) { @@ -128,40 +116,37 @@ static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_b log_debug("Coming back from suspend, resetting all connections..."); - manager_reset_all(m); + (void) manager_reset_all(m); return 0; } -int manager_connect_bus(Manager *m) { - int r; +static int on_connected(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) { + Manager *m = userdata; + assert(message); assert(m); - r = sd_bus_default_system(&m->bus); - if (r < 0) { - /* We failed to connect? Yuck, we must be in early - * boot. Let's try in 5s again. */ + /* Did we get a timezone or transient hostname from DHCP while D-Bus wasn't up yet? */ + if (m->dynamic_hostname) + (void) manager_set_hostname(m, m->dynamic_hostname); + if (m->dynamic_timezone) + (void) manager_set_timezone(m, m->dynamic_timezone); + + return 0; +} - log_debug_errno(r, "Failed to connect to bus, trying again in 5s: %m"); +int manager_connect_bus(Manager *m) { + int r; - r = sd_event_add_time(m->event, &m->bus_retry_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 5*USEC_PER_SEC, 0, on_bus_retry, m); - if (r < 0) - return log_error_errno(r, "Failed to install bus reconnect time event: %m"); + assert(m); + if (m->bus) return 0; - } - r = sd_bus_add_match(m->bus, &m->prepare_for_sleep_slot, - "type='signal'," - "sender='org.freedesktop.login1'," - "interface='org.freedesktop.login1.Manager'," - "member='PrepareForSleep'," - "path='/org/freedesktop/login1'", - match_prepare_for_sleep, - m); + r = bus_open_system_watch_bind(&m->bus); if (r < 0) - return log_error_errno(r, "Failed to add match for PrepareForSleep: %m"); + return log_error_errno(r, "Failed to connect to bus: %m"); r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/network1", "org.freedesktop.network1.Manager", manager_vtable, m); if (r < 0) @@ -183,25 +168,35 @@ int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to add network enumerator: %m"); - r = sd_bus_request_name(m->bus, "org.freedesktop.network1", 0); + r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.network1", 0, NULL, NULL); if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); + return log_error_errno(r, "Failed to request name: %m"); r = sd_bus_attach_event(m->bus, m->event, 0); if (r < 0) return log_error_errno(r, "Failed to attach bus to event loop: %m"); - /* Did we get a timezone or transient hostname from DHCP while D-Bus wasn't up yet? */ - if (m->dynamic_hostname) { - r = manager_set_hostname(m, m->dynamic_hostname); - if (r < 0) - return r; - } - if (m->dynamic_timezone) { - r = manager_set_timezone(m, m->dynamic_timezone); - if (r < 0) - return r; - } + r = sd_bus_match_signal_async( + m->bus, + &m->connected_slot, + "org.freedesktop.DBus.Local", + NULL, + "org.freedesktop.DBus.Local", + "Connected", + on_connected, NULL, m); + if (r < 0) + return log_error_errno(r, "Failed to request match on Connected signal: %m"); + + r = sd_bus_match_signal_async( + m->bus, + &m->prepare_for_sleep_slot, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "PrepareForSleep", + match_prepare_for_sleep, NULL, m); + if (r < 0) + log_warning_errno(r, "Failed to request match for PrepareForSleep, ignoring: %m"); return 0; } @@ -244,7 +239,8 @@ static int manager_dispatch_link_udev(sd_event_source *source, int fd, uint32_t if (!device) return -ENOMEM; - manager_udev_process_link(m, device); + (void) manager_udev_process_link(m, device); + return 0; } @@ -309,17 +305,17 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo if (sd_netlink_message_is_error(message)) { r = sd_netlink_message_get_errno(message); if (r < 0) - log_warning_errno(r, "rtnl: failed to receive route: %m"); + log_warning_errno(r, "rtnl: failed to receive route, ignoring: %m"); return 0; } r = sd_netlink_message_get_type(message, &type); if (r < 0) { - log_warning_errno(r, "rtnl: could not get message type: %m"); + log_warning_errno(r, "rtnl: could not get message type, ignoring: %m"); return 0; } else if (!IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE)) { - log_warning("rtnl: received unexpected message type when processing route"); + log_warning("rtnl: received unexpected message type when processing route, ignoring"); return 0; } @@ -346,7 +342,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo r = sd_rtnl_message_route_get_family(message, &family); if (r < 0 || !IN_SET(family, AF_INET, AF_INET6)) { - log_link_warning(link, "rtnl: received address with invalid family, ignoring."); + log_link_warning(link, "rtnl: received address with invalid family, ignoring"); return 0; } @@ -458,15 +454,17 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo return 0; } - route_get(link, family, &dst, dst_prefixlen, tos, priority, table, &route); + (void) route_get(link, family, &dst, dst_prefixlen, tos, priority, table, &route); switch (type) { case RTM_NEWROUTE: if (!route) { /* A route appeared that we did not request */ r = route_add_foreign(link, family, &dst, dst_prefixlen, tos, priority, table, &route); - if (r < 0) + if (r < 0) { + log_link_warning_errno(link, r, "Failed to add route, ignoring: %m"); return 0; + } } route_update(route, &src, src_prefixlen, &gw, &prefsrc, scope, protocol, rt_type); @@ -506,26 +504,26 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, if (sd_netlink_message_is_error(message)) { r = sd_netlink_message_get_errno(message); if (r < 0) - log_warning_errno(r, "rtnl: failed to receive address: %m"); + log_warning_errno(r, "rtnl: failed to receive address, ignoring: %m"); return 0; } r = sd_netlink_message_get_type(message, &type); if (r < 0) { - log_warning_errno(r, "rtnl: could not get message type: %m"); + log_warning_errno(r, "rtnl: could not get message type, ignoring: %m"); return 0; } else if (!IN_SET(type, RTM_NEWADDR, RTM_DELADDR)) { - log_warning("rtnl: received unexpected message type when processing address"); + log_warning("rtnl: received unexpected message type when processing address, ignoring"); return 0; } r = sd_rtnl_message_addr_get_ifindex(message, &ifindex); if (r < 0) { - log_warning_errno(r, "rtnl: could not get ifindex from address: %m"); + log_warning_errno(r, "rtnl: could not get ifindex from address, ignoring: %m"); return 0; } else if (ifindex <= 0) { - log_warning("rtnl: received address message with invalid ifindex: %d", ifindex); + log_warning("rtnl: received address message with invalid ifindex, ignoring: %d", ifindex); return 0; } else { r = link_get(m, ifindex, &link); @@ -540,7 +538,7 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, r = sd_rtnl_message_addr_get_family(message, &family); if (r < 0 || !IN_SET(family, AF_INET, AF_INET6)) { - log_link_warning(link, "rtnl: received address with invalid family, ignoring."); + log_link_warning(link, "rtnl: received address with invalid family, ignoring"); return 0; } @@ -582,23 +580,26 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, break; default: - log_link_debug(link, "rtnl: ignoring unsupported address family: %d", family); + assert_not_reached("Received unsupported address family"); } if (!inet_ntop(family, &in_addr, buf, INET6_ADDRSTRLEN)) { - log_link_warning(link, "Could not print address"); + log_link_warning(link, "Could not print address, ignoring"); return 0; } r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &cinfo); - if (r >= 0) { + if (r < 0 && r != -ENODATA) { + log_link_warning_errno(link, r, "rtnl: cannot get IFA_CACHEINFO attribute, ignoring: %m"); + return 0; + } else if (r >= 0) { if (cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME) valid_str = format_timespan(valid_buf, FORMAT_TIMESPAN_MAX, cinfo.ifa_valid * USEC_PER_SEC, USEC_PER_SEC); } - address_get(link, family, &in_addr, prefixlen, &address); + (void) address_get(link, family, &in_addr, prefixlen, &address); switch (type) { case RTM_NEWADDR: @@ -609,14 +610,18 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, /* An address appeared that we did not request */ r = address_add_foreign(link, family, &in_addr, prefixlen, &address); if (r < 0) { - log_link_warning_errno(link, r, "Failed to add address %s/%u: %m", buf, prefixlen); + log_link_warning_errno(link, r, "Failed to add address %s/%u, ignoring: %m", buf, prefixlen); return 0; } else log_link_debug(link, "Adding address: %s/%u (valid %s%s)", buf, prefixlen, valid_str ? "for " : "forever", strempty(valid_str)); } - address_update(address, flags, scope, &cinfo); + r = address_update(address, flags, scope, &cinfo); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to update address %s/%u, ignoring: %m", buf, prefixlen); + return 0; + } break; @@ -625,9 +630,9 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, if (address) { log_link_debug(link, "Removing address: %s/%u (valid %s%s)", buf, prefixlen, valid_str ? "for " : "forever", strempty(valid_str)); - address_drop(address); + (void) address_drop(address); } else - log_link_warning(link, "Removing non-existent address: %s/%u (valid %s%s)", buf, prefixlen, + log_link_warning(link, "Removing non-existent address: %s/%u (valid %s%s), ignoring", buf, prefixlen, valid_str ? "for " : "forever", strempty(valid_str)); break; @@ -653,32 +658,32 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa if (sd_netlink_message_is_error(message)) { r = sd_netlink_message_get_errno(message); if (r < 0) - log_warning_errno(r, "rtnl: Could not receive link: %m"); + log_warning_errno(r, "rtnl: Could not receive link, ignoring: %m"); return 0; } r = sd_netlink_message_get_type(message, &type); if (r < 0) { - log_warning_errno(r, "rtnl: Could not get message type: %m"); + log_warning_errno(r, "rtnl: Could not get message type, ignoring: %m"); return 0; } else if (!IN_SET(type, RTM_NEWLINK, RTM_DELLINK)) { - log_warning("rtnl: Received unexpected message type when processing link"); + log_warning("rtnl: Received unexpected message type when processing link, ignoring"); return 0; } r = sd_rtnl_message_link_get_ifindex(message, &ifindex); if (r < 0) { - log_warning_errno(r, "rtnl: Could not get ifindex from link: %m"); + log_warning_errno(r, "rtnl: Could not get ifindex from link, ignoring: %m"); return 0; } else if (ifindex <= 0) { - log_warning("rtnl: received link message with invalid ifindex: %d", ifindex); + log_warning("rtnl: received link message with invalid ifindex %d, ignoring", ifindex); return 0; } r = sd_netlink_message_read_string(message, IFLA_IFNAME, &name); if (r < 0) { - log_warning_errno(r, "rtnl: Received link message without ifname: %m"); + log_warning_errno(r, "rtnl: Received link message without ifname, ignoring: %m"); return 0; } @@ -691,7 +696,7 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa /* link is new, so add it */ r = link_add(m, message, &link); if (r < 0) { - log_warning_errno(r, "Could not add new link: %m"); + log_warning_errno(r, "Could not add new link, ignoring: %m"); return 0; } } @@ -700,14 +705,16 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa /* netdev exists, so make sure the ifindex matches */ r = netdev_set_ifindex(netdev, message); if (r < 0) { - log_warning_errno(r, "Could not set ifindex on netdev: %m"); + log_warning_errno(r, "Could not set ifindex on netdev, ignoring: %m"); return 0; } } r = link_update(link, message); - if (r < 0) + if (r < 0) { + log_warning_errno(r, "Could not update link, ignoring: %m"); return 0; + } break; @@ -726,11 +733,11 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) { uint8_t tos = 0, to_prefixlen = 0, from_prefixlen = 0; + union in_addr_union to = {}, from = {}; RoutingPolicyRule *rule = NULL; - union in_addr_union to, from; uint32_t fwmark = 0, table = 0; + char *iif = NULL, *oif = NULL; Manager *m = userdata; - char *iif, *oif; uint16_t type; int family; int r; @@ -742,60 +749,80 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi if (sd_netlink_message_is_error(message)) { r = sd_netlink_message_get_errno(message); if (r < 0) - log_warning_errno(r, "rtnl: failed to receive rule: %m"); + log_warning_errno(r, "rtnl: failed to receive rule, ignoring: %m"); return 0; } r = sd_netlink_message_get_type(message, &type); if (r < 0) { - log_warning_errno(r, "rtnl: could not get message type: %m"); + log_warning_errno(r, "rtnl: could not get message type, ignoring: %m"); return 0; } else if (!IN_SET(type, RTM_NEWRULE, RTM_DELRULE)) { - log_warning("rtnl: received unexpected message type '%u' when processing rule.", type); + log_warning("rtnl: received unexpected message type '%u' when processing rule, ignoring", type); return 0; } r = sd_rtnl_message_get_family(message, &family); if (r < 0) { - log_warning_errno(r, "rtnl: could not get rule family: %m"); + log_warning_errno(r, "rtnl: could not get rule family, ignoring: %m"); return 0; } else if (!IN_SET(family, AF_INET, AF_INET6)) { - log_debug("rtnl: received address with invalid family %u, ignoring.", family); + log_debug("rtnl: received address with invalid family %u, ignoring", family); return 0; } switch (family) { case AF_INET: r = sd_netlink_message_read_in_addr(message, FRA_SRC, &from.in); - if (r >= 0) { + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_SRC attribute, ignoring: %m"); + return 0; + } else if (r >= 0) { r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &from_prefixlen); - if (r < 0) - log_warning_errno(r, "rtnl: failed to retrive rule from prefix length: %m"); + if (r < 0) { + log_warning_errno(r, "rtnl: failed to retrive rule from prefix length, ignoring: %m"); + return 0; + } } r = sd_netlink_message_read_in_addr(message, FRA_DST, &to.in); - if (r >= 0) { + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_DST attribute, ignoring: %m"); + return 0; + } else if (r >= 0) { r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &to_prefixlen); - if (r < 0) - log_warning_errno(r, "rtnl: failed to retrive rule to prefix length: %m"); + if (r < 0) { + log_warning_errno(r, "rtnl: failed to retrive rule to prefix length, ignoring: %m"); + return 0; + } } break; case AF_INET6: r = sd_netlink_message_read_in6_addr(message, FRA_SRC, &from.in6); - if (r >= 0) { + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_SRC attribute, ignoring: %m"); + return 0; + } else if (r >= 0) { r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &from_prefixlen); - if (r < 0) - log_warning_errno(r, "rtnl: failed to retrive rule from prefix length: %m"); + if (r < 0) { + log_warning_errno(r, "rtnl: failed to retrive rule from prefix length, ignoring: %m"); + return 0; + } } r = sd_netlink_message_read_in6_addr(message, FRA_DST, &to.in6); - if (r >= 0) { + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_DST attribute, ignoring: %m"); + return 0; + } else if (r >= 0) { r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &to_prefixlen); - if (r < 0) - log_warning_errno(r, "rtnl: failed to retrive rule to prefix length: %m"); + if (r < 0) { + log_warning_errno(r, "rtnl: failed to retrive rule to prefix length, ignoring: %m"); + return 0; + } } break; @@ -807,11 +834,35 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi if (from_prefixlen == 0 && to_prefixlen == 0) return 0; - (void) sd_netlink_message_read_u32(message, FRA_FWMARK, &fwmark); - (void) sd_netlink_message_read_u32(message, FRA_TABLE, &table); - (void) sd_rtnl_message_routing_policy_rule_get_tos(message, &tos); - (void) sd_netlink_message_read_string(message, FRA_IIFNAME, (const char **) &iif); - (void) sd_netlink_message_read_string(message, FRA_OIFNAME, (const char **) &oif); + r = sd_netlink_message_read_u32(message, FRA_FWMARK, &fwmark); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_FWMARK attribute, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_u32(message, FRA_TABLE, &table); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_TABLE attribute, ignoring: %m"); + return 0; + } + + r = sd_rtnl_message_routing_policy_rule_get_tos(message, &tos); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get ip rule TOS, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_string(message, FRA_IIFNAME, (const char **) &iif); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_IIFNAME attribute, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_string(message, FRA_OIFNAME, (const char **) &oif); + if (r < 0 && r != -ENODATA) { + log_warning_errno(r, "rtnl: could not get FRA_OIFNAME attribute, ignoring: %m"); + return 0; + } (void) routing_policy_rule_get(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, iif, oif, &rule); @@ -820,7 +871,7 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi if (!rule) { r = routing_policy_rule_add_foreign(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, iif, oif, &rule); if (r < 0) { - log_warning_errno(r, "Could not add rule: %m"); + log_warning_errno(r, "Could not add rule, ignoring: %m"); return 0; } } @@ -856,6 +907,26 @@ static int systemd_netlink_fd(void) { return rtnl_fd; } +static int manager_connect_genl(Manager *m) { + int r; + + assert(m); + + r = sd_genl_socket_open(&m->genl); + if (r < 0) + return r; + + r = sd_netlink_inc_rcvbuf(m->genl, RCVBUF_SIZE); + if (r < 0) + return r; + + r = sd_netlink_attach_event(m->genl, m->event, 0); + if (r < 0) + return r; + + return 0; +} + static int manager_connect_rtnl(Manager *m) { int fd, r; @@ -1178,6 +1249,145 @@ static int manager_dirty_handler(sd_event_source *s, void *userdata) { return 1; } +Link *manager_dhcp6_prefix_get(Manager *m, struct in6_addr *addr) { + assert_return(m, NULL); + assert_return(m->dhcp6_prefixes, NULL); + assert_return(addr, NULL); + + return hashmap_get(m->dhcp6_prefixes, addr); +} + +static int dhcp6_route_add_callback(sd_netlink *nl, sd_netlink_message *m, + void *userdata) { + Link *l = userdata; + int r; + union in_addr_union prefix; + _cleanup_free_ char *buf = NULL; + + r = sd_netlink_message_get_errno(m); + if (r != 0) { + log_link_debug_errno(l, r, "Received error adding DHCPv6 Prefix Delegation route: %m"); + return 0; + } + + r = sd_netlink_message_read_in6_addr(m, RTA_DST, &prefix.in6); + if (r < 0) { + log_link_debug_errno(l, r, "Could not read IPv6 address from DHCPv6 Prefix Delegation while adding route: %m"); + return 0; + } + + (void) in_addr_to_string(AF_INET6, &prefix, &buf); + log_link_debug(l, "Added DHCPv6 Prefix Deleagtion route %s/64", + strnull(buf)); + + return 0; +} + +int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) { + int r; + Route *route; + + assert_return(m, -EINVAL); + assert_return(m->dhcp6_prefixes, -ENODATA); + assert_return(addr, -EINVAL); + + r = route_add(link, AF_INET6, (union in_addr_union *) addr, 64, + 0, 0, 0, &route); + if (r < 0) + return r; + + r = route_configure(route, link, dhcp6_route_add_callback); + if (r < 0) + return r; + + return hashmap_put(m->dhcp6_prefixes, addr, link); +} + +static int dhcp6_route_remove_callback(sd_netlink *nl, sd_netlink_message *m, + void *userdata) { + Link *l = userdata; + int r; + union in_addr_union prefix; + _cleanup_free_ char *buf = NULL; + + r = sd_netlink_message_get_errno(m); + if (r != 0) { + log_link_debug_errno(l, r, "Received error on DHCPv6 Prefix Delegation route removal: %m"); + return 0; + } + + r = sd_netlink_message_read_in6_addr(m, RTA_DST, &prefix.in6); + if (r < 0) { + log_link_debug_errno(l, r, "Could not read IPv6 address from DHCPv6 Prefix Delegation while removing route: %m"); + return 0; + } + + (void) in_addr_to_string(AF_INET6, &prefix, &buf); + log_link_debug(l, "Removed DHCPv6 Prefix Delegation route %s/64", + strnull(buf)); + + return 0; +} + +int manager_dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) { + Link *l; + int r; + Route *route; + + assert_return(m, -EINVAL); + assert_return(m->dhcp6_prefixes, -ENODATA); + assert_return(addr, -EINVAL); + + l = hashmap_remove(m->dhcp6_prefixes, addr); + if (!l) + return -EINVAL; + + (void) sd_radv_remove_prefix(l->radv, addr, 64); + r = route_get(l, AF_INET6, (union in_addr_union *) addr, 64, + 0, 0, 0, &route); + if (r >= 0) + (void) route_remove(route, l, dhcp6_route_remove_callback); + + return 0; +} + +int manager_dhcp6_prefix_remove_all(Manager *m, Link *link) { + Iterator i; + Link *l; + struct in6_addr *addr; + + assert_return(m, -EINVAL); + assert_return(link, -EINVAL); + + HASHMAP_FOREACH_KEY(l, addr, m->dhcp6_prefixes, i) { + if (l != link) + continue; + + (void) manager_dhcp6_prefix_remove(m, addr); + } + + return 0; +} + +static void dhcp6_prefixes_hash_func(const void *p, struct siphash *state) { + const struct in6_addr *addr = p; + + assert(p); + + siphash24_compress(addr, sizeof(*addr), state); +} + +static int dhcp6_prefixes_compare_func(const void *_a, const void *_b) { + const struct in6_addr *a = _a, *b = _b; + + return memcmp(&a, &b, sizeof(*a)); +} + +static const struct hash_ops dhcp6_prefixes_hash_ops = { + .hash = dhcp6_prefixes_hash_func, + .compare = dhcp6_prefixes_compare_func, +}; + int manager_new(Manager **ret, sd_event *event) { _cleanup_manager_free_ Manager *m = NULL; int r; @@ -1200,6 +1410,10 @@ int manager_new(Manager **ret, sd_event *event) { if (r < 0) return r; + r = manager_connect_genl(m); + if (r < 0) + return r; + r = manager_connect_udev(m); if (r < 0) return r; @@ -1210,10 +1424,22 @@ int manager_new(Manager **ret, sd_event *event) { LIST_HEAD_INIT(m->networks); + r = sd_resolve_default(&m->resolve); + if (r < 0) + return r; + + r = sd_resolve_attach_event(m->resolve, m->event, 0); + if (r < 0) + return r; + r = setup_default_address_pool(m); if (r < 0) return r; + m->dhcp6_prefixes = hashmap_new(&dhcp6_prefixes_hash_ops); + if (!m->dhcp6_prefixes) + return -ENOMEM; + m->duid.type = DUID_TYPE_EN; (void) routing_policy_load_rules(m->state_file, &m->rules_saved); @@ -1238,6 +1464,10 @@ void manager_free(Manager *m) { while ((network = m->networks)) network_free(network); + while ((link = hashmap_first(m->dhcp6_prefixes))) + link_unref(link); + hashmap_free(m->dhcp6_prefixes); + while ((link = hashmap_first(m->links))) link_unref(link); hashmap_free(m->links); @@ -1259,12 +1489,15 @@ void manager_free(Manager *m) { sd_netlink_unref(m->rtnl); sd_event_unref(m->event); + sd_resolve_unref(m->resolve); + sd_event_source_unref(m->udev_event_source); udev_monitor_unref(m->udev_monitor); udev_unref(m->udev); sd_bus_unref(m->bus); sd_bus_slot_unref(m->prepare_for_sleep_slot); + sd_bus_slot_unref(m->connected_slot); sd_event_source_unref(m->bus_retry_event_source); free(m->dynamic_timezone); @@ -1539,12 +1772,12 @@ int manager_set_hostname(Manager *m, const char *hostname) { int r; log_debug("Setting transient hostname: '%s'", strna(hostname)); + if (free_and_strdup(&m->dynamic_hostname, hostname) < 0) return log_oom(); - if (!m->bus) { - /* TODO: replace by assert when we can rely on kdbus */ - log_info("Not connected to system bus, ignoring transient hostname."); + if (!m->bus || sd_bus_is_ready(m->bus) <= 0) { + log_info("Not connected to system bus, not setting hostname."); return 0; } @@ -1591,8 +1824,8 @@ int manager_set_timezone(Manager *m, const char *tz) { if (free_and_strdup(&m->dynamic_timezone, tz) < 0) return log_oom(); - if (!m->bus) { - log_info("Not connected to system bus, ignoring timezone."); + if (!m->bus || sd_bus_is_ready(m->bus) <= 0) { + log_info("Not connected to system bus, not setting hostname."); return 0; } diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 186cb41891..229812ae0e 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -25,6 +25,7 @@ #include "sd-bus.h" #include "sd-event.h" #include "sd-netlink.h" +#include "sd-resolve.h" #include "udev.h" #include "dhcp-identifier.h" @@ -39,10 +40,14 @@ extern const char* const network_dirs[]; struct Manager { sd_netlink *rtnl; + /* lazy initialized */ + sd_netlink *genl; sd_event *event; + sd_resolve *resolve; sd_event_source *bus_retry_event_source; sd_bus *bus; sd_bus_slot *prepare_for_sleep_slot; + sd_bus_slot *connected_slot; struct udev *udev; struct udev_monitor *udev_monitor; sd_event_source *udev_event_source; @@ -58,6 +63,7 @@ struct Manager { Hashmap *links; Hashmap *netdevs; Hashmap *networks_by_name; + Hashmap *dhcp6_prefixes; LIST_HEAD(Network, networks); LIST_HEAD(AddressPool, address_pools); @@ -109,5 +115,10 @@ Link* manager_find_uplink(Manager *m, Link *exclude); int manager_set_hostname(Manager *m, const char *hostname); int manager_set_timezone(Manager *m, const char *timezone); +Link *manager_dhcp6_prefix_get(Manager *m, struct in6_addr *addr); +int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link); +int manager_dhcp6_prefix_remove(Manager *m, struct in6_addr *addr); +int manager_dhcp6_prefix_remove_all(Manager *m, Link *link); + DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); #define _cleanup_manager_free_ _cleanup_(manager_freep) diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 57a96aff94..281ba657b3 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -27,7 +27,8 @@ Match.Type, config_parse_strv, Match.Name, config_parse_ifnames, 0, offsetof(Network, match_name) Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, match_host) Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, match_virt) -Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, match_kernel) +Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, match_kernel_cmdline) +Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(Network, match_kernel_version) Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, match_arch) Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac) Link.MTUBytes, config_parse_iec_size, 0, offsetof(Network, mtu) @@ -107,6 +108,9 @@ Route.GatewayOnlink, config_parse_gateway_onlink, Route.IPv6Preference, config_parse_ipv6_route_preference, 0, 0 Route.Protocol, config_parse_route_protocol, 0, 0 Route.Type, config_parse_route_type, 0, 0 +Route.InitialCongestionWindow, config_parse_tcp_window, 0, 0 +Route.InitialAdvertisedReceiveWindow, config_parse_tcp_window, 0, 0 +Route.QuickAck, config_parse_quickack, 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) @@ -127,6 +131,7 @@ DHCP.RouteTable, config_parse_dhcp_route_table, DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone) DHCP.IAID, config_parse_iaid, 0, offsetof(Network, iaid) DHCP.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port) +DHCP.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit) IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns) IPv6AcceptRA.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains) IPv6AcceptRA.RouteTable, config_parse_uint32, 0, offsetof(Network, ipv6_accept_ra_route_table) @@ -153,7 +158,7 @@ BridgeFDB.VLANId, config_parse_fdb_vlan_id, BridgeVLAN.PVID, config_parse_brvlan_pvid, 0, 0 BridgeVLAN.VLAN, config_parse_brvlan_vlan, 0, 0 BridgeVLAN.EgressUntagged, config_parse_brvlan_untagged, 0, 0 -Network.IPv6PrefixDelegation, config_parse_bool, 0, offsetof(Network, router_prefix_delegation) +Network.IPv6PrefixDelegation, config_parse_router_prefix_delegation, 0, 0 IPv6PrefixDelegation.RouterLifetimeSec, config_parse_sec, 0, offsetof(Network, router_lifetime_usec) IPv6PrefixDelegation.Managed, config_parse_bool, 0, offsetof(Network, router_managed) IPv6PrefixDelegation.OtherInformation, config_parse_bool, 0, offsetof(Network, router_other_information) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 8e37a0a229..2dc3de3f6a 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -228,6 +228,7 @@ static int network_load_one(Manager *manager, const char *filename) { network->dhcp_use_mtu = false; /* NOTE: from man: UseTimezone=... Defaults to "no".*/ network->dhcp_use_timezone = false; + network->rapid_commit = true; network->dhcp_server_emit_dns = true; network->dhcp_server_emit_ntp = true; @@ -431,7 +432,8 @@ void network_free(Network *network) { condition_free_list(network->match_host); condition_free_list(network->match_virt); - condition_free_list(network->match_kernel); + condition_free_list(network->match_kernel_cmdline); + condition_free_list(network->match_kernel_version); condition_free_list(network->match_arch); free(network->dhcp_server_timezone); @@ -485,8 +487,8 @@ int network_get(Manager *manager, struct udev_device *device, if (net_match_config(network->match_mac, network->match_path, network->match_driver, network->match_type, network->match_name, network->match_host, - network->match_virt, network->match_kernel, - network->match_arch, + network->match_virt, network->match_kernel_cmdline, + network->match_kernel_version, network->match_arch, address, path, parent_driver, driver, devtype, ifname)) { if (network->match_name && device) { diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 49c62654b6..7b40ba51e6 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -34,6 +34,7 @@ #include "networkd-fdb.h" #include "networkd-lldp-tx.h" #include "networkd-ipv6-proxy-ndp.h" +#include "networkd-radv.h" #include "networkd-route.h" #include "networkd-routing-policy-rule.h" #include "networkd-util.h" @@ -85,6 +86,13 @@ typedef struct DUID { uint8_t raw_data[MAX_DUID_LEN]; } DUID; +typedef enum RADVPrefixDelegation { + RADV_PREFIX_DELEGATION_NONE, + RADV_PREFIX_DELEGATION_STATIC, + RADV_PREFIX_DELEGATION_DHCP6, + RADV_PREFIX_DELEGATION_BOTH, +} RADVPrefixDelegation; + typedef struct NetworkConfigSection { unsigned line; char filename[]; @@ -112,7 +120,8 @@ struct Network { Condition *match_host; Condition *match_virt; - Condition *match_kernel; + Condition *match_kernel_cmdline; + Condition *match_kernel_version; Condition *match_arch; char *description; @@ -139,6 +148,7 @@ struct Network { bool dhcp_use_mtu; bool dhcp_use_routes; bool dhcp_use_timezone; + bool rapid_commit; bool dhcp_use_hostname; bool dhcp_route_table_set; DHCPUseDomains dhcp_use_domains; @@ -163,7 +173,7 @@ struct Network { bool ipv4ll_route; /* IPv6 prefix delegation support */ - bool router_prefix_delegation; + RADVPrefixDelegation router_prefix_delegation; usec_t router_lifetime_usec; uint8_t router_preference; bool router_managed; diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index 535454c472..a921d120b3 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -24,7 +24,296 @@ #include "networkd-address.h" #include "networkd-manager.h" #include "networkd-radv.h" +#include "parse-util.h" #include "sd-radv.h" +#include "string-util.h" + +int config_parse_router_prefix_delegation( + 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; + int d; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + if (streq(rvalue, "static")) + network->router_prefix_delegation = RADV_PREFIX_DELEGATION_STATIC; + else if (streq(rvalue, "dhcpv6")) + network->router_prefix_delegation = RADV_PREFIX_DELEGATION_DHCP6; + else { + d = parse_boolean(rvalue); + if (d > 0) + network->router_prefix_delegation = RADV_PREFIX_DELEGATION_BOTH; + else + network->router_prefix_delegation = RADV_PREFIX_DELEGATION_NONE; + + if (d < 0) + log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router prefix delegation '%s' is invalid, ignoring assignment: %m", rvalue); + } + + return 0; +} + +int config_parse_router_preference(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Network *network = userdata; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + if (streq(rvalue, "high")) + network->router_preference = SD_NDISC_PREFERENCE_HIGH; + else if (STR_IN_SET(rvalue, "medium", "normal", "default")) + network->router_preference = SD_NDISC_PREFERENCE_MEDIUM; + else if (streq(rvalue, "low")) + network->router_preference = SD_NDISC_PREFERENCE_LOW; + else + log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router preference '%s' is invalid, ignoring assignment: %m", rvalue); + + return 0; +} + +void prefix_free(Prefix *prefix) { + if (!prefix) + return; + + if (prefix->network) { + LIST_REMOVE(prefixes, prefix->network->static_prefixes, prefix); + assert(prefix->network->n_static_prefixes > 0); + prefix->network->n_static_prefixes--; + + if (prefix->section) + hashmap_remove(prefix->network->prefixes_by_section, + prefix->section); + } + + prefix->radv_prefix = sd_radv_prefix_unref(prefix->radv_prefix); + + free(prefix); +} + +int prefix_new(Prefix **ret) { + Prefix *prefix = NULL; + + prefix = new0(Prefix, 1); + if (!prefix) + return -ENOMEM; + + if (sd_radv_prefix_new(&prefix->radv_prefix) < 0) + return -ENOMEM; + + *ret = prefix; + prefix = NULL; + + return 0; +} + +int prefix_new_static(Network *network, const char *filename, + unsigned section_line, Prefix **ret) { + _cleanup_network_config_section_free_ NetworkConfigSection *n = NULL; + _cleanup_prefix_free_ Prefix *prefix = NULL; + int r; + + assert(network); + assert(ret); + assert(!!filename == (section_line > 0)); + + if (filename) { + r = network_config_section_new(filename, section_line, &n); + if (r < 0) + return r; + + if (section_line) { + prefix = hashmap_get(network->prefixes_by_section, n); + if (prefix) { + *ret = prefix; + prefix = NULL; + + return 0; + } + } + } + + r = prefix_new(&prefix); + if (r < 0) + return r; + + if (filename) { + prefix->section = n; + n = NULL; + + r = hashmap_put(network->prefixes_by_section, prefix->section, + prefix); + if (r < 0) + return r; + } + + prefix->network = network; + LIST_APPEND(prefixes, network->static_prefixes, prefix); + network->n_static_prefixes++; + + *ret = prefix; + prefix = NULL; + + return 0; +} + +int config_parse_prefix(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Network *network = userdata; + _cleanup_prefix_free_ Prefix *p = NULL; + uint8_t prefixlen = 64; + union in_addr_union in6addr; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = prefix_new_static(network, filename, section_line, &p); + if (r < 0) + return r; + + r = in_addr_prefix_from_string(rvalue, AF_INET6, &in6addr, &prefixlen); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Prefix is invalid, ignoring assignment: %s", rvalue); + return 0; + } + + if (sd_radv_prefix_set_prefix(p->radv_prefix, &in6addr.in6, prefixlen) < 0) + return -EADDRNOTAVAIL; + + log_syntax(unit, LOG_INFO, filename, line, r, "Found prefix %s", rvalue); + + p = NULL; + + return 0; +} + +int config_parse_prefix_flags(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Network *network = userdata; + _cleanup_prefix_free_ Prefix *p = NULL; + int r, val; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = prefix_new_static(network, filename, section_line, &p); + if (r < 0) + return r; + + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address flag, ignoring: %s", rvalue); + return 0; + } + + val = r; + + if (streq(lvalue, "OnLink")) + r = sd_radv_prefix_set_onlink(p->radv_prefix, val); + else if (streq(lvalue, "AddressAutoconfiguration")) + r = sd_radv_prefix_set_address_autoconfiguration(p->radv_prefix, val); + if (r < 0) + return r; + + p = NULL; + + return 0; +} + +int config_parse_prefix_lifetime(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Network *network = userdata; + _cleanup_prefix_free_ Prefix *p = NULL; + usec_t usec; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = prefix_new_static(network, filename, section_line, &p); + if (r < 0) + return r; + + r = parse_sec(rvalue, &usec); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Lifetime is invalid, ignoring assignment: %s", rvalue); + return 0; + } + + /* a value of 0xffffffff represents infinity */ + if (streq(lvalue, "PreferredLifetimeSec")) + r = sd_radv_prefix_set_preferred_lifetime(p->radv_prefix, + DIV_ROUND_UP(usec, USEC_PER_SEC)); + else if (streq(lvalue, "ValidLifetimeSec")) + r = sd_radv_prefix_set_valid_lifetime(p->radv_prefix, + DIV_ROUND_UP(usec, USEC_PER_SEC)); + if (r < 0) + return r; + + p = NULL; + + return 0; +} static int radv_get_ip6dns(Network *network, struct in6_addr **dns, size_t *n_dns) { @@ -211,10 +500,14 @@ int radv_configure(Link *link) { return r; } - LIST_FOREACH(prefixes, p, link->network->static_prefixes) { - r = sd_radv_add_prefix(link->radv, p->radv_prefix); - if (r != -EEXIST && r < 0) - return r; + if (IN_SET(link->network->router_prefix_delegation, + RADV_PREFIX_DELEGATION_STATIC, + RADV_PREFIX_DELEGATION_BOTH)) { + LIST_FOREACH(prefixes, p, link->network->static_prefixes) { + r = sd_radv_add_prefix(link->radv, p->radv_prefix, false); + if (r != -EEXIST && r < 0) + return r; + } } return radv_emit_dns(link); diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h index f23029935a..22a169d263 100644 --- a/src/network/networkd-radv.h +++ b/src/network/networkd-radv.h @@ -20,7 +20,33 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "networkd-address.h" #include "networkd-link.h" +typedef struct Prefix Prefix; + +struct Prefix { + Network *network; + NetworkConfigSection *section; + + sd_radv_prefix *radv_prefix; + + LIST_FIELDS(Prefix, prefixes); +}; + +int prefix_new(Prefix **ret); +void prefix_free(Prefix *prefix); +int prefix_new_static(Network *network, const char *filename, unsigned section, + Prefix **ret); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Prefix*, prefix_free); +#define _cleanup_prefix_free_ _cleanup_(prefix_freep) + +int config_parse_router_prefix_delegation(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_router_preference(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_prefix(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_prefix_flags(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_prefix_lifetime(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); + int radv_emit_dns(Link *link); int radv_configure(Link *link); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index b0ad707811..70dca5219b 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -74,6 +74,7 @@ int route_new(Route **ret) { route->type = RTN_UNICAST; route->table = RT_TABLE_MAIN; route->lifetime = USEC_INFINITY; + route->quickack = -1; *ret = route; route = NULL; @@ -636,6 +637,24 @@ int route_configure( return log_error_errno(r, "Could not append RTAX_MTU attribute: %m"); } + if (route->initcwnd > 0) { + r = sd_netlink_message_append_u32(req, RTAX_INITCWND, route->initcwnd); + if (r < 0) + return log_error_errno(r, "Could not append RTAX_INITCWND attribute: %m"); + } + + if (route->initrwnd > 0) { + r = sd_netlink_message_append_u32(req, RTAX_INITRWND, route->initrwnd); + if (r < 0) + return log_error_errno(r, "Could not append RTAX_INITRWND attribute: %m"); + } + + if (route->quickack != -1) { + r = sd_netlink_message_append_u32(req, RTAX_QUICKACK, route->quickack); + if (r < 0) + return log_error_errno(r, "Could not append RTAX_QUICKACK attribute: %m"); + } + r = sd_netlink_message_close_container(req); if (r < 0) return log_error_errno(r, "Could not append RTA_METRICS attribute: %m"); @@ -1069,3 +1088,85 @@ int config_parse_route_type(const char *unit, return 0; } + +int config_parse_tcp_window(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; + uint64_t k; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = route_new_static(network, filename, section_line, &n); + if (r < 0) + return r; + + r = parse_size(rvalue, 1024, &k); + if (r < 0 || k > UINT32_MAX) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Could not parse TCP %s \"%s\" bytes, ignoring assignment: %m", rvalue, lvalue); + return 0; + } + + if (streq(lvalue, "InitialCongestionWindow")) + n->initcwnd = k; + else if (streq(lvalue, "InitialAdvertisedReceiveWindow")) + n->initrwnd = k; + else { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse TCP %s: %s", lvalue, rvalue); + return 0; + } + + n = NULL; + + return 0; +} + +int config_parse_quickack(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; + int k, r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = route_new_static(network, filename, section_line, &n); + if (r < 0) + return r; + + k = parse_boolean(rvalue); + if (k < 0) { + log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse TCP quickack, ignoring: %s", rvalue); + return 0; + } + + n->quickack = !!k; + n = NULL; + + return 0; +} diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index cfb85cbd6d..6db9d592ea 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -32,6 +32,8 @@ struct Route { Link *link; int family; + int quickack; + unsigned char dst_prefixlen; unsigned char src_prefixlen; unsigned char scope; @@ -41,6 +43,8 @@ struct Route { uint32_t priority; /* note that ip(8) calls this 'metric' */ uint32_t table; uint32_t mtu; + uint32_t initcwnd; + uint32_t initrwnd; unsigned char pref; unsigned flags; @@ -81,3 +85,5 @@ int config_parse_gateway_onlink(const char *unit, const char *filename, unsigned int config_parse_ipv6_route_preference(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_route_protocol(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_route_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_tcp_window(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_quickack(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-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c index 1314564c11..fdbaec58eb 100644 --- a/src/network/networkd-routing-policy-rule.c +++ b/src/network/networkd-routing-policy-rule.c @@ -60,10 +60,11 @@ void routing_policy_rule_free(RoutingPolicyRule *rule) { network_config_section_free(rule->section); } - if (rule->network->manager) { - set_remove(rule->network->manager->rules, rule); - set_remove(rule->network->manager->rules_foreign, rule); - } + } + + if (rule->manager) { + set_remove(rule->manager->rules, rule); + set_remove(rule->manager->rules_foreign, rule); } free(rule->iif); @@ -236,7 +237,8 @@ int routing_policy_rule_make_local(Manager *m, RoutingPolicyRule *rule) { return -ENOENT; } -static int routing_policy_rule_add_internal(Set **rules, +static int routing_policy_rule_add_internal(Manager *m, + Set **rules, int family, const union in_addr_union *from, uint8_t from_prefixlen, @@ -258,6 +260,7 @@ static int routing_policy_rule_add_internal(Set **rules, if (r < 0) return r; + rule->manager = m; rule->family = family; rule->from = *from; rule->from_prefixlen = from_prefixlen; @@ -298,7 +301,7 @@ int routing_policy_rule_add(Manager *m, char *oif, RoutingPolicyRule **ret) { - return routing_policy_rule_add_internal(&m->rules, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, ret); + return routing_policy_rule_add_internal(m, &m->rules, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, ret); } int routing_policy_rule_add_foreign(Manager *m, @@ -313,7 +316,7 @@ int routing_policy_rule_add_foreign(Manager *m, char *iif, char *oif, RoutingPolicyRule **ret) { - return routing_policy_rule_add_internal(&m->rules_foreign, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, ret); + return routing_policy_rule_add_internal(m, &m->rules_foreign, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, ret); } static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { diff --git a/src/network/networkd-routing-policy-rule.h b/src/network/networkd-routing-policy-rule.h index 70a861723b..48353b9d10 100644 --- a/src/network/networkd-routing-policy-rule.h +++ b/src/network/networkd-routing-policy-rule.h @@ -33,8 +33,10 @@ typedef struct RoutingPolicyRule RoutingPolicyRule; typedef struct Network Network; typedef struct Link Link; typedef struct NetworkConfigSection NetworkConfigSection; +typedef struct Manager Manager; struct RoutingPolicyRule { + Manager *manager; Network *network; Link *link; NetworkConfigSection *section; diff --git a/src/network/networkd.c b/src/network/networkd.c index 9243384af8..79c15d4111 100644 --- a/src/network/networkd.c +++ b/src/network/networkd.c @@ -53,24 +53,13 @@ int main(int argc, char *argv[]) { goto out; } - /* Always create the directories people can create inotify - * watches in. */ + /* Create runtime directory. This is not necessary when networkd is + * started with "RuntimeDirectory=systemd/netif", or after + * systemd-tmpfiles-setup.service. */ r = mkdir_safe_label("/run/systemd/netif", 0755, uid, gid, false); if (r < 0) log_warning_errno(r, "Could not create runtime directory: %m"); - r = mkdir_safe_label("/run/systemd/netif/links", 0755, uid, gid, false); - if (r < 0) - log_warning_errno(r, "Could not create runtime directory 'links': %m"); - - r = mkdir_safe_label("/run/systemd/netif/leases", 0755, uid, gid, false); - if (r < 0) - log_warning_errno(r, "Could not create runtime directory 'leases': %m"); - - r = mkdir_safe_label("/run/systemd/netif/lldp", 0755, uid, gid, false); - if (r < 0) - log_warning_errno(r, "Could not create runtime directory 'lldp': %m"); - /* Drop privileges, but only if we have been started as root. If we are not running as root we assume all * privileges are already dropped. */ if (geteuid() == 0) { @@ -83,6 +72,21 @@ int main(int argc, char *argv[]) { goto out; } + /* Always create the directories people can create inotify watches in. + * It is necessary to create the following subdirectories after drop_privileges() + * to support old kernels not supporting AmbientCapabilities=. */ + r = mkdir_safe_label("/run/systemd/netif/links", 0755, uid, gid, false); + if (r < 0) + log_warning_errno(r, "Could not create runtime directory 'links': %m"); + + r = mkdir_safe_label("/run/systemd/netif/leases", 0755, uid, gid, false); + if (r < 0) + log_warning_errno(r, "Could not create runtime directory 'leases': %m"); + + r = mkdir_safe_label("/run/systemd/netif/lldp", 0755, uid, gid, false); + if (r < 0) + log_warning_errno(r, "Could not create runtime directory 'lldp': %m"); + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); r = sd_event_default(&event); diff --git a/src/network/test-routing-policy-rule.c b/src/network/test-routing-policy-rule.c index c29d134de2..3bbdd25b76 100644 --- a/src/network/test-routing-policy-rule.c +++ b/src/network/test-routing-policy-rule.c @@ -41,13 +41,15 @@ static void test_rule_serialization(const char *title, const char *ruleset, cons log_info("========== %s ==========", title); log_info("put:\n%s\n", ruleset); - assert_se((fd = mkostemp_safe(pattern)) >= 0); + fd = mkostemp_safe(pattern); + assert_se(fd >= 0); assert_se(f = fdopen(fd, "a+e")); assert_se(write_string_stream(f, ruleset, 0) == 0); assert_se(routing_policy_load_rules(pattern, &rules) == 0); - assert_se((fd2 = mkostemp_safe(pattern2)) >= 0); + fd2 = mkostemp_safe(pattern2); + assert_se(fd2 >= 0); assert_se(f2 = fdopen(fd2, "a+e")); assert_se(routing_policy_serialize_rules(rules, f2) == 0); @@ -57,7 +59,8 @@ static void test_rule_serialization(const char *title, const char *ruleset, cons log_info("got:\n%s", buf); - assert_se((fd3 = mkostemp_safe(pattern3)) >= 0); + fd3 = mkostemp_safe(pattern3); + assert_se(fd3 >= 0); assert_se(f3 = fdopen(fd3, "we")); assert_se(write_string_stream(f3, expected ?: ruleset, 0) == 0); diff --git a/src/notify/notify.c b/src/notify/notify.c index 3e511b7e47..d58a45cdd2 100644 --- a/src/notify/notify.c +++ b/src/notify/notify.c @@ -33,12 +33,15 @@ #include "parse-util.h" #include "string-util.h" #include "strv.h" +#include "user-util.h" #include "util.h" static bool arg_ready = false; static pid_t arg_pid = 0; static const char *arg_status = NULL; static bool arg_booted = false; +static uid_t arg_uid = UID_INVALID; +static gid_t arg_gid = GID_INVALID; static void help(void) { printf("%s [OPTIONS...] [VARIABLE=VALUE...]\n\n" @@ -46,7 +49,8 @@ static void help(void) { " -h --help Show this help\n" " --version Show package version\n" " --ready Inform the init system about service start-up completion\n" - " --pid[=PID] Set main pid of daemon\n" + " --pid[=PID] Set main PID of daemon\n" + " --uid=USER Set user to send from\n" " --status=TEXT Set status text\n" " --booted Check if the system was booted up with systemd\n", program_invocation_short_name); @@ -60,6 +64,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_PID, ARG_STATUS, ARG_BOOTED, + ARG_UID, }; static const struct option options[] = { @@ -69,10 +74,11 @@ static int parse_argv(int argc, char *argv[]) { { "pid", optional_argument, NULL, ARG_PID }, { "status", required_argument, NULL, ARG_STATUS }, { "booted", no_argument, NULL, ARG_BOOTED }, + { "uid", required_argument, NULL, ARG_UID }, {} }; - int c; + int c, r; assert(argc >= 0); assert(argv); @@ -112,6 +118,18 @@ static int parse_argv(int argc, char *argv[]) { arg_booted = true; break; + case ARG_UID: { + const char *u = optarg; + + r = get_user_creds(&u, &arg_uid, &arg_gid, NULL, NULL); + if (r == -ESRCH) /* If the user doesn't exist, then accept it anyway as numeric */ + r = parse_uid(u, &arg_uid); + if (r < 0) + return log_error_errno(r, "Can't resolve user %s: %m", optarg); + + break; + } + case '?': return -EINVAL; @@ -179,7 +197,7 @@ int main(int argc, char* argv[]) { goto finish; } - if (strv_length(final_env) <= 0) { + if (strv_isempty(final_env)) { r = 0; goto finish; } @@ -190,6 +208,22 @@ int main(int argc, char* argv[]) { goto finish; } + /* If this is requested change to the requested UID/GID. Note thta we only change the real UID here, and leave + the effective UID in effect (which is 0 for this to work). That's because we want the privileges to fake the + ucred data, and sd_pid_notify() uses the real UID for filling in ucred. */ + + if (arg_gid != GID_INVALID) + if (setregid(arg_gid, (gid_t) -1) < 0) { + r = log_error_errno(errno, "Failed to change GID: %m"); + goto finish; + } + + if (arg_uid != UID_INVALID) + if (setreuid(arg_uid, (uid_t) -1) < 0) { + r = log_error_errno(errno, "Failed to change UID: %m"); + goto finish; + } + r = sd_pid_notify(arg_pid ? arg_pid : getppid(), false, n); if (r < 0) { log_error_errno(r, "Failed to notify init system: %m"); diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 920e114718..c9236ea3d1 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -474,9 +474,9 @@ static int mkdir_userns(const char *path, mode_t mode, MountSettingsMask mask, u assert(path); - r = mkdir(path, mode); - if (r < 0 && errno != EEXIST) - return -errno; + r = mkdir_errno_wrapper(path, mode); + if (r < 0 && r != -EEXIST) + return r; if ((mask & MOUNT_USE_USERNS) == 0) return 0; @@ -484,8 +484,7 @@ static int mkdir_userns(const char *path, mode_t mode, MountSettingsMask mask, u if (mask & MOUNT_IN_USERNS) return 0; - r = lchown(path, uid_shift, uid_shift); - if (r < 0) + if (lchown(path, uid_shift, uid_shift) < 0) return -errno; return 0; diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c index ef9db31df7..07d68242c6 100644 --- a/src/nspawn/nspawn-register.c +++ b/src/nspawn/nspawn-register.c @@ -203,7 +203,7 @@ int register_machine( if (r < 0) return r; - r = bus_append_unit_property_assignment_many(m, properties); + r = bus_append_unit_property_assignment_many(m, UNIT_SERVICE, properties); if (r < 0) return r; @@ -339,7 +339,7 @@ int allocate_scope( if (r < 0) return r; - r = bus_append_unit_property_assignment_many(m, properties); + r = bus_append_unit_property_assignment_many(m, UNIT_SCOPE, properties); if (r < 0) return r; diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index c0c5a153b0..10b8a63052 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -22,6 +22,8 @@ #include <stdio.h> +#include "sd-id128.h" + #include "macro.h" #include "nspawn-expose-ports.h" #include "nspawn-mount.h" diff --git a/src/nspawn/nspawn-setuid.c b/src/nspawn/nspawn-setuid.c index 31f5dd3cdd..b08bcd988a 100644 --- a/src/nspawn/nspawn-setuid.c +++ b/src/nspawn/nspawn-setuid.c @@ -23,6 +23,7 @@ #include <unistd.h> #include "alloc-util.h" +#include "errno.h" #include "fd-util.h" #include "mkdir.h" #include "nspawn-setuid.h" @@ -33,7 +34,7 @@ #include "util.h" static int spawn_getent(const char *database, const char *key, pid_t *rpid) { - int pipe_fds[2]; + int pipe_fds[2], r; pid_t pid; assert(database); @@ -43,10 +44,10 @@ static int spawn_getent(const char *database, const char *key, pid_t *rpid) { if (pipe2(pipe_fds, O_CLOEXEC) < 0) return log_error_errno(errno, "Failed to allocate pipe: %m"); - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork getent child: %m"); - else if (pid == 0) { + r = safe_fork("(getent)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); + if (r < 0) + return r; + if (r == 0) { int nullfd; char *empty_env = NULL; @@ -71,8 +72,6 @@ static int spawn_getent(const char *database, const char *key, pid_t *rpid) { if (nullfd > 2) safe_close(nullfd); - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); close_all_fds(NULL, 0); execle("/usr/bin/getent", "getent", database, key, NULL, &empty_env); @@ -135,7 +134,7 @@ int change_uid_gid(const char *user, char **_home) { truncate_nl(line); - wait_for_terminate_and_warn("getent passwd", pid, true); + (void) wait_for_terminate_and_check("getent passwd", pid, WAIT_LOG); x = strchr(line, ':'); if (!x) { @@ -218,7 +217,7 @@ int change_uid_gid(const char *user, char **_home) { truncate_nl(line); - wait_for_terminate_and_warn("getent initgroups", pid, true); + (void) wait_for_terminate_and_check("getent initgroups", pid, WAIT_LOG); /* Skip over the username and subsequent separator whitespace */ x = line; diff --git a/src/nspawn/nspawn-stub-pid1.c b/src/nspawn/nspawn-stub-pid1.c index 7f2f8f1f13..58f4636866 100644 --- a/src/nspawn/nspawn-stub-pid1.c +++ b/src/nspawn/nspawn-stub-pid1.c @@ -25,6 +25,7 @@ #include "fd-util.h" #include "log.h" +#include "missing.h" #include "nspawn-stub-pid1.h" #include "process-util.h" #include "signal-util.h" @@ -92,7 +93,7 @@ int stub_pid1(sd_id128_t uuid) { sd_id128_to_string(uuid, new_environment + sizeof(new_environment) - SD_ID128_STRING_MAX); reset_environ(new_environment, sizeof(new_environment)); - rename_process("STUBINIT"); + (void) rename_process("(sd-stubinit)"); assert_se(sigemptyset(&waitmask) >= 0); assert_se(sigset_add_many(&waitmask, diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 71b14e2302..0f05ecff03 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -1313,13 +1313,14 @@ static int userns_lchown(const char *p, uid_t uid, gid_t gid) { static int userns_mkdir(const char *root, const char *path, mode_t mode, uid_t uid, gid_t gid) { const char *q; + int r; q = prefix_roota(root, path); - if (mkdir(q, mode) < 0) { - if (errno == EEXIST) - return 0; - return -errno; - } + r = mkdir_errno_wrapper(q, mode); + if (r == -EEXIST) + return 0; + if (r < 0) + return r; return userns_lchown(q, uid, gid); } @@ -1599,8 +1600,10 @@ static int setup_pts(const char *dest) { /* Mount /dev/pts itself */ p = prefix_roota(dest, "/dev/pts"); - if (mkdir(p, 0755) < 0) - return log_error_errno(errno, "Failed to create /dev/pts: %m"); + r = mkdir_errno_wrapper(p, 0755); + if (r < 0) + return log_error_errno(r, "Failed to create /dev/pts: %m"); + r = mount_verbose(LOG_ERR, "devpts", p, "devpts", MS_NOSUID|MS_NOEXEC, options); if (r < 0) return r; @@ -1846,12 +1849,13 @@ static int setup_journal(const char *directory) { /* 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) { + r = mkdir_errno_wrapper(p, 0755); + if (r < 0 && r != -EEXIST) { if (try) { - log_debug_errno(errno, "Failed to create %s, skipping journal setup: %m", p); + log_debug_errno(r, "Failed to create %s, skipping journal setup: %m", p); return 0; } else - return log_error_errno(errno, "Failed to create %s: %m", p); + return log_error_errno(r, "Failed to create %s: %m", p); } } else if (access(p, F_OK) < 0) @@ -2159,8 +2163,11 @@ static int determine_names(void) { if (!arg_ephemeral) arg_read_only = arg_read_only || i->read_only; - } else - arg_directory = get_current_dir_name(); + } else { + r = safe_getcwd(&arg_directory); + if (r < 0) + return log_error_errno(r, "Failed to determine current directory: %m"); + } if (!arg_directory && !arg_image) { log_error("Failed to determine path, please use -D or -i."); @@ -3512,9 +3519,11 @@ static int run(int master, } /* Wait for the outer child. */ - r = wait_for_terminate_and_warn("namespace helper", *pid, NULL); - if (r != 0) - return r < 0 ? r : -EIO; + r = wait_for_terminate_and_check("(sd-namespace)", *pid, WAIT_LOG_ABNORMAL); + if (r < 0) + return r; + if (r != EXIT_SUCCESS) + return -EIO; /* And now retrieve the PID of the inner child. */ l = recv(pid_socket_pair[0], pid, sizeof *pid, 0); @@ -3616,14 +3625,16 @@ static int run(int master, * case PID 1 will send us a friendly RequestStop signal, when it is asked to terminate the * scope. Let's hook into that, and cleanly shut down the container, and print a friendly message. */ - r = sd_bus_add_match(bus, NULL, - "type='signal'," - "sender='org.freedesktop.systemd1'," - "interface='org.freedesktop.systemd1.Scope'," - "member='RequestStop'", - on_request_stop, PID_TO_PTR(*pid)); + r = sd_bus_match_signal_async( + bus, + NULL, + "org.freedesktop.systemd1", + NULL, + "org.freedesktop.systemd1.Scope", + "RequestStop", + on_request_stop, NULL, PID_TO_PTR(*pid)); if (r < 0) - return log_error_errno(r, "Failed to install request stop match: %m"); + return log_error_errno(r, "Failed to request RequestStop match: %m"); } if (arg_register) { diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c index cab3c22bb2..fe6ce4cbbf 100644 --- a/src/nss-resolve/nss-resolve.c +++ b/src/nss-resolve/nss-resolve.c @@ -30,6 +30,7 @@ #include "in-addr-util.h" #include "macro.h" #include "nss-util.h" +#include "resolved-def.h" #include "string-util.h" #include "util.h" #include "signal-util.h" @@ -37,8 +38,6 @@ NSS_GETHOSTBYNAME_PROTOTYPES(resolve); NSS_GETHOSTBYADDR_PROTOTYPES(resolve); -#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC) - static bool bus_error_shall_fallback(sd_bus_error *e) { return sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN) || sd_bus_error_has_name(e, SD_BUS_ERROR_NAME_HAS_NO_OWNER) || @@ -157,7 +156,7 @@ enum nss_status _nss_resolve_gethostbyname4_r( if (r < 0) goto fail; - r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); + r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); if (r < 0) { if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) { *errnop = ESRCH; @@ -335,7 +334,7 @@ enum nss_status _nss_resolve_gethostbyname3_r( if (r < 0) goto fail; - r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); + r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); if (r < 0) { if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) { *errnop = ESRCH; @@ -533,7 +532,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( if (r < 0) goto fail; - r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); + r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); if (r < 0) { if (sd_bus_error_has_name(&error, _BUS_ERROR_DNS "NXDOMAIN")) { *errnop = ESRCH; diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c index cc641e1615..f75405d2e5 100644 --- a/src/nss-systemd/nss-systemd.c +++ b/src/nss-systemd/nss-systemd.c @@ -136,7 +136,8 @@ enum nss_status _nss_systemd_getpwnam_r( *errnop = 0; return NSS_STATUS_SUCCESS; } - if (streq(name, nobody_passwd.pw_name)) { + if (synthesize_nobody() && + streq(name, nobody_passwd.pw_name)) { *pwd = nobody_passwd; *errnop = 0; return NSS_STATUS_SUCCESS; @@ -244,7 +245,8 @@ enum nss_status _nss_systemd_getpwuid_r( *errnop = 0; return NSS_STATUS_SUCCESS; } - if (uid == nobody_passwd.pw_uid) { + if (synthesize_nobody() && + uid == nobody_passwd.pw_uid) { *pwd = nobody_passwd; *errnop = 0; return NSS_STATUS_SUCCESS; @@ -351,7 +353,8 @@ enum nss_status _nss_systemd_getgrnam_r( *errnop = 0; return NSS_STATUS_SUCCESS; } - if (streq(name, nobody_group.gr_name)) { + if (synthesize_nobody() && + streq(name, nobody_group.gr_name)) { *gr = nobody_group; *errnop = 0; return NSS_STATUS_SUCCESS; @@ -456,7 +459,8 @@ enum nss_status _nss_systemd_getgrgid_r( *errnop = 0; return NSS_STATUS_SUCCESS; } - if (gid == nobody_group.gr_gid) { + if (synthesize_nobody() && + gid == nobody_group.gr_gid) { *gr = nobody_group; *errnop = 0; return NSS_STATUS_SUCCESS; diff --git a/src/partition/growfs.c b/src/partition/growfs.c index 901b33e39e..41b4e872be 100644 --- a/src/partition/growfs.c +++ b/src/partition/growfs.c @@ -28,6 +28,7 @@ #include <sys/types.h> #include <sys/vfs.h> +#include "blockdev-util.h" #include "crypt-util.h" #include "device-nodes.h" #include "dissect-image.h" diff --git a/src/partition/makefs.c b/src/partition/makefs.c index e5e125255b..a957967dfe 100644 --- a/src/partition/makefs.c +++ b/src/partition/makefs.c @@ -28,12 +28,14 @@ #include "alloc-util.h" #include "dissect-image.h" +#include "process-util.h" #include "signal-util.h" #include "string-util.h" static int makefs(const char *type, const char *device) { const char *mkfs; pid_t pid; + int r; if (streq(type, "swap")) mkfs = "/sbin/mkswap"; @@ -42,24 +44,19 @@ static int makefs(const char *type, const char *device) { if (access(mkfs, X_OK) != 0) return log_error_errno(errno, "%s is not executable: %m", mkfs); - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "fork(): %m"); - - if (pid == 0) { + r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); + if (r < 0) + return r; + if (r == 0) { const char *cmdline[3] = { mkfs, device, NULL }; /* Child */ - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - execv(cmdline[0], (char**) cmdline); _exit(EXIT_FAILURE); } - return wait_for_terminate_and_warn(mkfs, pid, true); + return wait_for_terminate_and_check(mkfs, pid, WAIT_LOG); } int main(int argc, char *argv[]) { diff --git a/src/quotacheck/quotacheck.c b/src/quotacheck/quotacheck.c index ec5be21a34..1bf718e4f6 100644 --- a/src/quotacheck/quotacheck.c +++ b/src/quotacheck/quotacheck.c @@ -71,14 +71,6 @@ static void test_files(void) { } int main(int argc, char *argv[]) { - - static const char * const cmdline[] = { - QUOTACHECK, - "-anug", - NULL - }; - - pid_t pid; int r; if (argc > 1) { @@ -106,25 +98,22 @@ int main(int argc, char *argv[]) { return EXIT_SUCCESS; } - pid = fork(); - if (pid < 0) { - r = log_error_errno(errno, "fork(): %m"); + r = safe_fork("(quotacheck)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + if (r < 0) goto finish; - } - if (pid == 0) { + if (r == 0) { + static const char * const cmdline[] = { + QUOTACHECK, + "-anug", + NULL + }; /* Child */ - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - execv(cmdline[0], (char**) cmdline); - _exit(1); /* Operational error */ + _exit(EXIT_FAILURE); /* Operational error */ } - r = wait_for_terminate_and_warn("quotacheck", pid, true); - finish: return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/rc-local-generator/rc-local-generator.c b/src/rc-local-generator/rc-local-generator.c index 196947ca51..2762fa7e89 100644 --- a/src/rc-local-generator/rc-local-generator.c +++ b/src/rc-local-generator/rc-local-generator.c @@ -23,7 +23,6 @@ #include <stdio.h> #include <unistd.h> -#include "alloc-util.h" #include "log.h" #include "mkdir.h" #include "string-util.h" @@ -32,21 +31,16 @@ static const char *arg_dest = "/tmp"; static int add_symlink(const char *service, const char *where) { - _cleanup_free_ char *from = NULL, *to = NULL; + const char *from, *to; int r; assert(service); assert(where); - from = strjoin(SYSTEM_DATA_UNIT_PATH, "/", service); - if (!from) - return log_oom(); + from = strjoina(SYSTEM_DATA_UNIT_PATH "/", service); + to = strjoina(arg_dest, "/", where, ".wants/", service); - to = strjoin(arg_dest, "/", where, ".wants/", service); - if (!to) - return log_oom(); - - mkdir_parents_label(to, 0755); + (void) mkdir_parents_label(to, 0755); r = symlink(from, to); if (r < 0) { @@ -60,7 +54,7 @@ static int add_symlink(const char *service, const char *where) { } int main(int argc, char *argv[]) { - int r = EXIT_SUCCESS; + int ret = EXIT_SUCCESS; if (argc > 1 && argc != 4) { log_error("This program takes three or no arguments."); @@ -70,7 +64,8 @@ int main(int argc, char *argv[]) { if (argc > 1) arg_dest = argv[1]; - log_set_target(LOG_TARGET_SAFE); + log_set_prohibit_ipc(true); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); @@ -80,15 +75,15 @@ int main(int argc, char *argv[]) { log_debug("Automatically adding rc-local.service."); if (add_symlink("rc-local.service", "multi-user.target") < 0) - r = EXIT_FAILURE; + ret = EXIT_FAILURE; } if (access(RC_LOCAL_SCRIPT_PATH_STOP, X_OK) >= 0) { log_debug("Automatically adding halt-local.service."); if (add_symlink("halt-local.service", "final.target") < 0) - r = EXIT_FAILURE; + ret = EXIT_FAILURE; } - return r; + return ret; } diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c index 2d7cf723f4..c61777c3fe 100644 --- a/src/remount-fs/remount-fs.c +++ b/src/remount-fs/remount-fs.c @@ -87,19 +87,12 @@ int main(int argc, char *argv[]) { log_debug("Remounting %s", me->mnt_dir); - pid = fork(); - if (pid < 0) { - r = log_error_errno(errno, "Failed to fork: %m"); + r = safe_fork("(remount)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); + if (r < 0) goto finish; - } - - if (pid == 0) { + if (r == 0) { /* Child */ - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - (void) prctl(PR_SET_PDEATHSIG, SIGTERM); - execv(MOUNT_PATH, STRV_MAKE(MOUNT_PATH, me->mnt_dir, "-o", "remount")); log_error_errno(errno, "Failed to execute " MOUNT_PATH ": %m"); diff --git a/src/resolve/dns-type.c b/src/resolve/dns-type.c index 347252a90f..0c366b5e8e 100644 --- a/src/resolve/dns-type.c +++ b/src/resolve/dns-type.c @@ -19,6 +19,7 @@ ***/ #include <sys/socket.h> +#include <errno.h> #include "dns-type.h" #include "parse-util.h" diff --git a/src/resolve/meson.build b/src/resolve/meson.build index ee1acb5166..15752d24ff 100644 --- a/src/resolve/meson.build +++ b/src/resolve/meson.build @@ -31,7 +31,7 @@ basic_dns_sources = files(''' dns_type_h = files('dns-type.h')[0] -systemd_resolved_only_sources = files(''' +systemd_resolved_sources = files(''' resolved.c resolved-manager.c resolved-manager.h @@ -80,7 +80,7 @@ systemd_resolved_only_sources = files(''' resolved-etc-hosts.c '''.split()) -systemd_resolve_only_sources = files('resolve-tool.c') +systemd_resolve_sources = files('resolve-tool.c') ############################################################ @@ -141,14 +141,13 @@ resolved_dnssd_gperf_c = custom_target( output : 'resolved-dnssd-gperf.c', command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@']) -systemd_resolved_sources = (basic_dns_sources + - [resolved_gperf_c, resolved_dnssd_gperf_c] + - systemd_resolved_only_sources + - dns_type_headers) +libsystemd_resolve_core = static_library( + 'systemd-resolve-core', + basic_dns_sources, + dns_type_headers, + include_directories : includes) -systemd_resolve_sources = (basic_dns_sources + - systemd_resolve_only_sources + - dns_type_headers) +systemd_resolved_sources += [resolved_gperf_c, resolved_dnssd_gperf_c] if conf.get('ENABLE_RESOLVE') == 1 install_data('org.freedesktop.resolve1.conf', @@ -178,37 +177,37 @@ endif tests += [ [['src/resolve/test-resolve-tables.c', - basic_dns_sources, dns_type_headers, 'src/shared/test-tables.h'], - [], + [libsystemd_resolve_core, + libshared], [libgcrypt, libgpg_error, libm], 'ENABLE_RESOLVE'], [['src/resolve/test-dns-packet.c', - basic_dns_sources, dns_type_headers], - [], + [libsystemd_resolve_core, + libshared], [libgcrypt, libgpg_error, libm], 'ENABLE_RESOLVE'], [['src/resolve/test-resolved-packet.c', - basic_dns_sources, dns_type_headers], - [], + [libsystemd_resolve_core, + libshared], [libgcrypt, libgpg_error, libm], 'ENABLE_RESOLVE'], [['src/resolve/test-dnssec.c', - basic_dns_sources, dns_type_headers], - [], + [libsystemd_resolve_core, + libshared], [libgcrypt, libgpg_error, libm], diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c index 0252bdfcd7..2a6bf94070 100644 --- a/src/resolve/resolve-tool.c +++ b/src/resolve/resolve-tool.c @@ -41,8 +41,6 @@ #include "strv.h" #include "terminal-util.h" -#define DNS_CALL_TIMEOUT_USEC (90*USEC_PER_SEC) - static int arg_family = AF_UNSPEC; static int arg_ifindex = 0; static uint16_t arg_type = 0; @@ -175,7 +173,7 @@ static int resolve_host(sd_bus *bus, const char *name) { ts = now(CLOCK_MONOTONIC); - r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); + r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); if (r < 0) return log_error_errno(r, "%s: resolve call failed: %s", name, bus_error_message(&error, r)); @@ -306,7 +304,7 @@ static int resolve_address(sd_bus *bus, int family, const union in_addr_union *a ts = now(CLOCK_MONOTONIC); - r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); + r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); if (r < 0) { log_error("%s: resolve call failed: %s", pretty, bus_error_message(&error, r)); return r; @@ -442,7 +440,7 @@ static int resolve_record(sd_bus *bus, const char *name, uint16_t class, uint16_ ts = now(CLOCK_MONOTONIC); - r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); + r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); if (r < 0) { if (warn_missing || r != -ENXIO) log_error("%s: resolve call failed: %s", name, bus_error_message(&error, r)); @@ -685,7 +683,7 @@ static int resolve_service(sd_bus *bus, const char *name, const char *type, cons ts = now(CLOCK_MONOTONIC); - r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); + r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); if (r < 0) return log_error_errno(r, "Resolve call failed: %s", bus_error_message(&error, r)); diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 9157d9ea68..ffd7c4824e 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -1844,18 +1844,6 @@ static const sd_bus_vtable resolve_vtable[] = { SD_BUS_VTABLE_END, }; -static int on_bus_retry(sd_event_source *s, usec_t usec, void *userdata) { - Manager *m = userdata; - - assert(s); - assert(m); - - m->bus_retry_event_source = sd_event_source_unref(m->bus_retry_event_source); - - manager_connect_bus(m); - return 0; -} - static int match_prepare_for_sleep(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) { Manager *m = userdata; int b, r; @@ -1886,20 +1874,9 @@ int manager_connect_bus(Manager *m) { if (m->bus) return 0; - r = sd_bus_default_system(&m->bus); - if (r < 0) { - /* We failed to connect? Yuck, we must be in early - * boot. Let's try in 5s again. */ - - log_debug_errno(r, "Failed to connect to bus, trying again in 5s: %m"); - - r = sd_event_add_time(m->event, &m->bus_retry_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 5*USEC_PER_SEC, 0, on_bus_retry, m); - if (r < 0) - return log_error_errno(r, "Failed to install bus reconnect time event: %m"); - - (void) sd_event_source_set_description(m->bus_retry_event_source, "bus-retry"); - return 0; - } + r = bus_open_system_watch_bind(&m->bus); + if (r < 0) + return log_error_errno(r, "Failed to connect to system bus: %m"); r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", resolve_vtable, m); if (r < 0) @@ -1921,24 +1898,26 @@ int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to register dnssd enumerator: %m"); - r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0); + r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.resolve1", 0, NULL, NULL); if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); + return log_error_errno(r, "Failed to request name: %m"); r = sd_bus_attach_event(m->bus, m->event, 0); if (r < 0) return log_error_errno(r, "Failed to attach bus to event loop: %m"); - r = sd_bus_add_match(m->bus, &m->prepare_for_sleep_slot, - "type='signal'," - "sender='org.freedesktop.login1'," - "interface='org.freedesktop.login1.Manager'," - "member='PrepareForSleep'," - "path='/org/freedesktop/login1'", - match_prepare_for_sleep, - m); - if (r < 0) - log_error_errno(r, "Failed to add match for PrepareForSleep: %m"); + r = sd_bus_match_signal_async( + m->bus, + &m->prepare_for_sleep_slot, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "PrepareForSleep", + match_prepare_for_sleep, + NULL, + m); + if (r < 0) + log_warning_errno(r, "Failed to request match for PrepareForSleep, ignoring: %m"); return 0; } diff --git a/src/resolve/resolved-def.h b/src/resolve/resolved-def.h index 64c2b1503e..96f93107ad 100644 --- a/src/resolve/resolved-def.h +++ b/src/resolve/resolved-def.h @@ -22,6 +22,8 @@ #include <inttypes.h> +#include "time-util.h" + #define SD_RESOLVED_DNS (UINT64_C(1) << 0) #define SD_RESOLVED_LLMNR_IPV4 (UINT64_C(1) << 1) #define SD_RESOLVED_LLMNR_IPV6 (UINT64_C(1) << 2) @@ -37,3 +39,5 @@ #define SD_RESOLVED_MDNS (SD_RESOLVED_MDNS_IPV4|SD_RESOLVED_MDNS_IPV6) #define SD_RESOLVED_PROTOCOLS_ALL (SD_RESOLVED_MDNS|SD_RESOLVED_LLMNR|SD_RESOLVED_DNS) + +#define SD_RESOLVED_QUERY_TIMEOUT_USEC (120 * USEC_PER_SEC) diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 942956dd71..e9197f1dfd 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -476,7 +476,7 @@ static int dns_cache_put_positive( if (r < 0) return r; - if (log_get_max_level() >= LOG_DEBUG) { + if (DEBUG_LOGGING) { _cleanup_free_ char *t = NULL; (void) in_addr_to_string(i->owner_family, &i->owner_address, &t); diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index d7a839a823..2067dd5182 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -18,6 +18,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#if HAVE_GCRYPT +#include <gcrypt.h> +#endif + #include "alloc-util.h" #include "dns-domain.h" #include "resolved-dns-packet.h" @@ -752,13 +756,20 @@ int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, in static const uint8_t rfc6975[] = { 0, 5, /* OPTION_CODE: DAU */ +#if HAVE_GCRYPT && GCRYPT_VERSION_NUMBER >= 0x010600 + 0, 7, /* LIST_LENGTH */ +#else 0, 6, /* LIST_LENGTH */ +#endif DNSSEC_ALGORITHM_RSASHA1, DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1, DNSSEC_ALGORITHM_RSASHA256, DNSSEC_ALGORITHM_RSASHA512, DNSSEC_ALGORITHM_ECDSAP256SHA256, DNSSEC_ALGORITHM_ECDSAP384SHA384, +#if HAVE_GCRYPT && GCRYPT_VERSION_NUMBER >= 0x010600 + DNSSEC_ALGORITHM_ED25519, +#endif 0, 6, /* OPTION_CODE: DHU */ 0, 3, /* LIST_LENGTH */ @@ -1837,6 +1848,9 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl if (r < 0) return r; + if (rdlength < 4) + return -EBADMSG; + r = dns_packet_read_memdup(p, rdlength - 4, &rr->ds.digest, &rr->ds.digest_size, NULL); @@ -1859,6 +1873,9 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl if (r < 0) return r; + if (rdlength < 2) + return -EBADMSG; + r = dns_packet_read_memdup(p, rdlength - 2, &rr->sshfp.fingerprint, &rr->sshfp.fingerprint_size, NULL); @@ -1883,6 +1900,9 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl if (r < 0) return r; + if (rdlength < 4) + return -EBADMSG; + r = dns_packet_read_memdup(p, rdlength - 4, &rr->dnskey.key, &rr->dnskey.key_size, NULL); @@ -1927,6 +1947,9 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl if (r < 0) return r; + if (rdlength + offset < p->rindex) + return -EBADMSG; + r = dns_packet_read_memdup(p, offset + rdlength - p->rindex, &rr->rrsig.signature, &rr->rrsig.signature_size, NULL); @@ -2016,6 +2039,9 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl if (r < 0) return r; + if (rdlength < 3) + return -EBADMSG; + r = dns_packet_read_memdup(p, rdlength - 3, &rr->tlsa.data, &rr->tlsa.data_size, NULL); @@ -2036,6 +2062,9 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl if (r < 0) return r; + if (rdlength + offset < p->rindex) + return -EBADMSG; + r = dns_packet_read_memdup(p, rdlength + offset - p->rindex, &rr->caa.value, &rr->caa.value_size, NULL); @@ -2111,19 +2140,11 @@ static bool opt_is_good(DnsResourceRecord *rr, bool *rfc6975) { return true; } -int dns_packet_extract(DnsPacket *p) { +static int dns_packet_extract_question(DnsPacket *p, DnsQuestion **ret_question) { _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; - _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; - _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = {}; unsigned n, i; int r; - if (p->extracted) - return 0; - - 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); @@ -2150,107 +2171,146 @@ int dns_packet_extract(DnsPacket *p) { } } + *ret_question = question; + question = NULL; + return 0; +} + +static int dns_packet_extract_answer(DnsPacket *p, DnsAnswer **ret_answer) { + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; + unsigned n, i; + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *previous = NULL; + bool bad_opt = false; + int r; + n = DNS_PACKET_RRCOUNT(p); - if (n > 0) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *previous = NULL; - bool bad_opt = false; + if (n == 0) + return 0; - answer = dns_answer_new(n); - if (!answer) - return -ENOMEM; + answer = dns_answer_new(n); + if (!answer) + return -ENOMEM; - for (i = 0; i < n; i++) { - _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; - bool cache_flush = false; + for (i = 0; i < n; i++) { + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; + bool cache_flush = false; - r = dns_packet_read_rr(p, &rr, &cache_flush, NULL); - if (r < 0) - return r; + r = dns_packet_read_rr(p, &rr, &cache_flush, NULL); + if (r < 0) + return r; - /* Try to reduce memory usage a bit */ - if (previous) - dns_resource_key_reduce(&rr->key, &previous->key); + /* Try to reduce memory usage a bit */ + if (previous) + dns_resource_key_reduce(&rr->key, &previous->key); - if (rr->key->type == DNS_TYPE_OPT) { - bool has_rfc6975; + if (rr->key->type == DNS_TYPE_OPT) { + bool has_rfc6975; - if (p->opt || bad_opt) { - /* Multiple OPT RRs? if so, let's ignore all, because there's something wrong - * with the server, and if one is valid we wouldn't know which one. */ - log_debug("Multiple OPT RRs detected, ignoring all."); - bad_opt = true; - continue; - } + if (p->opt || bad_opt) { + /* Multiple OPT RRs? if so, let's ignore all, because there's + * something wrong with the server, and if one is valid we wouldn't + * know which one. */ + log_debug("Multiple OPT RRs detected, ignoring all."); + bad_opt = true; + continue; + } - 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."); - bad_opt = true; - continue; - } + 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."); + bad_opt = true; + continue; + } - if (i < DNS_PACKET_ANCOUNT(p) + DNS_PACKET_NSCOUNT(p)) { - /* OPT RR is in the wrong section? Some Belkin routers do this. This is a hint - * the EDNS implementation is borked, like the Belkin one is, hence ignore - * it. */ - log_debug("OPT RR in wrong section, ignoring."); - bad_opt = true; - continue; + if (i < DNS_PACKET_ANCOUNT(p) + DNS_PACKET_NSCOUNT(p)) { + /* OPT RR is in the wrong section? Some Belkin routers do this. This + * is a hint the EDNS implementation is borked, like the Belkin one + * is, hence ignore it. */ + log_debug("OPT RR in wrong section, ignoring."); + bad_opt = true; + continue; + } + + if (!opt_is_good(rr, &has_rfc6975)) { + log_debug("Malformed OPT RR, ignoring."); + bad_opt = true; + continue; + } + + if (DNS_PACKET_QR(p)) { + /* Additional checks for responses */ + + if (!DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(rr)) { + /* If this is a reply and we don't know the EDNS version + * then something is weird... */ + log_debug("EDNS version newer that our request, bad server."); + return -EBADMSG; } - if (!opt_is_good(rr, &has_rfc6975)) { - log_debug("Malformed OPT RR, ignoring."); + if (has_rfc6975) { + /* If the OPT RR contains RFC6975 algorithm data, then this + * is indication that the server just copied the OPT it got + * from us (which contained that data) back into the reply. + * If so, then it doesn't properly support EDNS, as RFC6975 + * makes it very clear that the algorithm data should only + * be contained in questions, never in replies. Crappy + * Belkin routers copy the OPT data for example, hence let's + * detect this so that we downgrade early. */ + log_debug("OPT RR contained RFC6975 data, ignoring."); bad_opt = true; continue; } + } - if (DNS_PACKET_QR(p)) { - /* Additional checks for responses */ - - if (!DNS_RESOURCE_RECORD_OPT_VERSION_SUPPORTED(rr)) { - /* If this is a reply and we don't know the EDNS version then something - * is weird... */ - log_debug("EDNS version newer that our request, bad server."); - return -EBADMSG; - } - - if (has_rfc6975) { - /* If the OPT RR contains RFC6975 algorithm data, then this is indication that - * the server just copied the OPT it got from us (which contained that data) - * back into the reply. If so, then it doesn't properly support EDNS, as - * RFC6975 makes it very clear that the algorithm data should only be contained - * in questions, never in replies. Crappy Belkin routers copy the OPT data for - * example, hence let's detect this so that we downgrade early. */ - log_debug("OPT RR contained RFC6975 data, ignoring."); - bad_opt = true; - continue; - } - } + p->opt = dns_resource_record_ref(rr); + } else { + /* According to RFC 4795, section 2.9. only the RRs from the Answer section + * shall be cached. Hence mark only those RRs as cacheable by default, but + * not the ones from the Additional or Authority sections. */ + DnsAnswerFlags flags = + (i < DNS_PACKET_ANCOUNT(p) ? DNS_ANSWER_CACHEABLE : 0) | + (p->protocol == DNS_PROTOCOL_MDNS && !cache_flush ? DNS_ANSWER_SHARED_OWNER : 0); + + r = dns_answer_add(answer, rr, p->ifindex, flags); + if (r < 0) + return r; + } - p->opt = dns_resource_record_ref(rr); - } else { + /* Remember this RR, so that we potentically can merge it's ->key object with the + * next RR. Note that we only do this if we actually decided to keep the RR around. + */ + dns_resource_record_unref(previous); + previous = dns_resource_record_ref(rr); + } - /* According to RFC 4795, section 2.9. only the RRs from the Answer section shall be - * cached. Hence mark only those RRs as cacheable by default, but not the ones from the - * Additional or Authority sections. */ + if (bad_opt) + p->opt = dns_resource_record_unref(p->opt); - r = dns_answer_add(answer, rr, p->ifindex, - (i < DNS_PACKET_ANCOUNT(p) ? DNS_ANSWER_CACHEABLE : 0) | - (p->protocol == DNS_PROTOCOL_MDNS && !cache_flush ? DNS_ANSWER_SHARED_OWNER : 0)); - if (r < 0) - return r; - } + *ret_answer = answer; + answer = NULL; + return 0; +} - /* Remember this RR, so that we potentically can merge it's ->key object with the next RR. Note - * that we only do this if we actually decided to keep the RR around. */ - dns_resource_record_unref(previous); - previous = dns_resource_record_ref(rr); - } +int dns_packet_extract(DnsPacket *p) { + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; + _cleanup_(rewind_dns_packet) DnsPacketRewinder rewinder = {}; + int r; - if (bad_opt) - p->opt = dns_resource_record_unref(p->opt); - } + if (p->extracted) + return 0; + + INIT_REWINDER(rewinder, p); + dns_packet_rewind(p, DNS_PACKET_HEADER_SIZE); + + r = dns_packet_extract_question(p, &question); + if (r < 0) + return r; + + r = dns_packet_extract_answer(p, &answer); + if (r < 0) + return r; p->question = question; question = NULL; diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index 227d0b5d11..5f51340743 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -28,9 +28,6 @@ #include "resolved-etc-hosts.h" #include "string-util.h" -/* How long to wait for the query in total */ -#define QUERY_TIMEOUT_USEC (60 * USEC_PER_SEC) - #define CNAME_MAX 8 #define QUERIES_MAX 2048 #define AUXILIARY_QUERIES_MAX 64 @@ -769,8 +766,8 @@ int dns_query_go(DnsQuery *q) { q->manager->event, &q->timeout_event_source, clock_boottime_or_monotonic(), - now(clock_boottime_or_monotonic()) + QUERY_TIMEOUT_USEC, 0, - on_query_timeout, q); + now(clock_boottime_or_monotonic()) + SD_RESOLVED_QUERY_TIMEOUT_USEC, + 0, on_query_timeout, q); if (r < 0) goto fail; diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index eb5592d3cf..4056bda558 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -517,9 +517,13 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr) { case DNS_TYPE_OPENPGPKEY: default: - free(rr->generic.data); + if (!rr->unparseable) + free(rr->generic.data); } + if (rr->unparseable) + free(rr->generic.data); + free(rr->wire_format); dns_resource_key_unref(rr->key); } diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index ea4459a89a..bf6aac8300 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -885,13 +885,17 @@ static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata scope->conflict_event_source = sd_event_source_unref(scope->conflict_event_source); for (;;) { + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - rr = ordered_hashmap_steal_first(scope->conflict_queue); - if (!rr) + key = ordered_hashmap_first_key(scope->conflict_queue); + if (!key) break; + rr = ordered_hashmap_remove(scope->conflict_queue, key); + assert(rr); + r = dns_scope_make_conflict_packet(scope, rr, &p); if (r < 0) { log_error_errno(r, "Failed to make conflict packet: %m"); @@ -930,6 +934,7 @@ int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr) { if (r < 0) return log_debug_errno(r, "Failed to queue conflicting RR: %m"); + dns_resource_key_ref(rr->key); dns_resource_record_ref(rr); if (scope->conflict_event_source) @@ -953,7 +958,7 @@ int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr) { } void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) { - unsigned i; + DnsResourceRecord *rr; int r; assert(scope); @@ -984,21 +989,24 @@ void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) { log_debug("Checking for conflicts..."); - for (i = 0; i < p->answer->n_rrs; i++) { + DNS_ANSWER_FOREACH(rr, p->answer) { + /* No conflict if it is DNS-SD RR used for service enumeration. */ + if (dns_resource_key_is_dnssd_ptr(rr->key)) + continue; /* Check for conflicts against the local zone. If we * found one, we won't check any further */ - r = dns_zone_check_conflicts(&scope->zone, p->answer->items[i].rr); + r = dns_zone_check_conflicts(&scope->zone, rr); if (r != 0) continue; /* Check for conflicts against the local cache. If so, * send out an advisory query, to inform everybody */ - r = dns_cache_check_conflicts(&scope->cache, p->answer->items[i].rr, p->family, &p->sender); + r = dns_cache_check_conflicts(&scope->cache, rr, p->family, &p->sender); if (r <= 0) continue; - dns_scope_notify_conflict(scope, p->answer->items[i].rr); + dns_scope_notify_conflict(scope, rr); } } diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index d470a64524..68c5d5c1e3 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -30,7 +30,7 @@ /* After how much time to repeat classic DNS requests */ #define DNS_TIMEOUT_MIN_USEC (750 * USEC_PER_MSEC) -#define DNS_TIMEOUT_MAX_USEC (5 * USEC_PER_SEC) +#define DNS_TIMEOUT_MAX_USEC (SD_RESOLVED_QUERY_TIMEOUT_USEC / DNS_TRANSACTION_ATTEMPTS_MAX) /* The amount of time to wait before retrying with a full feature set */ #define DNS_SERVER_FEATURE_GRACE_PERIOD_MAX_USEC (6 * USEC_PER_HOUR) diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index 2dbf432df9..2ee027791a 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -488,7 +488,8 @@ static int manager_watch_hostname(Manager *m) { assert(m); - m->hostname_fd = open("/proc/sys/kernel/hostname", O_RDONLY|O_CLOEXEC|O_NDELAY|O_NOCTTY); + m->hostname_fd = open("/proc/sys/kernel/hostname", + O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); if (m->hostname_fd < 0) { log_warning_errno(errno, "Failed to watch hostname: %m"); return 0; @@ -1410,7 +1411,7 @@ void manager_dnssec_verdict(Manager *m, DnssecVerdict verdict, const DnsResource assert(verdict >= 0); assert(verdict < _DNSSEC_VERDICT_MAX); - if (log_get_max_level() >= LOG_DEBUG) { + if (DEBUG_LOGGING) { char s[DNS_RESOURCE_KEY_STRING_MAX]; log_debug("Found verdict for lookup %s: %s", diff --git a/src/resolve/test-dns-packet.c b/src/resolve/test-dns-packet.c index 458c908fad..c1f118fecc 100644 --- a/src/resolve/test-dns-packet.c +++ b/src/resolve/test-dns-packet.c @@ -21,6 +21,8 @@ #include <net/if.h> #include <glob.h> +#include "sd-id128.h" + #include "alloc-util.h" #include "fileio.h" #include "glob-util.h" diff --git a/src/resolve/test-dnssec-complex.c b/src/resolve/test-dnssec-complex.c index e7b077939f..514f72eeeb 100644 --- a/src/resolve/test-dnssec-complex.c +++ b/src/resolve/test-dnssec-complex.c @@ -27,11 +27,10 @@ #include "bus-common-errors.h" #include "dns-type.h" #include "random-util.h" +#include "resolved-def.h" #include "string-util.h" #include "time-util.h" -#define DNS_CALL_TIMEOUT_USEC (45*USEC_PER_SEC) - static void prefix_random(const char *name, char **ret) { uint64_t i, u; char *m = NULL; @@ -75,7 +74,7 @@ static void test_rr_lookup(sd_bus *bus, const char *name, uint16_t type, const c assert_se(sd_bus_message_append(req, "isqqt", 0, name, DNS_CLASS_IN, type, UINT64_C(0)) >= 0); - r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); + r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); if (r < 0) { assert_se(result); @@ -112,7 +111,7 @@ static void test_hostname_lookup(sd_bus *bus, const char *name, int family, cons assert_se(sd_bus_message_append(req, "isit", 0, name, family, UINT64_C(0)) >= 0); - r = sd_bus_call(bus, req, DNS_CALL_TIMEOUT_USEC, &error, &reply); + r = sd_bus_call(bus, req, SD_RESOLVED_QUERY_TIMEOUT_USEC, &error, &reply); if (r < 0) { assert_se(result); diff --git a/src/resolve/test-dnssec.c b/src/resolve/test-dnssec.c index 2d2b5e31bf..ebabfba96d 100644 --- a/src/resolve/test-dnssec.c +++ b/src/resolve/test-dnssec.c @@ -19,7 +19,9 @@ ***/ #include <arpa/inet.h> +#if HAVE_GCRYPT #include <gcrypt.h> +#endif #include <netinet/in.h> #include <sys/socket.h> diff --git a/src/run/run.c b/src/run/run.c index 5d7441ac93..a30501169c 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -40,6 +40,7 @@ #include "spawn-polkit-agent.h" #include "strv.h" #include "terminal-util.h" +#include "unit-def.h" #include "unit-name.h" #include "user-util.h" @@ -68,6 +69,8 @@ static enum { ARG_STDIO_DIRECT, /* Directly pass our stdin/stdout/stderr to the activated service, useful for usage in shell pipelines, requested by --pipe */ ARG_STDIO_AUTO, /* If --pipe and --pty are used together we use --pty when invoked on a TTY, and --pipe otherwise */ } arg_stdio = ARG_STDIO_NONE; +static char **arg_path_property = NULL; +static char **arg_socket_property = NULL; static char **arg_timer_property = NULL; static bool with_timer = false; static bool arg_quiet = false; @@ -101,6 +104,10 @@ static void help(void) { " -P --pipe Pass STDIN/STDOUT/STDERR directly to service\n" " -q --quiet Suppress information messages during runtime\n" " -G --collect Unload unit after it ran, even when failed\n\n" + "Path options:\n" + " --path-property=NAME=VALUE Set path unit property\n\n" + "Socket options:\n" + " --socket-property=NAME=VALUE Set socket unit property\n\n" "Timer options:\n" " --on-active=SECONDS Run after SECONDS delay\n" " --on-boot=SECONDS Run SECONDS after machine was booted up\n" @@ -113,7 +120,7 @@ static void help(void) { } static int add_timer_property(const char *name, const char *val) { - _cleanup_free_ char *p = NULL; + char *p; assert(name); assert(val); @@ -125,8 +132,6 @@ static int add_timer_property(const char *name, const char *val) { if (strv_consume(&arg_timer_property, p) < 0) return log_oom(); - p = NULL; - return 0; } @@ -152,6 +157,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_ON_UNIT_INACTIVE, ARG_ON_CALENDAR, ARG_TIMER_PROPERTY, + ARG_PATH_PROPERTY, + ARG_SOCKET_PROPERTY, ARG_NO_BLOCK, ARG_NO_ASK_PASSWORD, ARG_WAIT, @@ -188,12 +195,15 @@ static int parse_argv(int argc, char *argv[]) { { "on-unit-inactive", required_argument, NULL, ARG_ON_UNIT_INACTIVE }, { "on-calendar", required_argument, NULL, ARG_ON_CALENDAR }, { "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY }, + { "path-property", required_argument, NULL, ARG_PATH_PROPERTY }, + { "socket-property", required_argument, NULL, ARG_SOCKET_PROPERTY }, { "no-block", no_argument, NULL, ARG_NO_BLOCK }, { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, { "collect", no_argument, NULL, 'G' }, {}, }; + bool with_trigger = false; int r, c; assert(argc >= 0); @@ -368,6 +378,20 @@ static int parse_argv(int argc, char *argv[]) { !!startswith(optarg, "OnCalendar="); break; + case ARG_PATH_PROPERTY: + + if (strv_extend(&arg_path_property, optarg) < 0) + return log_oom(); + + break; + + case ARG_SOCKET_PROPERTY: + + if (strv_extend(&arg_socket_property, optarg) < 0) + return log_oom(); + + break; + case ARG_NO_BLOCK: arg_no_block = true; break; @@ -387,6 +411,13 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } + with_trigger = !!arg_path_property || !!arg_socket_property || with_timer; + + /* currently, only single trigger (path, socket, timer) unit can be created simultaneously */ + if ((int) !!arg_path_property + (int) !!arg_socket_property + (int) with_timer > 1) { + log_error("Only single trigger (path, socket, timer) unit can be created."); + return -EINVAL; + } if (arg_stdio == ARG_STDIO_AUTO) { /* If we both --pty and --pipe are specified we'll automatically pick --pty if we are connected fully @@ -397,7 +428,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_STDIO_DIRECT; } - if ((optind >= argc) && (!arg_unit || !with_timer)) { + if ((optind >= argc) && (!arg_unit || !with_trigger)) { log_error("Command line to execute required."); return -EINVAL; } @@ -417,7 +448,7 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } - if (arg_stdio != ARG_STDIO_NONE && (with_timer || arg_scope)) { + if (arg_stdio != ARG_STDIO_NONE && (with_trigger || arg_scope)) { log_error("--pty/--pipe is not compatible in timer or --scope mode."); return -EINVAL; } @@ -432,8 +463,8 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } - if (arg_scope && with_timer) { - log_error("Timer options are not supported in --scope mode."); + if (arg_scope && with_trigger) { + log_error("Path, socket or timer options are not supported in --scope mode."); return -EINVAL; } @@ -448,8 +479,8 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } - if (with_timer) { - log_error("--wait may not be combined with timer operations."); + if (with_trigger) { + log_error("--wait may not be combined with path, socket or timer operations."); return -EINVAL; } @@ -462,7 +493,7 @@ static int parse_argv(int argc, char *argv[]) { return 1; } -static int transient_unit_set_properties(sd_bus_message *m, char **properties) { +static int transient_unit_set_properties(sd_bus_message *m, UnitType t, char **properties) { int r; r = sd_bus_message_append(m, "(sv)", "Description", "s", arg_description); @@ -475,7 +506,7 @@ static int transient_unit_set_properties(sd_bus_message *m, char **properties) { return bus_log_create_error(r); } - r = bus_append_unit_property_assignment_many(m, properties); + r = bus_append_unit_property_assignment_many(m, t, properties); if (r < 0) return r; @@ -521,7 +552,7 @@ static int transient_service_set_properties(sd_bus_message *m, char **argv, cons assert(m); - r = transient_unit_set_properties(m, arg_property); + r = transient_unit_set_properties(m, UNIT_SERVICE, arg_property); if (r < 0) return r; @@ -694,7 +725,7 @@ static int transient_scope_set_properties(sd_bus_message *m) { assert(m); - r = transient_unit_set_properties(m, arg_property); + r = transient_unit_set_properties(m, UNIT_SCOPE, arg_property); if (r < 0) return r; @@ -718,7 +749,7 @@ static int transient_timer_set_properties(sd_bus_message *m) { assert(m); - r = transient_unit_set_properties(m, arg_timer_property); + r = transient_unit_set_properties(m, UNIT_TIMER, arg_timer_property); if (r < 0) return r; @@ -1028,7 +1059,6 @@ static int start_transient_service( .inactive_enter_usec = USEC_INFINITY, }; _cleanup_free_ char *path = NULL; - const char *mt; c.bus = sd_bus_ref(bus); @@ -1058,18 +1088,20 @@ static int start_transient_service( if (!path) return log_oom(); - mt = strjoina("type='signal'," - "sender='org.freedesktop.systemd1'," - "path='", path, "'," - "interface='org.freedesktop.DBus.Properties'," - "member='PropertiesChanged'"); - r = sd_bus_add_match(bus, &c.match, mt, on_properties_changed, &c); + r = sd_bus_match_signal_async( + bus, + &c.match, + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + on_properties_changed, NULL, &c); if (r < 0) - return log_error_errno(r, "Failed to add properties changed signal."); + return log_error_errno(r, "Failed to request properties changed signal match: %m"); r = sd_bus_attach_event(bus, c.event, SD_EVENT_PRIORITY_NORMAL); if (r < 0) - return log_error_errno(r, "Failed to attach bus to event loop."); + return log_error_errno(r, "Failed to attach bus to event loop: %m"); r = run_context_update(&c, path); if (r < 0) @@ -1282,14 +1314,15 @@ static int start_transient_scope( return log_error_errno(errno, "Failed to execute: %m"); } -static int start_transient_timer( +static int start_transient_trigger( sd_bus *bus, - char **argv) { + char **argv, + const char *suffix) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; - _cleanup_free_ char *timer = NULL, *service = NULL; + _cleanup_free_ char *trigger = NULL, *service = NULL; const char *object = NULL; int r; @@ -1308,17 +1341,17 @@ static int start_transient_timer( if (!service) return log_oom(); - r = unit_name_change_suffix(service, ".timer", &timer); + r = unit_name_change_suffix(service, suffix, &trigger); if (r < 0) return log_error_errno(r, "Failed to change unit suffix: %m"); break; case UNIT_TIMER: - timer = strdup(arg_unit); - if (!timer) + trigger = strdup(arg_unit); + if (!trigger) return log_oom(); - r = unit_name_change_suffix(timer, ".service", &service); + r = unit_name_change_suffix(trigger, ".service", &service); if (r < 0) return log_error_errno(r, "Failed to change unit suffix: %m"); break; @@ -1328,7 +1361,7 @@ static int start_transient_timer( if (r < 0) return log_error_errno(r, "Failed to mangle unit name: %m"); - r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, ".timer", &timer); + r = unit_name_mangle_with_suffix(arg_unit, UNIT_NAME_NOGLOB, suffix, &trigger); if (r < 0) return log_error_errno(r, "Failed to mangle unit name: %m"); @@ -1339,7 +1372,7 @@ static int start_transient_timer( if (r < 0) return r; - r = unit_name_change_suffix(service, ".timer", &timer); + r = unit_name_change_suffix(service, suffix, &trigger); if (r < 0) return log_error_errno(r, "Failed to change unit suffix: %m"); } @@ -1359,7 +1392,7 @@ static int start_transient_timer( return bus_log_create_error(r); /* Name and Mode */ - r = sd_bus_message_append(m, "ss", timer, "fail"); + r = sd_bus_message_append(m, "ss", trigger, "fail"); if (r < 0) return bus_log_create_error(r); @@ -1368,7 +1401,14 @@ static int start_transient_timer( if (r < 0) return bus_log_create_error(r); - r = transient_timer_set_properties(m); + if (streq(suffix, ".path")) + r = transient_unit_set_properties(m, UNIT_PATH, arg_path_property); + else if (streq(suffix, ".socket")) + r = transient_unit_set_properties(m, UNIT_SOCKET, arg_socket_property); + else if (streq(suffix, ".timer")) + r = transient_timer_set_properties(m); + else + assert_not_reached("Invalid suffix"); if (r < 0) return r; @@ -1414,7 +1454,7 @@ static int start_transient_timer( r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0) { - log_error("Failed to start transient timer unit: %s", bus_error_message(&error, -r)); + log_error("Failed to start transient %s unit: %s", suffix + 1, bus_error_message(&error, -r)); return r; } @@ -1427,7 +1467,7 @@ static int start_transient_timer( return r; if (!arg_quiet) { - log_info("Running timer as unit: %s", timer); + log_info("Running %s as unit: %s", suffix + 1, trigger); if (argv[0]) log_info("Will run service as unit: %s", service); } @@ -1488,14 +1528,20 @@ int main(int argc, char* argv[]) { if (arg_scope) r = start_transient_scope(bus, argv + optind); + else if (arg_path_property) + r = start_transient_trigger(bus, argv + optind, ".path"); + else if (arg_socket_property) + r = start_transient_trigger(bus, argv + optind, ".socket"); else if (with_timer) - r = start_transient_timer(bus, argv + optind); + r = start_transient_trigger(bus, argv + optind, ".timer"); else r = start_transient_service(bus, argv + optind, &retval); finish: strv_free(arg_environment); strv_free(arg_property); + strv_free(arg_path_property); + strv_free(arg_socket_property); strv_free(arg_timer_property); return r < 0 ? EXIT_FAILURE : retval; diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index 17928a9732..99d6a9b143 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -316,7 +316,7 @@ int ask_password_tty( } if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0) - flush_fd(notify); + (void) flush_fd(notify); if (pollfd[POLL_TTY].revents == 0) continue; @@ -656,7 +656,7 @@ int ask_password_agent( goto finish; } - if (strv_length(l) <= 0) { + if (strv_isempty(l)) { l = strv_free(l); log_debug("Invalid packet"); continue; diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index c0a10417d8..9c3bdd47de 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -20,6 +20,8 @@ #include <stdio.h> #include <linux/magic.h> +#include "sd-id128.h" + #include "alloc-util.h" #include "blkid-util.h" #include "bootspec.h" @@ -52,22 +54,30 @@ void boot_entry_free(BootEntry *entry) { } int boot_entry_load(const char *path, BootEntry *entry) { + _cleanup_(boot_entry_free) BootEntry tmp = {}; _cleanup_fclose_ FILE *f = NULL; unsigned line = 1; - _cleanup_(boot_entry_free) BootEntry tmp = {}; + char *b, *c; int r; assert(path); assert(entry); - f = fopen(path, "re"); - if (!f) - return log_error_errno(errno, "Failed to open \"%s\": %m", path); + c = endswith_no_case(path, ".conf"); + if (!c) { + log_error("Invalid loader entry filename: %s", path); + return -EINVAL; + } - tmp.filename = strdup(basename(path)); + b = basename(path); + tmp.filename = strndup(b, c - b); if (!tmp.filename) return log_oom(); + f = fopen(path, "re"); + if (!f) + return log_error_errno(errno, "Failed to open \"%s\": %m", path); + for (;;) { _cleanup_free_ char *buf = NULL; char *p; @@ -195,67 +205,8 @@ int boot_loader_read_conf(const char *path, BootConfig *config) { return 0; } -/* This is a direct translation of str_verscmp from boot.c */ -static bool is_digit(int c) { - return c >= '0' && c <= '9'; -} - -static int c_order(int c) { - if (c == '\0') - return 0; - if (is_digit(c)) - return 0; - else if ((c >= 'a') && (c <= 'z')) - return c; - else - return c + 0x10000; -} - -static int str_verscmp(const char *s1, const char *s2) { - const char *os1 = s1; - const char *os2 = s2; - - while (*s1 || *s2) { - int first; - - while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) { - int order; - - order = c_order(*s1) - c_order(*s2); - if (order) - return order; - s1++; - s2++; - } - - while (*s1 == '0') - s1++; - while (*s2 == '0') - s2++; - - first = 0; - while (is_digit(*s1) && is_digit(*s2)) { - if (first == 0) - first = *s1 - *s2; - s1++; - s2++; - } - - if (is_digit(*s1)) - return 1; - if (is_digit(*s2)) - return -1; - - if (first != 0) - return first; - } - - return strcmp(os1, os2); -} - static int boot_entry_compare(const void *a, const void *b) { - const BootEntry *aa = a; - const BootEntry *bb = b; + const BootEntry *aa = a, *bb = b; return str_verscmp(aa->filename, bb->filename); } diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index b58abed2b5..bc77c3abdb 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -24,6 +24,7 @@ #include "bus-util.h" #include "cap-list.h" #include "cgroup-util.h" +#include "condition.h" #include "cpu-set-util.h" #include "env-util.h" #include "errno-list.h" @@ -42,9 +43,11 @@ #include "rlimit-util.h" #include "securebits-util.h" #include "signal-util.h" +#include "socket-protocol-list.h" #include "string-util.h" #include "syslog-util.h" #include "terminal-util.h" +#include "unit-def.h" #include "user-util.h" #include "utf8.h" #include "util.h" @@ -70,478 +73,456 @@ int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) { &u->job_path); } -static int bus_append_ip_address_access(sd_bus_message *m, int family, const union in_addr_union *prefix, unsigned char prefixlen) { +#define DEFINE_BUS_APPEND_PARSE_PTR(bus_type, cast_type, type, parse_func) \ + static int bus_append_##parse_func( \ + sd_bus_message *m, \ + const char *field, \ + const char *eq) { \ + type val; \ + int r; \ + \ + r = parse_func(eq, &val); \ + if (r < 0) \ + return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); \ + \ + r = sd_bus_message_append(m, "(sv)", field, \ + bus_type, (cast_type) val); \ + if (r < 0) \ + return bus_log_create_error(r); \ + \ + return 1; \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +#define DEFINE_BUS_APPEND_PARSE(bus_type, parse_func) \ + static int bus_append_##parse_func( \ + sd_bus_message *m, \ + const char *field, \ + const char *eq) { \ + int r; \ + \ + r = parse_func(eq); \ + if (r < 0) { \ + log_error("Failed to parse %s: %s", field, eq); \ + return -EINVAL; \ + } \ + \ + r = sd_bus_message_append(m, "(sv)", field, \ + bus_type, (int32_t) r); \ + if (r < 0) \ + return bus_log_create_error(r); \ + \ + return 1; \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +DEFINE_BUS_APPEND_PARSE("b", parse_boolean); +DEFINE_BUS_APPEND_PARSE("i", ioprio_class_from_string); +DEFINE_BUS_APPEND_PARSE("i", ip_tos_from_string); +DEFINE_BUS_APPEND_PARSE("i", log_facility_unshifted_from_string); +DEFINE_BUS_APPEND_PARSE("i", log_level_from_string); +DEFINE_BUS_APPEND_PARSE("i", parse_errno); +DEFINE_BUS_APPEND_PARSE("i", sched_policy_from_string); +DEFINE_BUS_APPEND_PARSE("i", secure_bits_from_string); +DEFINE_BUS_APPEND_PARSE("i", signal_from_string_try_harder); +DEFINE_BUS_APPEND_PARSE("i", socket_protocol_from_name); +DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, ioprio_parse_priority); +DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, parse_nice); +DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, safe_atoi); +DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, nsec_t, parse_nsec); +DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_blkio_weight_parse); +DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_cpu_shares_parse); +DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, cg_weight_parse); +DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, unsigned long, mount_propagation_flags_from_string); +DEFINE_BUS_APPEND_PARSE_PTR("t", uint64_t, uint64_t, safe_atou64); +DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, mode_t, parse_mode); +DEFINE_BUS_APPEND_PARSE_PTR("u", uint32_t, unsigned, safe_atou); +DEFINE_BUS_APPEND_PARSE_PTR("x", int64_t, int64_t, safe_atoi64); + +static inline int bus_append_string(sd_bus_message *m, const char *field, const char *eq) { int r; - assert(m); - assert(prefix); - - r = sd_bus_message_open_container(m, 'r', "iayu"); + r = sd_bus_message_append(m, "(sv)", field, "s", eq); if (r < 0) - return r; + return bus_log_create_error(r); - r = sd_bus_message_append(m, "i", family); + return 1; +} + +static int bus_append_strv(sd_bus_message *m, const char *field, const char *eq, ExtractFlags flags) { + const char *p; + int r; + + r = sd_bus_message_open_container(m, 'r', "sv"); if (r < 0) - return r; + return bus_log_create_error(r); - r = sd_bus_message_append_array(m, 'y', prefix, FAMILY_ADDRESS_SIZE(family)); + r = sd_bus_message_append_basic(m, 's', field); if (r < 0) - return r; + return bus_log_create_error(r); - r = sd_bus_message_append(m, "u", prefixlen); + r = sd_bus_message_open_container(m, 'v', "as"); if (r < 0) - return r; + return bus_log_create_error(r); - return sd_bus_message_close_container(m); -} + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return bus_log_create_error(r); -int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) { - const char *eq, *field; - UnitDependency dep; - int r, rl; + for (p = eq;;) { + _cleanup_free_ char *word = NULL; - assert(m); - assert(assignment); + r = extract_first_word(&p, &word, NULL, flags); + if (r == 0) + break; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) + return log_error_errno(r, "Invalid syntax: %s", eq); - eq = strchr(assignment, '='); - if (!eq) { - log_error("Not an assignment: %s", assignment); - return -EINVAL; + r = sd_bus_message_append_basic(m, 's', word); + if (r < 0) + return bus_log_create_error(r); } - r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + r = sd_bus_message_close_container(m); if (r < 0) return bus_log_create_error(r); - field = strndupa(assignment, eq - assignment); - eq++; + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); - if (streq(field, "CPUQuota")) { + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); - if (isempty(eq)) - r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY); - else { - r = parse_percent_unbounded(eq); - if (r <= 0) { - log_error_errno(r, "CPU quota '%s' invalid.", eq); - return -EINVAL; - } + return 1; +} - r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) r * USEC_PER_SEC / 100U); - } +static int bus_append_byte_array(sd_bus_message *m, const char *field, const void *buf, size_t n) { + int r; - goto finish; + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); - } else if (streq(field, "EnvironmentFile")) { + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); - if (isempty(eq)) - r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 0); - else - r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1, - eq[0] == '-' ? eq + 1 : eq, - eq[0] == '-'); - goto finish; + r = sd_bus_message_open_container(m, 'v', "ay"); + if (r < 0) + return bus_log_create_error(r); - } else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) { - char *n; - usec_t t; - size_t l; + r = sd_bus_message_append_array(m, 'y', buf, n); + if (r < 0) + return bus_log_create_error(r); - r = parse_sec(eq, &t); - if (r < 0) - return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq); + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); - l = strlen(field); - n = newa(char, l + 2); - if (!n) - return log_oom(); + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); - /* Change suffix Sec → USec */ - strcpy(mempcpy(n, field, l - 3), "USec"); - r = sd_bus_message_append(m, "sv", n, "t", t); - goto finish; + return 1; +} - } else if (streq(field, "LogExtraFields")) { +static int bus_append_parse_sec_rename(sd_bus_message *m, const char *field, const char *eq) { + char *n; + usec_t t; + size_t l; + int r; - r = sd_bus_message_append(m, "s", "LogExtraFields"); - if (r < 0) - goto finish; + r = parse_sec(eq, &t); + if (r < 0) + return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); - r = sd_bus_message_open_container(m, 'v', "aay"); - if (r < 0) - goto finish; + l = strlen(field); + n = newa(char, l + 2); + /* Change suffix Sec → USec */ + strcpy(mempcpy(n, field, l - 3), "USec"); - r = sd_bus_message_open_container(m, 'a', "ay"); - if (r < 0) - goto finish; + r = sd_bus_message_append(m, "(sv)", n, "t", t); + if (r < 0) + return bus_log_create_error(r); - r = sd_bus_message_append_array(m, 'y', eq, strlen(eq)); - if (r < 0) - goto finish; + return 1; +} - r = sd_bus_message_close_container(m); - if (r < 0) - goto finish; +static int bus_append_parse_size(sd_bus_message *m, const char *field, const char *eq, uint64_t base) { + uint64_t v; + int r; - r = sd_bus_message_close_container(m); - goto finish; + r = parse_size(eq, base, &v); + if (r < 0) + return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); - } else if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit")) { - uint64_t bytes; + r = sd_bus_message_append(m, "(sv)", field, "t", v); + if (r < 0) + return bus_log_create_error(r); - if (isempty(eq) || streq(eq, "infinity")) - bytes = CGROUP_LIMIT_MAX; - else { - r = parse_percent(eq); - if (r >= 0) { - char *n; + return 1; +} - /* When this is a percentage we'll convert this into a relative value in the range - * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related - * ones). This way the physical memory size can be determined server-side */ +static int bus_append_exec_command(sd_bus_message *m, const char *field, const char *eq) { + bool ignore_failure = false, explicit_path = false, done = false; + _cleanup_strv_free_ char **l = NULL; + _cleanup_free_ char *path = NULL; + int r; - n = strjoina(field, "Scale"); - r = sd_bus_message_append(m, "sv", n, "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U)); - goto finish; + do { + switch (*eq) { - } else { - r = parse_size(eq, 1024, &bytes); - if (r < 0) - return log_error_errno(r, "Failed to parse bytes specification %s", assignment); + case '-': + if (ignore_failure) + done = true; + else { + ignore_failure = true; + eq++; } - } - - r = sd_bus_message_append(m, "sv", field, "t", bytes); - goto finish; - - } else if (streq(field, "Delegate")) { - - r = parse_boolean(eq); - if (r < 0) { - const char *p = eq; - - r = sd_bus_message_append(m, "s", "DelegateControllers"); - if (r < 0) - goto finish; - - r = sd_bus_message_open_container(m, 'v', "as"); - if (r < 0) - goto finish; - - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - goto finish; - - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); - if (r == 0) - break; - if (r == -ENOMEM) - return log_oom(); - if (r < 0) - return log_error_errno(r, "Invalid syntax: %s", eq); + break; - r = sd_bus_message_append(m, "s", word); - if (r < 0) - goto finish; + case '@': + if (explicit_path) + done = true; + else { + explicit_path = true; + eq++; } + break; - r = sd_bus_message_close_container(m); - if (r < 0) - goto finish; - - r = sd_bus_message_close_container(m); - } else - r = sd_bus_message_append(m, "sv", "Delegate", "b", r); - - goto finish; - - } else if (streq(field, "TasksMax")) { - uint64_t t; - - if (isempty(eq) || streq(eq, "infinity")) - t = (uint64_t) -1; - else { - r = parse_percent(eq); - if (r >= 0) { - r = sd_bus_message_append(m, "sv", "TasksMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U)); - goto finish; - } else { - r = safe_atou64(eq, &t); - if (r < 0) - return log_error_errno(r, "Failed to parse maximum tasks specification %s", assignment); - } + case '+': + case '!': + /* The bus API doesn't support +, ! and !! currently, unfortunately. :-( */ + log_error("Sorry, but +, ! and !! are currently not supported for transient services."); + return -EOPNOTSUPP; + default: + done = true; + break; } + } while (!done); - r = sd_bus_message_append(m, "sv", "TasksMax", "t", t); - goto finish; - - } else if (STR_IN_SET(field, "StandardInput", "StandardOutput", "StandardError")) { - const char *n, *appended; + if (explicit_path) { + r = extract_first_word(&eq, &path, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); + if (r < 0) + return log_error_errno(r, "Failed to parse path: %m"); + } - n = startswith(eq, "fd:"); - if (n) { - appended = strjoina(field, "FileDescriptorName"); - r = sd_bus_message_append(m, "sv", appended, "s", n); + r = strv_split_extract(&l, eq, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); + if (r < 0) + return log_error_errno(r, "Failed to parse command line: %m"); - } else if ((n = startswith(eq, "file:"))) { - appended = strjoina(field, "File"); - r = sd_bus_message_append(m, "sv", appended, "s", n); - } else - r = sd_bus_message_append(m, "sv", field, "s", eq); + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); - goto finish; + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); - } else if (streq(field, "StandardInputText")) { - _cleanup_free_ char *unescaped = NULL; + r = sd_bus_message_open_container(m, 'v', "a(sasb)"); + if (r < 0) + return bus_log_create_error(r); - r = cunescape(eq, 0, &unescaped); - if (r < 0) - return log_error_errno(r, "Failed to unescape text '%s': %m", eq); + r = sd_bus_message_open_container(m, 'a', "(sasb)"); + if (r < 0) + return bus_log_create_error(r); - if (!strextend(&unescaped, "\n", NULL)) - return log_oom(); + if (!strv_isempty(l)) { - /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic - * interface anyway */ + r = sd_bus_message_open_container(m, 'r', "sasb"); + if (r < 0) + return bus_log_create_error(r); - r = sd_bus_message_append(m, "s", "StandardInputData"); + r = sd_bus_message_append(m, "s", path ?: l[0]); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_open_container(m, 'v', "ay"); + r = sd_bus_message_append_strv(m, l); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_append_array(m, 'y', unescaped, strlen(unescaped)); + r = sd_bus_message_append(m, "b", ignore_failure); if (r < 0) return bus_log_create_error(r); r = sd_bus_message_close_container(m); - goto finish; + if (r < 0) + return bus_log_create_error(r); } - r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + r = sd_bus_message_close_container(m); 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_close_container(m); - 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); + return 1; +} - 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", "IPAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem", - "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate", - "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", - "NoNewPrivileges", "SyslogLevelPrefix", "RemainAfterElapse", "Persistent", - "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC", - "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS", - "CPUSchedulingResetOnFork", "LockPersonality")) { +static int bus_append_ip_address_access(sd_bus_message *m, int family, const union in_addr_union *prefix, unsigned char prefixlen) { + int r; - r = parse_boolean(eq); - if (r < 0) - return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment); + assert(m); + assert(prefix); - r = sd_bus_message_append(m, "v", "b", r); + r = sd_bus_message_open_container(m, 'r', "iayu"); + if (r < 0) + return r; - } else if (STR_IN_SET(field, "CPUWeight", "StartupCPUWeight")) { - uint64_t u; + r = sd_bus_message_append(m, "i", family); + if (r < 0) + return r; - r = cg_weight_parse(eq, &u); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); + r = sd_bus_message_append_array(m, 'y', prefix, FAMILY_ADDRESS_SIZE(family)); + if (r < 0) + return r; - r = sd_bus_message_append(m, "v", "t", u); + r = sd_bus_message_append(m, "u", prefixlen); + if (r < 0) + return r; - } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) { - uint64_t u; + return sd_bus_message_close_container(m); +} - r = cg_cpu_shares_parse(eq, &u); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); +static int bus_append_cgroup_property(sd_bus_message *m, const char *field, const char *eq) { + int r; - r = sd_bus_message_append(m, "v", "t", u); + if (STR_IN_SET(field, "DevicePolicy", "Slice")) - } else if (STR_IN_SET(field, "IOWeight", "StartupIOWeight")) { - uint64_t u; + return bus_append_string(m, field, eq); - r = cg_weight_parse(eq, &u); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); + if (STR_IN_SET(field, + "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting", + "TasksAccounting", "IPAccounting")) - r = sd_bus_message_append(m, "v", "t", u); + return bus_append_parse_boolean(m, field, eq); - } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) { - uint64_t u; + if (STR_IN_SET(field, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight")) - r = cg_blkio_weight_parse(eq, &u); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); + return bus_append_cg_weight_parse(m, field, eq); - r = sd_bus_message_append(m, "v", "t", u); + if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) - } else if (STR_IN_SET(field, - "User", "Group", "DevicePolicy", "KillMode", - "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath", - "Description", "Slice", "Type", "WorkingDirectory", - "RootDirectory", "SyslogIdentifier", "ProtectSystem", - "ProtectHome", "SELinuxContext", "Restart", "RootImage", - "NotifyAccess", "RuntimeDirectoryPreserve", "Personality", - "KeyringMode", "CollectMode", "FailureAction", "SuccessAction", - "OnCalendar")) + return bus_append_cg_cpu_shares_parse(m, field, eq); - r = sd_bus_message_append(m, "v", "s", eq); + if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) - else if (streq(field, "StandardInputData")) { - _cleanup_free_ void *decoded = NULL; - size_t sz; + return bus_append_cg_blkio_weight_parse(m, field, eq); - r = unbase64mem(eq, (size_t) -1, &decoded, &sz); - if (r < 0) - return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq); + if (streq(field, "Delegate")) { - r = sd_bus_message_open_container(m, 'v', "ay"); + r = parse_boolean(eq); if (r < 0) - return bus_log_create_error(r); + return bus_append_strv(m, "DelegateControllers", eq, EXTRACT_QUOTES); - r = sd_bus_message_append_array(m, 'y', decoded, sz); + r = sd_bus_message_append(m, "(sv)", "Delegate", "b", r); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_close_container(m); + return 1; + } - } else if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) { - bool ignore; - const char *s; + if (STR_IN_SET(field, "MemoryLow", "MemoryHigh", "MemoryMax", "MemorySwapMax", "MemoryLimit", "TasksMax")) { - if (eq[0] == '-') { - ignore = true; - s = eq + 1; - } else { - ignore = false; - s = eq; + if (isempty(eq) || streq(eq, "infinity")) { + r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX); + if (r < 0) + return bus_log_create_error(r); + return 1; } - r = sd_bus_message_append(m, "v", "(bs)", ignore, s); - - } else if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax")) { - int level; + r = parse_percent(eq); + if (r >= 0) { + char *n; - level = log_level_from_string(eq); - if (level < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } + /* When this is a percentage we'll convert this into a relative value in the range + * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related + * ones). This way the physical memory size can be determined server-side */ - r = sd_bus_message_append(m, "v", "i", level); - - } else if (streq(field, "SyslogFacility")) { - int facility; + n = strjoina(field, "Scale"); + r = sd_bus_message_append(m, "(sv)", n, "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U)); + if (r < 0) + return bus_log_create_error(r); - facility = log_facility_unshifted_from_string(eq); - if (facility < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; + return 1; } - r = sd_bus_message_append(m, "v", "i", facility); + if (streq(field, "TasksMax")) + return bus_append_safe_atou64(m, field, eq); - } else if (streq(field, "SecureBits")) { + return bus_append_parse_size(m, field, eq, 1024); - r = secure_bits_from_string(eq); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); + } - r = sd_bus_message_append(m, "v", "i", r); + if (streq(field, "CPUQuota")) { - } else if (STR_IN_SET(field, "CapabilityBoundingSet", "AmbientCapabilities")) { - uint64_t sum = 0; - bool invert = false; - const char *p; + if (isempty(eq)) + r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY); + else { + r = parse_percent_unbounded(eq); + if (r <= 0) { + log_error_errno(r, "CPU quota '%s' invalid.", eq); + return -EINVAL; + } - p = eq; - if (*p == '~') { - invert = true; - p++; + r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", (usec_t) r * USEC_PER_SEC / 100U); } - r = capability_set_from_string(p, &sum); if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); - - sum = invert ? ~sum : sum; + return bus_log_create_error(r); - r = sd_bus_message_append(m, "v", "t", sum); + return 1; + } - } else if (streq(field, "DeviceAllow")) { + if (streq(field, "DeviceAllow")) { if (isempty(eq)) - r = sd_bus_message_append(m, "v", "a(ss)", 0); + r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0); else { - const char *path, *rwm, *e; + const char *path = eq, *rwm = NULL, *e; e = strchr(eq, ' '); if (e) { path = strndupa(eq, e - eq); rwm = e+1; - } else { - path = eq; - rwm = ""; - } - - if (!is_deviceallow_pattern(path)) { - 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); + r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 1, path, strempty(rwm)); } - } else if (cgroup_io_limit_type_from_string(field) >= 0 || STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) { + if (r < 0) + return bus_log_create_error(r); + + return 1; + } + + 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); + r = sd_bus_message_append(m, "(sv)", field, "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 { + if (!e) { 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; - } + path = strndupa(eq, e - eq); + bandwidth = e+1; if (streq(bandwidth, "infinity")) { bytes = CGROUP_LIMIT_MAX; @@ -551,315 +532,290 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen return log_error_errno(r, "Failed to parse byte value %s: %m", bandwidth); } - r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes); + r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, bytes); } - } else if (STR_IN_SET(field, "IODeviceWeight", "BlockIODeviceWeight")) { + if (r < 0) + return bus_log_create_error(r); + + return 1; + } + + if (STR_IN_SET(field, "IODeviceWeight", "BlockIODeviceWeight")) { if (isempty(eq)) - r = sd_bus_message_append(m, "v", "a(st)", 0); + r = sd_bus_message_append(m, "(sv)", field, "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 { + if (!e) { 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; - } + path = strndupa(eq, e - eq); + weight = e+1; r = safe_atou64(weight, &u); if (r < 0) return log_error_errno(r, "Failed to parse %s value %s: %m", field, weight); - r = sd_bus_message_append(m, "v", "a(st)", 1, path, u); + r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, u); } - } else if (STR_IN_SET(field, "IPAddressAllow", "IPAddressDeny")) { + if (r < 0) + return bus_log_create_error(r); - if (isempty(eq)) - r = sd_bus_message_append(m, "v", "a(iayu)", 0); - else { - unsigned char prefixlen; - union in_addr_union prefix = {}; - int family; + return 1; + } - r = sd_bus_message_open_container(m, 'v', "a(iayu)"); - if (r < 0) - return bus_log_create_error(r); + if (STR_IN_SET(field, "IPAddressAllow", "IPAddressDeny")) { + unsigned char prefixlen; + union in_addr_union prefix = {}; + int family; - r = sd_bus_message_open_container(m, 'a', "(iayu)"); + if (isempty(eq)) { + r = sd_bus_message_append(m, "(sv)", field, "a(iayu)", 0); if (r < 0) return bus_log_create_error(r); - if (streq(eq, "any")) { - /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */ + return 1; + } - r = bus_append_ip_address_access(m, AF_INET, &prefix, 0); - 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); - r = bus_append_ip_address_access(m, AF_INET6, &prefix, 0); - if (r < 0) - return bus_log_create_error(r); + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); - } else if (is_localhost(eq)) { - /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */ + r = sd_bus_message_open_container(m, 'v', "a(iayu)"); + if (r < 0) + return bus_log_create_error(r); - prefix.in.s_addr = htobe32(0x7f000000); - r = bus_append_ip_address_access(m, AF_INET, &prefix, 8); - if (r < 0) - return bus_log_create_error(r); + r = sd_bus_message_open_container(m, 'a', "(iayu)"); + if (r < 0) + return bus_log_create_error(r); - prefix.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT; - r = bus_append_ip_address_access(m, AF_INET6, &prefix, 128); - if (r < 0) - return r; + if (streq(eq, "any")) { + /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */ - } else if (streq(eq, "link-local")) { + r = bus_append_ip_address_access(m, AF_INET, &prefix, 0); + if (r < 0) + return bus_log_create_error(r); - /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */ + r = bus_append_ip_address_access(m, AF_INET6, &prefix, 0); + if (r < 0) + return bus_log_create_error(r); - prefix.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16)); - r = bus_append_ip_address_access(m, AF_INET, &prefix, 16); - if (r < 0) - return bus_log_create_error(r); + } else if (is_localhost(eq)) { + /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */ - prefix.in6 = (struct in6_addr) { - .s6_addr32[0] = htobe32(0xfe800000) - }; - r = bus_append_ip_address_access(m, AF_INET6, &prefix, 64); - if (r < 0) - return bus_log_create_error(r); + prefix.in.s_addr = htobe32(0x7f000000); + r = bus_append_ip_address_access(m, AF_INET, &prefix, 8); + if (r < 0) + return bus_log_create_error(r); - } else if (streq(eq, "multicast")) { + prefix.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT; + r = bus_append_ip_address_access(m, AF_INET6, &prefix, 128); + if (r < 0) + return r; - /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */ + } else if (streq(eq, "link-local")) { + /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */ - prefix.in.s_addr = htobe32((UINT32_C(224) << 24)); - r = bus_append_ip_address_access(m, AF_INET, &prefix, 4); - if (r < 0) - return bus_log_create_error(r); + prefix.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16)); + r = bus_append_ip_address_access(m, AF_INET, &prefix, 16); + if (r < 0) + return bus_log_create_error(r); - prefix.in6 = (struct in6_addr) { - .s6_addr32[0] = htobe32(0xff000000) - }; - r = bus_append_ip_address_access(m, AF_INET6, &prefix, 8); - if (r < 0) - return bus_log_create_error(r); + prefix.in6 = (struct in6_addr) { + .s6_addr32[0] = htobe32(0xfe800000) + }; + r = bus_append_ip_address_access(m, AF_INET6, &prefix, 64); + if (r < 0) + return bus_log_create_error(r); - } else { - r = in_addr_prefix_from_string_auto(eq, &family, &prefix, &prefixlen); - if (r < 0) - return log_error_errno(r, "Failed to parse IP address prefix: %s", eq); + } else if (streq(eq, "multicast")) { + /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */ - r = bus_append_ip_address_access(m, family, &prefix, prefixlen); - if (r < 0) - return bus_log_create_error(r); - } + prefix.in.s_addr = htobe32((UINT32_C(224) << 24)); + r = bus_append_ip_address_access(m, AF_INET, &prefix, 4); + if (r < 0) + return bus_log_create_error(r); - r = sd_bus_message_close_container(m); + prefix.in6 = (struct in6_addr) { + .s6_addr32[0] = htobe32(0xff000000) + }; + r = bus_append_ip_address_access(m, AF_INET6, &prefix, 8); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_close_container(m); + } else { + r = in_addr_prefix_from_string_auto(eq, &family, &prefix, &prefixlen); + if (r < 0) + return log_error_errno(r, "Failed to parse IP address prefix: %s", eq); + + r = bus_append_ip_address_access(m, family, &prefix, prefixlen); if (r < 0) return bus_log_create_error(r); } - } else if (streq(field, "CPUSchedulingPolicy")) { - int n; + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); - n = sched_policy_from_string(eq); - if (n < 0) - return log_error_errno(r, "Failed to parse CPUSchedulingPolicy: %s", eq); + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); - r = sd_bus_message_append(m, "v", "i", (int32_t) n); + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); - } else if (streq(field, "CPUSchedulingPriority")) { - int n; + return 1; + } - r = safe_atoi(eq, &n); - if (r < 0) - return log_error_errno(r, "Failed to parse CPUSchedulingPriority: %s", eq); - if (!sched_priority_is_valid(n)) - return log_error_errno(r, "Invalid CPUSchedulingPriority: %s", eq); + return 0; +} - r = sd_bus_message_append(m, "v", "i", (int32_t) n); +static int bus_append_automount_property(sd_bus_message *m, const char *field, const char *eq) { - } else if (streq(field, "CPUAffinity")) { - _cleanup_cpu_free_ cpu_set_t *cpuset = NULL; - int ncpus; + if (streq(field, "Where")) - ncpus = parse_cpu_set(eq, &cpuset); - if (ncpus < 0) - return log_error_errno(r, "Failed to parse %s value: %s", field, eq); + return bus_append_string(m, field, eq); - r = sd_bus_message_open_container(m, 'v', "ay"); - if (r < 0) - return bus_log_create_error(r); + if (streq(field, "DirectoryMode")) - r = sd_bus_message_append_array(m, 'y', cpuset, CPU_ALLOC_SIZE(ncpus)); - if (r < 0) - return bus_log_create_error(r); + return bus_append_parse_mode(m, field, eq); - r = sd_bus_message_close_container(m); + if (streq(field, "TimeoutIdleSec")) - } else if (streq(field, "Nice")) { - int n; + return bus_append_parse_sec_rename(m, field, eq); - r = parse_nice(eq, &n); - if (r < 0) - return log_error_errno(r, "Failed to parse nice value: %s", eq); + return 0; +} - r = sd_bus_message_append(m, "v", "i", (int32_t) n); +static int bus_append_execute_property(sd_bus_message *m, const char *field, const char *eq) { + int r, rl; - } else if (streq(field, "SystemCallFilter")) { - int whitelist; - _cleanup_strv_free_ char **l = NULL; - const char *p; + if (STR_IN_SET(field, + "User", "Group", + "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath", + "WorkingDirectory", "RootDirectory", "SyslogIdentifier", + "ProtectSystem", "ProtectHome", "SELinuxContext", "RootImage", + "RuntimeDirectoryPreserve", "Personality", "KeyringMode")) - p = eq; - if (*p == '~') { - whitelist = 0; - p++; - } else - whitelist = 1; + return bus_append_string(m, field, eq); - if (whitelist != 0) { - r = strv_extend(&l, "@default"); - if (r < 0) - return log_oom(); - } + if (STR_IN_SET(field, + "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate", + "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", + "NoNewPrivileges", "SyslogLevelPrefix", + "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC", + "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups", + "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality")) - for (;;) { - _cleanup_free_ char *word = NULL; + return bus_append_parse_boolean(m, field, eq); - 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; + if (STR_IN_SET(field, + "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories", + "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths", + "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory", + "SupplementaryGroups", "SystemCallArchitectures")) - r = strv_extend(&l, word); - if (r < 0) - return log_oom(); - } + return bus_append_strv(m, field, eq, EXTRACT_QUOTES); - r = sd_bus_message_open_container(m, 'v', "(bas)"); - if (r < 0) - return bus_log_create_error(r); + if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax")) - r = sd_bus_message_open_container(m, 'r', "bas"); - if (r < 0) - return bus_log_create_error(r); + return bus_append_log_level_from_string(m, field, eq); - r = sd_bus_message_append_basic(m, 'b', &whitelist); - if (r < 0) - return bus_log_create_error(r); + if (streq(field, "SyslogFacility")) - r = sd_bus_message_append_strv(m, l); - if (r < 0) - return bus_log_create_error(r); + return bus_append_log_facility_unshifted_from_string(m, field, eq); - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); + if (streq(field, "SecureBits")) - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); + return bus_append_secure_bits_from_string(m, field, eq); - } else if (streq(field, "SystemCallArchitectures")) { - const char *p; + if (streq(field, "CPUSchedulingPolicy")) - r = sd_bus_message_open_container(m, 'v', "as"); - if (r < 0) - return bus_log_create_error(r); + return bus_append_sched_policy_from_string(m, field, eq); - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - return bus_log_create_error(r); + if (STR_IN_SET(field, "CPUSchedulingPriority", "OOMScoreAdjust")) - for (p = eq;;) { - _cleanup_free_ char *word = NULL; + return bus_append_safe_atoi(m, field, eq); - 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; + if (streq(field, "Nice")) - r = sd_bus_message_append_basic(m, 's', word); - if (r < 0) - return bus_log_create_error(r); - } + return bus_append_parse_nice(m, field, eq); - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); + if (streq(field, "SystemCallErrorNumber")) - r = sd_bus_message_close_container(m); + return bus_append_parse_errno(m, field, eq); - } else if (streq(field, "SystemCallErrorNumber")) { - int n; + if (streq(field, "IOSchedulingClass")) - n = parse_errno(eq); - if (n <= 0) - return log_error_errno(r, "Failed to parse %s value: %s", field, eq); + return bus_append_ioprio_class_from_string(m, field, eq); - r = sd_bus_message_append(m, "v", "i", (int32_t) n); + if (streq(field, "IOSchedulingPriority")) - } else if (streq(field, "RestrictAddressFamilies")) { - int whitelist; - _cleanup_strv_free_ char **l = NULL; - const char *p = eq; + return bus_append_ioprio_parse_priority(m, field, eq); - if (*p == '~') { - whitelist = 0; - p++; - } else - whitelist = 1; + if (STR_IN_SET(field, + "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", + "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) - for (;;) { - _cleanup_free_ char *word = NULL; + return bus_append_parse_mode(m, field, eq); - 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; + if (streq(field, "TimerSlackNSec")) - r = strv_extend(&l, word); - if (r < 0) - return log_oom(); - } + return bus_append_parse_nsec(m, field, eq); - r = sd_bus_message_open_container(m, 'v', "(bas)"); + if (streq(field, "MountFlags")) + + return bus_append_mount_propagation_flags_from_string(m, field, eq); + + if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment")) + + return bus_append_strv(m, field, eq, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); + + if (streq(field, "EnvironmentFile")) { + + if (isempty(eq)) + r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 0); + else + r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 1, + eq[0] == '-' ? eq + 1 : eq, + eq[0] == '-'); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_open_container(m, 'r', "bas"); + return 1; + } + + if (streq(field, "LogExtraFields")) { + + r = sd_bus_message_open_container(m, 'r', "sv"); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_append_basic(m, 'b', &whitelist); + r = sd_bus_message_append_basic(m, 's', "LogExtraFields"); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_append_strv(m, l); + r = sd_bus_message_open_container(m, 'v', "aay"); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_close_container(m); + r = sd_bus_message_open_container(m, 'a', "ay"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_array(m, 'y', eq, strlen(eq)); if (r < 0) return bus_log_create_error(r); @@ -867,165 +823,158 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen if (r < 0) return bus_log_create_error(r); - } else if (streq(field, "FileDescriptorStoreMax")) { - unsigned u; + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); - r = safe_atou(eq, &u); + r = sd_bus_message_close_container(m); if (r < 0) - return log_error_errno(r, "Failed to parse file descriptor store limit: %s", eq); + return bus_log_create_error(r); - r = sd_bus_message_append(m, "v", "u", (uint32_t) u); + return 1; + } - } else if (streq(field, "IOSchedulingClass")) { - int c; + if (STR_IN_SET(field, "StandardInput", "StandardOutput", "StandardError")) { + const char *n, *appended; + + if ((n = startswith(eq, "fd:"))) { + appended = strjoina(field, "FileDescriptorName"); + r = sd_bus_message_append(m, "(sv)", appended, "s", n); + } else if ((n = startswith(eq, "file:"))) { + appended = strjoina(field, "File"); + r = sd_bus_message_append(m, "(sv)", appended, "s", n); + } else + r = sd_bus_message_append(m, "(sv)", field, "s", eq); - c = ioprio_class_from_string(eq); - if (c < 0) - return log_error_errno(r, "Failed to parse IO scheduling class: %s", eq); + if (r < 0) + return bus_log_create_error(r); - r = sd_bus_message_append(m, "v", "i", (int32_t) c); + return 1; + } - } else if (streq(field, "IOSchedulingPriority")) { - int q; + if (streq(field, "StandardInputText")) { + _cleanup_free_ char *unescaped = NULL; - r = ioprio_parse_priority(eq, &q); + r = cunescape(eq, 0, &unescaped); if (r < 0) - return log_error_errno(r, "Failed to parse IO scheduling priority: %s", eq); + return log_error_errno(r, "Failed to unescape text '%s': %m", eq); - r = sd_bus_message_append(m, "v", "i", (int32_t) q); + if (!strextend(&unescaped, "\n", NULL)) + return log_oom(); - } else if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment")) { - const char *p; + /* Note that we don't expand specifiers here, but that should be OK, as this is a programmatic + * interface anyway */ - r = sd_bus_message_open_container(m, 'v', "as"); - if (r < 0) - return bus_log_create_error(r); + return bus_append_byte_array(m, field, unescaped, strlen(unescaped)); + } - r = sd_bus_message_open_container(m, 'a', "s"); + if (streq(field, "StandardInputData")) { + _cleanup_free_ void *decoded = NULL; + size_t sz; + + r = unbase64mem(eq, (size_t) -1, &decoded, &sz); if (r < 0) - return bus_log_create_error(r); + return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq); - for (p = eq;;) { - _cleanup_free_ char *word = NULL; + return bus_append_byte_array(m, field, decoded, sz); + } - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); - if (r < 0) - return log_error_errno(r, "Failed to parse Environment value %s: %m", eq); - if (r == 0) - break; + rl = rlimit_from_string(field); + if (rl >= 0) { + const char *sn; + struct rlimit l; - if (streq(field, "Environment")) { - if (!env_assignment_is_valid(word)) { - log_error("Invalid environment assignment: %s", word); - return -EINVAL; - } - } else if (streq(field, "UnsetEnvironment")) { - if (!env_assignment_is_valid(word) && !env_name_is_valid(word)) { - log_error("Invalid environment name or assignment: %s", word); - return -EINVAL; - } - } else { /* PassEnvironment */ - if (!env_name_is_valid(word)) { - log_error("Invalid environment variable name: %s", word); - return -EINVAL; - } - } + 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_basic(m, 's', word); - if (r < 0) - return bus_log_create_error(r); - } + r = sd_bus_message_append(m, "(sv)", field, "t", l.rlim_max); + if (r < 0) + return bus_log_create_error(r); - r = sd_bus_message_close_container(m); + sn = strjoina(field, "Soft"); + r = sd_bus_message_append(m, "(sv)", sn, "t", l.rlim_cur); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_close_container(m); + return 1; + } - } else if (streq(field, "KillSignal")) { - int sig; + if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) { + int ignore = 0; + const char *s = eq; - sig = signal_from_string_try_harder(eq); - if (sig < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; + if (eq[0] == '-') { + ignore = 1; + s = eq + 1; } - r = sd_bus_message_append(m, "v", "i", sig); - - } else if (streq(field, "TimerSlackNSec")) { - nsec_t n; - - r = parse_nsec(eq, &n); + r = sd_bus_message_append(m, "(sv)", field, "(bs)", ignore, s); if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); + return bus_log_create_error(r); - r = sd_bus_message_append(m, "v", "t", n); - } else if (streq(field, "OOMScoreAdjust")) { - int oa; + return 1; + } - r = safe_atoi(eq, &oa); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); + if (STR_IN_SET(field, "CapabilityBoundingSet", "AmbientCapabilities")) { + uint64_t sum = 0; + bool invert = false; + const char *p = eq; - if (!oom_score_adjust_is_valid(oa)) { - log_error("OOM score adjust value out of range"); - return -EINVAL; + if (*p == '~') { + invert = true; + p++; } - r = sd_bus_message_append(m, "v", "i", oa); - } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories", - "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths")) { - const char *p; - - r = sd_bus_message_open_container(m, 'v', "as"); + r = capability_set_from_string(p, &sum); if (r < 0) - return bus_log_create_error(r); + return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); - r = sd_bus_message_open_container(m, 'a', "s"); + sum = invert ? ~sum : sum; + + r = sd_bus_message_append(m, "(sv)", field, "t", sum); if (r < 0) return bus_log_create_error(r); - for (p = eq;;) { - _cleanup_free_ char *word = NULL; - size_t offset; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); - if (r == 0) - break; + return 1; + } - if (!utf8_is_valid(word)) { - log_error("Failed to parse %s value %s", field, eq); - return -EINVAL; - } + if (streq(field, "CPUAffinity")) { + _cleanup_cpu_free_ cpu_set_t *cpuset = NULL; - offset = word[0] == '-'; - offset += word[offset] == '+'; + r = parse_cpu_set(eq, &cpuset); + if (r < 0) + return log_error_errno(r, "Failed to parse %s value: %s", field, eq); - if (!path_is_absolute(word + offset)) { - log_error("Failed to parse %s value %s", field, eq); - return -EINVAL; - } + return bus_append_byte_array(m, field, cpuset, CPU_ALLOC_SIZE(r)); + } - path_kill_slashes(word + offset); + if (STR_IN_SET(field, "RestrictAddressFamilies", "SystemCallFilter")) { + int whitelist = 1; + const char *p = eq; - r = sd_bus_message_append_basic(m, 's', word); - if (r < 0) - return bus_log_create_error(r); + if (*p == '~') { + whitelist = 0; + p++; } - r = sd_bus_message_close_container(m); + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_close_container(m); + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); - } else if (streq(field, "SupplementaryGroups")) { - const char *p; + r = sd_bus_message_open_container(m, 'v', "(bas)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'r', "bas"); + if (r < 0) + return bus_log_create_error(r); - r = sd_bus_message_open_container(m, 'v', "as"); + r = sd_bus_message_append_basic(m, 'b', &whitelist); if (r < 0) return bus_log_create_error(r); @@ -1037,15 +986,12 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen _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: %m", field, eq); if (r == 0) break; - - if (!valid_user_group_name_or_id(word)) { - log_error("Failed to parse %s value %s", field, eq); - return -EINVAL; - } + if (r == -ENOMEM) + return log_oom(); + if (r < 0) + return log_error_errno(r, "Invalid syntax: %s", eq); r = sd_bus_message_append_basic(m, 's', word); if (r < 0) @@ -1057,50 +1003,21 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen return bus_log_create_error(r); r = sd_bus_message_close_container(m); - - } else if (STR_IN_SET(field, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) { - mode_t mode; - - r = parse_mode(eq, &mode); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s", field, eq); - - r = sd_bus_message_append(m, "v", "u", mode); - - } else if (STR_IN_SET(field, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) { - 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"); + r = sd_bus_message_close_container(m); if (r < 0) return bus_log_create_error(r); - for (p = eq;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); - if (r == -ENOMEM) - return log_oom(); - 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); + return 1; + } - } else if (streq(field, "RestrictNamespaces")) { + if (streq(field, "RestrictNamespaces")) { bool invert = false; unsigned long flags = 0; @@ -1123,27 +1040,31 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen if (invert) flags = (~flags) & NAMESPACE_FLAGS_ALL; - r = sd_bus_message_append(m, "v", "t", (uint64_t) flags); - } else if ((dep = unit_dependency_from_string(field)) >= 0) - r = sd_bus_message_append(m, "v", "as", 1, eq); - else if (streq(field, "MountFlags")) { - unsigned long f; - - r = mount_propagation_flags_from_string(eq, &f); + r = sd_bus_message_append(m, "(sv)", field, "t", (uint64_t) flags); if (r < 0) - return log_error_errno(r, "Failed to parse mount propagation flags: %s", eq); + return bus_log_create_error(r); - r = sd_bus_message_append(m, "v", "t", (uint64_t) f); - } else if (STR_IN_SET(field, "BindPaths", "BindReadOnlyPaths")) { + return 1; + } + + if (STR_IN_SET(field, "BindPaths", "BindReadOnlyPaths")) { const char *p = eq; + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); + r = sd_bus_message_open_container(m, 'v', "a(ssbt)"); if (r < 0) - return r; + return bus_log_create_error(r); r = sd_bus_message_open_container(m, 'a', "(ssbt)"); if (r < 0) - return r; + return bus_log_create_error(r); for (;;) { _cleanup_free_ char *source = NULL, *destination = NULL; @@ -1196,137 +1117,528 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags); if (r < 0) - return r; + return bus_log_create_error(r); } r = sd_bus_message_close_container(m); if (r < 0) - return r; + return bus_log_create_error(r); r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); - } else if (STR_IN_SET(field, "ExecStartPre", "ExecStart", "ExecStartPost", - "ExecReload", "ExecStop", "ExecStopPost")) { + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); - bool ignore_failure = false, explicit_path = false, done = false; - _cleanup_strv_free_ char **l = NULL; - _cleanup_free_ char *path = NULL; + return 1; + } - do { - switch (*eq) { + return 0; +} - case '-': - if (ignore_failure) - done = true; - else { - ignore_failure = true; - eq++; - } - break; +static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) { - case '@': - if (explicit_path) - done = true; - else { - explicit_path = true; - eq++; - } - break; + if (streq(field, "KillMode")) - case '+': - case '!': - /* The bus API doesn't support +, ! and !! currently, unfortunately. :-( */ - log_error("Sorry, but +, ! and !! are currently not supported for transient services."); - return -EOPNOTSUPP; + return bus_append_string(m, field, eq); - default: - done = true; - break; - } - } while (!done); + if (STR_IN_SET(field, "SendSIGHUP", "SendSIGKILL")) - if (explicit_path) { - r = extract_first_word(&eq, &path, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); - if (r < 0) - return log_error_errno(r, "Failed to parse path: %m"); - } + return bus_append_parse_boolean(m, field, eq); - r = strv_split_extract(&l, eq, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); - if (r < 0) - return log_error_errno(r, "Failed to parse command line: %m"); + if (streq(field, "KillSignal")) + + return bus_append_signal_from_string_try_harder(m, field, eq); + + return 0; +} + +static int bus_append_mount_property(sd_bus_message *m, const char *field, const char *eq) { + + if (STR_IN_SET(field, "What", "Where", "Options", "Type")) + + return bus_append_string(m, field, eq); + + if (streq(field, "TimeoutSec")) + + return bus_append_parse_sec_rename(m, field, eq); + + if (streq(field, "DirectoryMode")) + + return bus_append_parse_mode(m, field, eq); + + if (STR_IN_SET(field, "SloppyOptions", "LazyUnmount", "ForceUnmount")) + + return bus_append_parse_boolean(m, field, eq); + + return 0; +} + +static int bus_append_path_property(sd_bus_message *m, const char *field, const char *eq) { + int r; + + if (streq(field, "MakeDirectory")) + + return bus_append_parse_boolean(m, field, eq); + + if (streq(field, "DirectoryMode")) + + return bus_append_parse_mode(m, field, eq); + + if (STR_IN_SET(field, + "PathExists", "PathExistsGlob", "PathChanged", + "PathModified", "DirectoryNotEmpty")) { - r = sd_bus_message_open_container(m, 'v', "a(sasb)"); + if (isempty(eq)) + r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 0); + else + r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 1, field, eq); if (r < 0) - return r; + return bus_log_create_error(r); + + return 1; + } + + return 0; +} + +static int bus_append_service_property(sd_bus_message *m, const char *field, const char *eq) { + int r; + + if (STR_IN_SET(field, + "PIDFile", "Type", "Restart", "BusName", "NotifyAccess", + "USBFunctionDescriptors", "USBFunctionStrings")) + + return bus_append_string(m, field, eq); + + if (STR_IN_SET(field, "PermissionsStartOnly", "RootDirectoryStartOnly", "RemainAfterExit", "GuessMainPID")) + + return bus_append_parse_boolean(m, field, eq); - r = sd_bus_message_open_container(m, 'a', "(sasb)"); + if (STR_IN_SET(field, "RestartSec", "TimeoutStartSec", "TimeoutStopSec", "RuntimeMaxSec", "WatchdogSec")) + + return bus_append_parse_sec_rename(m, field, eq); + + if (streq(field, "TimeoutSec")) { + + r = bus_append_parse_sec_rename(m, "TimeoutStartSec", eq); if (r < 0) return r; - if (strv_length(l) > 0) { + return bus_append_parse_sec_rename(m, "TimeoutStopSec", eq); + } - r = sd_bus_message_open_container(m, 'r', "sasb"); - if (r < 0) - return r; + if (streq(field, "FileDescriptorStoreMax")) - r = sd_bus_message_append(m, "s", path ?: l[0]); - if (r < 0) - return r; + return bus_append_safe_atou(m, field, eq); - r = sd_bus_message_append_strv(m, l); - if (r < 0) - return r; + if (STR_IN_SET(field, + "ExecStartPre", "ExecStart", "ExecStartPost", + "ExecReload", "ExecStop", "ExecStopPost")) - r = sd_bus_message_append(m, "b", ignore_failure); - if (r < 0) - return r; + return bus_append_exec_command(m, field, eq); + + if (STR_IN_SET(field, "RestartPreventExitStatus", "RestartForceExitStatus", "SuccessExitStatus")) { + _cleanup_free_ int *status = NULL, *signal = NULL; + size_t sz_status = 0, sz_signal = 0; + const char *p; - r = sd_bus_message_close_container(m); + for (p = eq;;) { + _cleanup_free_ char *word = NULL; + int val; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); + if (r == 0) + break; + if (r == -ENOMEM) + return log_oom(); if (r < 0) - return r; + return log_error_errno(r, "Invalid syntax in %s: %s", field, eq); + + r = safe_atoi(word, &val); + if (r < 0) { + val = signal_from_string_try_harder(word); + if (val < 0) + return log_error_errno(r, "Invalid status or signal %s in %s: %m", word, field); + + signal = realloc_multiply(signal, sizeof(int), sz_signal + 1); + if (!signal) + return log_oom(); + + signal[sz_signal++] = val; + } else { + status = realloc_multiply(status, sizeof(int), sz_status + 1); + if (!status) + return log_oom(); + + status[sz_status++] = val; + } } + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', "(aiai)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'r', "aiai"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_array(m, 'i', status, sz_status); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_array(m, 'i', signal, sz_signal); + if (r < 0) + return bus_log_create_error(r); + r = sd_bus_message_close_container(m); if (r < 0) - return r; + 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); + if (r < 0) + return bus_log_create_error(r); + + return 1; + } + + return 0; +} + +static int bus_append_socket_property(sd_bus_message *m, const char *field, const char *eq) { + int r; + + if (STR_IN_SET(field, + "Accept", "Writable", "KeepAlive", "NoDelay", "FreeBind", "Transparent", "Broadcast", + "PassCredentials", "PassSecurity", "ReusePort", "RemoveOnStop", "SELinuxContextFromNet")) + + return bus_append_parse_boolean(m, field, eq); + + if (STR_IN_SET(field, "Priority", "IPTTL", "Mark")) + + return bus_append_safe_atoi(m, field, eq); + + if (streq(field, "IPTOS")) + + return bus_append_ip_tos_from_string(m, field, eq); + + if (STR_IN_SET(field, "Backlog", "MaxConnections", "MaxConnectionsPerSource", "KeepAliveProbes", "TriggerLimitBurst")) - } else if (STR_IN_SET(field, - "OnActiveSec", "OnBootSec", "OnStartupSec", - "OnUnitActiveSec","OnUnitInactiveSec")) { - usec_t t; + return bus_append_safe_atou(m, field, eq); - r = parse_sec(eq, &t); + if (STR_IN_SET(field, "SocketMode", "DirectoryMode")) + + return bus_append_parse_mode(m, field, eq); + + if (STR_IN_SET(field, "MessageQueueMaxMessages", "MessageQueueMessageSize")) + + return bus_append_safe_atoi64(m, field, eq); + + if (STR_IN_SET(field, "TimeoutSec", "KeepAliveTimeSec", "KeepAliveIntervalSec", "DeferAcceptSec", "TriggerLimitIntervalSec")) + + return bus_append_parse_sec_rename(m, field, eq); + + if (STR_IN_SET(field, "ReceiveBuffer", "SendBuffer", "PipeSize")) + + return bus_append_parse_size(m, field, eq, 1024); + + if (STR_IN_SET(field, "ExecStartPre", "ExecStartPost", "ExecReload", "ExecStopPost")) + + return bus_append_exec_command(m, field, eq); + + if (STR_IN_SET(field, + "SmackLabel", "SmackLabelIPIn", "SmackLabelIPOut", "TCPCongestion", + "BindToDevice", "BindIPv6Only", "FileDescriptorName", + "SocketUser", "SocketGroup")) + + return bus_append_string(m, field, eq); + + if (streq(field, "Symlinks")) + + return bus_append_strv(m, field, eq, EXTRACT_QUOTES); + + if (streq(field, "SocketProtocol")) + + return bus_append_socket_protocol_from_name(m, field, eq); + + if (STR_IN_SET(field, + "ListenStream", "ListenDatagram", "ListenSequentialPacket", "ListenNetlink", + "ListenSpecial", "ListenMessageQueue", "ListenFIFO", "ListenUSBFunction")) { + + if (isempty(eq)) + r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 0); + else + r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 1, field + STRLEN("Listen"), eq); if (r < 0) - return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq); + return bus_log_create_error(r); - r = sd_bus_message_append(m, "v", "t", t); + return 1; + } - } else { - log_error("Unknown assignment: %s", assignment); - return -EINVAL; + return 0; +} +static int bus_append_timer_property(sd_bus_message *m, const char *field, const char *eq) { + int r; + + if (STR_IN_SET(field, "WakeSystem", "RemainAfterElapse", "Persistent")) + + return bus_append_parse_boolean(m, field, eq); + + if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec")) + + return bus_append_parse_sec_rename(m, field, eq); + + if (STR_IN_SET(field, + "OnActiveSec", "OnBootSec", "OnStartupSec", + "OnUnitActiveSec","OnUnitInactiveSec")) { + + if (isempty(eq)) + r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 0); + else { + usec_t t; + r = parse_sec(eq, &t); + if (r < 0) + return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); + + r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 1, field, t); + } + if (r < 0) + return bus_log_create_error(r); + + return 1; } -finish: - if (r < 0) - return bus_log_create_error(r); + if (streq(field, "OnCalendar")) { - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); + if (isempty(eq)) + r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 0); + else + r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 1, field, eq); + if (r < 0) + return bus_log_create_error(r); + + return 1; + } + + return 0; +} + +static int bus_append_unit_property(sd_bus_message *m, const char *field, const char *eq) { + ConditionType t = _CONDITION_TYPE_INVALID; + bool is_condition = false; + int r; + + if (STR_IN_SET(field, + "Description", "SourcePath", "OnFailureJobMode", + "JobTimeoutAction", "JobTimeoutRebootArgument", + "StartLimitAction", "FailureAction", "SuccessAction", + "RebootArgument", "CollectMode")) + + return bus_append_string(m, field, eq); + + if (STR_IN_SET(field, + "StopWhenUnneeded", "RefuseManualStart", "RefuseManualStop", + "AllowIsolate", "IgnoreOnIsolate", "DefaultDependencies")) + + return bus_append_parse_boolean(m, field, eq); + + if (STR_IN_SET(field, "JobTimeoutSec", "JobRunningTimeoutSec", "StartLimitIntervalSec")) + + return bus_append_parse_sec_rename(m, field, eq); + + if (streq(field, "StartLimitBurst")) + + return bus_append_safe_atou(m, field, eq); + + if (unit_dependency_from_string(field) >= 0 || + STR_IN_SET(field, "Documentation", "RequiresMountsFor")) + + return bus_append_strv(m, field, eq, EXTRACT_QUOTES); + + t = condition_type_from_string(field); + if (t >= 0) + is_condition = true; + else + t = assert_type_from_string(field); + if (t >= 0) { + if (isempty(eq)) + r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 0); + else { + const char *p = eq; + int trigger, negate; + + trigger = *p == '|'; + if (trigger) + p++; + + negate = *p == '!'; + if (negate) + p++; + + r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 1, + field, trigger, negate, p); + } + if (r < 0) + return bus_log_create_error(r); + + return 1; + } return 0; } -int bus_append_unit_property_assignment_many(sd_bus_message *m, char **l) { +int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const char *assignment) { + const char *eq, *field; + int r; + + assert(m); + assert(assignment); + + eq = strchr(assignment, '='); + if (!eq) { + log_error("Not an assignment: %s", assignment); + return -EINVAL; + } + + field = strndupa(assignment, eq - assignment); + eq++; + + switch (t) { + case UNIT_SERVICE: + r = bus_append_cgroup_property(m, field, eq); + if (r != 0) + return r; + + r = bus_append_execute_property(m, field, eq); + if (r != 0) + return r; + + r = bus_append_kill_property(m, field, eq); + if (r != 0) + return r; + + r = bus_append_service_property(m, field, eq); + if (r != 0) + return r; + break; + + case UNIT_SOCKET: + r = bus_append_cgroup_property(m, field, eq); + if (r != 0) + return r; + + r = bus_append_execute_property(m, field, eq); + if (r != 0) + return r; + + r = bus_append_kill_property(m, field, eq); + if (r != 0) + return r; + + r = bus_append_socket_property(m, field, eq); + if (r != 0) + return r; + break; + + case UNIT_TIMER: + r = bus_append_timer_property(m, field, eq); + if (r != 0) + return r; + break; + + case UNIT_PATH: + r = bus_append_path_property(m, field, eq); + if (r != 0) + return r; + break; + + case UNIT_SLICE: + r = bus_append_cgroup_property(m, field, eq); + if (r != 0) + return r; + break; + + case UNIT_SCOPE: + + if (streq(field, "TimeoutStopSec")) + return bus_append_parse_sec_rename(m, field, eq); + + r = bus_append_cgroup_property(m, field, eq); + if (r != 0) + return r; + + r = bus_append_kill_property(m, field, eq); + if (r != 0) + return r; + break; + + case UNIT_MOUNT: + r = bus_append_cgroup_property(m, field, eq); + if (r != 0) + return r; + + r = bus_append_execute_property(m, field, eq); + if (r != 0) + return r; + + r = bus_append_kill_property(m, field, eq); + if (r != 0) + return r; + + r = bus_append_mount_property(m, field, eq); + if (r != 0) + return r; + + break; + + case UNIT_AUTOMOUNT: + r = bus_append_automount_property(m, field, eq); + if (r != 0) + return r; + + break; + + case UNIT_TARGET: + case UNIT_DEVICE: + case UNIT_SWAP: + log_error("Not supported unit type"); + return -EINVAL; + + default: + log_error("Invalid unit type"); + return -EINVAL; + } + + r = bus_append_unit_property(m, field, eq); + if (r != 0) + return r; + + log_error("Unknown assignment: %s", assignment); + return -EINVAL; +} + +int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l) { char **i; int r; assert(m); STRV_FOREACH(i, l) { - r = bus_append_unit_property_assignment(m, *i); + r = bus_append_unit_property_assignment(m, t, *i); if (r < 0) return r; } @@ -1418,31 +1730,25 @@ int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) { /* 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( + r = sd_bus_match_signal_async( 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); + bus->bus_client ? "org.freedesktop.systemd1" : NULL, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "JobRemoved", + match_job_removed, NULL, d); if (r < 0) return r; - r = sd_bus_add_match( + r = sd_bus_match_signal_async( bus, &d->slot_disconnected, - "type='signal'," - "sender='org.freedesktop.DBus.Local'," - "interface='org.freedesktop.DBus.Local'," - "member='Disconnected'", - match_disconnected, d); + "org.freedesktop.DBus.Local", + NULL, + "org.freedesktop.DBus.Local", + "Disconnected", + match_disconnected, NULL, d); if (r < 0) return r; diff --git a/src/shared/bus-unit-util.h b/src/shared/bus-unit-util.h index 1a137e8b84..514e6edb76 100644 --- a/src/shared/bus-unit-util.h +++ b/src/shared/bus-unit-util.h @@ -20,10 +20,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "sd-bus.h" - -#include "output-mode.h" #include "install.h" +#include "output-mode.h" +#include "sd-bus.h" +#include "unit-def.h" typedef struct UnitInfo { const char *machine; @@ -41,8 +41,8 @@ typedef struct 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); -int bus_append_unit_property_assignment_many(sd_bus_message *m, char **l); +int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const char *assignment); +int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l); typedef struct BusWaitForJobs BusWaitForJobs; diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 7a185461a3..548e817105 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -68,7 +68,7 @@ static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_ } int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) { - _cleanup_free_ char *match = NULL; + const char *match; const char *unique; int r; @@ -85,23 +85,21 @@ int bus_async_unregister_and_exit(sd_event *e, sd_bus *bus, const char *name) { if (r < 0) return r; - r = asprintf(&match, - "sender='org.freedesktop.DBus'," - "type='signal'," - "interface='org.freedesktop.DBus'," - "member='NameOwnerChanged'," - "path='/org/freedesktop/DBus'," - "arg0='%s'," - "arg1='%s'," - "arg2=''", name, unique); - if (r < 0) - return -ENOMEM; - - r = sd_bus_add_match(bus, NULL, match, name_owner_change_callback, e); + match = strjoina( + "sender='org.freedesktop.DBus'," + "type='signal'," + "interface='org.freedesktop.DBus'," + "member='NameOwnerChanged'," + "path='/org/freedesktop/DBus'," + "arg0='", name, "',", + "arg1='", unique, "',", + "arg2=''"); + + r = sd_bus_add_match_async(bus, NULL, match, name_owner_change_callback, NULL, e); if (r < 0) return r; - r = sd_bus_release_name(bus, name); + r = sd_bus_release_name_async(bus, NULL, name, NULL, NULL); if (r < 0) return r; @@ -560,8 +558,7 @@ void bus_verify_polkit_async_registry_free(Hashmap *registry) { int bus_check_peercred(sd_bus *c) { struct ucred ucred; - socklen_t l; - int fd; + int fd, r; assert(c); @@ -569,12 +566,9 @@ int bus_check_peercred(sd_bus *c) { if (fd < 0) return fd; - l = sizeof(struct ucred); - if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0) - return -errno; - - if (l != sizeof(struct ucred)) - return -E2BIG; + r = getpeercred(fd, &ucred); + if (r < 0) + return r; if (ucred.uid != 0 && ucred.uid != geteuid()) return -EPERM; @@ -717,7 +711,7 @@ int bus_print_property(const char *name, sd_bus_message *property, bool value, b /* Yes, heuristics! But we can change this check * should it turn out to not be sufficient */ - if (endswith(name, "Timestamp")) { + if (endswith(name, "Timestamp") || STR_IN_SET(name, "NextElapseUSecRealtime", "LastTriggerUSec")) { char timestamp[FORMAT_TIMESTAMP_MAX], *t; t = format_timestamp(timestamp, sizeof(timestamp), u); @@ -1344,6 +1338,25 @@ int bus_property_get_bool( return sd_bus_message_append_basic(reply, 'b', &b); } +int bus_property_set_bool( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *value, + void *userdata, + sd_bus_error *error) { + + int b, r; + + r = sd_bus_message_read(value, "b", &b); + if (r < 0) + return r; + + *(bool *) userdata = !!b; + return 0; +} + int bus_property_get_id128( sd_bus *bus, const char *path, @@ -1604,3 +1617,54 @@ int bus_track_add_name_many(sd_bus_track *t, char **l) { return r; } + +int bus_open_system_watch_bind(sd_bus **ret) { + _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; + const char *e; + int r; + + assert(ret); + + /* Match like sd_bus_open_system(), but with the "watch_bind" feature and the Connected() signal turned on. */ + + r = sd_bus_new(&bus); + if (r < 0) + return r; + + e = secure_getenv("DBUS_SYSTEM_BUS_ADDRESS"); + if (!e) + e = DEFAULT_SYSTEM_BUS_ADDRESS; + + r = sd_bus_set_address(bus, e); + if (r < 0) + return r; + + r = sd_bus_set_bus_client(bus, true); + if (r < 0) + return r; + + r = sd_bus_set_trusted(bus, true); + if (r < 0) + return r; + + r = sd_bus_negotiate_creds(bus, true, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS); + if (r < 0) + return r; + + r = sd_bus_set_watch_bind(bus, true); + if (r < 0) + return r; + + r = sd_bus_set_connected_signal(bus, true); + if (r < 0) + return r; + + r = sd_bus_start(bus); + if (r < 0) + return r; + + *ret = bus; + bus = NULL; + + return 0; +} diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h index a9f4969d7d..969a444d83 100644 --- a/src/shared/bus-util.h +++ b/src/shared/bus-util.h @@ -80,6 +80,7 @@ int bus_print_property(const char *name, sd_bus_message *property, bool value, b 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); +int bus_property_set_bool(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, void *userdata, sd_bus_error *error); int bus_property_get_id128(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); #define bus_property_get_usec ((sd_bus_property_get_t) NULL) @@ -162,3 +163,5 @@ int bus_path_decode_unique(const char *path, const char *prefix, char **ret_send int bus_property_get_rlimit(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); int bus_track_add_name_many(sd_bus_track *t, char **l); + +int bus_open_system_watch_bind(sd_bus **ret); diff --git a/src/shared/condition.c b/src/shared/condition.c index 3f32dfb7b6..a2fd05c425 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -26,6 +26,7 @@ #include <string.h> #include <sys/stat.h> #include <sys/types.h> +#include <sys/utsname.h> #include <time.h> #include <unistd.h> @@ -36,6 +37,7 @@ #include "architecture.h" #include "audit-util.h" #include "cap-list.h" +#include "cgroup-util.h" #include "condition.h" #include "extract-word.h" #include "fd-util.h" @@ -142,6 +144,70 @@ static int condition_test_kernel_command_line(Condition *c) { return false; } +static int condition_test_kernel_version(Condition *c) { + enum { + /* Listed in order of checking. Note that some comparators are prefixes of others, hence the longest + * should be listed first. */ + LOWER_OR_EQUAL, + GREATER_OR_EQUAL, + LOWER, + GREATER, + EQUAL, + _ORDER_MAX, + }; + + static const char *const prefix[_ORDER_MAX] = { + [LOWER_OR_EQUAL] = "<=", + [GREATER_OR_EQUAL] = ">=", + [LOWER] = "<", + [GREATER] = ">", + [EQUAL] = "=", + }; + const char *p = NULL; + struct utsname u; + size_t i; + int k; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_KERNEL_VERSION); + + assert_se(uname(&u) >= 0); + + for (i = 0; i < _ORDER_MAX; i++) { + p = startswith(c->parameter, prefix[i]); + if (p) + break; + } + + /* No prefix? Then treat as glob string */ + if (!p) + return fnmatch(skip_leading_chars(c->parameter, NULL), u.release, 0) == 0; + + k = str_verscmp(u.release, skip_leading_chars(p, NULL)); + + switch (i) { + + case LOWER: + return k < 0; + + case LOWER_OR_EQUAL: + return k <= 0; + + case EQUAL: + return k == 0; + + case GREATER_OR_EQUAL: + return k >= 0; + + case GREATER: + return k > 0; + + default: + assert_not_reached("Can't compare"); + } +} + static int condition_test_user(Condition *c) { uid_t id; int r; @@ -177,6 +243,30 @@ static int condition_test_user(Condition *c) { return id == getuid() || id == geteuid(); } +static int condition_test_control_group_controller(Condition *c) { + int r; + CGroupMask system_mask, wanted_mask = 0; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_CONTROL_GROUP_CONTROLLER); + + r = cg_mask_supported(&system_mask); + if (r < 0) + return log_debug_errno(r, "Failed to determine supported controllers: %m"); + + r = cg_mask_from_string(c->parameter, &wanted_mask); + if (r < 0 || wanted_mask <= 0) { + /* This won't catch the case that we have an unknown controller + * mixed in with valid ones -- these are only assessed on the + * validity of the valid controllers found. */ + log_debug("Failed to parse cgroup string: %s", c->parameter); + return 1; + } + + return (system_mask & wanted_mask) == wanted_mask; +} + static int condition_test_group(Condition *c) { gid_t id; int r; @@ -527,6 +617,7 @@ int condition_test(Condition *c) { [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty, [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable, [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line, + [CONDITION_KERNEL_VERSION] = condition_test_kernel_version, [CONDITION_VIRTUALIZATION] = condition_test_virtualization, [CONDITION_SECURITY] = condition_test_security, [CONDITION_CAPABILITY] = condition_test_capability, @@ -537,6 +628,7 @@ int condition_test(Condition *c) { [CONDITION_FIRST_BOOT] = condition_test_first_boot, [CONDITION_USER] = condition_test_user, [CONDITION_GROUP] = condition_test_group, + [CONDITION_CONTROL_GROUP_CONTROLLER] = condition_test_control_group_controller, [CONDITION_NULL] = condition_test_null, }; @@ -561,8 +653,7 @@ void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_ assert(c); assert(f); - if (!prefix) - prefix = ""; + prefix = strempty(prefix); fprintf(f, "%s\t%s: %s%s%s %s\n", @@ -586,6 +677,7 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = { [CONDITION_VIRTUALIZATION] = "ConditionVirtualization", [CONDITION_HOST] = "ConditionHost", [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine", + [CONDITION_KERNEL_VERSION] = "ConditionKernelVersion", [CONDITION_SECURITY] = "ConditionSecurity", [CONDITION_CAPABILITY] = "ConditionCapability", [CONDITION_AC_POWER] = "ConditionACPower", @@ -602,6 +694,7 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = { [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable", [CONDITION_USER] = "ConditionUser", [CONDITION_GROUP] = "ConditionGroup", + [CONDITION_CONTROL_GROUP_CONTROLLER] = "ConditionControlGroupController", [CONDITION_NULL] = "ConditionNull" }; @@ -612,6 +705,7 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = { [CONDITION_VIRTUALIZATION] = "AssertVirtualization", [CONDITION_HOST] = "AssertHost", [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine", + [CONDITION_KERNEL_VERSION] = "AssertKernelVersion", [CONDITION_SECURITY] = "AssertSecurity", [CONDITION_CAPABILITY] = "AssertCapability", [CONDITION_AC_POWER] = "AssertACPower", @@ -628,6 +722,7 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = { [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable", [CONDITION_USER] = "AssertUser", [CONDITION_GROUP] = "AssertGroup", + [CONDITION_CONTROL_GROUP_CONTROLLER] = "AssertControlGroupController", [CONDITION_NULL] = "AssertNull" }; diff --git a/src/shared/condition.h b/src/shared/condition.h index 534906b6d6..a84d993370 100644 --- a/src/shared/condition.h +++ b/src/shared/condition.h @@ -31,6 +31,7 @@ typedef enum ConditionType { CONDITION_VIRTUALIZATION, CONDITION_HOST, CONDITION_KERNEL_COMMAND_LINE, + CONDITION_KERNEL_VERSION, CONDITION_SECURITY, CONDITION_CAPABILITY, CONDITION_AC_POWER, @@ -53,6 +54,8 @@ typedef enum ConditionType { CONDITION_USER, CONDITION_GROUP, + CONDITION_CONTROL_GROUP_CONTROLLER, + _CONDITION_TYPE_MAX, _CONDITION_TYPE_INVALID = -1 } ConditionType; @@ -96,3 +99,17 @@ ConditionType assert_type_from_string(const char *s) _pure_; const char* condition_result_to_string(ConditionResult r) _const_; ConditionResult condition_result_from_string(const char *s) _pure_; + +static inline bool condition_takes_path(ConditionType t) { + return IN_SET(t, + CONDITION_PATH_EXISTS, + CONDITION_PATH_EXISTS_GLOB, + CONDITION_PATH_IS_DIRECTORY, + CONDITION_PATH_IS_SYMBOLIC_LINK, + CONDITION_PATH_IS_MOUNT_POINT, + CONDITION_PATH_IS_READ_WRITE, + CONDITION_DIRECTORY_NOT_EMPTY, + CONDITION_FILE_NOT_EMPTY, + CONDITION_FILE_IS_EXECUTABLE, + CONDITION_NEEDS_UPDATE); +} diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index e8d7db8f0c..86114e3dd1 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -22,9 +22,12 @@ #include <sys/prctl.h> #include <sys/wait.h> +#include "sd-id128.h" + #include "architecture.h" #include "ask-password-api.h" #include "blkid-util.h" +#include "blockdev-util.h" #include "copy.h" #include "crypt-util.h" #include "def.h" @@ -38,6 +41,7 @@ #include "hostname-util.h" #include "id128-util.h" #include "linux-3.13/dm-ioctl.h" +#include "missing.h" #include "mount-util.h" #include "path-util.h" #include "process-util.h" @@ -351,7 +355,8 @@ int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectI /* Filter out weird MMC RPMB partitions, which cannot reasonably be read, see * https://github.com/systemd/systemd/issues/5806 */ sysname = udev_device_get_sysname(q); - if (sysname && startswith(sysname, "mmcblk") && endswith(sysname, "rpmb")) + if (sysname && startswith(sysname, "mmcblk") && + (endswith(sysname, "rpmb") || endswith(sysname, "boot0" ) || endswith(sysname, "boot1"))) continue; node = udev_device_get_devnode(q); @@ -1242,7 +1247,6 @@ int dissected_image_acquire_metadata(DissectedImage *m) { _cleanup_free_ char *hostname = NULL; unsigned n_meta_initialized = 0, k; int fds[2 * _META_MAX], r; - siginfo_t si; BLOCK_SIGNALS(SIGCHLD); @@ -1258,18 +1262,10 @@ int dissected_image_acquire_metadata(DissectedImage *m) { if (r < 0) goto finish; - child = raw_clone(SIGCHLD|CLONE_NEWNS); - if (child < 0) { - r = -errno; + r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_NEW_MOUNTNS, &child); + if (r < 0) goto finish; - } - - if (child == 0) { - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - + if (r == 0) { /* Make sure we never propagate to the host */ if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) _exit(EXIT_FAILURE); @@ -1364,15 +1360,12 @@ int dissected_image_acquire_metadata(DissectedImage *m) { } } - r = wait_for_terminate(child, &si); - if (r < 0) - goto finish; + r = wait_for_terminate_and_check("(sd-dissect)", child, 0); child = 0; - - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) { - r = -EPROTO; + if (r < 0) goto finish; - } + if (r != EXIT_SUCCESS) + return -EPROTO; free_and_replace(m->hostname, hostname); m->machine_id = machine_id; diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 53a1554a28..10e251ff09 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -22,6 +22,8 @@ #include <stdbool.h> +#include "sd-id128.h" + #include "macro.h" typedef struct DissectedImage DissectedImage; diff --git a/src/shared/efivars.h b/src/shared/efivars.h index 9a4880de7d..5b5951321c 100644 --- a/src/shared/efivars.h +++ b/src/shared/efivars.h @@ -20,6 +20,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#if ! ENABLE_EFI +#include <errno.h> +#endif #include <stdbool.h> #include <stddef.h> #include <stdint.h> diff --git a/src/shared/generator.c b/src/shared/generator.c index 3495a7ef7d..2b0a4ecdc8 100644 --- a/src/shared/generator.c +++ b/src/shared/generator.c @@ -19,6 +19,7 @@ ***/ #include <errno.h> +#include <stdio_ext.h> #include <unistd.h> #include "alloc-util.h" @@ -39,6 +40,39 @@ #include "unit-name.h" #include "util.h" +int generator_open_unit_file( + const char *dest, + const char *source, + const char *name, + FILE **file) { + + const char *unit; + FILE *f; + + unit = strjoina(dest, "/", name); + + f = fopen(unit, "wxe"); + if (!f) { + if (source && errno == EEXIST) + return log_error_errno(errno, + "Failed to create unit file %s, as it already exists. Duplicate entry in %s?", + unit, source); + else + return log_error_errno(errno, + "Failed to create unit file %s: %m", + unit); + } + + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + + fprintf(f, + "# Automatically generated by %s\n\n", + program_invocation_short_name); + + *file = f; + return 0; +} + int generator_add_symlink(const char *root, const char *dst, const char *dep_type, const char *src) { /* Adds a symlink from <dst>.<dep_type>.d/ to ../<src> */ diff --git a/src/shared/generator.h b/src/shared/generator.h index 32d1ad021c..e1c636a218 100644 --- a/src/shared/generator.h +++ b/src/shared/generator.h @@ -22,6 +22,12 @@ #include <stdio.h> +int generator_open_unit_file( + const char *dest, + const char *source, + const char *name, + FILE **file); + int generator_add_symlink(const char *root, const char *dst, const char *dep_type, const char *src); int generator_write_fsck_deps( diff --git a/src/shared/install.c b/src/shared/install.c index 05ccc586a9..026aa32302 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -2784,6 +2784,7 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres if (scope == UNIT_FILE_SYSTEM) r = conf_files_list(&files, ".preset", root_dir, 0, "/etc/systemd/system-preset", + "/run/systemd/system-preset", "/usr/local/lib/systemd/system-preset", "/usr/lib/systemd/system-preset", #if HAVE_SPLIT_USR @@ -2793,6 +2794,7 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres else if (scope == UNIT_FILE_GLOBAL) r = conf_files_list(&files, ".preset", root_dir, 0, "/etc/systemd/user-preset", + "/run/systemd/user-preset", "/usr/local/lib/systemd/user-preset", "/usr/lib/systemd/user-preset", NULL); diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index afc3dcd219..5609e42feb 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -166,8 +166,17 @@ static bool shall_print(const char *p, size_t l, OutputFlags flags) { return true; } -static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, OutputFlags flags, int priority, const char* message, size_t message_len) { - const char *color_on = "", *color_off = ""; +static bool print_multiline( + FILE *f, + unsigned prefix, + unsigned n_columns, + OutputFlags flags, + int priority, + const char* message, + size_t message_len, + size_t highlight[2]) { + + const char *color_on = "", *color_off = "", *highlight_on = ""; const char *pos, *end; bool ellipsized = false; int line = 0; @@ -176,9 +185,11 @@ static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, Output if (priority <= LOG_ERR) { color_on = ANSI_HIGHLIGHT_RED; color_off = ANSI_NORMAL; + highlight_on = ANSI_HIGHLIGHT; } else if (priority <= LOG_NOTICE) { color_on = ANSI_HIGHLIGHT; color_off = ANSI_NORMAL; + highlight_on = ANSI_HIGHLIGHT_RED; } } @@ -209,9 +220,28 @@ static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, Output if (flags & (OUTPUT_FULL_WIDTH | OUTPUT_SHOW_ALL) || (prefix + len + 1 < n_columns && !tail_line)) { - fprintf(f, "%*s%s%.*s%s\n", - continuation * prefix, "", - color_on, len, pos, color_off); + if (highlight && + (size_t) (pos - message) <= highlight[0] && + highlight[0] < (size_t) len) { + + fprintf(f, "%*s%s%.*s", + continuation * prefix, "", + color_on, (int) highlight[0], pos); + fprintf(f, "%s%.*s", + highlight_on, + (int) (MIN((size_t) len, highlight[1]) - highlight[0]), + pos + highlight[0]); + if ((size_t) len > highlight[1]) + fprintf(f, "%s%.*s", + color_on, + (int) (len - highlight[1]), + pos + highlight[1]); + fprintf(f, "%s\n", color_off); + + } else + fprintf(f, "%*s%s%.*s%s\n", + continuation * prefix, "", + color_on, len, pos, color_off); continue; } @@ -369,7 +399,8 @@ static int output_short( OutputMode mode, unsigned n_columns, OutputFlags flags, - Set *output_fields) { + Set *output_fields, + size_t highlight[2]) { int r; const void *data; @@ -390,6 +421,7 @@ static int output_short( PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len), PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len), }; + size_t highlight_shifted[] = {highlight ? highlight[0] : 0, highlight ? highlight[1] : 0}; assert(f); assert(j); @@ -421,7 +453,7 @@ static int output_short( } if (!(flags & OUTPUT_SHOW_ALL)) - strip_tab_ansi(&message, &message_len); + strip_tab_ansi(&message, &message_len, highlight_shifted); if (priority_len == 1 && *priority >= '0' && *priority <= '7') p = *priority - '0'; @@ -468,7 +500,9 @@ static int output_short( } else { fputs(": ", f); ellipsized |= - print_multiline(f, n + 2, n_columns, flags, p, message, message_len); + print_multiline(f, n + 2, n_columns, flags, p, + message, message_len, + highlight_shifted); } if (flags & OUTPUT_CATALOG) @@ -483,7 +517,8 @@ static int output_verbose( OutputMode mode, unsigned n_columns, OutputFlags flags, - Set *output_fields) { + Set *output_fields, + size_t highlight[2]) { const void *data; size_t length; @@ -561,7 +596,7 @@ static int output_verbose( (((length < PRINT_CHAR_THRESHOLD) || flags & OUTPUT_FULL_WIDTH) && utf8_is_printable(data, length))) { fprintf(f, " %s%.*s=", on, fieldlen, (const char*)data); - print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1); + print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1, NULL); fputs(off, f); } else { char bytes[FORMAT_BYTES_MAX]; @@ -590,7 +625,8 @@ static int output_export( OutputMode mode, unsigned n_columns, OutputFlags flags, - Set *output_fields) { + Set *output_fields, + size_t highlight[2]) { sd_id128_t boot_id; char sid[33]; @@ -661,6 +697,10 @@ static int output_export( fputc('\n', f); } + if (r == -EBADMSG) { + log_debug_errno(r, "Skipping message we can't read: %m"); + return 0; + } if (r < 0) return r; @@ -728,7 +768,8 @@ static int output_json( OutputMode mode, unsigned n_columns, OutputFlags flags, - Set *output_fields) { + Set *output_fields, + size_t highlight[2]) { uint64_t realtime, monotonic; _cleanup_free_ char *cursor = NULL; @@ -824,6 +865,11 @@ static int output_json( } } + if (r == -EBADMSG) { + log_debug_errno(r, "Skipping message we can't read: %m"); + return 0; + } + if (r < 0) return r; @@ -952,18 +998,29 @@ static int output_cat( OutputMode mode, unsigned n_columns, OutputFlags flags, - Set *output_fields) { + Set *output_fields, + size_t highlight[2]) { const void *data; size_t l; int r; + const char *highlight_on = "", *highlight_off = ""; assert(j); assert(f); + if (flags & OUTPUT_COLOR) { + highlight_on = ANSI_HIGHLIGHT_RED; + highlight_off = ANSI_NORMAL; + } + sd_journal_set_data_threshold(j, 0); r = sd_journal_get_data(j, "MESSAGE", &data, &l); + if (r == -EBADMSG) { + log_debug_errno(r, "Skipping message we can't read: %m"); + return 0; + } if (r < 0) { /* An entry without MESSAGE=? */ if (r == -ENOENT) @@ -974,7 +1031,17 @@ static int output_cat( assert(l >= 8); - fwrite((const char*) data + 8, 1, l - 8, f); + if (highlight && (flags & OUTPUT_COLOR)) { + assert(highlight[0] <= highlight[1]); + assert(highlight[1] <= l - 8); + + fwrite((const char*) data + 8, 1, highlight[0], f); + fwrite(highlight_on, 1, strlen(highlight_on), f); + fwrite((const char*) data + 8 + highlight[0], 1, highlight[1] - highlight[0], f); + fwrite(highlight_off, 1, strlen(highlight_off), f); + fwrite((const char*) data + 8 + highlight[1], 1, l - 8 - highlight[1], f); + } else + fwrite((const char*) data + 8, 1, l - 8, f); fputc('\n', f); return 0; @@ -986,7 +1053,8 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])( OutputMode mode, unsigned n_columns, OutputFlags flags, - Set *output_fields) = { + Set *output_fields, + size_t highlight[2]) = { [OUTPUT_SHORT] = output_short, [OUTPUT_SHORT_ISO] = output_short, @@ -1010,6 +1078,7 @@ int output_journal( unsigned n_columns, OutputFlags flags, char **output_fields, + size_t highlight[2], bool *ellipsized) { int ret; @@ -1030,7 +1099,7 @@ int output_journal( return ret; } - ret = output_funcs[mode](f, j, mode, n_columns, flags, fields); + ret = output_funcs[mode](f, j, mode, n_columns, flags, fields, highlight); if (ellipsized && ret > 0) *ellipsized = true; @@ -1112,7 +1181,7 @@ static int show_journal(FILE *f, line++; maybe_print_begin_newline(f, &flags); - r = output_journal(f, j, mode, n_columns, flags, NULL, ellipsized); + r = output_journal(f, j, mode, n_columns, flags, NULL, NULL, ellipsized); if (r < 0) return r; } @@ -1256,7 +1325,6 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) { _cleanup_close_pair_ int pair[2] = { -1, -1 }; _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1; pid_t pid, child; - siginfo_t si; char buf[37]; ssize_t k; int r; @@ -1278,11 +1346,10 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) { if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) return -errno; - child = fork(); - if (child < 0) - return -errno; - - if (child == 0) { + r = safe_fork("(sd-bootid)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child); + if (r < 0) + return r; + if (r == 0) { int fd; pair[0] = safe_close(pair[0]); @@ -1309,9 +1376,11 @@ static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) { pair[1] = safe_close(pair[1]); - r = wait_for_terminate(child, &si); - if (r < 0 || si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) - return r < 0 ? r : -EIO; + r = wait_for_terminate_and_check("(sd-bootid)", child, 0); + if (r < 0) + return r; + if (r != EXIT_SUCCESS) + return -EIO; k = recv(pair[0], buf, 36, 0); if (k != 36) @@ -1392,7 +1461,7 @@ int show_journal_by_unit( if (r < 0) return log_error_errno(r, "Failed to add unit matches: %m"); - if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) { + if (DEBUG_LOGGING) { _cleanup_free_ char *filter; filter = journal_make_match_string(j); diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h index eaa69b6e90..c876dcc46a 100644 --- a/src/shared/logs-show.h +++ b/src/shared/logs-show.h @@ -39,6 +39,7 @@ int output_journal( unsigned n_columns, OutputFlags flags, char **output_fields, + size_t highlight[2], bool *ellipsized); int add_match_this_boot(sd_journal *j, const char *machine); diff --git a/src/shared/loop-util.c b/src/shared/loop-util.c index 097de690e5..37b8479f88 100644 --- a/src/shared/loop-util.c +++ b/src/shared/loop-util.c @@ -18,6 +18,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <fcntl.h> #include <linux/loop.h> #include <sys/ioctl.h> diff --git a/src/shared/machine-pool.c b/src/shared/machine-pool.c index 167bcfad36..031d443e9e 100644 --- a/src/shared/machine-pool.c +++ b/src/shared/machine-pool.c @@ -42,6 +42,7 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "label.h" #include "lockfile-util.h" #include "log.h" #include "machine-pool.h" @@ -78,7 +79,6 @@ static int setup_machine_raw(uint64_t size, sd_bus_error *error) { _cleanup_close_ int fd = -1; struct statvfs ss; pid_t pid = 0; - siginfo_t si; int r; /* We want to be able to make use of btrfs-specific file @@ -122,20 +122,15 @@ static int setup_machine_raw(uint64_t size, sd_bus_error *error) { goto fail; } - pid = fork(); - if (pid < 0) { - r = sd_bus_error_set_errnof(error, errno, "Failed to fork mkfs.btrfs: %m"); + r = safe_fork("(mkfs)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid); + if (r < 0) { + sd_bus_error_set_errnof(error, r, "Failed to fork mkfs.btrfs: %m"); goto fail; } - - if (pid == 0) { + if (r == 0) { /* Child */ - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - fd = safe_close(fd); execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp, NULL); @@ -145,24 +140,19 @@ static int setup_machine_raw(uint64_t size, sd_bus_error *error) { _exit(EXIT_FAILURE); } - r = wait_for_terminate(pid, &si); - if (r < 0) { - sd_bus_error_set_errnof(error, r, "Failed to wait for mkfs.btrfs: %m"); - goto fail; - } - + r = wait_for_terminate_and_check("mkfs", pid, 0); pid = 0; - if (si.si_code != CLD_EXITED) { - r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs died abnormally."); + if (r < 0) { + sd_bus_error_set_errnof(error, r, "Failed to wait for mkfs.btrfs: %m"); goto fail; } - if (si.si_status == 99) { + if (r == 99) { r = sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing"); goto fail; } - if (si.si_status != 0) { - r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs failed with error code %i", si.si_status); + if (r != EXIT_SUCCESS) { + r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs failed with error code %i", r); goto fail; } diff --git a/src/shared/meson.build b/src/shared/meson.build index 06a944c49d..91fecf896c 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -56,8 +56,6 @@ shared_sources = ''' firewall-util.h fstab-util.c fstab-util.h - gcrypt-util.c - gcrypt-util.h generator.c generator.h gpt.h @@ -120,6 +118,7 @@ shared_sources = ''' volatile-util.h watchdog.c watchdog.h + wireguard-netlink.h '''.split() test_tables_h = files('test-tables.h') @@ -159,24 +158,25 @@ libshared_deps = [threads, libshared_sym_path = '@0@/libshared.sym'.format(meson.current_source_dir()) -libshared = shared_library( +libshared_static = static_library( libshared_name, shared_sources, - basic_sources, - journal_internal_sources, - libsystemd_internal_sources, + include_directories : includes, + dependencies : libshared_deps, + c_args : ['-fvisibility=default']) + +libshared = shared_library( + libshared_name, libudev_sources, include_directories : includes, link_args : ['-shared', - '-Wl,--version-script=' + libshared_sym_path], + '-Wl,--version-script=' + libshared_sym_path], + link_whole : [libshared_static, + libbasic, + libbasic_gcrypt, + libsystemd_static, + libjournal_client], c_args : ['-fvisibility=default'], dependencies : libshared_deps, install : true, install_dir : rootlibexecdir) - -libshared_static = static_library( - libshared_name, - shared_sources, - basic_sources, - include_directories : includes, - dependencies : libshared_deps) diff --git a/src/shared/nsflags.h b/src/shared/nsflags.h index dcac6cd0b2..51bc590621 100644 --- a/src/shared/nsflags.h +++ b/src/shared/nsflags.h @@ -42,6 +42,13 @@ unsigned long namespace_flag_from_string(const char *name); int namespace_flag_from_string_many(const char *name, unsigned long *ret); int namespace_flag_to_string_many(unsigned long flags, char **ret); +static inline int namespace_flag_to_string_many_with_check(unsigned long n, char **s) { + if ((n & NAMESPACE_FLAGS_ALL) != n) + return -EINVAL; + + return namespace_flag_to_string_many(n, s); +} + struct namespace_flag_map { unsigned long flag; const char *name; diff --git a/src/shared/pager.c b/src/shared/pager.c index 39997278f1..75db3c985b 100644 --- a/src/shared/pager.c +++ b/src/shared/pager.c @@ -42,6 +42,11 @@ static pid_t pager_pid = 0; +static int stored_stdout = -1; +static int stored_stderr = -1; +static bool stdout_redirected = false; +static bool stderr_redirected = false; + noreturn static void pager_fallback(void) { int r; @@ -54,15 +59,10 @@ noreturn static void pager_fallback(void) { _exit(EXIT_SUCCESS); } -static int stored_stdout = -1; -static int stored_stderr = -1; -static bool stdout_redirected = false; -static bool stderr_redirected = false; - int pager_open(bool no_pager, bool jump_to_end) { _cleanup_close_pair_ int fd[2] = { -1, -1 }; const char *pager; - pid_t parent_pid; + int r; if (no_pager) return 0; @@ -73,6 +73,9 @@ int pager_open(bool no_pager, bool jump_to_end) { if (terminal_is_dumb()) return 0; + if (!is_main_thread()) + return -EPERM; + pager = getenv("SYSTEMD_PAGER"); if (!pager) pager = getenv("PAGER"); @@ -89,18 +92,13 @@ int pager_open(bool no_pager, bool jump_to_end) { if (pipe2(fd, O_CLOEXEC) < 0) return log_error_errno(errno, "Failed to create pager pipe: %m"); - parent_pid = getpid_cached(); - - pager_pid = fork(); - if (pager_pid < 0) - return log_error_errno(errno, "Failed to fork pager: %m"); - - /* In the child start the pager */ - if (pager_pid == 0) { + r = safe_fork("(pager)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pager_pid); + if (r < 0) + return r; + if (r == 0) { const char* less_opts, *less_charset; - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); + /* In the child start the pager */ (void) dup2(fd[0], STDIN_FILENO); safe_close_pair(fd); @@ -124,15 +122,6 @@ int pager_open(bool no_pager, bool jump_to_end) { setenv("LESSCHARSET", less_charset, 1) < 0) _exit(EXIT_FAILURE); - /* Make sure the pager goes away when the parent dies */ - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) - _exit(EXIT_FAILURE); - - /* Check whether our parent died before we were able - * to set the death signal */ - if (getppid() != parent_pid) - _exit(EXIT_SUCCESS); - if (pager) { execlp(pager, pager, NULL); execl("/bin/sh", "sh", "-c", pager, NULL); @@ -204,7 +193,6 @@ int show_man_page(const char *desc, bool null_stdio) { pid_t pid; size_t k; int r; - siginfo_t status; k = strlen(desc); @@ -222,33 +210,15 @@ int show_man_page(const char *desc, bool null_stdio) { } else args[1] = desc; - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork: %m"); - - if (pid == 0) { + r = safe_fork("(man)", FORK_RESET_SIGNALS|FORK_DEATHSIG|(null_stdio ? FORK_NULL_STDIO : 0)|FORK_LOG, &pid); + if (r < 0) + return r; + if (r == 0) { /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - if (null_stdio) { - r = make_null_stdio(); - if (r < 0) { - log_error_errno(r, "Failed to kill stdio: %m"); - _exit(EXIT_FAILURE); - } - } - execvp(args[0], (char**) args); log_error_errno(errno, "Failed to execute man: %m"); _exit(EXIT_FAILURE); } - r = wait_for_terminate(pid, &status); - if (r < 0) - return r; - - log_debug("Exit code %i status %i", status.si_code, status.si_status); - return status.si_status; + return wait_for_terminate_and_check(NULL, pid, 0); } diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index 62742858c7..fbb232cd45 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -950,11 +950,70 @@ int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, u return 0; } +int seccomp_parse_syscall_filter_internal( + bool invert, + const char *name, + int errno_num, + Hashmap *filter, + bool whitelist, + bool warn, + const char *unit, + const char *filename, + unsigned line) { + + int r; + + assert(name); + assert(filter); + + if (name[0] == '@') { + const SyscallFilterSet *set; + const char *i; + + set = syscall_filter_set_find(name); + if (!set) { + if (warn) { + log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown system call group, ignoring: %s", name); + return 0; + } else + return -EINVAL; + } + + NULSTR_FOREACH(i, set->value) { + r = seccomp_parse_syscall_filter_internal(invert, i, errno_num, filter, whitelist, warn, unit, filename, line); + if (r < 0) + return r; + } + } else { + int id; + + id = seccomp_syscall_resolve_name(name); + if (id == __NR_SCMP_ERROR) { + if (warn) { + log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse system call, ignoring: %s", name); + return 0; + } else + return -EINVAL; + } + + /* If we previously wanted to forbid a syscall and now + * we want to allow it, then remove it from the list. */ + if (!invert == whitelist) { + r = hashmap_put(filter, INT_TO_PTR(id + 1), INT_TO_PTR(errno_num)); + if (r < 0) + return warn ? log_oom() : -ENOMEM; + } else + (void) hashmap_remove(filter, INT_TO_PTR(id + 1)); + } + + return 0; +} + int seccomp_restrict_namespaces(unsigned long retain) { uint32_t arch; int r; - if (log_get_max_level() >= LOG_DEBUG) { + if (DEBUG_LOGGING) { _cleanup_free_ char *s = NULL; (void) namespace_flag_to_string_many(retain, &s); @@ -1386,6 +1445,7 @@ int seccomp_memory_deny_write_execute(void) { block_syscall = SCMP_SYS(mmap); break; + case SCMP_ARCH_PPC: case SCMP_ARCH_PPC64: case SCMP_ARCH_PPC64LE: filter_syscall = SCMP_SYS(mmap); @@ -1410,7 +1470,7 @@ int seccomp_memory_deny_write_execute(void) { /* Please add more definitions here, if you port systemd to other architectures! */ -#if !defined(__i386__) && !defined(__x86_64__) && !defined(__powerpc64__) && !defined(__arm__) && !defined(__aarch64__) +#if !defined(__i386__) && !defined(__x86_64__) && !defined(__powerpc__) && !defined(__powerpc64__) && !defined(__arm__) && !defined(__aarch64__) #warning "Consider adding the right mmap() syscall definitions here!" #endif } diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h index ad2ab7f6b5..0b30cdf388 100644 --- a/src/shared/seccomp-util.h +++ b/src/shared/seccomp-util.h @@ -81,6 +81,24 @@ int seccomp_add_syscall_filter_item(scmp_filter_ctx *ctx, const char *name, uint int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action); int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, uint32_t action); +int seccomp_parse_syscall_filter_internal( + bool invert, const char *name, int errno_num, Hashmap *filter, bool whitelist, + bool warn, const char *unit, const char *filename, unsigned line); + +static inline int seccomp_parse_syscall_filter_and_warn( + bool invert, const char *name, int errno_num, Hashmap *filter, bool whitelist, + const char *unit, const char *filename, unsigned line) { + assert(unit); + assert(filename); + + return seccomp_parse_syscall_filter_internal(invert, name, errno_num, filter, whitelist, true, unit, filename, line); +} + +static inline int seccomp_parse_syscall_filter( + bool invert, const char *name, int errno_num, Hashmap *filter, bool whitelist) { + return seccomp_parse_syscall_filter_internal(invert, name, errno_num, filter, whitelist, false, NULL, NULL, 0); +} + int seccomp_restrict_archs(Set *archs); int seccomp_restrict_namespaces(unsigned long retain); int seccomp_protect_sysctl(void); diff --git a/src/shared/spawn-ask-password-agent.c b/src/shared/spawn-ask-password-agent.c index 9af5faa3dd..f78167c25c 100644 --- a/src/shared/spawn-ask-password-agent.c +++ b/src/shared/spawn-ask-password-agent.c @@ -40,8 +40,12 @@ int ask_password_agent_open(void) { if (!isatty(STDIN_FILENO)) return 0; - r = fork_agent(&agent_pid, + if (!is_main_thread()) + return -EPERM; + + r = fork_agent("(sd-askpwagent)", NULL, 0, + &agent_pid, SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL); if (r < 0) @@ -56,8 +60,7 @@ void ask_password_agent_close(void) { return; /* Inform agent that we are done */ - (void) kill(agent_pid, SIGTERM); - (void) kill(agent_pid, SIGCONT); + (void) kill_and_sigcont(agent_pid, SIGTERM); (void) wait_for_terminate(agent_pid, NULL); agent_pid = 0; } diff --git a/src/shared/spawn-polkit-agent.c b/src/shared/spawn-polkit-agent.c index 423069fb0e..3c93c79ceb 100644 --- a/src/shared/spawn-polkit-agent.c +++ b/src/shared/spawn-polkit-agent.c @@ -38,9 +38,8 @@ static pid_t agent_pid = 0; int polkit_agent_open(void) { - int r; - int pipe_fd[2]; char notify_fd[DECIMAL_STR_MAX(int) + 1]; + int pipe_fd[2], r; if (agent_pid > 0) return 0; @@ -49,18 +48,21 @@ int polkit_agent_open(void) { if (geteuid() == 0) return 0; - /* We check STDIN here, not STDOUT, since this is about input, - * not output */ + /* We check STDIN here, not STDOUT, since this is about input, not output */ if (!isatty(STDIN_FILENO)) return 0; + if (!is_main_thread()) + return -EPERM; + if (pipe2(pipe_fd, 0) < 0) return -errno; xsprintf(notify_fd, "%i", pipe_fd[1]); - r = fork_agent(&agent_pid, + r = fork_agent("(polkit-agent)", &pipe_fd[1], 1, + &agent_pid, POLKIT_AGENT_BINARY_PATH, POLKIT_AGENT_BINARY_PATH, "--notify-fd", notify_fd, "--fallback", NULL); @@ -84,9 +86,7 @@ void polkit_agent_close(void) { return; /* Inform agent that we are done */ - (void) kill(agent_pid, SIGTERM); - (void) kill(agent_pid, SIGCONT); - + (void) kill_and_sigcont(agent_pid, SIGTERM); (void) wait_for_terminate(agent_pid, NULL); agent_pid = 0; } diff --git a/src/shared/sysctl-util.c b/src/shared/sysctl-util.c index 189580e3ed..0bc81aaa56 100644 --- a/src/shared/sysctl-util.c +++ b/src/shared/sysctl-util.c @@ -18,9 +18,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> +#include <fcntl.h> #include <stdio.h> #include <string.h> +#include <unistd.h> +#include "fd-util.h" #include "fileio.h" #include "log.h" #include "macro.h" @@ -53,6 +57,7 @@ char *sysctl_normalize(char *s) { int sysctl_write(const char *property, const char *value) { char *p; + _cleanup_close_ int fd = -1; assert(property); assert(value); @@ -60,7 +65,17 @@ int sysctl_write(const char *property, const char *value) { log_debug("Setting '%s' to '%s'", property, value); p = strjoina("/proc/sys/", property); - return write_string_file(p, value, WRITE_STRING_FILE_DISABLE_BUFFER); + fd = open(p, O_WRONLY|O_CLOEXEC); + if (fd < 0) + return -errno; + + if (!endswith(value, "\n")) + value = strjoina(value, "\n"); + + if (write(fd, value, strlen(value)) < 0) + return -errno; + + return 0; } int sysctl_read(const char *property, char **content) { diff --git a/src/shared/test-tables.h b/src/shared/test-tables.h index 6b223b1ee5..dd8bf0b582 100644 --- a/src/shared/test-tables.h +++ b/src/shared/test-tables.h @@ -20,6 +20,7 @@ #include <stdio.h> #include <stdlib.h> +#include <string.h> typedef const char* (*lookup_t)(int); typedef int (*reverse_t)(const char*); diff --git a/src/shared/tests.c b/src/shared/tests.c index d78ab7b069..d4781acabf 100644 --- a/src/shared/tests.c +++ b/src/shared/tests.c @@ -48,7 +48,7 @@ const char* get_testdata_dir(const char *suffix) { if (env) { if (access(env, F_OK) < 0) { fputs("ERROR: $SYSTEMD_TEST_DATA directory does not exist\n", stderr); - exit(1); + exit(EXIT_FAILURE); } strncpy(testdir, env, sizeof(testdir) - 1); } else { @@ -65,7 +65,7 @@ const char* get_testdata_dir(const char *suffix) { /* test this without the suffix, as it may contain a glob */ if (access(testdir, F_OK) < 0) { fputs("ERROR: Cannot find testdata directory, set $SYSTEMD_TEST_DATA\n", stderr); - exit(1); + exit(EXIT_FAILURE); } } diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c index 1715c0fb24..cab1cd6a2d 100644 --- a/src/shared/utmp-wtmp.c +++ b/src/shared/utmp-wtmp.c @@ -330,7 +330,7 @@ static int write_to_terminal(const char *tty, const char *message) { assert(tty); assert(message); - fd = open(tty, O_WRONLY|O_NDELAY|O_NOCTTY|O_CLOEXEC); + fd = open(tty, O_WRONLY|O_NONBLOCK|O_NOCTTY|O_CLOEXEC); if (fd < 0 || !isatty(fd)) return -errno; diff --git a/src/shared/volatile-util.c b/src/shared/volatile-util.c index 85512d00af..c92ad0adc0 100644 --- a/src/shared/volatile-util.c +++ b/src/shared/volatile-util.c @@ -18,6 +18,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> + #include "alloc-util.h" #include "macro.h" #include "parse-util.h" diff --git a/src/shared/wireguard-netlink.h b/src/shared/wireguard-netlink.h new file mode 100644 index 0000000000..eb170915a6 --- /dev/null +++ b/src/shared/wireguard-netlink.h @@ -0,0 +1,179 @@ +#pragma once + +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR MIT) + * + * Copyright (C) 2015-2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. + * + * Documentation + * ============= + * + * The below enums and macros are for interfacing with WireGuard, using generic + * netlink, with family WG_GENL_NAME and version WG_GENL_VERSION. It defines two + * methods: get and set. Note that while they share many common attributes, these + * two functions actually accept a slightly different set of inputs and outputs. + * + * WG_CMD_GET_DEVICE + * ----------------- + * + * May only be called via NLM_F_REQUEST | NLM_F_DUMP. The command should contain + * one but not both of: + * + * WGDEVICE_A_IFINDEX: NLA_U32 + * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1 + * + * The kernel will then return several messages (NLM_F_MULTI) containing the following + * tree of nested items: + * + * WGDEVICE_A_IFINDEX: NLA_U32 + * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1 + * WGDEVICE_A_PRIVATE_KEY: len WG_KEY_LEN + * WGDEVICE_A_PUBLIC_KEY: len WG_KEY_LEN + * WGDEVICE_A_LISTEN_PORT: NLA_U16 + * WGDEVICE_A_FWMARK: NLA_U32 + * WGDEVICE_A_PEERS: NLA_NESTED + * 0: NLA_NESTED + * WGPEER_A_PUBLIC_KEY: len WG_KEY_LEN + * WGPEER_A_PRESHARED_KEY: len WG_KEY_LEN + * WGPEER_A_ENDPOINT: struct sockaddr_in or struct sockaddr_in6 + * WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL: NLA_U16 + * WGPEER_A_LAST_HANDSHAKE_TIME: struct timespec + * WGPEER_A_RX_BYTES: NLA_U64 + * WGPEER_A_TX_BYTES: NLA_U64 + * WGPEER_A_ALLOWEDIPS: NLA_NESTED + * 0: NLA_NESTED + * WGALLOWEDIP_A_FAMILY: NLA_U16 + * WGALLOWEDIP_A_IPADDR: struct in_addr or struct in6_addr + * WGALLOWEDIP_A_CIDR_MASK: NLA_U8 + * 1: NLA_NESTED + * ... + * 2: NLA_NESTED + * ... + * ... + * 1: NLA_NESTED + * ... + * ... + * + * It is possible that all of the allowed IPs of a single peer will not + * fit within a single netlink message. In that case, the same peer will + * be written in the following message, except it will only contain + * WGPEER_A_PUBLIC_KEY and WGPEER_A_ALLOWEDIPS. This may occur several + * times in a row for the same peer. It is then up to the receiver to + * coalesce adjacent peers. Likewise, it is possible that all peers will + * not fit within a single message. So, subsequent peers will be sent + * in following messages, except those will only contain WGDEVICE_A_IFNAME + * and WGDEVICE_A_PEERS. It is then up to the receiver to coalesce these + * messages to form the complete list of peers. + * + * Since this is an NLA_F_DUMP command, the final message will always be + * NLMSG_DONE, even if an error occurs. However, this NLMSG_DONE message + * contains an integer error code. It is either zero or a negative error + * code corresponding to the errno. + * + * WG_CMD_SET_DEVICE + * ----------------- + * + * May only be called via NLM_F_REQUEST. The command should contain the following + * tree of nested items, containing one but not both of WGDEVICE_A_IFINDEX + * and WGDEVICE_A_IFNAME: + * + * WGDEVICE_A_IFINDEX: NLA_U32 + * WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1 + * WGDEVICE_A_FLAGS: NLA_U32, 0 or WGDEVICE_F_REPLACE_PEERS if all current + * peers should be removed prior to adding the list below. + * WGDEVICE_A_PRIVATE_KEY: len WG_KEY_LEN, all zeros to remove + * WGDEVICE_A_LISTEN_PORT: NLA_U16, 0 to choose randomly + * WGDEVICE_A_FWMARK: NLA_U32, 0 to disable + * WGDEVICE_A_PEERS: NLA_NESTED + * 0: NLA_NESTED + * WGPEER_A_PUBLIC_KEY: len WG_KEY_LEN + * WGPEER_A_FLAGS: NLA_U32, 0 and/or WGPEER_F_REMOVE_ME if the specified peer + * should be removed rather than added/updated and/or + * WGPEER_F_REPLACE_ALLOWEDIPS if all current allowed IPs of + * this peer should be removed prior to adding the list below. + * WGPEER_A_PRESHARED_KEY: len WG_KEY_LEN, all zeros to remove + * WGPEER_A_ENDPOINT: struct sockaddr_in or struct sockaddr_in6 + * WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL: NLA_U16, 0 to disable + * WGPEER_A_ALLOWEDIPS: NLA_NESTED + * 0: NLA_NESTED + * WGALLOWEDIP_A_FAMILY: NLA_U16 + * WGALLOWEDIP_A_IPADDR: struct in_addr or struct in6_addr + * WGALLOWEDIP_A_CIDR_MASK: NLA_U8 + * 1: NLA_NESTED + * ... + * 2: NLA_NESTED + * ... + * ... + * 1: NLA_NESTED + * ... + * ... + * + * It is possible that the amount of configuration data exceeds that of + * the maximum message length accepted by the kernel. In that case, + * several messages should be sent one after another, with each + * successive one filling in information not contained in the prior. Note + * that if WGDEVICE_F_REPLACE_PEERS is specified in the first message, it + * probably should not be specified in fragments that come after, so that + * the list of peers is only cleared the first time but appened after. + * Likewise for peers, if WGPEER_F_REPLACE_ALLOWEDIPS is specified in the + * first message of a peer, it likely should not be specified in subsequent + * fragments. + * + * If an error occurs, NLMSG_ERROR will reply containing an errno. + */ + +#define WG_GENL_NAME "wireguard" +#define WG_GENL_VERSION 1 + +#define WG_KEY_LEN 32 + +enum wg_cmd { + WG_CMD_GET_DEVICE, + WG_CMD_SET_DEVICE, + __WG_CMD_MAX +}; +#define WG_CMD_MAX (__WG_CMD_MAX - 1) + +enum wgdevice_flag { + WGDEVICE_F_REPLACE_PEERS = 1U << 0 +}; +enum wgdevice_attribute { + WGDEVICE_A_UNSPEC, + WGDEVICE_A_IFINDEX, + WGDEVICE_A_IFNAME, + WGDEVICE_A_PRIVATE_KEY, + WGDEVICE_A_PUBLIC_KEY, + WGDEVICE_A_FLAGS, + WGDEVICE_A_LISTEN_PORT, + WGDEVICE_A_FWMARK, + WGDEVICE_A_PEERS, + __WGDEVICE_A_LAST +}; +#define WGDEVICE_A_MAX (__WGDEVICE_A_LAST - 1) + +enum wgpeer_flag { + WGPEER_F_REMOVE_ME = 1U << 0, + WGPEER_F_REPLACE_ALLOWEDIPS = 1U << 1 +}; +enum wgpeer_attribute { + WGPEER_A_UNSPEC, + WGPEER_A_PUBLIC_KEY, + WGPEER_A_PRESHARED_KEY, + WGPEER_A_FLAGS, + WGPEER_A_ENDPOINT, + WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, + WGPEER_A_LAST_HANDSHAKE_TIME, + WGPEER_A_RX_BYTES, + WGPEER_A_TX_BYTES, + WGPEER_A_ALLOWEDIPS, + __WGPEER_A_LAST +}; +#define WGPEER_A_MAX (__WGPEER_A_LAST - 1) + +enum wgallowedip_attribute { + WGALLOWEDIP_A_UNSPEC, + WGALLOWEDIP_A_FAMILY, + WGALLOWEDIP_A_IPADDR, + WGALLOWEDIP_A_CIDR_MASK, + __WGALLOWEDIP_A_LAST +}; +#define WGALLOWEDIP_A_MAX (__WGALLOWEDIP_A_LAST - 1) diff --git a/src/sulogin-shell/sulogin-shell.c b/src/sulogin-shell/sulogin-shell.c index 70659df417..33dc07c5bd 100644 --- a/src/sulogin-shell/sulogin-shell.c +++ b/src/sulogin-shell/sulogin-shell.c @@ -81,24 +81,19 @@ static int start_default_target(sd_bus *bus) { static int fork_wait(const char* const cmdline[]) { pid_t pid; + int r; - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "fork(): %m"); - if (pid == 0) { - + r = safe_fork("(sulogin)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); + if (r < 0) + return r; + if (r == 0) { /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - execv(cmdline[0], (char**) cmdline); log_error_errno(errno, "Failed to execute %s: %m", cmdline[0]); _exit(EXIT_FAILURE); /* Operational error */ } - return wait_for_terminate_and_warn(cmdline[0], pid, false); + return wait_for_terminate_and_check(cmdline[0], pid, WAIT_LOG_ABNORMAL); } static void print_mode(const char* mode) { diff --git a/src/system-update-generator/system-update-generator.c b/src/system-update-generator/system-update-generator.c index 1796bae340..ec05e7b9e6 100644 --- a/src/system-update-generator/system-update-generator.c +++ b/src/system-update-generator/system-update-generator.c @@ -79,7 +79,8 @@ int main(int argc, char *argv[]) { if (argc > 1) arg_dest = argv[2]; - log_set_target(LOG_TARGET_SAFE); + log_set_prohibit_ipc(true); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 18c64241ba..5732d88a17 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -84,6 +84,7 @@ #include "stat-util.h" #include "strv.h" #include "terminal-util.h" +#include "unit-def.h" #include "unit-name.h" #include "user-util.h" #include "util.h" @@ -332,7 +333,7 @@ static bool install_client_side(void) { /* Decides when to execute enable/disable/... operations * client-side rather than server-side. */ - if (running_in_chroot() > 0) + if (running_in_chroot_or_offline()) return true; if (sd_booted() <= 0) @@ -2649,55 +2650,32 @@ static int unit_find_paths( 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; + _cleanup_free_ char *buf = NULL, *path = NULL; 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( + path = unit_dbus_path_from_name(name); + if (!path) + return log_oom(); + + r = sd_bus_get_property_string( bus, "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnit", + path, + "org.freedesktop.systemd1.Unit", + "ActiveState", &error, - &reply, - "s", name); - if (r < 0) { - if (!sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT)) - return log_error_errno(r, "Failed to retrieve unit: %s", bus_error_message(&error, r)); - - /* The unit is currently not loaded, hence say it's "inactive", since all units that aren't loaded are - * considered inactive. */ - state = UNIT_INACTIVE; - - } else { - r = sd_bus_message_read(reply, "o", &path); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_get_property_string( - bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Unit", - "ActiveState", - &error, - &buf); - if (r < 0) - return log_error_errno(r, "Failed to retrieve unit state: %s", bus_error_message(&error, r)); + &buf); + if (r < 0) + return log_error_errno(r, "Failed to retrieve unit state: %s", bus_error_message(&error, r)); - 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; - } + 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; } *active_state = state; @@ -2904,7 +2882,6 @@ static int start_unit_one( if (wait_context) { _cleanup_free_ char *unit_path = NULL; - const char* mt; log_debug("Watching for property changes of %s", name); r = sd_bus_call_method( @@ -2927,13 +2904,15 @@ static int start_unit_one( if (r < 0) return log_error_errno(r, "Failed to add unit path %s to set: %m", unit_path); - mt = strjoina("type='signal'," - "interface='org.freedesktop.DBus.Properties'," - "path='", unit_path, "'," - "member='PropertiesChanged'"); - r = sd_bus_add_match(bus, &wait_context->match, mt, on_properties_changed, wait_context); + r = sd_bus_match_signal_async(bus, + &wait_context->match, + NULL, + unit_path, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + on_properties_changed, NULL, wait_context); if (r < 0) - return log_error_errno(r, "Failed to add match for PropertiesChanged signal: %m"); + return log_error_errno(r, "Failed to request match for PropertiesChanged signal: %m"); } log_debug("%s manager for %s on %s, %s", @@ -3149,22 +3128,21 @@ static int start_unit(int argc, char *argv[], void *userdata) { } if (arg_wait) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - wait_context.unit_paths = set_new(&string_hash_ops); if (!wait_context.unit_paths) return log_oom(); - r = sd_bus_call_method( + r = sd_bus_call_method_async( bus, + NULL, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "Subscribe", - &error, - NULL, NULL); + NULL, NULL, + NULL); if (r < 0) - return log_error_errno(r, "Failed to enable subscription: %s", bus_error_message(&error, r)); + return log_error_errno(r, "Failed to enable subscription: %m"); r = sd_event_default(&wait_context.event); if (r < 0) return log_error_errno(r, "Failed to allocate event loop: %m"); @@ -3538,10 +3516,10 @@ static int load_kexec_kernel(void) { if (arg_dry_run) return 0; - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork: %m"); - else if (pid == 0) { + r = safe_fork("(kexec)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); + if (r < 0) + return r; + if (r == 0) { const char* const args[] = { KEXEC, @@ -3551,15 +3529,11 @@ static int load_kexec_kernel(void) { NULL }; /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); - execv(args[0], (char * const *) args); _exit(EXIT_FAILURE); - } else - return wait_for_terminate_and_warn("kexec", pid, true); + } + + return wait_for_terminate_and_check("kexec", pid, WAIT_LOG); } static int set_exit_code(uint8_t code) { @@ -4044,7 +4018,8 @@ static void print_status_info( if (i->load_error != 0) printf(" Loaded: %s%s%s (Reason: %s)\n", on, strna(i->load_state), off, i->load_error); - else if (path && !isempty(i->unit_file_state) && !isempty(i->unit_file_preset)) + else if (path && !isempty(i->unit_file_state) && !isempty(i->unit_file_preset) && + !STR_IN_SET(i->unit_file_state, "generated", "transient")) printf(" Loaded: %s%s%s (%s; %s; vendor preset: %s)\n", on, strna(i->load_state), off, path, i->unit_file_state, i->unit_file_preset); else if (path && !isempty(i->unit_file_state)) @@ -5582,6 +5557,7 @@ static int set_property(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_free_ char *n = NULL; + UnitType t; sd_bus *bus; int r; @@ -5605,6 +5581,12 @@ static int set_property(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to mangle unit name: %m"); + t = unit_name_to_type(n); + if (t < 0) { + log_error("Invalid unit type: %s", n); + return -EINVAL; + } + r = sd_bus_message_append(m, "sb", n, arg_runtime); if (r < 0) return bus_log_create_error(r); @@ -5613,7 +5595,7 @@ static int set_property(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_create_error(r); - r = bus_append_unit_property_assignment_many(m, strv_skip(argv, 2)); + r = bus_append_unit_property_assignment_many(m, t, strv_skip(argv, 2)); if (r < 0) return r; @@ -6060,7 +6042,6 @@ static int enable_sysv_units(const char *verb, char **args) { _cleanup_free_ char *p = NULL, *q = NULL, *l = NULL; bool found_native = false, found_sysv; - siginfo_t status; const char *name; unsigned c = 1; pid_t pid; @@ -6114,41 +6095,31 @@ static int enable_sysv_units(const char *verb, char **args) { if (!arg_quiet) log_info("Executing: %s", l); - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork: %m"); - else if (pid == 0) { + j = safe_fork("(sysv-install)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); + if (j < 0) + return j; + if (j == 0) { /* Child */ - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - execv(argv[0], (char**) argv); log_error_errno(errno, "Failed to execute %s: %m", argv[0]); _exit(EXIT_FAILURE); } - j = wait_for_terminate(pid, &status); + j = wait_for_terminate_and_check("sysv-install", pid, WAIT_LOG_ABNORMAL); if (j < 0) - return log_error_errno(j, "Failed to wait for child: %m"); - - if (status.si_code == CLD_EXITED) { - if (streq(verb, "is-enabled")) { - if (status.si_status == 0) { - if (!arg_quiet) - puts("enabled"); - r = 1; - } else { - if (!arg_quiet) - puts("disabled"); - } + return j; + if (streq(verb, "is-enabled")) { + if (j == EXIT_SUCCESS) { + if (!arg_quiet) + puts("enabled"); + r = 1; + } else { + if (!arg_quiet) + puts("disabled"); + } - } else if (status.si_status != 0) - return -EBADE; /* We don't warn here, under the assumption the script already showed an explanation */ - } else { - log_error("Unexpected waitid() result."); - return -EPROTO; - } + } else if (j != EXIT_SUCCESS) + return -EBADE; /* We don't warn here, under the assumption the script already showed an explanation */ if (found_native) continue; @@ -6253,7 +6224,6 @@ static int normalize_names(char **names, bool warn_if_path) { } static int unit_exists(LookupPaths *lp, const char *unit) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_free_ char *path = NULL; static const struct bus_properties_map property_map[] = { @@ -6276,22 +6246,10 @@ static int unit_exists(LookupPaths *lp, const char *unit) { if (r < 0) return r; - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.DBus.Properties", - "GetAll", - &error, - &reply, - "s", ""); + r = bus_map_all_properties(bus, "org.freedesktop.systemd1", path, property_map, &error, &info); if (r < 0) return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r)); - r = bus_message_map_all_properties(reply, property_map, &error, &info); - if (r < 0) - return log_error_errno(r, "Failed to map properties: %s", bus_error_message(&error, r)); - return !streq_ptr(info.load_state, "not-found") || !streq_ptr(info.active_state, "inactive"); } @@ -6986,25 +6944,20 @@ static int unit_file_create_copy( } static int run_editor(char **paths) { - pid_t pid; int r; assert(paths); - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork: %m"); - - if (pid == 0) { + r = safe_fork("(editor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + if (r < 0) + return r; + if (r == 0) { const char **args; char *editor, **editor_args = NULL; char **tmp_path, **original_path, *p; unsigned n_editor_args = 0, i = 1; size_t argc; - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - argc = strv_length(paths)/2 + 1; /* SYSTEMD_EDITOR takes precedence over EDITOR which takes precedence over VISUAL @@ -7060,10 +7013,6 @@ static int run_editor(char **paths) { _exit(EXIT_FAILURE); } - r = wait_for_terminate_and_warn("editor", pid, true); - if (r < 0) - return log_error_errno(r, "Failed to wait for child: %m"); - return 0; } @@ -8380,7 +8329,7 @@ static int talk_initctl(void) { request.runlevel = rl; - fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY); + fd = open(INIT_FIFO, O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY); if (fd < 0) { if (errno == ENOENT) return 0; @@ -8401,72 +8350,72 @@ static int talk_initctl(void) { static int systemctl_main(int argc, char *argv[]) { static const Verb verbs[] = { - { "list-units", VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_NOCHROOT, list_units }, - { "list-unit-files", VERB_ANY, VERB_ANY, 0, list_unit_files }, - { "list-sockets", VERB_ANY, VERB_ANY, VERB_NOCHROOT, list_sockets }, - { "list-timers", VERB_ANY, VERB_ANY, VERB_NOCHROOT, list_timers }, - { "list-jobs", VERB_ANY, VERB_ANY, VERB_NOCHROOT, list_jobs }, - { "list-machines", VERB_ANY, VERB_ANY, VERB_NOCHROOT|VERB_MUSTBEROOT, list_machines }, - { "clear-jobs", VERB_ANY, 1, VERB_NOCHROOT, trivial_method }, - { "cancel", VERB_ANY, VERB_ANY, VERB_NOCHROOT, cancel_job }, - { "start", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, - { "stop", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, - { "condstop", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatibility with ALTLinux */ - { "reload", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, - { "restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, - { "try-restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, - { "reload-or-restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, - { "reload-or-try-restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatbility with old systemctl <= 228 */ - { "try-reload-or-restart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, - { "force-reload", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatibility with SysV */ - { "condreload", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatibility with ALTLinux */ - { "condrestart", 2, VERB_ANY, VERB_NOCHROOT, start_unit }, /* For compatibility with RH */ - { "isolate", 2, 2, VERB_NOCHROOT, start_unit }, - { "kill", 2, VERB_ANY, VERB_NOCHROOT, kill_unit }, - { "is-active", 2, VERB_ANY, VERB_NOCHROOT, check_unit_active }, - { "check", 2, VERB_ANY, VERB_NOCHROOT, check_unit_active }, - { "is-failed", 2, VERB_ANY, VERB_NOCHROOT, check_unit_failed }, - { "show", VERB_ANY, VERB_ANY, VERB_NOCHROOT, show }, - { "cat", 2, VERB_ANY, VERB_NOCHROOT, cat }, - { "status", VERB_ANY, VERB_ANY, VERB_NOCHROOT, show }, - { "help", VERB_ANY, VERB_ANY, VERB_NOCHROOT, show }, - { "daemon-reload", VERB_ANY, 1, VERB_NOCHROOT, daemon_reload }, - { "daemon-reexec", VERB_ANY, 1, VERB_NOCHROOT, daemon_reload }, - { "show-environment", VERB_ANY, 1, VERB_NOCHROOT, show_environment }, - { "set-environment", 2, VERB_ANY, VERB_NOCHROOT, set_environment }, - { "unset-environment", 2, VERB_ANY, VERB_NOCHROOT, set_environment }, - { "import-environment", VERB_ANY, VERB_ANY, VERB_NOCHROOT, import_environment }, - { "halt", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, - { "poweroff", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, - { "reboot", VERB_ANY, 2, VERB_NOCHROOT, start_system_special }, - { "kexec", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, - { "suspend", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, - { "hibernate", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, - { "hybrid-sleep", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, - { "default", VERB_ANY, 1, VERB_NOCHROOT, start_special }, - { "rescue", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, - { "emergency", VERB_ANY, 1, VERB_NOCHROOT, start_system_special }, - { "exit", VERB_ANY, 2, VERB_NOCHROOT, start_special }, - { "reset-failed", VERB_ANY, VERB_ANY, VERB_NOCHROOT, reset_failed }, - { "enable", 2, VERB_ANY, 0, enable_unit }, - { "disable", 2, VERB_ANY, 0, enable_unit }, - { "is-enabled", 2, VERB_ANY, 0, unit_is_enabled }, - { "reenable", 2, VERB_ANY, 0, enable_unit }, - { "preset", 2, VERB_ANY, 0, enable_unit }, - { "preset-all", VERB_ANY, 1, 0, preset_all }, - { "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 }, - { "get-default", VERB_ANY, 1, 0, get_default }, - { "set-property", 3, VERB_ANY, VERB_NOCHROOT, set_property }, - { "is-system-running", VERB_ANY, 1, 0, is_system_running }, - { "add-wants", 3, VERB_ANY, 0, add_dependency }, - { "add-requires", 3, VERB_ANY, 0, add_dependency }, - { "edit", 2, VERB_ANY, VERB_NOCHROOT, edit }, + { "list-units", VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_ONLINE_ONLY, list_units }, + { "list-unit-files", VERB_ANY, VERB_ANY, 0, list_unit_files }, + { "list-sockets", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, list_sockets }, + { "list-timers", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, list_timers }, + { "list-jobs", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, list_jobs }, + { "list-machines", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY|VERB_MUST_BE_ROOT, list_machines }, + { "clear-jobs", VERB_ANY, 1, VERB_ONLINE_ONLY, trivial_method }, + { "cancel", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, cancel_job }, + { "start", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, + { "stop", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, + { "condstop", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with ALTLinux */ + { "reload", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, + { "restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, + { "try-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, + { "reload-or-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, + { "reload-or-try-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatbility with old systemctl <= 228 */ + { "try-reload-or-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, + { "force-reload", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with SysV */ + { "condreload", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with ALTLinux */ + { "condrestart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with RH */ + { "isolate", 2, 2, VERB_ONLINE_ONLY, start_unit }, + { "kill", 2, VERB_ANY, VERB_ONLINE_ONLY, kill_unit }, + { "is-active", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_active }, + { "check", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_active }, /* deprecated alias of is-active */ + { "is-failed", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_failed }, + { "show", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, show }, + { "cat", 2, VERB_ANY, VERB_ONLINE_ONLY, cat }, + { "status", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, show }, + { "help", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, show }, + { "daemon-reload", VERB_ANY, 1, VERB_ONLINE_ONLY, daemon_reload }, + { "daemon-reexec", VERB_ANY, 1, VERB_ONLINE_ONLY, daemon_reload }, + { "show-environment", VERB_ANY, 1, VERB_ONLINE_ONLY, show_environment }, + { "set-environment", 2, VERB_ANY, VERB_ONLINE_ONLY, set_environment }, + { "unset-environment", 2, VERB_ANY, VERB_ONLINE_ONLY, set_environment }, + { "import-environment", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, import_environment }, + { "halt", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special }, + { "poweroff", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special }, + { "reboot", VERB_ANY, 2, VERB_ONLINE_ONLY, start_system_special }, + { "kexec", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special }, + { "suspend", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special }, + { "hibernate", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special }, + { "hybrid-sleep", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special }, + { "default", VERB_ANY, 1, VERB_ONLINE_ONLY, start_special }, + { "rescue", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special }, + { "emergency", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special }, + { "exit", VERB_ANY, 2, VERB_ONLINE_ONLY, start_special }, + { "reset-failed", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, reset_failed }, + { "enable", 2, VERB_ANY, 0, enable_unit }, + { "disable", 2, VERB_ANY, 0, enable_unit }, + { "is-enabled", 2, VERB_ANY, 0, unit_is_enabled }, + { "reenable", 2, VERB_ANY, 0, enable_unit }, + { "preset", 2, VERB_ANY, 0, enable_unit }, + { "preset-all", VERB_ANY, 1, 0, preset_all }, + { "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_ONLINE_ONLY, switch_root }, + { "list-dependencies", VERB_ANY, 2, VERB_ONLINE_ONLY, list_dependencies }, + { "set-default", 2, 2, 0, set_default }, + { "get-default", VERB_ANY, 1, 0, get_default }, + { "set-property", 3, VERB_ANY, VERB_ONLINE_ONLY, set_property }, + { "is-system-running", VERB_ANY, 1, 0, is_system_running }, + { "add-wants", 3, VERB_ANY, 0, add_dependency }, + { "add-requires", 3, VERB_ANY, 0, add_dependency }, + { "edit", 2, VERB_ANY, VERB_ONLINE_ONLY, edit }, {} }; diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h index c5c7096d55..82e7d445d2 100644 --- a/src/systemd/sd-bus.h +++ b/src/systemd/sd-bus.h @@ -33,6 +33,10 @@ _SD_BEGIN_DECLARATIONS; +#define SD_BUS_DEFAULT ((sd_bus *) 1) +#define SD_BUS_DEFAULT_USER ((sd_bus *) 2) +#define SD_BUS_DEFAULT_SYSTEM ((sd_bus *) 3) + /* Types */ typedef struct sd_bus sd_bus; @@ -150,8 +154,14 @@ int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b); int sd_bus_get_allow_interactive_authorization(sd_bus *bus); int sd_bus_set_exit_on_disconnect(sd_bus *bus, int b); int sd_bus_get_exit_on_disconnect(sd_bus *bus); +int sd_bus_set_watch_bind(sd_bus *bus, int b); +int sd_bus_get_watch_bind(sd_bus *bus); +int sd_bus_set_connected_signal(sd_bus *bus, int b); +int sd_bus_get_connected_signal(sd_bus *bus); +int sd_bus_set_sender(sd_bus *bus, const char *sender); +int sd_bus_get_sender(sd_bus *bus, const char **ret); -int sd_bus_start(sd_bus *ret); +int sd_bus_start(sd_bus *bus); int sd_bus_try_close(sd_bus *bus); void sd_bus_close(sd_bus *bus); @@ -163,6 +173,7 @@ sd_bus *sd_bus_flush_close_unref(sd_bus *bus); void sd_bus_default_flush_close(void); int sd_bus_is_open(sd_bus *bus); +int sd_bus_is_ready(sd_bus *bus); int sd_bus_get_bus_id(sd_bus *bus, sd_id128_t *id); int sd_bus_get_scope(sd_bus *bus, const char **scope); @@ -193,6 +204,7 @@ sd_event *sd_bus_get_event(sd_bus *bus); int sd_bus_add_filter(sd_bus *bus, sd_bus_slot **slot, sd_bus_message_handler_t callback, void *userdata); int sd_bus_add_match(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, void *userdata); +int sd_bus_add_match_async(sd_bus *bus, sd_bus_slot **slot, const char *match, sd_bus_message_handler_t callback, sd_bus_message_handler_t install_callback, void *userdata); int sd_bus_add_object(sd_bus *bus, sd_bus_slot **slot, const char *path, sd_bus_message_handler_t callback, void *userdata); int sd_bus_add_fallback(sd_bus *bus, sd_bus_slot **slot, const char *prefix, sd_bus_message_handler_t callback, void *userdata); int sd_bus_add_object_vtable(sd_bus *bus, sd_bus_slot **slot, const char *path, const char *interface, const sd_bus_vtable *vtable, void *userdata); @@ -267,6 +279,7 @@ int sd_bus_message_set_auto_start(sd_bus_message *m, int b); int sd_bus_message_set_allow_interactive_authorization(sd_bus_message *m, int b); int sd_bus_message_set_destination(sd_bus_message *m, const char *destination); +int sd_bus_message_set_sender(sd_bus_message *m, const char *sender); int sd_bus_message_set_priority(sd_bus_message *m, int64_t priority); int sd_bus_message_append(sd_bus_message *m, const char *types, ...); @@ -300,7 +313,9 @@ int sd_bus_message_rewind(sd_bus_message *m, int complete); int sd_bus_get_unique_name(sd_bus *bus, const char **unique); int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags); +int sd_bus_request_name_async(sd_bus *bus, sd_bus_slot **ret_slot, const char *name, uint64_t flags, sd_bus_message_handler_t callback, void *userdata); int sd_bus_release_name(sd_bus *bus, const char *name); +int sd_bus_release_name_async(sd_bus *bus, sd_bus_slot **ret_slot, const char *name, sd_bus_message_handler_t callback, void *userdata); int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable); /* free the results */ int sd_bus_get_name_creds(sd_bus *bus, const char *name, uint64_t mask, sd_bus_creds **creds); /* unref the result! */ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine); @@ -336,6 +351,9 @@ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *in int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds); int sd_bus_query_sender_privilege(sd_bus_message *call, int capability); +int sd_bus_match_signal(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t callback, void *userdata); +int sd_bus_match_signal_async(sd_bus *bus, sd_bus_slot **ret, const char *sender, const char *path, const char *interface, const char *member, sd_bus_message_handler_t match_callback, sd_bus_message_handler_t add_callback, void *userdata); + /* Credential handling */ int sd_bus_creds_new_from_pid(sd_bus_creds **ret, pid_t pid, uint64_t creds_mask); diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index 37803c71d8..cadb32a051 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -23,6 +23,7 @@ #include <inttypes.h> #include <net/ethernet.h> +#include <stdbool.h> #include <sys/types.h> #include "sd-dhcp6-lease.h" @@ -64,6 +65,8 @@ enum { SD_DHCP6_OPTION_DNS_SERVERS = 23, /* RFC 3646 */ SD_DHCP6_OPTION_DOMAIN_LIST = 24, /* RFC 3646 */ + SD_DHCP6_OPTION_IA_PD = 25, /* RFC 3633, prefix delegation */ + SD_DHCP6_OPTION_IA_PD_PREFIX = 26, /* RFC 3633, prefix delegation */ SD_DHCP6_OPTION_SNTP_SERVERS = 31, /* RFC 4075, deprecated */ @@ -116,6 +119,8 @@ int sd_dhcp6_client_get_information_request( int sd_dhcp6_client_set_request_option( sd_dhcp6_client *client, uint16_t option); +int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, + bool delegation); int sd_dhcp6_client_get_lease( sd_dhcp6_client *client, diff --git a/src/systemd/sd-dhcp6-lease.h b/src/systemd/sd-dhcp6-lease.h index 5807b1836b..22a5f8ce75 100644 --- a/src/systemd/sd-dhcp6-lease.h +++ b/src/systemd/sd-dhcp6-lease.h @@ -36,6 +36,11 @@ int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr, uint32_t *lifetime_preferred, uint32_t *lifetime_valid); +void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease); +int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix, + uint8_t *prefix_len, + uint32_t *lifetime_preferred, + uint32_t *lifetime_valid); int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs); int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains); diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h index 9083d5fa9e..ec4b7bcf69 100644 --- a/src/systemd/sd-event.h +++ b/src/systemd/sd-event.h @@ -26,6 +26,7 @@ #include <sys/epoll.h> #include <sys/signalfd.h> #include <sys/types.h> +#include <time.h> #include "_sd-common.h" @@ -40,6 +41,8 @@ _SD_BEGIN_DECLARATIONS; +#define SD_EVENT_DEFAULT ((sd_event *) 1) + typedef struct sd_event sd_event; typedef struct sd_event_source sd_event_source; @@ -124,6 +127,8 @@ int sd_event_source_get_enabled(sd_event_source *s, int *enabled); int sd_event_source_set_enabled(sd_event_source *s, int enabled); int sd_event_source_get_io_fd(sd_event_source *s); int sd_event_source_set_io_fd(sd_event_source *s, int fd); +int sd_event_source_get_io_fd_own(sd_event_source *s); +int sd_event_source_set_io_fd_own(sd_event_source *s, int own); int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events); int sd_event_source_set_io_events(sd_event_source *s, uint32_t events); int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents); diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h index d6e3816c64..e742807e92 100644 --- a/src/systemd/sd-netlink.h +++ b/src/systemd/sd-netlink.h @@ -34,7 +34,9 @@ _SD_BEGIN_DECLARATIONS; typedef struct sd_netlink sd_netlink; +typedef struct sd_genl_socket sd_genl_socket; typedef struct sd_netlink_message sd_netlink_message; +typedef enum {SD_GENL_ID_CTRL, SD_GENL_WIREGUARD} sd_genl_family; /* callback */ @@ -94,6 +96,9 @@ int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type); int sd_netlink_message_exit_container(sd_netlink_message *m); +int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type); +int sd_netlink_message_cancel_array(sd_netlink_message *m); + int sd_netlink_message_rewind(sd_netlink_message *m); sd_netlink_message *sd_netlink_message_next(sd_netlink_message *m); @@ -177,6 +182,10 @@ int sd_rtnl_message_routing_policy_rule_get_rtm_type(sd_netlink_message *m, unsi _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink, sd_netlink_unref); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_netlink_message, sd_netlink_message_unref); +/* genl */ +int sd_genl_socket_open(sd_netlink **nl); +int sd_genl_message_new(sd_netlink *nl, sd_genl_family family, uint8_t cmd, sd_netlink_message **m); + _SD_END_DECLARATIONS; #endif diff --git a/src/systemd/sd-radv.h b/src/systemd/sd-radv.h index 94d5e71e8a..e319a82dbf 100644 --- a/src/systemd/sd-radv.h +++ b/src/systemd/sd-radv.h @@ -24,6 +24,7 @@ #include <inttypes.h> #include <net/ethernet.h> #include <netinet/in.h> +#include <stdbool.h> #include <sys/types.h> #include "sd-ndisc.h" @@ -62,7 +63,9 @@ int sd_radv_set_router_lifetime(sd_radv *ra, uint32_t router_lifetime); int sd_radv_set_managed_information(sd_radv *ra, int managed); int sd_radv_set_other_information(sd_radv *ra, int other); int sd_radv_set_preference(sd_radv *ra, unsigned preference); -int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p); +int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, bool dynamic); +sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra, struct in6_addr *prefix, + uint8_t prefixlen); int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime, const struct in6_addr *dns, size_t n_dns); int sd_radv_set_dnssl(sd_radv *ra, uint32_t lifetime, char **search_list); diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index d8009458ee..e06b4b6d5b 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -64,6 +64,7 @@ typedef struct Item { uid_t uid; bool gid_set:1; + bool gid_must_exist:1; bool uid_set:1; bool todo_user:1; @@ -74,9 +75,9 @@ static char *arg_root = NULL; static const char conf_file_dirs[] = CONF_PATHS_NULSTR("sysusers.d"); -static Hashmap *users = NULL, *groups = NULL; -static Hashmap *todo_uids = NULL, *todo_gids = NULL; -static Hashmap *members = NULL; +static OrderedHashmap *users = NULL, *groups = NULL; +static OrderedHashmap *todo_uids = NULL, *todo_gids = NULL; +static OrderedHashmap *members = NULL; static Hashmap *database_uid = NULL, *database_user = NULL; static Hashmap *database_gid = NULL, *database_group = NULL; @@ -256,7 +257,7 @@ static int putgrent_with_members(const struct group *gr, FILE *group) { assert(gr); assert(group); - a = hashmap_get(members, gr->gr_name); + a = ordered_hashmap_get(members, gr->gr_name); if (a) { _cleanup_strv_free_ char **l = NULL; bool added = false; @@ -307,7 +308,7 @@ static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) { assert(sg); assert(gshadow); - a = hashmap_get(members, sg->sg_namp); + a = ordered_hashmap_get(members, sg->sg_namp); if (a) { _cleanup_strv_free_ char **l = NULL; bool added = false; @@ -387,7 +388,7 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char Item *i; int r; - if (hashmap_size(todo_uids) == 0) + if (ordered_hashmap_size(todo_uids) == 0) return 0; r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp); @@ -405,13 +406,13 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char errno = 0; while ((pw = fgetpwent(original))) { - i = hashmap_get(users, pw->pw_name); + i = ordered_hashmap_get(users, pw->pw_name); if (i && i->todo_user) { log_error("%s: User \"%s\" already exists.", passwd_path, pw->pw_name); return -EEXIST; } - if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) { + if (ordered_hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) { log_error("%s: Detected collision for UID " UID_FMT ".", passwd_path, pw->pw_uid); return -EEXIST; } @@ -432,7 +433,7 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char return -errno; } - HASHMAP_FOREACH(i, todo_uids, iterator) { + ORDERED_HASHMAP_FOREACH(i, todo_uids, iterator) { struct passwd n = { .pw_name = i->name, .pw_uid = i->uid, @@ -474,7 +475,7 @@ static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char Item *i; int r; - if (hashmap_size(todo_uids) == 0) + if (ordered_hashmap_size(todo_uids) == 0) return 0; r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp); @@ -494,7 +495,7 @@ static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char errno = 0; while ((sp = fgetspent(original))) { - i = hashmap_get(users, sp->sp_namp); + i = ordered_hashmap_get(users, sp->sp_namp); if (i && i->todo_user) { /* we will update the existing entry */ sp->sp_lstchg = lstchg; @@ -502,7 +503,7 @@ static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char /* only the /etc/shadow stage is left, so we can * safely remove the item from the todo set */ i->todo_user = false; - hashmap_remove(todo_uids, UID_TO_PTR(i->uid)); + ordered_hashmap_remove(todo_uids, UID_TO_PTR(i->uid)); } errno = 0; @@ -521,7 +522,7 @@ static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char return -errno; } - HASHMAP_FOREACH(i, todo_uids, iterator) { + ORDERED_HASHMAP_FOREACH(i, todo_uids, iterator) { struct spwd n = { .sp_namp = i->name, .sp_pwdp = (char*) "!!", @@ -558,7 +559,7 @@ static int write_temporary_group(const char *group_path, FILE **tmpfile, char ** Item *i; int r; - if (hashmap_size(todo_gids) == 0 && hashmap_size(members) == 0) + if (ordered_hashmap_size(todo_gids) == 0 && ordered_hashmap_size(members) == 0) return 0; r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp); @@ -580,13 +581,13 @@ static int write_temporary_group(const char *group_path, FILE **tmpfile, char ** * entries anyway here, let's make an extra verification * step that we don't generate duplicate entries. */ - i = hashmap_get(groups, gr->gr_name); + i = ordered_hashmap_get(groups, gr->gr_name); if (i && i->todo_group) { log_error("%s: Group \"%s\" already exists.", group_path, gr->gr_name); return -EEXIST; } - if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) { + if (ordered_hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) { log_error("%s: Detected collision for GID " GID_FMT ".", group_path, gr->gr_gid); return -EEXIST; } @@ -609,7 +610,7 @@ static int write_temporary_group(const char *group_path, FILE **tmpfile, char ** return -errno; } - HASHMAP_FOREACH(i, todo_gids, iterator) { + ORDERED_HASHMAP_FOREACH(i, todo_gids, iterator) { struct group n = { .gr_name = i->name, .gr_gid = i->gid, @@ -645,7 +646,7 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, ch Item *i; int r; - if (hashmap_size(todo_gids) == 0 && hashmap_size(members) == 0) + if (ordered_hashmap_size(todo_gids) == 0 && ordered_hashmap_size(members) == 0) return 0; r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp); @@ -663,7 +664,7 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, ch errno = 0; while ((sg = fgetsgent(original))) { - i = hashmap_get(groups, sg->sg_namp); + i = ordered_hashmap_get(groups, sg->sg_namp); if (i && i->todo_group) { log_error("%s: Group \"%s\" already exists.", gshadow_path, sg->sg_namp); return -EEXIST; @@ -687,7 +688,7 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, ch return -errno; } - HASHMAP_FOREACH(i, todo_gids, iterator) { + ORDERED_HASHMAP_FOREACH(i, todo_gids, iterator) { struct sgrp n = { .sg_namp = i->name, .sg_passwd = (char*) "!!", @@ -807,12 +808,12 @@ static int uid_is_ok(uid_t uid, const char *name) { Item *i; /* Let's see if we already have assigned the UID a second time */ - if (hashmap_get(todo_uids, UID_TO_PTR(uid))) + if (ordered_hashmap_get(todo_uids, UID_TO_PTR(uid))) return 0; /* Try to avoid using uids that are already used by a group * that doesn't have the same name as our new user. */ - i = hashmap_get(todo_gids, GID_TO_PTR(uid)); + i = ordered_hashmap_get(todo_gids, GID_TO_PTR(uid)); if (i && !streq(i->name, name)) return 0; @@ -1012,11 +1013,11 @@ static int add_user(Item *i) { i->uid = search_uid; } - r = hashmap_ensure_allocated(&todo_uids, NULL); + r = ordered_hashmap_ensure_allocated(&todo_uids, NULL); if (r < 0) return log_oom(); - r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i); + r = ordered_hashmap_put(todo_uids, UID_TO_PTR(i->uid), i); if (r < 0) return log_oom(); @@ -1030,11 +1031,11 @@ static int gid_is_ok(gid_t gid) { struct group *g; struct passwd *p; - if (hashmap_get(todo_gids, GID_TO_PTR(gid))) + if (ordered_hashmap_get(todo_gids, GID_TO_PTR(gid))) return 0; /* Avoid reusing gids that are already used by a different user */ - if (hashmap_get(todo_uids, UID_TO_PTR(gid))) + if (ordered_hashmap_get(todo_uids, UID_TO_PTR(gid))) return 0; if (hashmap_contains(database_gid, GID_TO_PTR(gid))) @@ -1098,6 +1099,18 @@ static int add_group(Item *i) { r = gid_is_ok(i->gid); if (r < 0) return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); + if (i->gid_must_exist) { + /* If we require the gid to already exist we can return here: + * r > 0: means the gid does not exist -> fail + * r == 0: means the gid exists -> nothing more to do. + */ + if (r > 0) { + log_error("Failed to create %s: please create GID %d", i->name, i->gid); + return -EINVAL; + } + if (r == 0) + return 0; + } if (r == 0) { log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name); i->gid_set = false; @@ -1157,11 +1170,11 @@ static int add_group(Item *i) { i->gid = search_uid; } - r = hashmap_ensure_allocated(&todo_gids, NULL); + r = ordered_hashmap_ensure_allocated(&todo_gids, NULL); if (r < 0) return log_oom(); - r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i); + r = ordered_hashmap_put(todo_gids, GID_TO_PTR(i->gid), i); if (r < 0) return log_oom(); @@ -1185,30 +1198,8 @@ static int process_item(Item *i) { return add_user(i); - case ADD_GROUP: { - Item *j; - - j = hashmap_get(users, i->name); - if (j) { - /* There's already user to be created for this - * name, let's process that in one step */ - - if (i->gid_set) { - j->gid = i->gid; - j->gid_set = true; - } - - if (i->gid_path) { - r = free_and_strdup(&j->gid_path, i->gid_path); - if (r < 0) - return log_oom(); - } - - return 0; - } - + case ADD_GROUP: return add_group(i); - } default: assert_not_reached("Unknown item type"); @@ -1237,15 +1228,15 @@ static int add_implicit(void) { /* Implicitly create additional users and groups, if they were listed in "m" lines */ - HASHMAP_FOREACH_KEY(l, g, members, iterator) { + ORDERED_HASHMAP_FOREACH_KEY(l, g, members, iterator) { Item *i; char **m; - i = hashmap_get(groups, g); + i = ordered_hashmap_get(groups, g); if (!i) { _cleanup_(item_freep) Item *j = NULL; - r = hashmap_ensure_allocated(&groups, &string_hash_ops); + r = ordered_hashmap_ensure_allocated(&groups, &string_hash_ops); if (r < 0) return log_oom(); @@ -1258,7 +1249,7 @@ static int add_implicit(void) { if (!j->name) return log_oom(); - r = hashmap_put(groups, j->name, j); + r = ordered_hashmap_put(groups, j->name, j); if (r < 0) return log_oom(); @@ -1268,11 +1259,11 @@ static int add_implicit(void) { STRV_FOREACH(m, l) { - i = hashmap_get(users, *m); + i = ordered_hashmap_get(users, *m); if (!i) { _cleanup_(item_freep) Item *j = NULL; - r = hashmap_ensure_allocated(&users, &string_hash_ops); + r = ordered_hashmap_ensure_allocated(&users, &string_hash_ops); if (r < 0) return log_oom(); @@ -1285,7 +1276,7 @@ static int add_implicit(void) { if (!j->name) return log_oom(); - r = hashmap_put(users, j->name, j); + r = ordered_hashmap_put(users, j->name, j); if (r < 0) return log_oom(); @@ -1348,7 +1339,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL, *resolved_name = NULL, *resolved_id = NULL, *description = NULL, *home = NULL; _cleanup_(item_freep) Item *i = NULL; Item *existing; - Hashmap *h; + OrderedHashmap *h; int r; const char *p; @@ -1494,11 +1485,11 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { return -EINVAL; } - r = hashmap_ensure_allocated(&members, &string_hash_ops); + r = ordered_hashmap_ensure_allocated(&members, &string_hash_ops); if (r < 0) return log_oom(); - l = hashmap_get(members, resolved_id); + l = ordered_hashmap_get(members, resolved_id); if (l) { /* A list for this group name already exists, let's append to it */ r = strv_push(&l, resolved_name); @@ -1507,7 +1498,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { resolved_name = NULL; - assert_se(hashmap_update(members, resolved_id, l) >= 0); + assert_se(ordered_hashmap_update(members, resolved_id, l) >= 0); } else { /* No list for this group name exists yet, create one */ @@ -1518,7 +1509,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { l[0] = resolved_name; l[1] = NULL; - r = hashmap_put(members, resolved_id, l); + r = ordered_hashmap_put(members, resolved_id, l); if (r < 0) { free(l); return log_oom(); @@ -1536,7 +1527,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { return -EINVAL; } - r = hashmap_ensure_allocated(&users, &string_hash_ops); + r = ordered_hashmap_ensure_allocated(&users, &string_hash_ops); if (r < 0) return log_oom(); @@ -1551,11 +1542,18 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { path_kill_slashes(i->uid_path); } else { - r = parse_uid(resolved_id, &i->uid); - if (r < 0) { - log_error("Failed to parse UID: %s", id); - return -EBADMSG; + _cleanup_free_ char *uid = NULL, *gid = NULL; + if (split_pair(resolved_id, ":", &uid, &gid) == 0) { + r = parse_gid(gid, &i->gid); + if (r < 0) + return log_error_errno(r, "Failed to parse GID: '%s': %m", id); + i->gid_set = true; + i->gid_must_exist = true; + free_and_replace(resolved_id, uid); } + r = parse_uid(resolved_id, &i->uid); + if (r < 0) + return log_error_errno(r, "Failed to parse UID: '%s': %m", id); i->uid_set = true; } @@ -1586,7 +1584,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { return -EINVAL; } - r = hashmap_ensure_allocated(&groups, &string_hash_ops); + r = ordered_hashmap_ensure_allocated(&groups, &string_hash_ops); if (r < 0) return log_oom(); @@ -1602,10 +1600,8 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { path_kill_slashes(i->gid_path); } else { r = parse_gid(resolved_id, &i->gid); - if (r < 0) { - log_error("Failed to parse GID: %s", id); - return -EBADMSG; - } + if (r < 0) + return log_error_errno(r, "Failed to parse GID: '%s': %m", id); i->gid_set = true; } @@ -1622,7 +1618,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { i->name = resolved_name; resolved_name = NULL; - existing = hashmap_get(h, i->name); + existing = ordered_hashmap_get(h, i->name); if (existing) { /* Two identical items are fine */ @@ -1632,7 +1628,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { return 0; } - r = hashmap_put(h, i->name, i); + r = ordered_hashmap_put(h, i->name, i); if (r < 0) return log_oom(); @@ -1853,10 +1849,10 @@ int main(int argc, char *argv[]) { goto finish; } - HASHMAP_FOREACH(i, groups, iterator) + ORDERED_HASHMAP_FOREACH(i, groups, iterator) process_item(i); - HASHMAP_FOREACH(i, users, iterator) + ORDERED_HASHMAP_FOREACH(i, users, iterator) process_item(i); r = write_files(); @@ -1864,17 +1860,17 @@ int main(int argc, char *argv[]) { log_error_errno(r, "Failed to write files: %m"); finish: - hashmap_free_with_destructor(groups, item_free); - hashmap_free_with_destructor(users, item_free); + ordered_hashmap_free_with_destructor(groups, item_free); + ordered_hashmap_free_with_destructor(users, item_free); - while ((n = hashmap_first_key(members))) { - strv_free(hashmap_steal_first(members)); + while ((n = ordered_hashmap_first_key(members))) { + strv_free(ordered_hashmap_steal_first(members)); free(n); } - hashmap_free(members); + ordered_hashmap_free(members); - hashmap_free(todo_uids); - hashmap_free(todo_gids); + ordered_hashmap_free(todo_uids); + ordered_hashmap_free(todo_gids); free_database(database_user, database_uid); free_database(database_group, database_gid); diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c index 51306b5625..087ba08559 100644 --- a/src/sysv-generator/sysv-generator.c +++ b/src/sysv-generator/sysv-generator.c @@ -950,7 +950,8 @@ int main(int argc, char *argv[]) { if (argc > 1) arg_dest = argv[3]; - log_set_target(LOG_TARGET_SAFE); + log_set_prohibit_ipc(true); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); diff --git a/src/test/meson.build b/src/test/meson.build index 6bb5bd629e..1db8aa107d 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -67,7 +67,7 @@ tests += [ 'src/test/test-helper.c'], [libcore, libudev, - libsystemd_internal], + libshared], [threads, librt, libseccomp, @@ -119,6 +119,7 @@ tests += [ [['src/test/test-dns-domain.c'], [libcore, + libshared, libsystemd_network], []], @@ -167,7 +168,7 @@ tests += [ []], [['src/test/test-copy.c'], - [libshared_static], + [], []], [['src/test/test-sigbus.c'], @@ -376,6 +377,17 @@ tests += [ libselinux, libblkid]], + [['src/test/test-watch-pid.c', + 'src/test/test-helper.c'], + [libcore, + libshared], + [libmount, + threads, + librt, + libseccomp, + libselinux, + libblkid]], + [['src/test/test-hashmap.c', 'src/test/test-hashmap-plain.c', test_hashmap_ordered_c], @@ -399,6 +411,10 @@ tests += [ [], []], + [['src/test/test-procfs-util.c'], + [], + []], + [['src/test/test-unaligned.c'], [], []], @@ -410,7 +426,7 @@ tests += [ [libcore, libjournal_core, libudev_core, - libudev_internal, + libudev_static, libsystemd_network, libshared], [threads, @@ -499,7 +515,6 @@ tests += [ [], '', 'manual'], - [['src/test/test-cgroup-mask.c', 'src/test/test-helper.c'], [libcore, @@ -610,7 +625,7 @@ tests += [ [['src/test/test-udev.c'], [libudev_core, - libudev_internal, + libudev_static, libsystemd_network, libshared], [threads, @@ -761,6 +776,10 @@ tests += [ [], [threads]], + [['src/libsystemd/sd-bus/test-bus-watch-bind.c'], + [], + [threads], '', 'timeout=120'], + [['src/libsystemd/sd-bus/test-bus-chat.c'], [], [threads]], @@ -772,7 +791,7 @@ tests += [ [['src/libsystemd/sd-bus/test-bus-error.c'], [libshared_static, - libsystemd_internal], + libsystemd_static], []], [['src/libsystemd/sd-bus/test-bus-track.c'], diff --git a/src/test/test-async.c b/src/test/test-async.c index 2055ce25bf..87906b2e25 100644 --- a/src/test/test-async.c +++ b/src/test/test-async.c @@ -43,7 +43,7 @@ int main(int argc, char *argv[]) { assert_se(asynchronous_job(async_func, NULL) >= 0); - assert_se(asynchronous_sync() >= 0); + assert_se(asynchronous_sync(NULL) >= 0); sleep(1); diff --git a/src/test/test-capability.c b/src/test/test-capability.c index e5db52d404..10cddaf552 100644 --- a/src/test/test-capability.c +++ b/src/test/test-capability.c @@ -80,7 +80,7 @@ static void fork_test(void (*test_func)(void)) { assert_se(pid >= 0); if (pid == 0) { test_func(); - exit(0); + exit(EXIT_SUCCESS); } else if (pid > 0) { int status; diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c index 45eb3ef8b1..2248a30635 100644 --- a/src/test/test-cgroup-util.c +++ b/src/test/test-cgroup-util.c @@ -27,6 +27,7 @@ #include "parse-util.h" #include "proc-cmdline.h" #include "process-util.h" +#include "special.h" #include "stat-util.h" #include "string-util.h" #include "test-helper.h" @@ -141,10 +142,10 @@ static void check_p_g_slice(const char *path, int code, const char *result) { static void test_path_get_slice(void) { check_p_g_slice("/user.slice", 0, "user.slice"); - check_p_g_slice("/foobar", 0, "-.slice"); + check_p_g_slice("/foobar", 0, SPECIAL_ROOT_SLICE); check_p_g_slice("/user.slice/user-waldo.slice", 0, "user-waldo.slice"); - check_p_g_slice("", 0, "-.slice"); - check_p_g_slice("foobar", 0, "-.slice"); + check_p_g_slice("", 0, SPECIAL_ROOT_SLICE); + check_p_g_slice("foobar", 0, SPECIAL_ROOT_SLICE); check_p_g_slice("foobar.slice", 0, "foobar.slice"); check_p_g_slice("foo.slice/foo-bar.slice/waldo.service", 0, "foo-bar.slice"); } @@ -165,10 +166,10 @@ static void test_path_get_user_slice(void) { check_p_g_u_slice("foobar.slice", -ENXIO, NULL); check_p_g_u_slice("foo.slice/foo-bar.slice/waldo.service", -ENXIO, NULL); - check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service", 0, "-.slice"); - check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service/", 0, "-.slice"); - check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service///", 0, "-.slice"); - check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service/waldo.service", 0, "-.slice"); + check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service", 0, SPECIAL_ROOT_SLICE); + check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service/", 0, SPECIAL_ROOT_SLICE); + check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service///", 0, SPECIAL_ROOT_SLICE); + check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service/waldo.service", 0, SPECIAL_ROOT_SLICE); check_p_g_u_slice("foo.slice/foo-bar.slice/user@1000.service/piep.slice/foo.service", 0, "piep.slice"); check_p_g_u_slice("/foo.slice//foo-bar.slice/user@1000.service/piep.slice//piep-pap.slice//foo.service", 0, "piep-pap.slice"); } @@ -274,7 +275,7 @@ static void test_slice_to_path(void) { test_slice_to_path_one("foobar.slice", "foobar.slice", 0); test_slice_to_path_one("foobar-waldo.slice", "foobar.slice/foobar-waldo.slice", 0); test_slice_to_path_one("foobar-waldo.service", NULL, -EINVAL); - test_slice_to_path_one("-.slice", "", 0); + test_slice_to_path_one(SPECIAL_ROOT_SLICE, "", 0); test_slice_to_path_one("--.slice", NULL, -EINVAL); test_slice_to_path_one("-", NULL, -EINVAL); test_slice_to_path_one("-foo-.slice", NULL, -EINVAL); diff --git a/src/test/test-cgroup.c b/src/test/test-cgroup.c index 2ae95db162..b2440fc3a9 100644 --- a/src/test/test-cgroup.c +++ b/src/test/test-cgroup.c @@ -23,6 +23,7 @@ #include "cgroup-util.h" #include "path-util.h" +#include "process-util.h" #include "string-util.h" #include "util.h" diff --git a/src/test/test-condition.c b/src/test/test-condition.c index d43db3a7cd..ad64a2bb36 100644 --- a/src/test/test-condition.c +++ b/src/test/test-condition.c @@ -20,6 +20,7 @@ #include <stdio.h> #include <sys/types.h> +#include <sys/utsname.h> #include <unistd.h> #include "sd-id128.h" @@ -28,6 +29,7 @@ #include "apparmor-util.h" #include "architecture.h" #include "audit-util.h" +#include "cgroup-util.h" #include "condition.h" #include "hostname-util.h" #include "id128-util.h" @@ -35,11 +37,13 @@ #include "log.h" #include "macro.h" #include "selinux-util.h" +#include "set.h" #include "smack-util.h" +#include "string-util.h" #include "strv.h" -#include "virt.h" -#include "util.h" #include "user-util.h" +#include "util.h" +#include "virt.h" static void test_condition_test_path(void) { Condition *condition; @@ -125,6 +129,77 @@ static void test_condition_test_path(void) { condition_free(condition); } +static int test_condition_test_control_group_controller(void) { + Condition *condition; + CGroupMask system_mask; + CGroupController controller; + _cleanup_free_ char *controller_name = NULL; + int r; + + r = cg_unified_flush(); + if (r < 0) { + log_notice_errno(r, "Skipping ConditionControlGroupController tests: %m"); + return EXIT_TEST_SKIP; + } + + /* Invalid controllers are ignored */ + condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, "thisisnotarealcontroller", false, false); + assert_se(condition); + assert_se(condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, "thisisnotarealcontroller", false, true); + assert_se(condition); + assert_se(!condition_test(condition)); + condition_free(condition); + + assert_se(cg_mask_supported(&system_mask) >= 0); + + /* Individual valid controllers one by one */ + for (controller = 0; controller < _CGROUP_CONTROLLER_MAX; controller++) { + const char *local_controller_name = cgroup_controller_to_string(controller); + log_info("chosen controller is '%s'", local_controller_name); + if (system_mask & CGROUP_CONTROLLER_TO_MASK(controller)) { + log_info("this controller is available"); + condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, local_controller_name, false, false); + assert_se(condition); + assert_se(condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, local_controller_name, false, true); + assert_se(condition); + assert_se(!condition_test(condition)); + condition_free(condition); + } else { + log_info("this controller is unavailable"); + condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, local_controller_name, false, false); + assert_se(condition); + assert_se(!condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, local_controller_name, false, true); + assert_se(condition); + assert_se(condition_test(condition)); + condition_free(condition); + } + } + + /* Multiple valid controllers at the same time */ + assert_se(cg_mask_to_string(system_mask, &controller_name) >= 0); + + condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, strempty(controller_name), false, false); + assert_se(condition); + assert_se(condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, strempty(controller_name), false, true); + assert_se(condition); + assert_se(!condition_test(condition)); + condition_free(condition); + + return EXIT_SUCCESS; +} + static void test_condition_test_ac_power(void) { Condition *condition; @@ -225,6 +300,126 @@ static void test_condition_test_kernel_command_line(void) { condition_free(condition); } +static void test_condition_test_kernel_version(void) { + Condition *condition; + struct utsname u; + const char *v; + + condition = condition_new(CONDITION_KERNEL_VERSION, "*thisreallyshouldntbeinthekernelversion*", false, false); + assert_se(condition); + assert_se(!condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_KERNEL_VERSION, "*", false, false); + assert_se(condition); + assert_se(condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_KERNEL_VERSION, "", false, false); + assert_se(condition); + assert_se(!condition_test(condition)); + condition_free(condition); + + assert_se(uname(&u) >= 0); + + condition = condition_new(CONDITION_KERNEL_VERSION, u.release, false, false); + assert_se(condition); + assert_se(condition_test(condition)); + condition_free(condition); + + strshorten(u.release, 4); + strcpy(strchr(u.release, 0), "*"); + + condition = condition_new(CONDITION_KERNEL_VERSION, u.release, false, false); + assert_se(condition); + assert_se(condition_test(condition)); + condition_free(condition); + + /* 0.1.2 would be a very very very old kernel */ + condition = condition_new(CONDITION_KERNEL_VERSION, "> 0.1.2", false, false); + assert_se(condition); + assert_se(condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_KERNEL_VERSION, ">= 0.1.2", false, false); + assert_se(condition); + assert_se(condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_KERNEL_VERSION, "< 0.1.2", false, false); + assert_se(condition); + assert_se(!condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_KERNEL_VERSION, "<= 0.1.2", false, false); + assert_se(condition); + assert_se(!condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_KERNEL_VERSION, "= 0.1.2", false, false); + assert_se(condition); + assert_se(!condition_test(condition)); + condition_free(condition); + + /* 4711.8.15 is a very very very future kernel */ + condition = condition_new(CONDITION_KERNEL_VERSION, "< 4711.8.15", false, false); + assert_se(condition); + assert_se(condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_KERNEL_VERSION, "<= 4711.8.15", false, false); + assert_se(condition); + assert_se(condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_KERNEL_VERSION, "= 4711.8.15", false, false); + assert_se(condition); + assert_se(!condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_KERNEL_VERSION, "> 4711.8.15", false, false); + assert_se(condition); + assert_se(!condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_KERNEL_VERSION, ">= 4711.8.15", false, false); + assert_se(condition); + assert_se(!condition_test(condition)); + condition_free(condition); + + assert_se(uname(&u) >= 0); + + v = strjoina(">=", u.release); + condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false); + assert_se(condition); + assert_se(condition_test(condition)); + condition_free(condition); + + v = strjoina("= ", u.release); + condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false); + assert_se(condition); + assert_se(condition_test(condition)); + condition_free(condition); + + v = strjoina("<=", u.release); + condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false); + assert_se(condition); + assert_se(condition_test(condition)); + condition_free(condition); + + v = strjoina("> ", u.release); + condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false); + assert_se(condition); + assert_se(!condition_test(condition)); + condition_free(condition); + + v = strjoina("< ", u.release); + condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false); + assert_se(condition); + assert_se(!condition_test(condition)); + condition_free(condition); +} + static void test_condition_test_null(void) { Condition *condition; @@ -483,11 +678,13 @@ int main(int argc, char *argv[]) { test_condition_test_host(); test_condition_test_architecture(); test_condition_test_kernel_command_line(); + test_condition_test_kernel_version(); test_condition_test_null(); test_condition_test_security(); test_condition_test_virtualization(); test_condition_test_user(); test_condition_test_group(); + test_condition_test_control_group_controller(); return 0; } diff --git a/src/test/test-extract-word.c b/src/test/test-extract-word.c index 84ab083e87..3e7c197cfe 100644 --- a/src/test/test-extract-word.c +++ b/src/test/test-extract-word.c @@ -19,6 +19,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <stdlib.h> #include <string.h> diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index 86d963c4c7..9f3a500080 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -22,21 +22,25 @@ #include "alloc-util.h" #include "fd-util.h" +#include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "id128-util.h" #include "macro.h" #include "mkdir.h" #include "path-util.h" #include "rm-rf.h" +#include "stdio-util.h" #include "string-util.h" #include "strv.h" +#include "user-util.h" #include "util.h" static void test_chase_symlinks(void) { _cleanup_free_ char *result = NULL; char temp[] = "/tmp/test-chase.XXXXXX"; const char *top, *p, *pslash, *q, *qslash; - int r; + int r, pfd; assert_se(mkdtemp(temp)); @@ -235,6 +239,55 @@ static void test_chase_symlinks(void) { r = chase_symlinks(p, NULL, 0, &result); assert_se(r == -ENOENT); + if (geteuid() == 0) { + p = strjoina(temp, "/priv1"); + assert_se(mkdir(p, 0755) >= 0); + + q = strjoina(p, "/priv2"); + assert_se(mkdir(q, 0755) >= 0); + + assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0); + + assert_se(chown(q, UID_NOBODY, GID_NOBODY) >= 0); + assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0); + + assert_se(chown(p, UID_NOBODY, GID_NOBODY) >= 0); + assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0); + + assert_se(chown(q, 0, 0) >= 0); + assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) == -EPERM); + + assert_se(rmdir(q) >= 0); + assert_se(symlink("/etc/passwd", q) >= 0); + assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) == -EPERM); + + assert_se(chown(p, 0, 0) >= 0); + assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0); + } + + p = strjoina(temp, "/machine-id-test"); + assert_se(symlink("/usr/../etc/./machine-id", p) >= 0); + + pfd = chase_symlinks(p, NULL, CHASE_OPEN, NULL); + if (pfd != -ENOENT) { + char procfs[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(pfd) + 1]; + _cleanup_close_ int fd = -1; + sd_id128_t a, b; + + assert_se(pfd >= 0); + + xsprintf(procfs, "/proc/self/fd/%i", pfd); + + fd = open(procfs, O_RDONLY|O_CLOEXEC); + assert_se(fd >= 0); + + safe_close(pfd); + + assert_se(id128_read_fd(fd, ID128_PLAIN, &a) >= 0); + assert_se(sd_id128_get_machine(&b) >= 0); + assert_se(sd_id128_equal(a, b)); + } + assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); } @@ -273,7 +326,7 @@ static void test_readlink_and_make_absolute(void) { free(r); assert_se(unlink(name_alias) >= 0); - assert_se(pwd = get_current_dir_name()); + assert_se(safe_getcwd(&pwd) >= 0); assert_se(chdir(tempdir) >= 0); assert_se(symlink(name2, name_alias) >= 0); @@ -388,6 +441,92 @@ static void test_access_fd(void) { } } +static void test_touch_file(void) { + uid_t test_uid, test_gid; + _cleanup_(rm_rf_physical_and_freep) char *p = NULL; + struct stat st; + const char *a; + usec_t test_mtime; + + test_uid = geteuid() == 0 ? 65534 : getuid(); + test_gid = geteuid() == 0 ? 65534 : getgid(); + + test_mtime = usec_sub_unsigned(now(CLOCK_REALTIME), USEC_PER_WEEK); + + assert_se(mkdtemp_malloc("/dev/shm/touch-file-XXXXXX", &p) >= 0); + + a = strjoina(p, "/regular"); + assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0); + assert_se(lstat(a, &st) >= 0); + assert_se(st.st_uid == test_uid); + assert_se(st.st_gid == test_gid); + assert_se(S_ISREG(st.st_mode)); + assert_se((st.st_mode & 0777) == 0640); + assert_se(timespec_load(&st.st_mtim) == test_mtime); + + a = strjoina(p, "/dir"); + assert_se(mkdir(a, 0775) >= 0); + assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0); + assert_se(lstat(a, &st) >= 0); + assert_se(st.st_uid == test_uid); + assert_se(st.st_gid == test_gid); + assert_se(S_ISDIR(st.st_mode)); + assert_se((st.st_mode & 0777) == 0640); + assert_se(timespec_load(&st.st_mtim) == test_mtime); + + a = strjoina(p, "/fifo"); + assert_se(mkfifo(a, 0775) >= 0); + assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0); + assert_se(lstat(a, &st) >= 0); + assert_se(st.st_uid == test_uid); + assert_se(st.st_gid == test_gid); + assert_se(S_ISFIFO(st.st_mode)); + assert_se((st.st_mode & 0777) == 0640); + assert_se(timespec_load(&st.st_mtim) == test_mtime); + + a = strjoina(p, "/sock"); + assert_se(mknod(a, 0775 | S_IFSOCK, 0) >= 0); + assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0); + assert_se(lstat(a, &st) >= 0); + assert_se(st.st_uid == test_uid); + assert_se(st.st_gid == test_gid); + assert_se(S_ISSOCK(st.st_mode)); + assert_se((st.st_mode & 0777) == 0640); + assert_se(timespec_load(&st.st_mtim) == test_mtime); + + if (geteuid() == 0) { + a = strjoina(p, "/cdev"); + assert_se(mknod(a, 0775 | S_IFCHR, makedev(0, 0)) >= 0); + assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0); + assert_se(lstat(a, &st) >= 0); + assert_se(st.st_uid == test_uid); + assert_se(st.st_gid == test_gid); + assert_se(S_ISCHR(st.st_mode)); + assert_se((st.st_mode & 0777) == 0640); + assert_se(timespec_load(&st.st_mtim) == test_mtime); + + a = strjoina(p, "/bdev"); + assert_se(mknod(a, 0775 | S_IFBLK, makedev(0, 0)) >= 0); + assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0); + assert_se(lstat(a, &st) >= 0); + assert_se(st.st_uid == test_uid); + assert_se(st.st_gid == test_gid); + assert_se(S_ISBLK(st.st_mode)); + assert_se((st.st_mode & 0777) == 0640); + assert_se(timespec_load(&st.st_mtim) == test_mtime); + } + + a = strjoina(p, "/lnk"); + assert_se(symlink("target", a) >= 0); + assert_se(touch_file(a, false, test_mtime, test_uid, test_gid, 0640) >= 0); + assert_se(lstat(a, &st) >= 0); + assert_se(st.st_uid == test_uid); + assert_se(st.st_gid == test_gid); + assert_se(S_ISLNK(st.st_mode)); + assert_se((st.st_mode & 0777) == 0640); + assert_se(timespec_load(&st.st_mtim) == test_mtime); +} + int main(int argc, char *argv[]) { test_unlink_noerrno(); test_get_files_in_directory(); @@ -396,6 +535,7 @@ int main(int argc, char *argv[]) { test_chase_symlinks(); test_dot_or_dot_dot(); test_access_fd(); + test_touch_file(); return 0; } diff --git a/src/test/test-hash.c b/src/test/test-hash.c index f3b4258d6b..0366727476 100644 --- a/src/test/test-hash.c +++ b/src/test/test-hash.c @@ -18,6 +18,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <stdio.h> #include "alloc-util.h" diff --git a/src/test/test-hexdecoct.c b/src/test/test-hexdecoct.c index 4f19cb406f..3e25a0bac8 100644 --- a/src/test/test-hexdecoct.c +++ b/src/test/test-hexdecoct.c @@ -18,6 +18,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> + #include "alloc-util.h" #include "hexdecoct.h" #include "macro.h" diff --git a/src/test/test-log.c b/src/test/test-log.c index 9468349cba..fd19899480 100644 --- a/src/test/test-log.c +++ b/src/test/test-log.c @@ -23,6 +23,7 @@ #include "format-util.h" #include "log.h" +#include "process-util.h" #include "util.h" assert_cc(LOG_REALM_REMOVE_LEVEL(LOG_REALM_PLUS_LEVEL(LOG_REALM_SYSTEMD, LOG_FTP | LOG_DEBUG)) diff --git a/src/test/test-ns.c b/src/test/test-ns.c index 76e2b38b17..87b4facb85 100644 --- a/src/test/test-ns.c +++ b/src/test/test-ns.c @@ -18,6 +18,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <stdlib.h> #include <unistd.h> diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c index 8259e133c3..9375002133 100644 --- a/src/test/test-parse-util.c +++ b/src/test/test-parse-util.c @@ -19,6 +19,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <locale.h> #include <math.h> diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c index a38f917961..72edcbb7d6 100644 --- a/src/test/test-process-util.c +++ b/src/test/test-process-util.c @@ -38,6 +38,7 @@ #include "macro.h" #include "parse-util.h" #include "process-util.h" +#include "signal-util.h" #include "stdio-util.h" #include "string-util.h" #include "terminal-util.h" @@ -353,7 +354,7 @@ static void test_get_process_cmdline_harder(void) { line = mfree(line); safe_close(fd); - _exit(0); + _exit(EXIT_SUCCESS); } static void test_rename_process_now(const char *p, int ret) { @@ -462,7 +463,7 @@ static void test_getpid_cached(void) { c = getpid(); assert_se(a == b && a == c); - _exit(0); + _exit(EXIT_SUCCESS); } d = raw_getpid(); @@ -497,6 +498,47 @@ static void test_getpid_measure(void) { log_info("getpid_cached(): %llu/s\n", (unsigned long long) (MEASURE_ITERATIONS*USEC_PER_SEC/q)); } +static void test_safe_fork(void) { + siginfo_t status; + pid_t pid; + int r; + + BLOCK_SIGNALS(SIGCHLD); + + r = safe_fork("(test-child)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_NULL_STDIO|FORK_REOPEN_LOG, &pid); + assert_se(r >= 0); + + if (r == 0) { + /* child */ + usleep(100 * USEC_PER_MSEC); + + _exit(88); + } + + assert_se(wait_for_terminate(pid, &status) >= 0); + assert_se(status.si_code == CLD_EXITED); + assert_se(status.si_status == 88); +} + +static void test_pid_to_ptr(void) { + + assert_se(PTR_TO_PID(NULL) == 0); + assert_se(PID_TO_PTR(0) == NULL); + + assert_se(PTR_TO_PID(PID_TO_PTR(1)) == 1); + assert_se(PTR_TO_PID(PID_TO_PTR(2)) == 2); + assert_se(PTR_TO_PID(PID_TO_PTR(-1)) == -1); + assert_se(PTR_TO_PID(PID_TO_PTR(-2)) == -2); + + assert_se(PTR_TO_PID(PID_TO_PTR(INT16_MAX)) == INT16_MAX); + assert_se(PTR_TO_PID(PID_TO_PTR(INT16_MIN)) == INT16_MIN); + +#if SIZEOF_PID_T >= 4 + assert_se(PTR_TO_PID(PID_TO_PTR(INT32_MAX)) == INT32_MAX); + assert_se(PTR_TO_PID(PID_TO_PTR(INT32_MIN)) == INT32_MIN); +#endif +} + int main(int argc, char *argv[]) { log_set_max_level(LOG_DEBUG); @@ -523,6 +565,8 @@ int main(int argc, char *argv[]) { test_rename_process(); test_getpid_cached(); test_getpid_measure(); + test_safe_fork(); + test_pid_to_ptr(); return 0; } diff --git a/src/test/test-procfs-util.c b/src/test/test-procfs-util.c new file mode 100644 index 0000000000..a253182517 --- /dev/null +++ b/src/test/test-procfs-util.c @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include <errno.h> + +#include "log.h" +#include "procfs-util.h" + +int main(int argc, char *argv[]) { + uint64_t v; + int r; + + log_parse_environment(); + log_open(); + + assert_se(procfs_tasks_get_current(&v) >= 0); + log_info("Current number of tasks: %" PRIu64, v); + + assert_se(procfs_tasks_get_limit(&v) >= 0); + log_info("Limit of tasks: %" PRIu64, v); + assert_se(v > 0); + assert_se(procfs_tasks_set_limit(v) >= 0); + + if (v > 100) { + uint64_t w; + r = procfs_tasks_set_limit(v-1); + assert_se(IN_SET(r, 0, -EPERM, -EACCES, -EROFS)); + + assert_se(procfs_tasks_get_limit(&w) >= 0); + assert_se((r == 0 && w == v - 1) || (r < 0 && w == v)); + + assert_se(procfs_tasks_set_limit(v) >= 0); + + assert_se(procfs_tasks_get_limit(&w) >= 0); + assert_se(v == w); + } + + return 0; +} diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c index 36b49ebc71..aed307077e 100644 --- a/src/test/test-seccomp.c +++ b/src/test/test-seccomp.c @@ -141,7 +141,7 @@ static void test_filter_sets(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn(syscall_filter_sets[i].name, pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_check(syscall_filter_sets[i].name, pid, WAIT_LOG) == EXIT_SUCCESS); } } @@ -227,7 +227,7 @@ static void test_restrict_namespace(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn("nsseccomp", pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_check("nsseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); } static void test_protect_sysctl(void) { @@ -260,7 +260,7 @@ static void test_protect_sysctl(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn("sysctlseccomp", pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_check("sysctlseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); } static void test_restrict_address_families(void) { @@ -343,7 +343,7 @@ static void test_restrict_address_families(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn("socketseccomp", pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_check("socketseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); } static void test_restrict_realtime(void) { @@ -381,7 +381,7 @@ static void test_restrict_realtime(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn("realtimeseccomp", pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_check("realtimeseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); } static void test_memory_deny_write_execute_mmap(void) { @@ -424,7 +424,7 @@ static void test_memory_deny_write_execute_mmap(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn("memoryseccomp-mmap", pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_check("memoryseccomp-mmap", pid, WAIT_LOG) == EXIT_SUCCESS); } static void test_memory_deny_write_execute_shmat(void) { @@ -471,7 +471,7 @@ static void test_memory_deny_write_execute_shmat(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn("memoryseccomp-shmat", pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_check("memoryseccomp-shmat", pid, WAIT_LOG) == EXIT_SUCCESS); } static void test_restrict_archs(void) { @@ -505,7 +505,7 @@ static void test_restrict_archs(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn("archseccomp", pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_check("archseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); } static void test_load_syscall_filter_set_raw(void) { @@ -596,7 +596,7 @@ static void test_load_syscall_filter_set_raw(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn("syscallrawseccomp", pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_check("syscallrawseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); } static void test_lock_personality(void) { @@ -643,7 +643,7 @@ static void test_lock_personality(void) { _exit(EXIT_SUCCESS); } - assert_se(wait_for_terminate_and_warn("lockpersonalityseccomp", pid, true) == EXIT_SUCCESS); + assert_se(wait_for_terminate_and_check("lockpersonalityseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); } static void test_filter_sets_ordered(void) { diff --git a/src/test/test-signal-util.c b/src/test/test-signal-util.c index 13a1d2ba1f..f4b19ed69d 100644 --- a/src/test/test-signal-util.c +++ b/src/test/test-signal-util.c @@ -23,6 +23,7 @@ #include "macro.h" #include "signal-util.h" +#include "process-util.h" static void test_block_signals(void) { sigset_t ss; diff --git a/src/test/test-sizeof.c b/src/test/test-sizeof.c index f472edcfa1..aed6db8423 100644 --- a/src/test/test-sizeof.c +++ b/src/test/test-sizeof.c @@ -19,6 +19,7 @@ ***/ #include <stdio.h> +#include <string.h> #include "time-util.h" @@ -62,6 +63,7 @@ int main(void) { info(usec_t); info(__time_t); info(pid_t); + info(uid_t); info(gid_t); info(enum Enum); diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c index 6a91baf6d2..d1ab7486ed 100644 --- a/src/test/test-socket-util.c +++ b/src/test/test-socket-util.c @@ -18,12 +18,17 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <sys/types.h> +#include <unistd.h> +#include <grp.h> + #include "alloc-util.h" #include "async.h" #include "fd-util.h" #include "in-addr-util.h" #include "log.h" #include "macro.h" +#include "process-util.h" #include "socket-util.h" #include "string-util.h" #include "util.h" @@ -474,6 +479,68 @@ static void test_in_addr_is_multicast(void) { assert_se(in_addr_is_multicast(f, &b) == 0); } +static void test_getpeercred_getpeergroups(void) { + int r; + + r = safe_fork("(getpeercred)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + assert_se(r >= 0); + + if (r == 0) { + static const gid_t gids[] = { 3, 4, 5, 6, 7 }; + gid_t *test_gids; + _cleanup_free_ gid_t *peer_groups = NULL; + size_t n_test_gids; + uid_t test_uid; + gid_t test_gid; + struct ucred ucred; + int pair[2]; + + if (geteuid() == 0) { + test_uid = 1; + test_gid = 2; + test_gids = (gid_t*) gids; + n_test_gids = ELEMENTSOF(gids); + + assert_se(setgroups(n_test_gids, test_gids) >= 0); + assert_se(setresgid(test_gid, test_gid, test_gid) >= 0); + assert_se(setresuid(test_uid, test_uid, test_uid) >= 0); + + } else { + long ngroups_max; + + test_uid = getuid(); + test_gid = getgid(); + + ngroups_max = sysconf(_SC_NGROUPS_MAX); + assert(ngroups_max > 0); + + test_gids = newa(gid_t, ngroups_max); + + r = getgroups(ngroups_max, test_gids); + assert_se(r >= 0); + n_test_gids = (size_t) r; + } + + assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) >= 0); + + assert_se(getpeercred(pair[0], &ucred) >= 0); + + assert_se(ucred.uid == test_uid); + assert_se(ucred.gid == test_gid); + assert_se(ucred.pid == getpid_cached()); + + r = getpeergroups(pair[0], &peer_groups); + assert_se(r >= 0 || IN_SET(r, -EOPNOTSUPP, -ENOPROTOOPT)); + + if (r >= 0) { + assert_se((size_t) r == n_test_gids); + assert_se(memcmp(peer_groups, test_gids, sizeof(gid_t) * n_test_gids) == 0); + } + + safe_close_pair(pair); + } +} + int main(int argc, char *argv[]) { log_set_max_level(LOG_DEBUG); @@ -502,5 +569,7 @@ int main(int argc, char *argv[]) { test_in_addr_is_multicast(); + test_getpeercred_getpeergroups(); + return 0; } diff --git a/src/test/test-strip-tab-ansi.c b/src/test/test-strip-tab-ansi.c index aabb7c40e7..838a6e4db6 100644 --- a/src/test/test-strip-tab-ansi.c +++ b/src/test/test-strip-tab-ansi.c @@ -28,24 +28,24 @@ int main(int argc, char *argv[]) { char *p; assert_se(p = strdup("\tFoobar\tbar\twaldo\t")); - assert_se(strip_tab_ansi(&p, NULL)); + assert_se(strip_tab_ansi(&p, NULL, NULL)); fprintf(stdout, "<%s>\n", p); assert_se(streq(p, " Foobar bar waldo ")); free(p); assert_se(p = strdup(ANSI_HIGHLIGHT "Hello" ANSI_NORMAL ANSI_HIGHLIGHT_RED " world!" ANSI_NORMAL)); - assert_se(strip_tab_ansi(&p, NULL)); + assert_se(strip_tab_ansi(&p, NULL, NULL)); fprintf(stdout, "<%s>\n", p); assert_se(streq(p, "Hello world!")); free(p); assert_se(p = strdup("\x1B[\x1B[\t\x1B[" ANSI_HIGHLIGHT "\x1B[" "Hello" ANSI_NORMAL ANSI_HIGHLIGHT_RED " world!" ANSI_NORMAL)); - assert_se(strip_tab_ansi(&p, NULL)); + assert_se(strip_tab_ansi(&p, NULL, NULL)); assert_se(streq(p, "\x1B[\x1B[ \x1B[\x1B[Hello world!")); free(p); assert_se(p = strdup("\x1B[waldo")); - assert_se(strip_tab_ansi(&p, NULL)); + assert_se(strip_tab_ansi(&p, NULL, NULL)); assert_se(streq(p, "\x1B[waldo")); free(p); diff --git a/src/test/test-tmpfiles.c b/src/test/test-tmpfiles.c index c479eccb8b..8e57fe0461 100644 --- a/src/test/test-tmpfiles.c +++ b/src/test/test-tmpfiles.c @@ -29,6 +29,7 @@ #include "format-util.h" #include "fs-util.h" #include "log.h" +#include "process-util.h" #include "string-util.h" #include "util.h" diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c index e24892b590..416542c83f 100644 --- a/src/test/test-unit-name.c +++ b/src/test/test-unit-name.c @@ -32,6 +32,7 @@ #include "manager.h" #include "path-util.h" #include "rm-rf.h" +#include "special.h" #include "specifier.h" #include "string-util.h" #include "test-helper.h" @@ -338,7 +339,7 @@ static void test_unit_name_build(void) { } static void test_slice_name_is_valid(void) { - assert_se(slice_name_is_valid("-.slice")); + assert_se(slice_name_is_valid(SPECIAL_ROOT_SLICE)); assert_se(slice_name_is_valid("foo.slice")); assert_se(slice_name_is_valid("foo-bar.slice")); assert_se(slice_name_is_valid("foo-bar-baz.slice")); @@ -356,7 +357,7 @@ static void test_build_subslice(void) { char *a; char *b; - assert_se(slice_build_subslice("-.slice", "foo", &a) >= 0); + assert_se(slice_build_subslice(SPECIAL_ROOT_SLICE, "foo", &a) >= 0); assert_se(slice_build_subslice(a, "bar", &b) >= 0); free(a); assert_se(slice_build_subslice(b, "barfoo", &a) >= 0); @@ -378,8 +379,8 @@ static void test_build_parent_slice_one(const char *name, const char *expect, in } static void test_build_parent_slice(void) { - test_build_parent_slice_one("-.slice", NULL, 0); - test_build_parent_slice_one("foo.slice", "-.slice", 1); + test_build_parent_slice_one(SPECIAL_ROOT_SLICE, NULL, 0); + test_build_parent_slice_one("foo.slice", SPECIAL_ROOT_SLICE, 1); test_build_parent_slice_one("foo-bar.slice", "foo.slice", 1); test_build_parent_slice_one("foo-bar-baz.slice", "foo-bar.slice", 1); test_build_parent_slice_one("foo-bar--baz.slice", NULL, -EINVAL); diff --git a/src/test/test-util.c b/src/test/test-util.c index 2124511bf0..21d90f0888 100644 --- a/src/test/test-util.c +++ b/src/test/test-util.c @@ -28,6 +28,7 @@ #include "fileio.h" #include "fs-util.h" #include "parse-util.h" +#include "process-util.h" #include "raw-clone.h" #include "rm-rf.h" #include "string-util.h" diff --git a/src/test/test-watch-pid.c b/src/test/test-watch-pid.c new file mode 100644 index 0000000000..ed6c3d05cc --- /dev/null +++ b/src/test/test-watch-pid.c @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "log.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; + Unit *a, *b, *c, *u; + Manager *m; + int r; + + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + if (getuid() != 0) { + log_notice("Not running as root, skipping kernel related tests."); + return EXIT_TEST_SKIP; + } + + r = enter_cgroup_subroot(); + if (r == -ENOMEDIUM) { + log_notice("cgroupfs not available, skipping tests"); + return EXIT_TEST_SKIP; + } + + assert_se(set_unit_path(get_testdata_dir("")) >= 0); + assert_se(runtime_dir = setup_fake_runtime_dir()); + + assert_se(manager_new(UNIT_FILE_USER, true, &m) >= 0); + assert_se(manager_startup(m, NULL, NULL) >= 0); + + assert_se(a = unit_new(m, sizeof(Service))); + assert_se(unit_add_name(a, "a.service") >= 0); + assert_se(set_isempty(a->pids)); + + assert_se(b = unit_new(m, sizeof(Service))); + assert_se(unit_add_name(b, "b.service") >= 0); + assert_se(set_isempty(b->pids)); + + assert_se(c = unit_new(m, sizeof(Service))); + assert_se(unit_add_name(c, "c.service") >= 0); + assert_se(set_isempty(c->pids)); + + assert_se(hashmap_isempty(m->watch_pids)); + assert_se(manager_get_unit_by_pid(m, 4711) == NULL); + + assert_se(unit_watch_pid(a, 4711) >= 0); + assert_se(manager_get_unit_by_pid(m, 4711) == a); + + assert_se(unit_watch_pid(a, 4711) >= 0); + assert_se(manager_get_unit_by_pid(m, 4711) == a); + + assert_se(unit_watch_pid(b, 4711) >= 0); + u = manager_get_unit_by_pid(m, 4711); + assert_se(u == a || u == b); + + assert_se(unit_watch_pid(b, 4711) >= 0); + u = manager_get_unit_by_pid(m, 4711); + assert_se(u == a || u == b); + + assert_se(unit_watch_pid(c, 4711) >= 0); + u = manager_get_unit_by_pid(m, 4711); + assert_se(u == a || u == b || u == c); + + assert_se(unit_watch_pid(c, 4711) >= 0); + u = manager_get_unit_by_pid(m, 4711); + assert_se(u == a || u == b || u == c); + + unit_unwatch_pid(b, 4711); + u = manager_get_unit_by_pid(m, 4711); + assert_se(u == a || u == c); + + unit_unwatch_pid(b, 4711); + u = manager_get_unit_by_pid(m, 4711); + assert_se(u == a || u == c); + + unit_unwatch_pid(a, 4711); + assert_se(manager_get_unit_by_pid(m, 4711) == c); + + unit_unwatch_pid(a, 4711); + assert_se(manager_get_unit_by_pid(m, 4711) == c); + + unit_unwatch_pid(c, 4711); + assert_se(manager_get_unit_by_pid(m, 4711) == NULL); + + unit_unwatch_pid(c, 4711); + assert_se(manager_get_unit_by_pid(m, 4711) == NULL); + + manager_free(m); + + return 0; +} diff --git a/src/test/test-watchdog.c b/src/test/test-watchdog.c index e068d1ddd4..ffcf408f57 100644 --- a/src/test/test-watchdog.c +++ b/src/test/test-watchdog.c @@ -18,6 +18,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <string.h> #include <unistd.h> #include "env-util.h" diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c index d80a917870..19a382c1b2 100644 --- a/src/timedate/timedatectl.c +++ b/src/timedate/timedatectl.c @@ -129,8 +129,8 @@ static void print_status_info(const StatusInfo *i) { "systemd-timesyncd.service active: %s\n" " RTC in local TZ: %s\n", strna(i->timezone), have_time && n > 0 ? a : "n/a", - i->ntp_capable ? yes_no(i->ntp_enabled) : "n/a", yes_no(i->ntp_synced), + i->ntp_capable ? yes_no(i->ntp_enabled) : "n/a", yes_no(i->rtc_local)); if (i->rtc_local) @@ -473,7 +473,7 @@ static int timedatectl_main(sd_bus *bus, int argc, char *argv[]) { } int main(int argc, char *argv[]) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + sd_bus *bus = NULL; int r; setlocale(LC_ALL, ""); @@ -493,6 +493,9 @@ int main(int argc, char *argv[]) { r = timedatectl_main(bus, argc, argv); finish: + /* make sure we terminate the bus connection first, and then close the + * pager, see issue #3543 for the details. */ + sd_bus_flush_close_unref(bus); pager_close(); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c index a55a929a49..822835cce9 100644 --- a/src/timedate/timedated.c +++ b/src/timedate/timedated.c @@ -675,9 +675,9 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { if (r < 0) return log_error_errno(r, "Failed to register object: %m"); - r = sd_bus_request_name(bus, "org.freedesktop.timedate1", 0); + r = sd_bus_request_name_async(bus, NULL, "org.freedesktop.timedate1", 0, NULL, NULL); if (r < 0) - return log_error_errno(r, "Failed to register name: %m"); + return log_error_errno(r, "Failed to request name: %m"); r = sd_bus_attach_event(bus, event, 0); if (r < 0) diff --git a/src/timesync/timesyncd-gperf.gperf b/src/timesync/timesyncd-gperf.gperf index 7d4cd2808e..b5020276af 100644 --- a/src/timesync/timesyncd-gperf.gperf +++ b/src/timesync/timesyncd-gperf.gperf @@ -10,7 +10,7 @@ struct ConfigPerfItem; %null_strings %language=ANSI-C %define slot-name section_and_lvalue -%define hash-function-name timesyncdd_gperf_hash +%define hash-function-name timesyncd_gperf_hash %define lookup-function-name timesyncd_gperf_lookup %readonly-tables %omit-struct-type diff --git a/src/timesync/timesyncd-manager.c b/src/timesync/timesyncd-manager.c index 8bd111fe0c..a6d336c461 100644 --- a/src/timesync/timesyncd-manager.c +++ b/src/timesync/timesyncd-manager.c @@ -552,7 +552,7 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re /* check our "time cookie" (we just stored nanoseconds in the fraction field) */ if (be32toh(ntpmsg.origin_time.sec) != m->trans_time.tv_sec + OFFSET_1900_1970 || - be32toh(ntpmsg.origin_time.frac) != m->trans_time.tv_nsec) { + be32toh(ntpmsg.origin_time.frac) != (unsigned long) m->trans_time.tv_nsec) { log_debug("Invalid reply; not our transmit time. Ignoring."); return 0; } diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c index 962285f7b1..bea800171b 100644 --- a/src/timesync/timesyncd.c +++ b/src/timesync/timesyncd.c @@ -66,6 +66,7 @@ static int load_clock_timestamp(uid_t uid, gid_t gid) { if (r < 0) return log_error_errno(errno, "Failed to change file access mode: %m"); r = fchown(fd, uid, gid); + if (r < 0) return log_error_errno(errno, "Failed to change file owner: %m"); } @@ -96,7 +97,7 @@ static int load_clock_timestamp(uid_t uid, gid_t gid) { int main(int argc, char *argv[]) { _cleanup_(manager_freep) Manager *m = NULL; const char *user = "systemd-timesync"; - uid_t uid; + uid_t uid, uid_current; gid_t gid; int r; @@ -113,10 +114,15 @@ int main(int argc, char *argv[]) { goto finish; } - 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; + uid = uid_current = geteuid(); + gid = getegid(); + + if (uid_current == 0) { + 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 = load_clock_timestamp(uid, gid); @@ -125,7 +131,7 @@ int main(int argc, char *argv[]) { /* Drop privileges, but only if we have been started as root. If we are not running as root we assume all * privileges are already dropped. */ - if (geteuid() == 0) { + if (uid_current == 0) { r = drop_privileges(uid, gid, (1ULL << CAP_SYS_TIME)); if (r < 0) goto finish; diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index a7ce1a8049..38cbb739c0 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -199,12 +199,12 @@ static const Specifier specifier_table[] = { static int specifier_machine_id_safe(char specifier, void *data, void *userdata, char **ret) { int r; - /* If /etc/machine_id is missing (e.g. in a chroot environment), returns - * a recognizable error so that the caller can skip the rule + /* If /etc/machine_id is missing or empty (e.g. in a chroot environment) + * return a recognizable error so that the caller can skip the rule * gracefully. */ r = specifier_machine_id(specifier, data, userdata, ret); - if (r == -ENOENT) + if (IN_SET(r, -ENOENT, -ENOMEDIUM)) return -ENXIO; return r; @@ -375,35 +375,47 @@ static struct Item* find_glob(OrderedHashmap *h, const char *match) { static void load_unix_sockets(void) { _cleanup_fclose_ FILE *f = NULL; - char line[LINE_MAX]; + int r; if (unix_sockets) return; - /* We maintain a cache of the sockets we found in - * /proc/net/unix to speed things up a little. */ + /* We maintain a cache of the sockets we found in /proc/net/unix to speed things up a little. */ unix_sockets = set_new(&string_hash_ops); if (!unix_sockets) return; f = fopen("/proc/net/unix", "re"); - if (!f) - return; + if (!f) { + log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno, + "Failed to open /proc/net/unix, ignoring: %m"); + goto fail; + } /* Skip header */ - if (!fgets(line, sizeof(line), f)) + r = read_line(f, LONG_LINE_MAX, NULL); + if (r < 0) { + log_warning_errno(r, "Failed to skip /proc/net/unix header line: %m"); + goto fail; + } + if (r == 0) { + log_warning("Premature end of file reading /proc/net/unix."); goto fail; + } for (;;) { + _cleanup_free_ char *line = NULL; char *p, *s; - int k; - if (!fgets(line, sizeof(line), f)) + r = read_line(f, LONG_LINE_MAX, &line); + if (r < 0) { + log_warning_errno(r, "Failed to read /proc/net/unix line, ignoring: %m"); + goto fail; + } + if (r == 0) /* EOF */ break; - truncate_nl(line); - p = strchr(line, ':'); if (!p) continue; @@ -420,21 +432,24 @@ static void load_unix_sockets(void) { continue; s = strdup(p); - if (!s) + if (!s) { + log_oom(); goto fail; + } path_kill_slashes(s); - k = set_consume(unix_sockets, s); - if (k < 0 && k != -EEXIST) + r = set_consume(unix_sockets, s); + if (r < 0 && r != -EEXIST) { + log_warning_errno(r, "Failed to add AF_UNIX socket to set, ignoring: %m"); goto fail; + } } return; fail: - set_free_free(unix_sockets); - unix_sockets = NULL; + unix_sockets = set_free_free(unix_sockets); } static bool unix_socket_alive(const char *fn) { @@ -532,11 +547,8 @@ static int dir_cleanup( continue; /* FUSE, NFS mounts, SELinux might return EACCES */ - if (errno == EACCES) - log_debug_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name); - else - log_error_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name); - r = -errno; + r = log_full_errno(errno == EACCES ? LOG_DEBUG : LOG_ERR, errno, + "stat(%s/%s) failed: %m", p, dent->d_name); continue; } @@ -640,10 +652,8 @@ static int dir_cleanup( log_debug("Removing directory \"%s\".", sub_path); if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) - if (!IN_SET(errno, ENOENT, ENOTEMPTY)) { - log_error_errno(errno, "rmdir(%s): %m", sub_path); - r = -errno; - } + if (!IN_SET(errno, ENOENT, ENOTEMPTY)) + r = log_error_errno(errno, "rmdir(%s): %m", sub_path); } else { /* Skip files for which the sticky bit is @@ -743,13 +753,50 @@ finish: return r; } +static bool dangerous_hardlinks(void) { + _cleanup_free_ char *value = NULL; + static int cached = -1; + int r; + + /* Check whether the fs.protected_hardlinks sysctl is on. If we can't determine it we assume its off, as that's + * what the upstream default is. */ + + if (cached >= 0) + return cached; + + r = read_one_line_file("/proc/sys/fs/protected_hardlinks", &value); + if (r < 0) { + log_debug_errno(r, "Failed to read fs.protected_hardlinks sysctl: %m"); + return true; + } + + r = parse_boolean(value); + if (r < 0) { + log_debug_errno(r, "Failed to parse fs.protected_hardlinks sysctl: %m"); + return true; + } + + cached = r == 0; + return cached; +} + +static bool hardlink_vulnerable(struct stat *st) { + assert(st); + + return !S_ISDIR(st->st_mode) && st->st_nlink > 1 && dangerous_hardlinks(); +} + static int path_set_perms(Item *i, const char *path) { + char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; _cleanup_close_ int fd = -1; struct stat st; assert(i); assert(path); + if (!i->mode_set && !i->uid_set && !i->gid_set) + goto shortcut; + /* We open the file with O_PATH here, to make the operation * somewhat atomic. Also there's unfortunately no fchmodat() * with AT_SYMLINK_NOFOLLOW, hence we emulate it here via @@ -767,21 +814,23 @@ static int path_set_perms(Item *i, const char *path) { } log_full_errno(level, errno, "Adjusting owner and mode for %s failed: %m", path); - return r; } if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0) return log_error_errno(errno, "Failed to fstat() file %s: %m", path); - if (S_ISLNK(st.st_mode)) - log_debug("Skipping mode and owner fix for symlink %s.", path); - else { - char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; - xsprintf(fn, "/proc/self/fd/%i", fd); + if (hardlink_vulnerable(&st)) { + log_error("Refusing to set permissions on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path); + return -EPERM; + } - /* not using i->path directly because it may be a glob */ - if (i->mode_set) { + xsprintf(fn, "/proc/self/fd/%i", fd); + + if (i->mode_set) { + if (S_ISLNK(st.st_mode)) + log_debug("Skipping mode fix for symlink %s.", path); + else { mode_t m = i->mode; if (i->mask_perms) { @@ -796,29 +845,32 @@ static int path_set_perms(Item *i, const char *path) { } if (m == (st.st_mode & 07777)) - log_debug("\"%s\" has right mode %o", path, st.st_mode); + log_debug("\"%s\" has correct mode %o already.", path, st.st_mode); else { - log_debug("chmod \"%s\" to mode %o", path, m); + log_debug("Changing \"%s\" to mode %o.", path, m); + if (chmod(fn, m) < 0) return log_error_errno(errno, "chmod() of %s via %s failed: %m", path, fn); } } + } - if ((i->uid != st.st_uid || i->gid != st.st_gid) && - (i->uid_set || i->gid_set)) { - log_debug("chown \"%s\" to "UID_FMT"."GID_FMT, - path, - i->uid_set ? i->uid : UID_INVALID, - i->gid_set ? i->gid : GID_INVALID); - if (chown(fn, - i->uid_set ? i->uid : UID_INVALID, - i->gid_set ? i->gid : GID_INVALID) < 0) - return log_error_errno(errno, "chown() of %s via %s failed: %m", path, fn); - } + if ((i->uid_set && i->uid != st.st_uid) || + (i->gid_set && i->gid != st.st_gid)) { + log_debug("Changing \"%s\" to owner "UID_FMT":"GID_FMT, + path, + i->uid_set ? i->uid : UID_INVALID, + i->gid_set ? i->gid : GID_INVALID); + + if (chown(fn, + i->uid_set ? i->uid : UID_INVALID, + i->gid_set ? i->gid : GID_INVALID) < 0) + return log_error_errno(errno, "chown() of %s via %s failed: %m", path, fn); } fd = safe_close(fd); +shortcut: return label_fix(path, false, false); } @@ -867,11 +919,8 @@ static int path_set_xattrs(Item *i, const char *path) { assert(path); STRV_FOREACH_PAIR(name, value, i->xattrs) { - int n; - - n = strlen(*value); log_debug("Setting extended attribute '%s=%s' on %s.", *name, *value, path); - if (lsetxattr(path, *name, *value, n, 0) < 0) + if (lsetxattr(path, *name, *value, strlen(*value), 0) < 0) return log_error_errno(errno, "Setting extended attribute %s=%s on %s failed: %m", *name, *value, path); } @@ -960,6 +1009,11 @@ static int path_set_acls(Item *item, const char *path) { if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0) return log_error_errno(errno, "Failed to fstat() file %s: %m", path); + if (hardlink_vulnerable(&st)) { + log_error("Refusing to set ACLs on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path); + return -EPERM; + } + if (S_ISLNK(st.st_mode)) { log_debug("Skipping ACL fix for symlink %s.", path); return 0; @@ -1135,7 +1189,7 @@ static int write_one_file(Item *i, const char *path) { assert(i); assert(path); - flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND|O_NOFOLLOW : + flags = i->type == CREATE_FILE ? O_CREAT|O_EXCL|O_NOFOLLOW : i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC|O_NOFOLLOW : 0; RUN_WITH_UMASK(0000) { @@ -1146,9 +1200,13 @@ static int write_one_file(Item *i, const char *path) { if (fd < 0) { if (i->type == WRITE_FILE && errno == ENOENT) { - log_debug_errno(errno, "Not writing \"%s\": %m", path); + log_debug_errno(errno, "Not writing missing file \"%s\": %m", path); return 0; } + if (i->type == CREATE_FILE && errno == EEXIST) { + log_debug_errno(errno, "Not writing to pre-existing file \"%s\": %m", path); + goto done; + } r = -errno; if (!i->argument && errno == EROFS && stat(path, &st) == 0 && @@ -1169,6 +1227,7 @@ static int write_one_file(Item *i, const char *path) { fd = safe_close(fd); +done: if (stat(path, &st) < 0) return log_error_errno(errno, "stat(%s) failed: %m", path); @@ -1289,14 +1348,24 @@ static int create_item(Item *i) { case CREATE_FILE: case TRUNCATE_FILE: + RUN_WITH_UMASK(0000) + (void) mkdir_parents_label(i->path, 0755); + r = write_one_file(i, i->path); if (r < 0) return r; break; case COPY_FILES: { + + RUN_WITH_UMASK(0000) + (void) mkdir_parents_label(i->path, 0755); + log_debug("Copying tree \"%s\" to \"%s\".", i->argument, i->path); - r = copy_tree(i->argument, i->path, i->uid_set ? i->uid : UID_INVALID, i->gid_set ? i->gid : GID_INVALID, COPY_REFLINK); + r = copy_tree(i->argument, i->path, + i->uid_set ? i->uid : UID_INVALID, + i->gid_set ? i->gid : GID_INVALID, + COPY_REFLINK); if (r == -EROFS && stat(i->path, &st) == 0) r = -EEXIST; @@ -1338,7 +1407,7 @@ static int create_item(Item *i) { case CREATE_SUBVOLUME_INHERIT_QUOTA: case CREATE_SUBVOLUME_NEW_QUOTA: RUN_WITH_UMASK(0000) - mkdir_parents_label(i->path, 0755); + (void) mkdir_parents_label(i->path, 0755); if (IN_SET(i->type, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA)) { @@ -1420,6 +1489,8 @@ static int create_item(Item *i) { case CREATE_FIFO: RUN_WITH_UMASK(0000) { + (void) mkdir_parents_label(i->path, 0755); + mac_selinux_create_file_prepare(i->path, S_IFIFO); r = mkfifo(i->path, i->mode); mac_selinux_create_file_clear(); @@ -1462,6 +1533,9 @@ static int create_item(Item *i) { } case CREATE_SYMLINK: { + RUN_WITH_UMASK(0000) + (void) mkdir_parents_label(i->path, 0755); + mac_selinux_create_file_prepare(i->path, S_IFLNK); r = symlink(i->argument, i->path); mac_selinux_create_file_clear(); @@ -1520,6 +1594,9 @@ static int create_item(Item *i) { return 0; } + RUN_WITH_UMASK(0000) + (void) mkdir_parents_label(i->path, 0755); + file_type = i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR; RUN_WITH_UMASK(0000) { @@ -2254,6 +2331,9 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool } } else { existing = new0(ItemArray, 1); + if (!existing) + return log_oom(); + r = ordered_hashmap_put(h, i.path, existing); if (r < 0) return log_oom(); @@ -2514,7 +2594,7 @@ int main(int argc, char *argv[]) { } } - { + if (DEBUG_LOGGING) { _cleanup_free_ char *t = NULL; t = strv_join(config_dirs, "\n\t"); 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 1553655a28..9dfb0d80de 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -159,7 +159,7 @@ static int ask_password_plymouth( } if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0) - flush_fd(notify); + (void) flush_fd(notify); if (pollfd[POLL_SOCKET].revents == 0) continue; @@ -417,8 +417,8 @@ static int wall_tty_block(void) { if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0) return log_oom(); - mkdir_parents_label(p, 0700); - mkfifo(p, 0600); + (void) mkdir_parents_label(p, 0700); + (void) mkfifo(p, 0600); fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); if (fd < 0) @@ -693,11 +693,13 @@ static int parse_argv(int argc, char *argv[]) { * If one of the tasks does handle a password, the remaining tasks * will be terminated. */ -static int ask_on_this_console(const char *tty, pid_t *pid, int argc, char *argv[]) { +static int ask_on_this_console(const char *tty, pid_t *ret_pid, int argc, char *argv[]) { struct sigaction sig = { .sa_handler = nop_signal_handler, .sa_flags = SA_NOCLDSTOP | SA_RESTART, }; + pid_t pid; + int r; assert_se(sigprocmask_many(SIG_UNBLOCK, NULL, SIGHUP, SIGCHLD, -1) >= 0); @@ -707,18 +709,14 @@ static int ask_on_this_console(const char *tty, pid_t *pid, int argc, char *argv sig.sa_handler = SIG_DFL; assert_se(sigaction(SIGHUP, &sig, NULL) >= 0); - *pid = fork(); - if (*pid < 0) - return log_error_errno(errno, "Failed to fork process: %m"); - - if (*pid == 0) { + r = safe_fork("(sd-passwd)", FORK_RESET_SIGNALS|FORK_LOG, &pid); + if (r < 0) + return r; + if (r == 0) { int ac; assert_se(prctl(PR_SET_PDEATHSIG, SIGHUP) >= 0); - reset_signal_mask(); - reset_all_signal_handlers(); - for (ac = 0; ac < argc; ac++) { if (streq(argv[ac], "--console")) { argv[ac] = strjoina("--console=", tty); @@ -731,6 +729,8 @@ static int ask_on_this_console(const char *tty, pid_t *pid, int argc, char *argv execv(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, argv); _exit(EXIT_FAILURE); } + + *ret_pid = pid; return 0; } diff --git a/src/udev/generate-keyboard-keys-gperf.sh b/src/udev/generate-keyboard-keys-gperf.sh index eb977447e3..efb0da2a84 100755 --- a/src/udev/generate-keyboard-keys-gperf.sh +++ b/src/udev/generate-keyboard-keys-gperf.sh @@ -1,4 +1,5 @@ -#!/bin/sh -eu +#!/bin/sh +set -eu awk ' BEGIN { print "%{\n\ #if __GNUC__ >= 7\n\ diff --git a/src/udev/generate-keyboard-keys-list.sh b/src/udev/generate-keyboard-keys-list.sh index 7a74e0dae1..c055f7c756 100755 --- a/src/udev/generate-keyboard-keys-list.sh +++ b/src/udev/generate-keyboard-keys-list.sh @@ -1,4 +1,5 @@ -#!/bin/sh -eu +#!/bin/sh +set -eu $1 -dM -include linux/input.h - </dev/null | awk ' /\<(KEY_(MAX|MIN_INTERESTING))|(BTN_(MISC|MOUSE|JOYSTICK|GAMEPAD|DIGI|WHEEL|TRIGGER_HAPPY))\>/ { next } diff --git a/src/udev/meson.build b/src/udev/meson.build index d01cf8f194..de2fd2d9c4 100644 --- a/src/udev/meson.build +++ b/src/udev/meson.build @@ -113,15 +113,36 @@ if get_option('link-udev-shared') udev_rpath = rootlibexecdir else udev_link_with = [libshared_static, - libsystemd_internal] + libsystemd_static] udev_rpath = '' endif -libudev_internal = static_library( - 'udev', +libudev_basic = static_library( + 'udev-basic', libudev_sources, include_directories : includes, - link_with : udev_link_with) + c_args : ['-fvisibility=default']) + +libudev_static = static_library( + 'udev', + 'udev.h', + include_directories : includes, + link_with : udev_link_with, + link_whole : libudev_basic) + +libudev = shared_library( + 'udev', + 'udev.h', # pick a header file at random to work around old meson bug + version : libudev_version, + include_directories : includes, + link_args : ['-shared', + '-Wl,--version-script=' + libudev_sym_path], + link_with : [libsystemd_static, libshared_static], + link_whole : libudev_basic, + dependencies : [threads], + link_depends : libudev_sym, + install : true, + install_dir : rootlibdir) libudev_core_includes = [includes, include_directories('net')] libudev_core = static_library( @@ -130,6 +151,7 @@ libudev_core = static_library( link_config_gperf_c, keyboard_keys_from_name_h, include_directories : libudev_core_includes, + c_args : ['-DLOG_REALM=LOG_REALM_UDEV'], link_with : udev_link_with, dependencies : [libblkid, libkmod]) @@ -149,7 +171,7 @@ foreach prog : [['ata_id/ata_id.c'], prog, include_directories : includes, c_args : ['-DLOG_REALM=LOG_REALM_UDEV'], - link_with : [libudev_internal], + link_with : [libudev_static], install_rpath : udev_rpath, install : true, install_dir : udevlibexecdir) diff --git a/src/udev/mtd_probe/probe_smartmedia.c b/src/udev/mtd_probe/probe_smartmedia.c index eb74fe1eb6..5d58de6a87 100644 --- a/src/udev/mtd_probe/probe_smartmedia.c +++ b/src/udev/mtd_probe/probe_smartmedia.c @@ -28,6 +28,7 @@ #include <sys/types.h> #include <unistd.h> +#include "alloc-util.h" #include "mtd_probe.h" static const uint8_t cis_signature[] = { @@ -35,16 +36,16 @@ static const uint8_t cis_signature[] = { }; -void probe_smart_media(int mtd_fd, mtd_info_t* info) -{ +void probe_smart_media(int mtd_fd, mtd_info_t* info) { int sector_size; int block_size; int size_in_megs; int spare_count; - char* cis_buffer = malloc(SM_SECTOR_SIZE); + _cleanup_free_ uint8_t *cis_buffer = NULL; int offset; int cis_found = 0; + cis_buffer = malloc(SM_SECTOR_SIZE); if (!cis_buffer) return; @@ -89,9 +90,8 @@ void probe_smart_media(int mtd_fd, mtd_info_t* info) goto exit; printf("MTD_FTL=smartmedia\n"); - free(cis_buffer); - exit(0); + exit(EXIT_SUCCESS); + exit: - free(cis_buffer); return; } diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c index 3ed8a51fd4..9bdaef8d90 100644 --- a/src/udev/net/ethtool-util.c +++ b/src/udev/net/ethtool-util.c @@ -25,13 +25,13 @@ #include "conf-parser.h" #include "ethtool-util.h" -#include "log.h" #include "link-config.h" +#include "log.h" +#include "missing.h" #include "socket-util.h" #include "string-table.h" #include "strxcpyx.h" #include "util.h" -#include "missing.h" static const char* const duplex_table[_DUP_MAX] = { [DUP_FULL] = "full", @@ -83,6 +83,7 @@ int ethtool_connect(int *ret) { fd = socket_ioctl_fd(); if (fd < 0) return fd; + *ret = fd; return 0; @@ -265,7 +266,7 @@ int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol) { return 0; } -static int ethtool_get_stringset(int *fd, struct ifreq *ifr, int stringset_id, struct ethtool_gstrings **gstrings) { +static int get_stringset(int fd, struct ifreq *ifr, int stringset_id, struct ethtool_gstrings **gstrings) { _cleanup_free_ struct ethtool_gstrings *strings = NULL; struct { struct ethtool_sset_info info; @@ -281,7 +282,7 @@ static int ethtool_get_stringset(int *fd, struct ifreq *ifr, int stringset_id, s ifr->ifr_data = (void *) &buffer.info; - r = ioctl(*fd, SIOCETHTOOL, ifr); + r = ioctl(fd, SIOCETHTOOL, ifr); if (r < 0) return -errno; @@ -300,7 +301,7 @@ static int ethtool_get_stringset(int *fd, struct ifreq *ifr, int stringset_id, s ifr->ifr_data = (void *) strings; - r = ioctl(*fd, SIOCETHTOOL, ifr); + r = ioctl(fd, SIOCETHTOOL, ifr); if (r < 0) return -errno; @@ -335,7 +336,7 @@ int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features) { strscpy(ifr.ifr_name, IFNAMSIZ, ifname); - r = ethtool_get_stringset(fd, &ifr, ETH_SS_FEATURES, &strings); + r = get_stringset(*fd, &ifr, ETH_SS_FEATURES, &strings); if (r < 0) return log_warning_errno(r, "link_config: could not get ethtool features for %s", ifname); @@ -374,7 +375,7 @@ int ethtool_set_features(int *fd, const char *ifname, NetDevFeature *features) { return 0; } -static int get_glinksettings(int *fd, struct ifreq *ifr, struct ethtool_link_usettings **g) { +static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_usettings **g) { struct ecmd { struct ethtool_link_settings req; __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32]; @@ -395,29 +396,29 @@ static int get_glinksettings(int *fd, struct ifreq *ifr, struct ethtool_link_use ifr->ifr_data = (void *) &ecmd; - r = ioctl(*fd, SIOCETHTOOL, ifr); + r = ioctl(fd, SIOCETHTOOL, ifr); if (r < 0) return -errno; if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS) - return -ENOTSUP; + return -EOPNOTSUPP; ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords; ifr->ifr_data = (void *) &ecmd; - r = ioctl(*fd, SIOCETHTOOL, ifr); + r = ioctl(fd, SIOCETHTOOL, ifr); if (r < 0) return -errno; if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS) - return -ENOTSUP; + return -EOPNOTSUPP; u = new0(struct ethtool_link_usettings , 1); if (!u) return -ENOMEM; - memcpy(&u->base, &ecmd.req, sizeof(struct ethtool_link_settings)); + ecmd.req = u->base; offset = 0; memcpy(u->link_modes.supported, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords); @@ -433,7 +434,7 @@ static int get_glinksettings(int *fd, struct ifreq *ifr, struct ethtool_link_use return 0; } -static int get_gset(int *fd, struct ifreq *ifr, struct ethtool_link_usettings **u) { +static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **u) { struct ethtool_link_usettings *e; struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET, @@ -442,7 +443,7 @@ static int get_gset(int *fd, struct ifreq *ifr, struct ethtool_link_usettings ** ifr->ifr_data = (void *) &ecmd; - r = ioctl(*fd, SIOCETHTOOL, ifr); + r = ioctl(fd, SIOCETHTOOL, ifr); if (r < 0) return -errno; @@ -469,19 +470,19 @@ static int get_gset(int *fd, struct ifreq *ifr, struct ethtool_link_usettings ** return 0; } -static int set_slinksettings(int *fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) { +static int set_slinksettings(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) { struct { struct ethtool_link_settings req; __u32 link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32]; - } ecmd = { - .req.cmd = ETHTOOL_SLINKSETTINGS, - }; + } ecmd = {}; unsigned int offset; int r; if (u->base.cmd != ETHTOOL_GLINKSETTINGS || u->base.link_mode_masks_nwords <= 0) return -EINVAL; + ecmd.req = u->base; + ecmd.req.cmd = ETHTOOL_SLINKSETTINGS; offset = 0; memcpy(&ecmd.link_mode_data[offset], u->link_modes.supported, 4 * ecmd.req.link_mode_masks_nwords); @@ -493,14 +494,14 @@ static int set_slinksettings(int *fd, struct ifreq *ifr, const struct ethtool_li ifr->ifr_data = (void *) &ecmd; - r = ioctl(*fd, SIOCETHTOOL, ifr); + r = ioctl(fd, SIOCETHTOOL, ifr); if (r < 0) return -errno; return 0; } -static int set_sset(int *fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) { +static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettings *u) { struct ethtool_cmd ecmd = { .cmd = ETHTOOL_SSET, }; @@ -523,7 +524,7 @@ static int set_sset(int *fd, struct ifreq *ifr, const struct ethtool_link_usetti ifr->ifr_data = (void *) &ecmd; - r = ioctl(*fd, SIOCETHTOOL, ifr); + r = ioctl(fd, SIOCETHTOOL, ifr); if (r < 0) return -errno; @@ -555,10 +556,9 @@ int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *l strscpy(ifr.ifr_name, IFNAMSIZ, ifname); - r = get_glinksettings(fd, &ifr, &u); + r = get_glinksettings(*fd, &ifr, &u); if (r < 0) { - - r = get_gset(fd, &ifr, &u); + r = get_gset(*fd, &ifr, &u); if (r < 0) return log_warning_errno(r, "link_config: Cannot get device settings for %s : %m", ifname); } @@ -570,15 +570,14 @@ int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *l u->base.duplex = link->duplex; if (link->port != _NET_DEV_PORT_INVALID) - u->base.port = link->port; + u->base.port = link->port; u->base.autoneg = link->autonegotiation; if (u->base.cmd == ETHTOOL_GLINKSETTINGS) - r = set_slinksettings(fd, &ifr, u); + r = set_slinksettings(*fd, &ifr, u); else - r = set_sset(fd, &ifr, u); - + r = set_sset(*fd, &ifr, u); if (r < 0) return log_warning_errno(r, "link_config: Cannot set device settings for %s : %m", ifname); diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index 85f0a0625b..5cb126d870 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -26,7 +26,8 @@ Match.Driver, config_parse_strv, 0, Match.Type, config_parse_strv, 0, offsetof(link_config, match_type) Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(link_config, match_host) Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(link_config, match_virt) -Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(link_config, match_kernel) +Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(link_config, match_kernel_cmdline) +Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(link_config, match_kernel_version) Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(link_config, match_arch) Link.Description, config_parse_string, 0, offsetof(link_config, description) Link.MACAddressPolicy, config_parse_mac_policy, 0, offsetof(link_config, mac_policy) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 89891f9e27..a4368f088d 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -77,7 +77,8 @@ static void link_config_free(link_config *link) { free(link->match_name); free(link->match_host); free(link->match_virt); - free(link->match_kernel); + free(link->match_kernel_cmdline); + free(link->match_kernel_version); free(link->match_arch); free(link->description); @@ -171,7 +172,7 @@ static int load_link(link_config_ctx *ctx, const char *filename) { link->port = _NET_DEV_PORT_INVALID; link->autonegotiation = -1; - memset(&link->features, -1, sizeof(link->features)); + memset(&link->features, 0xFF, sizeof(link->features)); r = config_parse(NULL, filename, file, "Match\0Link\0Ethernet\0", @@ -186,6 +187,8 @@ static int load_link(link_config_ctx *ctx, const char *filename) { return -ERANGE; link->filename = strdup(filename); + if (!link->filename) + return log_oom(); LIST_PREPEND(links, ctx->links, link); link = NULL; @@ -246,7 +249,8 @@ int link_config_get(link_config_ctx *ctx, struct udev_device *device, if (net_match_config(link->match_mac, link->match_path, link->match_driver, link->match_type, link->match_name, link->match_host, - link->match_virt, link->match_kernel, link->match_arch, + link->match_virt, link->match_kernel_cmdline, + link->match_kernel_version, link->match_arch, attr_value ? ether_aton(attr_value) : NULL, udev_device_get_property_value(device, "ID_PATH"), udev_device_get_driver(udev_device_get_parent(device)), diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index a413251bd0..dabc3eff8f 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -58,7 +58,8 @@ struct link_config { char **match_name; Condition *match_host; Condition *match_virt; - Condition *match_kernel; + Condition *match_kernel_cmdline; + Condition *match_kernel_version; Condition *match_arch; char *description; diff --git a/src/udev/scsi_id/scsi_id.c b/src/udev/scsi_id/scsi_id.c index ab4ee7b00b..32c1a8def4 100644 --- a/src/udev/scsi_id/scsi_id.c +++ b/src/udev/scsi_id/scsi_id.c @@ -358,7 +358,7 @@ static int set_options(struct udev *udev, case 'h': help(); - exit(0); + exit(EXIT_SUCCESS); case 'p': if (streq(optarg, "0x80")) @@ -393,7 +393,7 @@ static int set_options(struct udev *udev, case 'V': printf("%s\n", PACKAGE_VERSION); - exit(0); + exit(EXIT_SUCCESS); case 'x': export = true; @@ -608,7 +608,7 @@ int main(int argc, char **argv) * Get command line options (overriding any config file settings). */ if (set_options(udev, argc, argv, maj_min_dev) < 0) - exit(1); + exit(EXIT_FAILURE); if (!dev_specified) { log_error("No device specified."); diff --git a/src/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c index b13fdb135b..fa830213ff 100644 --- a/src/udev/udev-builtin-input_id.c +++ b/src/udev/udev-builtin-input_id.c @@ -102,8 +102,7 @@ static void get_cap_mask(struct udev_device *dev, unsigned long val; v = udev_device_get_sysattr_value(pdev, attr); - if (!v) - v = ""; + v = strempty(v); xsprintf(text, "%s", v); log_debug("%s raw kernel attribute: %s", attr, text); diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c index f3be27fdd0..945585d82a 100644 --- a/src/udev/udev-builtin-net_id.c +++ b/src/udev/udev-builtin-net_id.c @@ -547,7 +547,7 @@ static int names_ccw(struct udev_device *dev, struct netnames *names) { * verify each bus-ID part... */ bus_id_len = strlen(bus_id); - if (!bus_id_len || bus_id_len < 8 || bus_id_len > 9) + if (!IN_SET(bus_id_len, 8, 9)) return -EINVAL; /* Strip leading zeros from the bus id for aesthetic purposes. This diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index 8f7c28f03d..d0befba29c 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -774,10 +774,10 @@ int udev_event_spawn(struct udev_event *event, } } - pid = fork(); - switch(pid) { - case 0: - { + err = safe_fork("(spawn)", FORK_RESET_SIGNALS|FORK_LOG, &pid); + if (err < 0) + goto out; + if (err == 0) { char arg[UTIL_PATH_SIZE]; char *argv[128]; char program[UTIL_PATH_SIZE]; @@ -802,23 +802,18 @@ int udev_event_spawn(struct udev_event *event, _exit(2); } - case -1: - log_error_errno(errno, "fork of '%s' failed: %m", cmd); - err = -1; - goto out; - default: - /* parent closed child's ends of pipes */ - outpipe[WRITE_END] = safe_close(outpipe[WRITE_END]); - errpipe[WRITE_END] = safe_close(errpipe[WRITE_END]); - spawn_read(event, - timeout_usec, - cmd, - outpipe[READ_END], errpipe[READ_END], - result, ressize); + /* parent closed child's ends of pipes */ + outpipe[WRITE_END] = safe_close(outpipe[WRITE_END]); + errpipe[WRITE_END] = safe_close(errpipe[WRITE_END]); - err = spawn_wait(event, timeout_usec, timeout_warn_usec, cmd, pid, accept_failure); - } + spawn_read(event, + timeout_usec, + cmd, + outpipe[READ_END], errpipe[READ_END], + result, ressize); + + err = spawn_wait(event, timeout_usec, timeout_warn_usec, cmd, pid, accept_failure); out: if (outpipe[READ_END] >= 0) diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c index d80d61583d..9546a6ebaf 100644 --- a/src/udev/udevadm-control.c +++ b/src/udev/udevadm-control.c @@ -21,6 +21,7 @@ #include <string.h> #include <unistd.h> +#include "process-util.h" #include "time-util.h" #include "udev-util.h" #include "udev.h" diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 1644935ff9..5c757d513f 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -1700,9 +1700,9 @@ int main(int argc, char *argv[]) { goto exit; } - r = mkdir("/run/udev", 0755); - if (r < 0 && errno != EEXIST) { - r = log_error_errno(errno, "could not create /run/udev: %m"); + r = mkdir_errno_wrapper("/run/udev", 0755); + if (r < 0 && r != -EEXIST) { + log_error_errno(r, "could not create /run/udev: %m"); goto exit; } diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index e19a1637bf..2e0e09d843 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -133,6 +133,7 @@ static int keyboard_load_and_wait(const char *vc, const char *map, const char *m const char *args[8]; unsigned i = 0; pid_t pid; + int r; /* An empty map means kernel map */ if (isempty(map)) @@ -152,19 +153,15 @@ static int keyboard_load_and_wait(const char *vc, const char *map, const char *m log_debug("Executing \"%s\"...", strnull((cmd = strv_join((char**) args, " ")))); - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork: %m"); - else if (pid == 0) { - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - + r = safe_fork("(loadkeys)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid); + if (r < 0) + return r; + if (r == 0) { execv(args[0], (char **) args); _exit(EXIT_FAILURE); } - return wait_for_terminate_and_warn(KBD_LOADKEYS, pid, true); + return wait_for_terminate_and_check(KBD_LOADKEYS, pid, WAIT_LOG); } static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *unimap) { @@ -172,6 +169,7 @@ static int font_load_and_wait(const char *vc, const char *font, const char *map, const char *args[9]; unsigned i = 0; pid_t pid; + int r; /* Any part can be set independently */ if (isempty(font) && isempty(map) && isempty(unimap)) @@ -195,19 +193,15 @@ static int font_load_and_wait(const char *vc, const char *font, const char *map, log_debug("Executing \"%s\"...", strnull((cmd = strv_join((char**) args, " ")))); - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork: %m"); - else if (pid == 0) { - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - + r = safe_fork("(setfont)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid); + if (r < 0) + return r; + if (r == 0) { execv(args[0], (char **) args); _exit(EXIT_FAILURE); } - return wait_for_terminate_and_warn(KBD_SETFONT, pid, true); + return wait_for_terminate_and_check(KBD_SETFONT, pid, WAIT_LOG); } /* @@ -458,8 +452,8 @@ int main(int argc, char **argv) { log_warning_errno(r, "Failed to read /proc/cmdline: %m"); } - toggle_utf8_sysfs(utf8); - toggle_utf8(vc, fd, utf8); + (void) toggle_utf8_sysfs(utf8); + (void) toggle_utf8(vc, fd, utf8); r = font_load_and_wait(vc, vc_font, vc_font_map, vc_font_unimap); keyboard_ok = keyboard_load_and_wait(vc, vc_keymap, vc_keymap_toggle, utf8) == 0; diff --git a/src/veritysetup/veritysetup-generator.c b/src/veritysetup/veritysetup-generator.c index 5919b1380e..d09f295392 100644 --- a/src/veritysetup/veritysetup-generator.c +++ b/src/veritysetup/veritysetup-generator.c @@ -18,6 +18,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <stdbool.h> #include <stdlib.h> #include <sys/stat.h> @@ -27,6 +28,7 @@ #include "fd-util.h" #include "fileio.h" #include "fstab-util.h" +#include "generator.h" #include "hexdecoct.h" #include "id128-util.h" #include "mkdir.h" @@ -36,6 +38,8 @@ #include "string-util.h" #include "unit-name.h" +#define SYSTEMD_VERITYSETUP_SERVICE "systemd-veritysetup@root.service" + static char *arg_dest = NULL; static bool arg_enabled = true; static char *arg_root_hash = NULL; @@ -45,7 +49,7 @@ static char *arg_hash_what = NULL; static int create_device(void) { _cleanup_free_ char *u = NULL, *v = NULL, *d = NULL, *e = NULL, *u_escaped = NULL, *v_escaped = NULL, *root_hash_escaped = NULL; _cleanup_fclose_ FILE *f = NULL; - const char *p, *to; + const char *to; int r; /* If all three pieces of information are missing, then verity is turned off */ @@ -67,8 +71,6 @@ static int create_device(void) { " hash device %s,\n" " and root hash %s.", arg_data_what, arg_hash_what, arg_root_hash); - p = strjoina(arg_dest, "/systemd-veritysetup@root.service"); - u = fstab_node_to_udev_node(arg_data_what); if (!u) return log_oom(); @@ -94,12 +96,11 @@ static int create_device(void) { if (!root_hash_escaped) return log_oom(); - f = fopen(p, "wxe"); - if (!f) - return log_error_errno(errno, "Failed to create unit file %s: %m", p); + r = generator_open_unit_file(arg_dest, NULL, SYSTEMD_VERITYSETUP_SERVICE, &f); + if (r < 0) + return r; fprintf(f, - "# Automatically generated by systemd-veritysetup-generator\n\n" "[Unit]\n" "Description=Integrity Protection Setup for %%I\n" "Documentation=man:systemd-veritysetup-generator(8) man:systemd-veritysetup@.service(8)\n" @@ -121,12 +122,12 @@ static int create_device(void) { r = fflush_and_check(f); if (r < 0) - return log_error_errno(r, "Failed to write file %s: %m", p); + return log_error_errno(r, "Failed to write file unit "SYSTEMD_VERITYSETUP_SERVICE": %m"); - to = strjoina(arg_dest, "/cryptsetup.target.requires/systemd-veritysetup@root.service"); + to = strjoina(arg_dest, "/cryptsetup.target.requires/" SYSTEMD_VERITYSETUP_SERVICE); (void) mkdir_parents(to, 0755); - if (symlink("../systemd-veritysetup@root.service", to) < 0) + if (symlink("../" SYSTEMD_VERITYSETUP_SERVICE, to) < 0) return log_error_errno(errno, "Failed to create symlink %s: %m", to); return 0; @@ -227,7 +228,8 @@ int main(int argc, char *argv[]) { if (argc > 1) arg_dest = argv[1]; - log_set_target(LOG_TARGET_SAFE); + log_set_prohibit_ipc(true); + log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); diff --git a/src/veritysetup/veritysetup.c b/src/veritysetup/veritysetup.c index d3066ca429..3b4e72bf9e 100644 --- a/src/veritysetup/veritysetup.c +++ b/src/veritysetup/veritysetup.c @@ -18,14 +18,15 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <errno.h> #include <stdio.h> #include <sys/stat.h> +#include "alloc-util.h" #include "crypt-util.h" -#include "log.h" #include "hexdecoct.h" +#include "log.h" #include "string-util.h" -#include "alloc-util.h" static char *arg_root_hash = NULL; static char *arg_data_what = NULL; |