diff options
Diffstat (limited to 'src')
73 files changed, 1648 insertions, 453 deletions
diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c index dc5434fcd1..1b816fb77a 100644 --- a/src/basic/hostname-util.c +++ b/src/basic/hostname-util.c @@ -62,16 +62,19 @@ static bool hostname_valid_char(char c) { } /** - * Check if s looks like a valid host name or fqdn. This does not do + * Check if s looks like a valid host name or FQDN. This does not do * full DNS validation, but only checks if the name is composed of * allowed characters and the length is not above the maximum allowed * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if - * relax is true and at least two components are present in the name. + * allow_trailing_dot is true and at least two components are present + * in the name. Note that due to the restricted charset and length + * this call is substantially more conservative than + * dns_domain_is_valid(). */ -bool hostname_is_valid(const char *s, bool relax) { +bool hostname_is_valid(const char *s, bool allow_trailing_dot) { + unsigned n_dots = 0; const char *p; bool dot; - unsigned dots = 0; if (isempty(s)) return false; @@ -87,7 +90,7 @@ bool hostname_is_valid(const char *s, bool relax) { return false; dot = true; - dots ++; + n_dots ++; } else { if (!hostname_valid_char(*p)) return false; @@ -96,10 +99,12 @@ bool hostname_is_valid(const char *s, bool relax) { } } - if (dot && (dots < 2 || !relax)) + if (dot && (n_dots < 2 || !allow_trailing_dot)) return false; - if (p-s > HOST_NAME_MAX) + if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on + * Linux, but DNS allows domain names + * up to 255 characters */ return false; return true; diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h index a1ca94980d..d4f5bfe45e 100644 --- a/src/basic/hostname-util.h +++ b/src/basic/hostname-util.h @@ -29,9 +29,11 @@ bool hostname_is_set(void); char* gethostname_malloc(void); -bool hostname_is_valid(const char *s, bool relax) _pure_; +bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_; char* hostname_cleanup(char *s); +#define machine_name_is_valid(s) hostname_is_valid(s, false) + bool is_localhost(const char *hostname); bool is_gateway_hostname(const char *hostname); diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 12f1b193be..e278196c90 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -88,6 +88,32 @@ dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { return ts; } +dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) { + int64_t delta; + + if (u == USEC_INFINITY) { + ts->realtime = ts->monotonic = USEC_INFINITY; + return ts; + } + ts->realtime = now(CLOCK_REALTIME); + ts->monotonic = now(CLOCK_MONOTONIC); + + delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u; + + if ((int64_t) ts->realtime > delta) + ts->realtime -= delta; + else + ts->realtime = 0; + + if ((int64_t) ts->monotonic > delta) + ts->monotonic -= delta; + else + ts->monotonic = 0; + + return ts; +} + + usec_t timespec_load(const struct timespec *ts) { assert(ts); diff --git a/src/basic/time-util.h b/src/basic/time-util.h index 7a64d454a0..2aba042217 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -74,6 +74,7 @@ usec_t now(clockid_t clock); dual_timestamp* dual_timestamp_get(dual_timestamp *ts); dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u); dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u); +dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u); static inline bool dual_timestamp_is_set(dual_timestamp *ts) { return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) || diff --git a/src/basic/util.c b/src/basic/util.c index 9571f0ab12..f752595ca1 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -3006,21 +3006,6 @@ char* strshorten(char *s, size_t l) { return s; } -bool machine_name_is_valid(const char *s) { - - if (!hostname_is_valid(s, false)) - return false; - - /* Machine names should be useful hostnames, but also be - * useful in unit names, hence we enforce a stricter length - * limitation. */ - - if (strlen(s) > 64) - return false; - - return true; -} - int pipe_eof(int fd) { struct pollfd pollfd = { .fd = fd, @@ -4928,6 +4913,9 @@ int container_get_leader(const char *machine, pid_t *pid) { assert(machine); assert(pid); + if (!machine_name_is_valid(machine)) + return -EINVAL; + p = strjoina("/run/systemd/machines/", machine); r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL); if (r == -ENOENT) diff --git a/src/basic/util.h b/src/basic/util.h index 8f21d56ed8..1ead7b5419 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -394,8 +394,6 @@ bool nulstr_contains(const char*nulstr, const char *needle); bool plymouth_running(void); -bool machine_name_is_valid(const char *s) _pure_; - char* strshorten(char *s, size_t l); int symlink_idempotent(const char *from, const char *to); diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index a9f7971cde..3c98a0b186 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -46,6 +46,8 @@ BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_output, exec_output, ExecOutp static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_input, exec_input, ExecInput); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode); + static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_home, protect_home, ProtectHome); static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_system, protect_system, ProtectSystem); @@ -653,6 +655,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("ProtectSystem", "s", bus_property_get_protect_system, offsetof(ExecContext, protect_system), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SameProcessGroup", "b", bus_property_get_bool, offsetof(ExecContext, same_pgrp), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("UtmpIdentifier", "s", NULL, offsetof(ExecContext, utmp_id), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("UtmpMode", "s", property_get_exec_utmp_mode, offsetof(ExecContext, utmp_mode), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SELinuxContext", "(bs)", property_get_selinux_context, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("AppArmorProfile", "(bs)", property_get_apparmor_profile, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SmackProcessLabel", "(bs)", property_get_smack_process_label, 0, SD_BUS_VTABLE_PROPERTY_CONST), @@ -932,6 +935,107 @@ int bus_exec_context_set_transient_property( return 1; + } else if (streq(name, "IgnoreSIGPIPE")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->ignore_sigpipe = b; + + unit_write_drop_in_private_format(u, mode, name, "IgnoreSIGPIPE=%s\n", yes_no(b)); + } + + return 1; + + } else if (streq(name, "TTYVHangup")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->tty_vhangup = b; + + unit_write_drop_in_private_format(u, mode, name, "TTYVHangup=%s\n", yes_no(b)); + } + + return 1; + + } else if (streq(name, "TTYReset")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->tty_reset = b; + + unit_write_drop_in_private_format(u, mode, name, "TTYReset=%s\n", 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 (mode != UNIT_CHECK) { + if (isempty(id)) + c->utmp_id = mfree(c->utmp_id); + else if (free_and_strdup(&c->utmp_id, id) < 0) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "UtmpIdentifier=%s\n", 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 (mode != UNIT_CHECK) { + c->utmp_mode = m; + + unit_write_drop_in_private_format(u, mode, name, "UtmpMode=%s\n", 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 (mode != UNIT_CHECK) { + if (isempty(n)) + c->pam_name = mfree(c->pam_name); + else if (free_and_strdup(&c->pam_name, n) < 0) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "PAMName=%s\n", strempty(n)); + } + + return 1; + } else if (streq(name, "Environment")) { _cleanup_strv_free_ char **l = NULL; diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 1892725f91..0a9effda71 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -697,10 +697,40 @@ static int property_get_cpu_usage( return sd_bus_message_append(reply, "t", ns); } +static int property_get_cgroup( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Unit *u = userdata; + const char *t; + + assert(bus); + assert(reply); + assert(u); + + /* Three cases: a) u->cgroup_path is NULL, in which case the + * unit has no control group, which we report as the empty + * string. b) u->cgroup_path is the empty string, which + * indicates the root cgroup, which we report as "/". c) all + * other cases we report as-is. */ + + if (u->cgroup_path) + t = isempty(u->cgroup_path) ? "/" : u->cgroup_path; + else + t = ""; + + return sd_bus_message_append(reply, "s", t); +} + const sd_bus_vtable bus_unit_cgroup_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0), - SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Unit, cgroup_path), 0), + SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0), SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0), SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0), SD_BUS_VTABLE_END diff --git a/src/core/execute.c b/src/core/execute.c index 3820165241..28eeeaad18 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -31,6 +31,7 @@ #include <grp.h> #include <poll.h> #include <glob.h> +#include <utmpx.h> #include <sys/personality.h> #ifdef HAVE_PAM @@ -1504,7 +1505,11 @@ static int exec_child( } if (context->utmp_id) - utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path); + utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path, + context->utmp_mode == EXEC_UTMP_INIT ? INIT_PROCESS : + context->utmp_mode == EXEC_UTMP_LOGIN ? LOGIN_PROCESS : + USER_PROCESS, + username ? "root" : context->user); if (context->user && is_terminal_input(context->std_input)) { r = chown_terminal(STDIN_FILENO, uid); @@ -2968,3 +2973,11 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput); + +static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = { + [EXEC_UTMP_INIT] = "init", + [EXEC_UTMP_LOGIN] = "login", + [EXEC_UTMP_USER] = "user", +}; + +DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode); diff --git a/src/core/execute.h b/src/core/execute.h index f5d5c1dee7..8d14fe23d0 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -38,6 +38,14 @@ typedef struct ExecParameters ExecParameters; #include "namespace.h" #include "bus-endpoint.h" +typedef enum ExecUtmpMode { + EXEC_UTMP_INIT, + EXEC_UTMP_LOGIN, + EXEC_UTMP_USER, + _EXEC_UTMP_MODE_MAX, + _EXEC_UTMP_MODE_INVALID = -1 +} ExecUtmpMode; + typedef enum ExecInput { EXEC_INPUT_NULL, EXEC_INPUT_TTY, @@ -131,6 +139,7 @@ struct ExecContext { char *pam_name; char *utmp_id; + ExecUtmpMode utmp_mode; bool selinux_context_ignore; char *selinux_context; @@ -265,3 +274,6 @@ ExecOutput exec_output_from_string(const char *s) _pure_; const char* exec_input_to_string(ExecInput i) _const_; ExecInput exec_input_from_string(const char *s) _pure_; + +const char* exec_utmp_mode_to_string(ExecUtmpMode i) _const_; +ExecUtmpMode exec_utmp_mode_from_string(const char *s) _pure_; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index aae81c80cb..60b97722db 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -91,6 +91,7 @@ m4_ifdef(`HAVE_PAM', `$1.PAMName, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') $1.IgnoreSIGPIPE, config_parse_bool, 0, offsetof($1, exec_context.ignore_sigpipe) $1.UtmpIdentifier, config_parse_unit_string_printf, 0, offsetof($1, exec_context.utmp_id) +$1.UtmpMode, config_parse_exec_utmp_mode, 0, offsetof($1, exec_context.utmp_mode) m4_ifdef(`HAVE_SELINUX', `$1.SELinuxContext, config_parse_exec_selinux_context, 0, offsetof($1, exec_context)', `$1.SELinuxContext, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index e1e3a5ffb7..b3bf8bdb40 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -1142,6 +1142,8 @@ int config_parse_sysv_priority(const char *unit, } #endif +DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode"); + DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); int config_parse_kill_signal(const char *unit, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index ce10d03c3f..fcca2b0221 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -104,6 +104,7 @@ int config_parse_cpu_quota(const char *unit, const char *filename, unsigned line int config_parse_protect_home(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_protect_system(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_bus_name(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_exec_utmp_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); /* gperf prototypes */ const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length); diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c index 50a90b0bac..2ecfa40974 100644 --- a/src/core/selinux-access.c +++ b/src/core/selinux-access.c @@ -38,6 +38,7 @@ #include "selinux-util.h" #include "audit-fd.h" #include "strv.h" +#include "path-util.h" static bool initialized = false; @@ -302,7 +303,10 @@ int mac_selinux_unit_access_check_strv( int r; STRV_FOREACH(i, units) { - r = manager_load_unit(m, *i, NULL, error, &u); + if (is_path(*i)) + r = manager_load_unit(m, NULL, *i, error, &u); + else + r = manager_load_unit(m, *i, NULL, error, &u); if (r < 0) return r; r = mac_selinux_unit_access_check(u, message, permission, error); diff --git a/src/import/export.c b/src/import/export.c index ec7dbe210a..b88d71fec6 100644 --- a/src/import/export.c +++ b/src/import/export.c @@ -24,6 +24,7 @@ #include "sd-event.h" #include "event-util.h" #include "signal-util.h" +#include "hostname-util.h" #include "verbs.h" #include "build.h" #include "machine-image.h" diff --git a/src/import/import-common.c b/src/import/import-common.c index 950c7b4acd..d8a3bbc249 100644 --- a/src/import/import-common.c +++ b/src/import/import-common.c @@ -210,7 +210,7 @@ int import_fork_tar_c(const char *path, pid_t *ret) { if (r < 0) log_error_errno(r, "Failed to drop capabilities, ignoring: %m"); - execlp("tar", "tar", "--sparse", "-C", path, "-c", ".", NULL); + execlp("tar", "tar", "-C", path, "-c", ".", NULL); log_error_errno(errno, "Failed to execute tar: %m"); _exit(EXIT_FAILURE); } diff --git a/src/import/import-raw.c b/src/import/import-raw.c index 43cd413042..a27e81bbd4 100644 --- a/src/import/import-raw.c +++ b/src/import/import-raw.c @@ -26,6 +26,7 @@ #include "util.h" #include "path-util.h" #include "btrfs-util.h" +#include "hostname-util.h" #include "copy.h" #include "mkdir.h" #include "rm-rf.h" diff --git a/src/import/import-tar.c b/src/import/import-tar.c index 2bf0b0680c..7ffe83cc33 100644 --- a/src/import/import-tar.c +++ b/src/import/import-tar.c @@ -26,6 +26,7 @@ #include "util.h" #include "path-util.h" #include "btrfs-util.h" +#include "hostname-util.h" #include "copy.h" #include "mkdir.h" #include "rm-rf.h" diff --git a/src/import/import.c b/src/import/import.c index b7772390e9..929a840298 100644 --- a/src/import/import.c +++ b/src/import/import.c @@ -26,6 +26,7 @@ #include "verbs.h" #include "build.h" #include "signal-util.h" +#include "hostname-util.h" #include "machine-image.h" #include "import-util.h" #include "import-tar.h" diff --git a/src/import/importd.c b/src/import/importd.c index dd314f5b00..8b508eaeec 100644 --- a/src/import/importd.c +++ b/src/import/importd.c @@ -35,6 +35,7 @@ #include "import-util.h" #include "process-util.h" #include "signal-util.h" +#include "hostname-util.h" typedef struct Transfer Transfer; typedef struct Manager Manager; diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c index 5bfaf012c0..d0e0faa261 100644 --- a/src/import/pull-raw.c +++ b/src/import/pull-raw.c @@ -33,6 +33,7 @@ #include "mkdir.h" #include "rm-rf.h" #include "path-util.h" +#include "hostname-util.h" #include "import-util.h" #include "import-common.h" #include "curl-util.h" diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index 71b8908a58..d38a2158c4 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -32,13 +32,14 @@ #include "mkdir.h" #include "rm-rf.h" #include "path-util.h" +#include "process-util.h" +#include "hostname-util.h" #include "import-util.h" #include "import-common.h" #include "curl-util.h" #include "pull-job.h" #include "pull-common.h" #include "pull-tar.h" -#include "process-util.h" typedef enum TarProgress { TAR_DOWNLOADING, diff --git a/src/import/pull.c b/src/import/pull.c index ca7be6be85..e13cd6af97 100644 --- a/src/import/pull.c +++ b/src/import/pull.c @@ -26,6 +26,7 @@ #include "verbs.h" #include "build.h" #include "signal-util.h" +#include "hostname-util.h" #include "machine-image.h" #include "import-util.h" #include "pull-tar.h" diff --git a/src/journal/coredumpctl.c b/src/journal/coredumpctl.c index 098f62af50..644ba91b0d 100644 --- a/src/journal/coredumpctl.c +++ b/src/journal/coredumpctl.c @@ -49,6 +49,7 @@ static enum { ACTION_GDB, } arg_action = ACTION_LIST; static const char* arg_field = NULL; +static const char *arg_directory = NULL; static int arg_no_pager = false; static int arg_no_legend = false; static int arg_one = false; @@ -131,6 +132,7 @@ static void help(void) { " -1 Show information about most recent entry only\n" " -F --field=FIELD List all values a certain field takes\n" " -o --output=FILE Write output to FILE\n\n" + " -D --directory=DIR Use journal files from directory\n\n" "Commands:\n" " list [MATCHES...] List available coredumps (default)\n" @@ -156,13 +158,14 @@ static int parse_argv(int argc, char *argv[], Set *matches) { { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, { "output", required_argument, NULL, 'o' }, { "field", required_argument, NULL, 'F' }, + { "directory", required_argument, NULL, 'D' }, {} }; assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "ho:F:1", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "ho:F:1D:", options, NULL)) >= 0) switch(c) { case 'h': @@ -208,6 +211,10 @@ static int parse_argv(int argc, char *argv[], Set *matches) { arg_one = true; break; + case 'D': + arg_directory = optarg; + break; + case '?': return -EINVAL; @@ -808,10 +815,18 @@ int main(int argc, char *argv[]) { sigbus_install(); - r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY); - if (r < 0) { - log_error_errno(r, "Failed to open journal: %m"); - goto end; + if (arg_directory) { + r = sd_journal_open_directory(&j, arg_directory, 0); + if (r < 0) { + log_error_errno(r, "Failed to open journals in directory: %s: %m", arg_directory); + goto end; + } + } else { + r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY); + if (r < 0) { + log_error_errno(r, "Failed to open journal: %m"); + goto end; + } } /* We want full data, nothing truncated. */ diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index bfd1901e16..13fa9b52fc 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -43,6 +43,7 @@ #include "replace-var.h" #include "fileio.h" #include "formats-util.h" +#include "hostname-util.h" #define JOURNAL_FILES_MAX 7168 diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 6da7ea7e27..2fa4d5fac8 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -405,7 +405,6 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, if (r < 0) goto fail; - ret = NULL; idx++; } diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 6a0d270739..46104afded 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -348,7 +348,7 @@ int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) { client->state != DHCP_STATE_REBINDING) return -EADDRNOTAVAIL; - *ret = sd_dhcp_lease_ref(client->lease); + *ret = client->lease; return 0; } diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index bc17c6adc5..10c3654020 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -260,7 +260,7 @@ int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) { if (!client->lease) return -ENOMSG; - *ret = sd_dhcp6_lease_ref(client->lease); + *ret = client->lease; return 0; } diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c index d341210887..200499d613 100644 --- a/src/libsystemd-network/test-dhcp-client.c +++ b/src/libsystemd-network/test-dhcp-client.c @@ -393,7 +393,6 @@ static void test_addr_acq_acquired(sd_dhcp_client *client, int event, if (verbose) printf(" DHCP address acquired\n"); - sd_dhcp_lease_unref(lease); sd_event_exit(e, 0); } diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index db4f21e9ff..31cdcb4e5b 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -33,6 +33,7 @@ #include "missing.h" #include "def.h" #include "cgroup-util.h" +#include "hostname-util.h" #include "bus-label.h" #include "sd-bus.h" diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c index 9bbf8974dd..0eadc8c747 100644 --- a/src/libsystemd/sd-login/sd-login.c +++ b/src/libsystemd/sd-login/sd-login.c @@ -32,6 +32,7 @@ #include "fileio.h" #include "login-util.h" #include "formats-util.h" +#include "hostname-util.h" #include "sd-login.h" _public_ int sd_pid_get_session(pid_t pid, char **session) { @@ -790,7 +791,7 @@ _public_ int sd_get_machine_names(char ***machines) { /* Filter out the unit: symlinks */ for (a = l, b = l; *a; a++) { - if (startswith(*a, "unit:")) + if (startswith(*a, "unit:") || !machine_name_is_valid(*a)) free(*a); else { *b = *a; diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 992a9f5b4a..5b2b36b9c0 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -1339,7 +1339,8 @@ static int bus_manager_log_shutdown( InhibitWhat w, const char *unit_name) { - const char *p, *q; + const char *p; + const char *q; assert(m); assert(unit_name); @@ -1364,6 +1365,9 @@ static int bus_manager_log_shutdown( q = NULL; } + if (m->wall_message) + p = strjoina(p, " (", m->wall_message, ")", NULL); + return log_struct(LOG_NOTICE, LOG_MESSAGE_ID(SD_MESSAGE_SHUTDOWN), p, @@ -2282,6 +2286,44 @@ static int method_can_reboot_to_firmware_setup( return sd_bus_reply_method_return(message, "s", result); } +static int method_set_wall_message( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + int r; + Manager *m = userdata; + char *wall_message; + bool enable_wall_messages; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "sb", &wall_message, &enable_wall_messages); + if (r < 0) + return r; + + r = bus_verify_polkit_async(message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-wall-message", + false, + UID_INVALID, + &m->polkit_registry, + error); + + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + r = free_and_strdup(&m->wall_message, wall_message); + if (r < 0) + return log_oom(); + m->enable_wall_messages = enable_wall_messages; + + return sd_bus_reply_method_return(message, NULL); +} + static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; const char *who, *why, *what, *mode; @@ -2463,6 +2505,7 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CanRebootToFirmwareSetup", NULL, "s", method_can_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetRebootToFirmwareSetup", "b", NULL, method_set_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetWallMessage", "sb", NULL, method_set_wall_message, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_SIGNAL("SessionNew", "so", 0), SD_BUS_SIGNAL("SessionRemoved", "so", 0), diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf index d8deb7bc8b..1662d4c428 100644 --- a/src/login/org.freedesktop.login1.conf +++ b/src/login/org.freedesktop.login1.conf @@ -182,6 +182,10 @@ <allow send_destination="org.freedesktop.login1" send_interface="org.freedesktop.login1.Manager" + send_member="SetWallMessage"/> + + <allow send_destination="org.freedesktop.login1" + send_interface="org.freedesktop.login1.Manager" send_member="AttachDevice"/> <allow send_destination="org.freedesktop.login1" diff --git a/src/login/org.freedesktop.login1.policy.in b/src/login/org.freedesktop.login1.policy.in index 83e7183323..23326bb79f 100644 --- a/src/login/org.freedesktop.login1.policy.in +++ b/src/login/org.freedesktop.login1.policy.in @@ -150,6 +150,7 @@ <allow_inactive>auth_admin_keep</allow_inactive> <allow_active>yes</allow_active> </defaults> + <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.set-wall-message</annotate> </action> <action id="org.freedesktop.login1.power-off-multiple-sessions"> @@ -182,6 +183,7 @@ <allow_inactive>auth_admin_keep</allow_inactive> <allow_active>yes</allow_active> </defaults> + <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.set-wall-message</annotate> </action> <action id="org.freedesktop.login1.reboot-multiple-sessions"> @@ -300,4 +302,14 @@ </defaults> </action> + <action id="org.freedesktop.login1.set-wall-message"> + <_description>Set a wall message</_description> + <_message>Authentication is required to set a wall message</_message> + <defaults> + <allow_any>auth_admin_keep</allow_any> + <allow_inactive>auth_admin_keep</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + </action> + </policyconfig> diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index b62a9bd813..af2b8eff06 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -44,6 +44,7 @@ #include "machine-dbus.h" #include "formats-util.h" #include "process-util.h" +#include "env-util.h" static int property_get_id( sd_bus *bus, @@ -185,141 +186,179 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_close_pair_ int pair[2] = { -1, -1 }; - _cleanup_free_ char *us = NULL, *them = NULL; - _cleanup_close_ int netns_fd = -1; Machine *m = userdata; - const char *p; - siginfo_t si; - pid_t child; int r; assert(message); assert(m); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines."); - - r = readlink_malloc("/proc/self/ns/net", &us); - if (r < 0) - return r; - - p = procfs_file_alloca(m->leader, "ns/net"); - r = readlink_malloc(p, &them); + r = sd_bus_message_new_method_return(message, &reply); if (r < 0) return r; - if (streq(us, them)) - return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name); - - r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL, NULL); + r = sd_bus_message_open_container(reply, 'a', "(iay)"); if (r < 0) return r; - 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"); + switch (m->class) { - if (child == 0) { + case MACHINE_HOST: { _cleanup_free_ struct local_address *addresses = NULL; struct local_address *a; - int i, n; - - pair[0] = safe_close(pair[0]); - - r = namespace_enter(-1, -1, netns_fd, -1, -1); - if (r < 0) - _exit(EXIT_FAILURE); + int n, i; n = local_addresses(NULL, 0, AF_UNSPEC, &addresses); if (n < 0) - _exit(EXIT_FAILURE); + return n; for (a = addresses, i = 0; i < n; a++, i++) { - struct iovec iov[2] = { - { .iov_base = &a->family, .iov_len = sizeof(a->family) }, - { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) }, - }; - r = writev(pair[1], iov, 2); + r = sd_bus_message_open_container(reply, 'r', "iay"); if (r < 0) - _exit(EXIT_FAILURE); - } - - pair[1] = safe_close(pair[1]); + return r; - _exit(EXIT_SUCCESS); - } - - pair[1] = safe_close(pair[1]); + r = sd_bus_message_append(reply, "i", addresses[i].family); + if (r < 0) + return r; - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; + r = sd_bus_message_append_array(reply, 'y', &addresses[i].address, FAMILY_ADDRESS_SIZE(addresses[i].family)); + if (r < 0) + return r; - r = sd_bus_message_open_container(reply, 'a', "(iay)"); - if (r < 0) - return r; + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + } - for (;;) { - int family; - ssize_t n; - union in_addr_union in_addr; - struct iovec iov[2]; - struct msghdr mh = { - .msg_iov = iov, - .msg_iovlen = 2, - }; + break; + } - iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) }; - iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) }; + case MACHINE_CONTAINER: { + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + _cleanup_free_ char *us = NULL, *them = NULL; + _cleanup_close_ int netns_fd = -1; + const char *p; + siginfo_t si; + pid_t child; - n = recvmsg(pair[0], &mh, 0); - if (n < 0) - return -errno; - if ((size_t) n < sizeof(family)) - break; + r = readlink_malloc("/proc/self/ns/net", &us); + if (r < 0) + return r; - r = sd_bus_message_open_container(reply, 'r', "iay"); + p = procfs_file_alloca(m->leader, "ns/net"); + r = readlink_malloc(p, &them); if (r < 0) return r; - r = sd_bus_message_append(reply, "i", family); + if (streq(us, them)) + return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name); + + r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL, NULL); if (r < 0) return r; - switch (family) { + 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) { + _cleanup_free_ struct local_address *addresses = NULL; + struct local_address *a; + int i, n; + + pair[0] = safe_close(pair[0]); - case AF_INET: - if (n != sizeof(struct in_addr) + sizeof(family)) - return -EIO; + r = namespace_enter(-1, -1, netns_fd, -1, -1); + if (r < 0) + _exit(EXIT_FAILURE); + + n = local_addresses(NULL, 0, AF_UNSPEC, &addresses); + if (n < 0) + _exit(EXIT_FAILURE); + + for (a = addresses, i = 0; i < n; a++, i++) { + struct iovec iov[2] = { + { .iov_base = &a->family, .iov_len = sizeof(a->family) }, + { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) }, + }; - r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in)); - break; + r = writev(pair[1], iov, 2); + if (r < 0) + _exit(EXIT_FAILURE); + } - case AF_INET6: - if (n != sizeof(struct in6_addr) + sizeof(family)) - return -EIO; + pair[1] = safe_close(pair[1]); - r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6)); - break; + _exit(EXIT_SUCCESS); } - if (r < 0) - return r; - r = sd_bus_message_close_container(reply); + pair[1] = safe_close(pair[1]); + + for (;;) { + int family; + ssize_t n; + union in_addr_union in_addr; + struct iovec iov[2]; + struct msghdr mh = { + .msg_iov = iov, + .msg_iovlen = 2, + }; + + iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) }; + iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) }; + + n = recvmsg(pair[0], &mh, 0); + if (n < 0) + return -errno; + if ((size_t) n < sizeof(family)) + break; + + r = sd_bus_message_open_container(reply, 'r', "iay"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "i", family); + if (r < 0) + return r; + + switch (family) { + + case AF_INET: + if (n != sizeof(struct in_addr) + sizeof(family)) + return -EIO; + + r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in)); + break; + + case AF_INET6: + if (n != sizeof(struct in6_addr) + sizeof(family)) + return -EIO; + + r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6)); + break; + } + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + } + + r = wait_for_terminate(child, &si); if (r < 0) - return r; + return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m"); + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally."); + break; } - r = wait_for_terminate(child, &si); - if (r < 0) - return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m"); - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) - return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally."); + default: + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines."); + } r = sd_bus_message_close_container(reply); if (r < 0) @@ -330,73 +369,88 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_close_ int mntns_fd = -1, root_fd = -1; - _cleanup_close_pair_ int pair[2] = { -1, -1 }; _cleanup_strv_free_ char **l = NULL; - _cleanup_fclose_ FILE *f = NULL; Machine *m = userdata; char **k, **v; - siginfo_t si; - pid_t child; int r; assert(message); assert(m); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines."); + switch (m->class) { - r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd); - if (r < 0) - return r; + case MACHINE_HOST: + r = load_env_file_pairs(NULL, "/etc/os-release", NULL, &l); + if (r < 0) + return r; - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) - return -errno; + break; - child = fork(); - if (child < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + case MACHINE_CONTAINER: { + _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; - if (child == 0) { - _cleanup_close_ int fd = -1; + r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd); + if (r < 0) + return r; - pair[0] = safe_close(pair[0]); + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) + return -errno; - r = namespace_enter(-1, mntns_fd, -1, -1, root_fd); - if (r < 0) - _exit(EXIT_FAILURE); + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + + if (child == 0) { + _cleanup_close_ int fd = -1; - fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC); - if (fd < 0) { - fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC); - if (fd < 0) + pair[0] = safe_close(pair[0]); + + r = namespace_enter(-1, mntns_fd, -1, -1, root_fd); + if (r < 0) + _exit(EXIT_FAILURE); + + fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC); + if (fd < 0) { + fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC); + if (fd < 0) + _exit(EXIT_FAILURE); + } + + r = copy_bytes(fd, pair[1], (off_t) -1, false); + if (r < 0) _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); } - r = copy_bytes(fd, pair[1], (off_t) -1, false); - if (r < 0) - _exit(EXIT_FAILURE); + pair[1] = safe_close(pair[1]); - _exit(EXIT_SUCCESS); - } + f = fdopen(pair[0], "re"); + if (!f) + return -errno; - pair[1] = safe_close(pair[1]); + pair[0] = -1; - f = fdopen(pair[0], "re"); - if (!f) - return -errno; + r = load_env_file_pairs(f, "/etc/os-release", NULL, &l); + if (r < 0) + return r; - pair[0] = -1; + r = wait_for_terminate(child, &si); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m"); + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally."); - r = load_env_file_pairs(f, "/etc/os-release", NULL, &l); - if (r < 0) - return r; + break; + } - r = wait_for_terminate(child, &si); - if (r < 0) - return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m"); - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) - return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally."); + default: + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines."); + } r = sd_bus_message_new_method_return(message, &reply); if (r < 0) @@ -429,10 +483,20 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_ assert(message); assert(m); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening pseudo TTYs is only supported on container machines."); + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty", + false, + UID_INVALID, + &m->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ - master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC); + master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); if (master < 0) return master; @@ -451,26 +515,67 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_ return sd_bus_send(NULL, reply, NULL); } +static int container_bus_new(Machine *m, sd_bus **ret) { + int r; + + assert(m); + assert(ret); + + switch (m->class) { + + case MACHINE_HOST: + *ret = NULL; + break; + + case MACHINE_CONTAINER: { + _cleanup_bus_unref_ sd_bus *bus = NULL; + char *address; + + r = sd_bus_new(&bus); + if (r < 0) + return r; + + if (asprintf(&address, "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI, m->leader) < 0) + return -ENOMEM; + + bus->address = address; + bus->bus_client = true; + bus->trusted = false; + bus->is_system = true; + + r = sd_bus_start(bus); + if (r < 0) + return r; + + *ret = bus; + bus = NULL; + break; + } + + default: + return -EOPNOTSUPP; + } + + return 0; +} + int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_free_ char *pty_name = NULL, *getty = NULL; - _cleanup_bus_unref_ sd_bus *container_bus = NULL; + _cleanup_free_ char *pty_name = NULL; + _cleanup_bus_flush_close_unref_ sd_bus *allocated_bus = NULL; _cleanup_close_ int master = -1; + sd_bus *container_bus = NULL; Machine *m = userdata; - const char *p; - char *address; + const char *p, *getty; int r; assert(message); assert(m); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening logins is only supported on container machines."); - r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, - "org.freedesktop.machine1.login", + m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login", false, UID_INVALID, &m->manager->polkit_registry, @@ -480,7 +585,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu if (r == 0) return 1; /* Will call us back */ - master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC); + master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); if (master < 0) return master; @@ -495,26 +600,13 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu if (unlockpt(master) < 0) return -errno; - r = sd_bus_new(&container_bus); + r = container_bus_new(m, &allocated_bus); if (r < 0) return r; -# define ADDRESS_FMT "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI - if (asprintf(&address, ADDRESS_FMT, m->leader) < 0) - return log_oom(); - - container_bus->address = address; - container_bus->bus_client = true; - container_bus->trusted = false; - container_bus->is_system = true; + container_bus = allocated_bus ?: m->manager->bus; - r = sd_bus_start(container_bus); - if (r < 0) - return r; - - getty = strjoin("container-getty@", p, ".service", NULL); - if (!getty) - return log_oom(); + getty = strjoina("container-getty@", p, ".service"); r = sd_bus_call_method( container_bus, @@ -527,7 +619,228 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu if (r < 0) return r; - container_bus = sd_bus_unref(container_bus); + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "hs", master, pty_name); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + +int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *tm = NULL; + _cleanup_free_ char *pty_name = NULL; + _cleanup_bus_flush_close_unref_ sd_bus *allocated_bus = NULL; + sd_bus *container_bus = NULL; + _cleanup_close_ int master = -1; + _cleanup_strv_free_ char **env = NULL, **args = NULL; + Machine *m = userdata; + const char *p, *unit, *user, *path, *description, *utmp_id; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "ss", &user, &path); + if (r < 0) + return r; + if (isempty(user)) + user = NULL; + 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); + if (r < 0) + return r; + if (strv_isempty(args)) { + args = strv_free(args); + + args = strv_new(path, NULL); + if (!args) + return -ENOMEM; + + args[0][0] = '-'; /* Tell /bin/sh that this shall be a login shell */ + } + + r = sd_bus_message_read_strv(message, &env); + if (r < 0) + return r; + if (!strv_env_is_valid(env)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments"); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell", + false, + UID_INVALID, + &m->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (master < 0) + return master; + + r = ptsname_malloc(master, &pty_name); + if (r < 0) + return r; + + p = path_startswith(pty_name, "/dev/pts/"); + if (!p) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name); + + utmp_id = path_startswith(pty_name, "/dev/"); + assert(utmp_id); + + if (unlockpt(master) < 0) + return -errno; + + r = container_bus_new(m, &allocated_bus); + if (r < 0) + return r; + + container_bus = allocated_bus ?: m->manager->bus; + + r = sd_bus_message_new_method_call( + container_bus, + &tm, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); + if (r < 0) + return r; + + /* Name and mode */ + unit = strjoina("container-shell@", p, ".service", NULL); + r = sd_bus_message_append(tm, "ss", unit, "fail"); + if (r < 0) + return r; + + /* Properties */ + r = sd_bus_message_open_container(tm, 'a', "(sv)"); + if (r < 0) + return r; + + description = strjoina("Shell for User ", isempty(user) ? "root" : user); + r = sd_bus_message_append(tm, + "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)", + "Description", "s", description, + "StandardInput", "s", "tty", + "StandardOutput", "s", "tty", + "StandardError", "s", "tty", + "TTYPath", "s", pty_name, + "SendSIGHUP", "b", true, + "IgnoreSIGPIPE", "b", false, + "KillMode", "s", "mixed", + "TTYVHangup", "b", true, + "TTYReset", "b", true, + "UtmpIdentifier", "s", utmp_id, + "UtmpMode", "s", "user", + "PAMName", "s", "login"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "(sv)", "User", "s", isempty(user) ? "root" : user); + if (r < 0) + return r; + + if (!strv_isempty(env)) { + r = sd_bus_message_open_container(tm, 'r', "sv"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "s", "Environment"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(tm, 'v', "as"); + if (r < 0) + return r; + + r = sd_bus_message_append_strv(tm, env); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + } + + /* Exec container */ + r = sd_bus_message_open_container(tm, 'r', "sv"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "s", "ExecStart"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(tm, 'v', "a(sasb)"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(tm, 'a', "(sasb)"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(tm, 'r', "sasb"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "s", path); + if (r < 0) + return r; + + r = sd_bus_message_append_strv(tm, args); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "b", true); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + r = sd_bus_message_close_container(tm); + if (r < 0) + return r; + + /* Auxiliary units */ + r = sd_bus_message_append(tm, "a(sa(sv))", 0); + if (r < 0) + return r; + + r = sd_bus_call(container_bus, tm, 0, error, NULL); + if (r < 0) + return r; r = sd_bus_message_new_method_return(message, &reply); if (r < 0) @@ -966,8 +1279,9 @@ const sd_bus_vtable machine_vtable[] = { SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, 0), + SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyFrom", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyTo", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h index d309131860..38b46ad936 100644 --- a/src/machine/machine-dbus.h +++ b/src/machine/machine-dbus.h @@ -35,6 +35,7 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/machine/machine.c b/src/machine/machine.c index f045159d41..0d1b119dc1 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -37,12 +37,17 @@ #include "machine-dbus.h" #include "formats-util.h" -Machine* machine_new(Manager *manager, const char *name) { +Machine* machine_new(Manager *manager, MachineClass class, const char *name) { Machine *m; assert(manager); + assert(class < _MACHINE_CLASS_MAX); assert(name); + /* Passing class == _MACHINE_CLASS_INVALID here is fine. It + * means as much as "we don't know yet", and that we'll figure + * it out later when loading the state file. */ + m = new0(Machine, 1); if (!m) return NULL; @@ -51,14 +56,17 @@ Machine* machine_new(Manager *manager, const char *name) { if (!m->name) goto fail; - m->state_file = strappend("/run/systemd/machines/", m->name); - if (!m->state_file) - goto fail; + if (class != MACHINE_HOST) { + m->state_file = strappend("/run/systemd/machines/", m->name); + if (!m->state_file) + goto fail; + } + + m->class = class; if (hashmap_put(manager->machines, m->name, m) < 0) goto fail; - m->class = _MACHINE_CLASS_INVALID; m->manager = manager; return m; @@ -86,6 +94,9 @@ void machine_free(Machine *m) { (void) hashmap_remove(m->manager->machines, m->name); + if (m->manager->host_machine == m) + m->manager->host_machine = NULL; + if (m->leader > 0) (void) hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m); @@ -105,7 +116,9 @@ int machine_save(Machine *m) { int r; assert(m); - assert(m->state_file); + + if (!m->state_file) + return 0; if (!m->started) return 0; @@ -244,6 +257,9 @@ int machine_load(Machine *m) { assert(m); + if (!m->state_file) + return 0; + r = parse_env_file(m->state_file, NEWLINE, "SCOPE", &m->unit, "SCOPE_JOB", &m->scope_job, @@ -325,6 +341,7 @@ static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_er int r = 0; assert(m); + assert(m->class != MACHINE_HOST); if (!m->unit) { _cleanup_free_ char *escaped = NULL; @@ -364,6 +381,9 @@ int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) { assert(m); + if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM)) + return -EOPNOTSUPP; + if (m->started) return 0; @@ -402,6 +422,7 @@ static int machine_stop_scope(Machine *m) { int r; assert(m); + assert(m->class != MACHINE_HOST); if (!m->unit) return 0; @@ -422,6 +443,9 @@ int machine_stop(Machine *m) { int r; assert(m); + if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM)) + return -EOPNOTSUPP; + r = machine_stop_scope(m); m->stopping = true; @@ -456,6 +480,9 @@ int machine_finalize(Machine *m) { bool machine_check_gc(Machine *m, bool drop_not_started) { assert(m); + if (m->class == MACHINE_HOST) + return true; + if (drop_not_started && !m->started) return false; @@ -481,6 +508,9 @@ void machine_add_to_gc_queue(Machine *m) { MachineState machine_get_state(Machine *s) { assert(s); + if (s->class == MACHINE_HOST) + return MACHINE_RUNNING; + if (s->stopping) return MACHINE_CLOSING; @@ -493,6 +523,9 @@ MachineState machine_get_state(Machine *s) { int machine_kill(Machine *m, KillWho who, int signo) { assert(m); + if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER)) + return -EOPNOTSUPP; + if (!m->unit) return -ESRCH; @@ -509,6 +542,25 @@ int machine_kill(Machine *m, KillWho who, int signo) { return manager_kill_unit(m->manager, m->unit, signo, NULL); } +int machine_openpt(Machine *m, int flags) { + assert(m); + + switch (m->class) { + + case MACHINE_HOST: + return posix_openpt(flags); + + case MACHINE_CONTAINER: + if (m->leader <= 0) + return -EINVAL; + + return openpt_in_namespace(m->leader, flags); + + default: + return -EOPNOTSUPP; + } +} + MachineOperation *machine_operation_unref(MachineOperation *o) { if (!o) return NULL; @@ -544,7 +596,8 @@ void machine_release_unit(Machine *m) { static const char* const machine_class_table[_MACHINE_CLASS_MAX] = { [MACHINE_CONTAINER] = "container", - [MACHINE_VM] = "vm" + [MACHINE_VM] = "vm", + [MACHINE_HOST] = "host", }; DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass); diff --git a/src/machine/machine.h b/src/machine/machine.h index 0132b65a97..5f978289f2 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -39,6 +39,7 @@ typedef enum MachineState { typedef enum MachineClass { MACHINE_CONTAINER, MACHINE_VM, + MACHINE_HOST, _MACHINE_CLASS_MAX, _MACHINE_CLASS_INVALID = -1 } MachineClass; @@ -95,7 +96,7 @@ struct Machine { unsigned n_operations; }; -Machine* machine_new(Manager *manager, const char *name); +Machine* machine_new(Manager *manager, MachineClass class, const char *name); void machine_free(Machine *m); bool machine_check_gc(Machine *m, bool drop_not_started); void machine_add_to_gc_queue(Machine *m); @@ -120,3 +121,5 @@ MachineState machine_state_from_string(const char *s) _pure_; const char *kill_who_to_string(KillWho k) _const_; KillWho kill_who_from_string(const char *s) _pure_; + +int machine_openpt(Machine *m, int flags); diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 66ed41087c..926035d185 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -55,6 +55,8 @@ #include "process-util.h" #include "terminal-util.h" #include "signal-util.h" +#include "env-util.h" +#include "hostname-util.h" static char **arg_property = NULL; static bool arg_all = false; @@ -75,6 +77,8 @@ static bool arg_force = false; static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE; static const char* arg_dkr_index_url = NULL; static const char* arg_format = NULL; +static const char *arg_uid = NULL; +static char **arg_setenv = NULL; static void pager_open_if_enabled(void) { @@ -154,6 +158,9 @@ static int list_machines(int argc, char *argv[], void *userdata) { while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) { size_t l; + if (name[0] == '.' && !arg_all) + continue; + if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1)) return log_oom(); @@ -354,7 +361,8 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { bus, "org.freedesktop.systemd1", path, - endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service", + endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : + endswith(unit, ".slice") ? "org.freedesktop.systemd1.Slice" : "org.freedesktop.systemd1.Service", "ControlGroup", &error, &reply, @@ -368,9 +376,6 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { if (r < 0) return bus_log_parse_error(r); - if (isempty(cgroup)) - return 0; - if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0) return 0; @@ -1170,20 +1175,72 @@ static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *r return 0; } +static int process_forward(sd_event *event, PTYForward **forward, int master, bool ignore_vhangup, const char *name) { + char last_char = 0; + bool machine_died; + int ret = 0, r; + + assert(event); + assert(master >= 0); + assert(name); + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0); + + if (streq(name, ".host")) + log_info("Connected to the local host. Press ^] three times within 1s to exit session."); + else + log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name); + + sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); + sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); + + r = pty_forward_new(event, master, ignore_vhangup, false, forward); + if (r < 0) + return log_error_errno(r, "Failed to create PTY forwarder: %m"); + + r = sd_event_loop(event); + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); + + pty_forward_get_last_char(*forward, &last_char); + + machine_died = + ignore_vhangup && + pty_forward_get_ignore_vhangup(*forward) == 0; + + *forward = pty_forward_free(*forward); + + if (last_char != '\n') + fputc('\n', stdout); + + if (machine_died) + log_info("Machine %s terminated.", name); + else if (streq(name, ".host")) + log_info("Connection to the local host terminated."); + else + log_info("Connection to machine %s terminated.", name); + + sd_event_get_exit_code(event, &ret); + return ret; +} + static int login_machine(int argc, char *argv[], void *userdata) { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(pty_forward_freep) PTYForward *forward = NULL; + _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL; _cleanup_event_unref_ sd_event *event = NULL; - int master = -1, r, ret = 0; + int master = -1, r; sd_bus *bus = userdata; - const char *pty, *match; - char last_char = 0; - bool machine_died; + const char *pty, *match, *machine; assert(bus); + if (!strv_isempty(arg_setenv) || arg_uid) { + log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead."); + return -EINVAL; + } + if (arg_transport != BUS_TRANSPORT_LOCAL && arg_transport != BUS_TRANSPORT_MACHINE) { log_error("Login only supported on local machines."); @@ -1200,14 +1257,14 @@ static int login_machine(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to attach bus to event loop: %m"); + machine = argc < 2 || isempty(argv[1]) ? ".host" : argv[1]; + match = strjoina("type='signal'," - "sender='org.freedesktop.machine1'," - "path='/org/freedesktop/machine1',", - "interface='org.freedesktop.machine1.Manager'," - "member='MachineRemoved'," - "arg0='", - argv[1], - "'"); + "sender='org.freedesktop.machine1'," + "path='/org/freedesktop/machine1',", + "interface='org.freedesktop.machine1.Manager'," + "member='MachineRemoved'," + "arg0='", machine, "'"); r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward); if (r < 0) @@ -1221,9 +1278,9 @@ static int login_machine(int argc, char *argv[], void *userdata) { "OpenMachineLogin", &error, &reply, - "s", argv[1]); + "s", machine); if (r < 0) { - log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r)); + log_error("Failed to get login PTY: %s", bus_error_message(&error, -r)); return r; } @@ -1231,36 +1288,111 @@ static int login_machine(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_parse_error(r); - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0); + return process_forward(event, &forward, master, true, machine); +} - log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]); +static int shell_machine(int argc, char *argv[], void *userdata) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(pty_forward_freep) PTYForward *forward = NULL; + _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL; + _cleanup_event_unref_ sd_event *event = NULL; + int master = -1, r; + sd_bus *bus = userdata; + const char *pty, *match, *machine, *path, *uid = NULL; - sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); - sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); + assert(bus); + + if (arg_transport != BUS_TRANSPORT_LOCAL && + arg_transport != BUS_TRANSPORT_MACHINE) { + log_error("Shell only supported on local machines."); + return -EOPNOTSUPP; + } - r = pty_forward_new(event, master, true, false, &forward); + /* Pass $TERM to shell session, if not explicitly specified. */ + if (!strv_find_prefix(arg_setenv, "TERM=")) { + const char *t; + + t = strv_find_prefix(environ, "TERM="); + if (t) { + if (strv_extend(&arg_setenv, t) < 0) + return log_oom(); + } + } + + polkit_agent_open_if_enabled(); + + r = sd_event_default(&event); if (r < 0) - return log_error_errno(r, "Failed to create PTY forwarder: %m"); + return log_error_errno(r, "Failed to get event loop: %m"); - r = sd_event_loop(event); + r = sd_bus_attach_event(bus, event, 0); if (r < 0) - return log_error_errno(r, "Failed to run event loop: %m"); + return log_error_errno(r, "Failed to attach bus to event loop: %m"); - pty_forward_get_last_char(forward, &last_char); - machine_died = pty_forward_get_ignore_vhangup(forward) == 0; + machine = argc < 2 || isempty(argv[1]) ? NULL : argv[1]; - forward = pty_forward_free(forward); + if (arg_uid) + uid = arg_uid; + else if (machine) { + const char *at; - if (last_char != '\n') - fputc('\n', stdout); + at = strchr(machine, '@'); + if (at) { + uid = strndupa(machine, at - machine); + machine = at + 1; + } + } - if (machine_died) - log_info("Machine %s terminated.", argv[1]); - else - log_info("Connection to machine %s terminated.", argv[1]); + if (isempty(machine)) + machine = ".host"; - sd_event_get_exit_code(event, &ret); - return ret; + match = strjoina("type='signal'," + "sender='org.freedesktop.machine1'," + "path='/org/freedesktop/machine1',", + "interface='org.freedesktop.machine1.Manager'," + "member='MachineRemoved'," + "arg0='", machine, "'"); + + r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward); + if (r < 0) + return log_error_errno(r, "Failed to add machine removal match: %m"); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "OpenMachineShell"); + if (r < 0) + return bus_log_create_error(r); + + path = argc < 3 || isempty(argv[2]) ? NULL : argv[2]; + + r = sd_bus_message_append(m, "sss", machine, uid, path); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, strv_length(argv) <= 3 ? NULL : argv + 2); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, arg_setenv); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) { + log_error("Failed to get shell PTY: %s", bus_error_message(&error, -r)); + return r; + } + + r = sd_bus_message_read(reply, "hs", &master, &pty); + if (r < 0) + return bus_log_parse_error(r); + + return process_forward(event, &forward, master, false, machine); } static int remove_image(int argc, char *argv[], void *userdata) { @@ -2314,6 +2446,8 @@ static int help(int argc, char *argv[], void *userdata) { " -l --full Do not ellipsize output\n" " --kill-who=WHO Who to send signal to\n" " -s --signal=SIGNAL Which signal to send\n" + " --uid=USER Specify user ID to invoke shell as\n" + " --setenv=VAR=VALUE Add an environment variable for shell\n" " --read-only Create read-only bind mount\n" " --mkdir Create directory before bind mounting, if missing\n" " -n --lines=INTEGER Number of journal entries to show\n" @@ -2328,9 +2462,13 @@ static int help(int argc, char *argv[], void *userdata) { "Machine Commands:\n" " list List running VMs and containers\n" " status NAME... Show VM/container details\n" - " show NAME... Show properties of one or more VMs/containers\n" + " show [NAME...] Show properties of one or more VMs/containers\n" " start NAME... Start container as a service\n" - " login NAME Get a login prompt on a container\n" + " login [NAME] Get a login prompt in a container or on the\n" + " local host\n" + " shell [[USER@]NAME [COMMAND...]]\n" + " Invoke a shell (or other command) in a container\n" + " or on the local host\n" " enable NAME... Enable automatic container start at boot\n" " disable NAME... Disable automatic container start at boot\n" " poweroff NAME... Power off one or more containers\n" @@ -2342,8 +2480,8 @@ static int help(int argc, char *argv[], void *userdata) { " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n" "Image Commands:\n" " list-images Show available container and VM images\n" - " image-status NAME... Show image details\n" - " show-image NAME... Show properties of image\n" + " image-status [NAME...] Show image details\n" + " show-image [NAME...] Show properties of image\n" " clone NAME NAME Clone an image\n" " rename NAME NAME Rename an image\n" " read-only NAME [BOOL] Mark or unmark image read-only\n" @@ -2378,6 +2516,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_FORCE, ARG_DKR_INDEX_URL, ARG_FORMAT, + ARG_UID, + ARG_SETENV, }; static const struct option options[] = { @@ -2402,6 +2542,8 @@ static int parse_argv(int argc, char *argv[]) { { "force", no_argument, NULL, ARG_FORCE }, { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL }, { "format", required_argument, NULL, ARG_FORMAT }, + { "uid", required_argument, NULL, ARG_UID }, + { "setenv", required_argument, NULL, ARG_SETENV }, {} }; @@ -2532,6 +2674,21 @@ static int parse_argv(int argc, char *argv[]) { arg_format = optarg; break; + case ARG_UID: + arg_uid = optarg; + break; + + case ARG_SETENV: + if (!env_assignment_is_valid(optarg)) { + log_error("Environment assignment invalid: %s", optarg); + return -EINVAL; + } + + r = strv_extend(&arg_setenv, optarg); + if (r < 0) + return log_oom(); + break; + case '?': return -EINVAL; @@ -2556,7 +2713,8 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) { { "reboot", 2, VERB_ANY, 0, reboot_machine }, { "poweroff", 2, VERB_ANY, 0, poweroff_machine }, { "kill", 2, VERB_ANY, 0, kill_machine }, - { "login", 2, 2, 0, login_machine }, + { "login", VERB_ANY, 2, 0, login_machine }, + { "shell", VERB_ANY, VERB_ANY, 0, shell_machine }, { "bind", 3, 4, 0, bind_mount }, { "copy-to", 3, 4, 0, copy_files }, { "copy-from", 3, 4, 0, copy_files }, @@ -2610,6 +2768,7 @@ finish: polkit_agent_close(); strv_free(arg_property); + strv_free(arg_setenv); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 08a7f58ef5..0d52c693e4 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -33,6 +33,7 @@ #include "btrfs-util.h" #include "formats-util.h" #include "process-util.h" +#include "hostname-util.h" #include "machine-image.h" #include "machine-pool.h" #include "image-dbus.h" @@ -637,6 +638,27 @@ static int method_open_machine_login(sd_bus_message *message, void *userdata, sd return bus_machine_method_open_login(message, machine, error); } +static int method_open_machine_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + const char *name; + + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + return bus_machine_method_open_shell(message, machine, error); +} + static int method_bind_mount_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; Machine *machine; @@ -860,6 +882,9 @@ static int method_map_from_machine_user(sd_bus_message *message, void *userdata, if (!machine) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + if (machine->class != MACHINE_CONTAINER) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines."); + p = procfs_file_alloca(machine->leader, "uid_map"); f = fopen(p, "re"); if (!f) @@ -912,6 +937,9 @@ static int method_map_to_machine_user(sd_bus_message *message, void *userdata, s _cleanup_fclose_ FILE *f = NULL; char p[strlen("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1]; + if (machine->class != MACHINE_CONTAINER) + continue; + xsprintf(p, "/proc/" UID_FMT "/uid_map", machine->leader); f = fopen(p, "re"); if (!f) { @@ -972,6 +1000,9 @@ static int method_map_from_machine_group(sd_bus_message *message, void *groupdat if (!machine) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + if (machine->class != MACHINE_CONTAINER) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines."); + p = procfs_file_alloca(machine->leader, "gid_map"); f = fopen(p, "re"); if (!f) @@ -1024,6 +1055,9 @@ static int method_map_to_machine_group(sd_bus_message *message, void *groupdata, _cleanup_fclose_ FILE *f = NULL; char p[strlen("/proc//gid_map") + DECIMAL_STR_MAX(pid_t) + 1]; + if (machine->class != MACHINE_CONTAINER) + continue; + xsprintf(p, "/proc/" GID_FMT "/gid_map", machine->leader); f = fopen(p, "re"); if (!f) { @@ -1085,6 +1119,7 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("GetMachineOSRelease", "s", "a{ss}", method_get_machine_os_release, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("OpenMachinePTY", "s", "hs", method_open_machine_pty, 0), SD_BUS_METHOD("OpenMachineLogin", "s", "hs", method_open_machine_login, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenMachineShell", "sssasas", "hs", method_open_machine_shell, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("BindMountMachine", "sssbb", NULL, method_bind_mount_machine, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), @@ -1455,7 +1490,6 @@ int manager_job_is_active(Manager *manager, const char *path) { } int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) { - _cleanup_free_ char *unit = NULL; Machine *mm; int r; @@ -1463,12 +1497,14 @@ int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) { assert(pid >= 1); assert(machine); - r = cg_pid_get_unit(pid, &unit); - if (r < 0) - mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid)); - else - mm = hashmap_get(m->machine_units, unit); + mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid)); + if (!mm) { + _cleanup_free_ char *unit = NULL; + r = cg_pid_get_unit(pid, &unit); + if (r >= 0) + mm = hashmap_get(m->machine_units, unit); + } if (!mm) return 0; @@ -1484,7 +1520,7 @@ int manager_add_machine(Manager *m, const char *name, Machine **_machine) { machine = hashmap_get(m->machines, name); if (!machine) { - machine = machine_new(m, name); + machine = machine_new(m, _MACHINE_CLASS_INVALID, name); if (!machine) return -ENOMEM; } diff --git a/src/machine/machined.c b/src/machine/machined.c index c8ad157326..df3cc9972a 100644 --- a/src/machine/machined.c +++ b/src/machine/machined.c @@ -30,6 +30,7 @@ #include "label.h" #include "formats-util.h" #include "signal-util.h" +#include "hostname-util.h" #include "machine-image.h" #include "machined.h" @@ -89,6 +90,45 @@ void manager_free(Manager *m) { free(m); } +static int manager_add_host_machine(Manager *m) { + _cleanup_free_ char *rd = NULL, *unit = NULL; + sd_id128_t mid; + Machine *t; + int r; + + if (m->host_machine) + return 0; + + r = sd_id128_get_machine(&mid); + if (r < 0) + return log_error_errno(r, "Failed to get machine ID: %m"); + + rd = strdup("/"); + if (!rd) + return log_oom(); + + unit = strdup("-.slice"); + if (!unit) + return log_oom(); + + t = machine_new(m, MACHINE_HOST, ".host"); + if (!t) + return log_oom(); + + t->leader = 1; + t->id = mid; + + t->root_directory = rd; + t->unit = unit; + rd = unit = NULL; + + dual_timestamp_from_boottime_or_monotonic(&t->timestamp, 0); + + m->host_machine = t; + + return 0; +} + int manager_enumerate_machines(Manager *m) { _cleanup_closedir_ DIR *d = NULL; struct dirent *de; @@ -96,6 +136,10 @@ int manager_enumerate_machines(Manager *m) { assert(m); + r = manager_add_host_machine(m); + if (r < 0) + return r; + /* Read in machine data stored on disk */ d = opendir("/run/systemd/machines"); if (!d) { @@ -117,11 +161,12 @@ int manager_enumerate_machines(Manager *m) { if (startswith(de->d_name, "unit:")) continue; + if (!machine_name_is_valid(de->d_name)) + continue; + k = manager_add_machine(m, de->d_name, &machine); if (k < 0) { - log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name); - - r = k; + r = log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name); continue; } diff --git a/src/machine/machined.h b/src/machine/machined.h index 61dbefb5f1..b3e59bf998 100644 --- a/src/machine/machined.h +++ b/src/machine/machined.h @@ -48,6 +48,8 @@ struct Manager { sd_event_source *image_cache_defer_event; LIST_HEAD(Machine, machine_gc_queue); + + Machine *host_machine; }; Manager *manager_new(void); diff --git a/src/machine/org.freedesktop.machine1.conf b/src/machine/org.freedesktop.machine1.conf index d58f01507b..9d40b90151 100644 --- a/src/machine/org.freedesktop.machine1.conf +++ b/src/machine/org.freedesktop.machine1.conf @@ -70,6 +70,10 @@ <allow send_destination="org.freedesktop.machine1" send_interface="org.freedesktop.machine1.Manager" + send_member="OpenMachineShell"/> + + <allow send_destination="org.freedesktop.machine1" + send_interface="org.freedesktop.machine1.Manager" send_member="TerminateMachine"/> <allow send_destination="org.freedesktop.machine1" @@ -142,6 +146,10 @@ <allow send_destination="org.freedesktop.machine1" send_interface="org.freedesktop.machine1.Machine" + send_member="OpenShell"/> + + <allow send_destination="org.freedesktop.machine1" + send_interface="org.freedesktop.machine1.Machine" send_member="Terminate"/> <allow send_destination="org.freedesktop.machine1" diff --git a/src/machine/org.freedesktop.machine1.policy.in b/src/machine/org.freedesktop.machine1.policy.in index 02714e83ae..69f78a5c25 100644 --- a/src/machine/org.freedesktop.machine1.policy.in +++ b/src/machine/org.freedesktop.machine1.policy.in @@ -26,6 +26,58 @@ </defaults> </action> + <action id="org.freedesktop.machine1.host-login"> + <_description>Log into the local host</_description> + <_message>Authentication is required to log into the local host.</_message> + <defaults> + <allow_any>auth_admin</allow_any> + <allow_inactive>auth_admin</allow_inactive> + <allow_active>yes</allow_active> + </defaults> + </action> + + <action id="org.freedesktop.machine1.shell"> + <_description>Acquire a shell in a local container</_description> + <_message>Authentication is required to acquire a shell in a local container.</_message> + <defaults> + <allow_any>auth_admin</allow_any> + <allow_inactive>auth_admin</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.login</annotate> + </action> + + <action id="org.freedesktop.machine1.host-shell"> + <_description>Acquire a shell on the local host</_description> + <_message>Authentication is required to acquire a shell on the local host.</_message> + <defaults> + <allow_any>auth_admin</allow_any> + <allow_inactive>auth_admin</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.host-login</annotate> + </action> + + <action id="org.freedesktop.machine1.open-pty"> + <_description>Acquire a pseudo TTY in a local container</_description> + <_message>Authentication is required to acquire a pseudo TTY in a local container.</_message> + <defaults> + <allow_any>auth_admin</allow_any> + <allow_inactive>auth_admin</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + </action> + + <action id="org.freedesktop.machine1.host-open-pty"> + <_description>Acquire a pseudo TTY on the local host</_description> + <_message>Authentication is required to acquire a pseudo TTY on the local host.</_message> + <defaults> + <allow_any>auth_admin</allow_any> + <allow_inactive>auth_admin</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + </action> + <action id="org.freedesktop.machine1.manage-machines"> <_description>Manage local virtual machines and containers</_description> <_message>Authentication is required to manage local virtual machines and containers.</_message> @@ -34,6 +86,7 @@ <allow_inactive>auth_admin</allow_inactive> <allow_active>auth_admin_keep</allow_active> </defaults> + <annotate key="org.freedesktop.policykit.imply">org.freedesktop.login1.shell org.freedesktop.login1.login</annotate> </action> <action id="org.freedesktop.machine1.manage-images"> diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 6288644a1a..1b2ff7c769 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -365,7 +365,7 @@ static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) { sd_dhcp_lease_unref(link->dhcp_lease); link->dhcp4_configured = false; - link->dhcp_lease = lease; + link->dhcp_lease = sd_dhcp_lease_ref(lease); r = sd_dhcp_lease_get_address(lease, &address); if (r < 0) { @@ -454,7 +454,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { "PREFIXLEN=%u", prefixlen, NULL); - link->dhcp_lease = lease; + link->dhcp_lease = sd_dhcp_lease_ref(lease); if (link->network->dhcp_mtu) { uint16_t mtu; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 91b9cdf30d..cc9dc393c6 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -2378,7 +2378,7 @@ int link_save(Link *link) { yes_no(link->network->wildcard_domain)); fprintf(f, "LLMNR=%s\n", - llmnr_support_to_string(link->network->llmnr)); + resolve_support_to_string(link->network->llmnr)); } if (!hashmap_isempty(link->bound_to_links)) { diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 8735b39581..7ac7ef1ea3 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -45,7 +45,7 @@ Network.Address, config_parse_address, 0 Network.Gateway, config_parse_gateway, 0, 0 Network.Domains, config_parse_domains, 0, offsetof(Network, domains) Network.DNS, config_parse_strv, 0, offsetof(Network, dns) -Network.LLMNR, config_parse_llmnr, 0, offsetof(Network, llmnr) +Network.LLMNR, config_parse_resolve, 0, offsetof(Network, llmnr) Network.NTP, config_parse_strv, 0, offsetof(Network, ntp) Network.IPForward, config_parse_address_family_boolean_with_kernel,0, offsetof(Network, ip_forward) Network.IPMasquerade, config_parse_bool, 0, offsetof(Network, ip_masquerade) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 3f7e77da3e..6587ea994c 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -111,7 +111,7 @@ static int network_load_one(Manager *manager, const char *filename) { network->allow_port_to_be_root = true; network->unicast_flood = true; - network->llmnr = LLMNR_SUPPORT_YES; + network->llmnr = RESOLVE_SUPPORT_YES; network->link_local = ADDRESS_FAMILY_IPV6; @@ -632,15 +632,15 @@ static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = { DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DCHPClientIdentifier); DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DCHPClientIdentifier, "Failed to parse client identifier type"); -static const char* const llmnr_support_table[_LLMNR_SUPPORT_MAX] = { - [LLMNR_SUPPORT_NO] = "no", - [LLMNR_SUPPORT_YES] = "yes", - [LLMNR_SUPPORT_RESOLVE] = "resolve", +static const char* const resolve_support_table[_RESOLVE_SUPPORT_MAX] = { + [RESOLVE_SUPPORT_NO] = "no", + [RESOLVE_SUPPORT_YES] = "yes", + [RESOLVE_SUPPORT_RESOLVE] = "resolve", }; -DEFINE_STRING_TABLE_LOOKUP(llmnr_support, LLMNRSupport); +DEFINE_STRING_TABLE_LOOKUP(resolve_support, ResolveSupport); -int config_parse_llmnr( +int config_parse_resolve( const char* unit, const char *filename, unsigned line, @@ -652,32 +652,32 @@ int config_parse_llmnr( void *data, void *userdata) { - LLMNRSupport *llmnr = data; + ResolveSupport *resolve = data; int k; assert(filename); assert(lvalue); assert(rvalue); - assert(llmnr); + assert(resolve); /* 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) - *llmnr = LLMNR_SUPPORT_YES; + *resolve = RESOLVE_SUPPORT_YES; else if (k == 0) - *llmnr = LLMNR_SUPPORT_NO; + *resolve = RESOLVE_SUPPORT_NO; else { - LLMNRSupport s; + ResolveSupport s; - s = llmnr_support_from_string(rvalue); + s = resolve_support_from_string(rvalue); if (s < 0){ - log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse LLMNR option, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse %s option, ignoring: %s", lvalue, rvalue); return 0; } - *llmnr = s; + *resolve = s; } return 0; diff --git a/src/network/networkd.h b/src/network/networkd.h index a285a4b08f..5340922bf1 100644 --- a/src/network/networkd.h +++ b/src/network/networkd.h @@ -64,13 +64,13 @@ typedef enum AddressFamilyBoolean { _ADDRESS_FAMILY_BOOLEAN_INVALID = -1, } AddressFamilyBoolean; -typedef enum LLMNRSupport { - LLMNR_SUPPORT_NO, - LLMNR_SUPPORT_YES, - LLMNR_SUPPORT_RESOLVE, - _LLMNR_SUPPORT_MAX, - _LLMNR_SUPPORT_INVALID = -1, -} LLMNRSupport; +typedef enum ResolveSupport { + RESOLVE_SUPPORT_NO, + RESOLVE_SUPPORT_YES, + RESOLVE_SUPPORT_RESOLVE, + _RESOLVE_SUPPORT_MAX, + _RESOLVE_SUPPORT_INVALID = -1, +} ResolveSupport; typedef enum LinkOperationalState { LINK_OPERSTATE_OFF, @@ -178,7 +178,7 @@ struct Network { bool wildcard_domain; char **domains, **dns, **ntp, **bind_carrier; - LLMNRSupport llmnr; + ResolveSupport llmnr; LIST_FIELDS(Network, networks); }; @@ -421,14 +421,14 @@ int config_parse_ipv6token(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); -/* LLMNR support */ +/* Resolve protocols support */ -const char* llmnr_support_to_string(LLMNRSupport i) _const_; -LLMNRSupport llmnr_support_from_string(const char *s) _pure_; +const char* resolve_support_to_string(ResolveSupport i) _const_; +ResolveSupport resolve_support_from_string(const char *s) _pure_; -int config_parse_llmnr(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_resolve(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); /* Address Pool */ diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index e8a023d023..837947ee28 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -559,9 +559,9 @@ static int parse_argv(int argc, char *argv[]) { break; case 'M': - if (isempty(optarg)) { + if (isempty(optarg)) arg_machine = mfree(arg_machine); - } else { + else { if (!machine_name_is_valid(optarg)) { log_error("Invalid machine name: %s", optarg); return -EINVAL; @@ -4002,6 +4002,17 @@ static int on_orderly_shutdown(sd_event_source *s, const struct signalfd_siginfo static int determine_names(void) { int r; + if (arg_template && !arg_directory && arg_machine) { + + /* If --template= was specified then we should not + * search for a machine, but instead create a new one + * in /var/lib/machine. */ + + arg_directory = strjoin("/var/lib/machines/", arg_machine, NULL); + if (!arg_directory) + return log_oom(); + } + if (!arg_image && !arg_directory) { if (arg_machine) { _cleanup_(image_unrefp) Image *i = NULL; diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c index cdec83d074..5758ea1569 100644 --- a/src/nss-mymachines/nss-mymachines.c +++ b/src/nss-mymachines/nss-mymachines.c @@ -30,6 +30,7 @@ #include "bus-util.h" #include "bus-common-errors.h" #include "in-addr-util.h" +#include "hostname-util.h" NSS_GETHOSTBYNAME_PROTOTYPES(mymachines); NSS_GETPW_PROTOTYPES(mymachines); diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c index 4a7d1e3173..9847effb53 100644 --- a/src/resolve-host/resolve-host.c +++ b/src/resolve-host/resolve-host.c @@ -490,7 +490,7 @@ static int parse_argv(int argc, char *argv[]) { { "version", no_argument, NULL, ARG_VERSION }, { "type", required_argument, NULL, 't' }, { "class", required_argument, NULL, 'c' }, - { "legend", optional_argument, NULL, ARG_LEGEND }, + { "legend", optional_argument, NULL, ARG_LEGEND }, { "protocol", required_argument, NULL, 'p' }, {} }; @@ -520,11 +520,21 @@ static int parse_argv(int argc, char *argv[]) { arg_family = AF_INET6; break; - case 'i': - arg_ifindex = if_nametoindex(optarg); - if (arg_ifindex <= 0) - return log_error_errno(errno, "Unknown interfaces %s: %m", optarg); + case 'i': { + int ifi; + + if (safe_atoi(optarg, &ifi) >= 0 && ifi > 0) + arg_ifindex = ifi; + else { + ifi = if_nametoindex(optarg); + if (ifi <= 0) + return log_error_errno(errno, "Unknown interface %s: %m", optarg); + + arg_ifindex = ifi; + } + break; + } case 't': if (streq(optarg, "help")) { diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 1f23834ce3..12c17003e9 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -226,10 +226,6 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { * query, this time with the cname */ if (added <= 0) { r = dns_query_go(q); - if (r == -ESRCH) { - r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); - goto finish; - } if (r < 0) { r = sd_bus_reply_method_errno(q->request, -r, NULL); goto finish; @@ -346,10 +342,6 @@ static int bus_method_resolve_hostname(sd_bus_message *message, void *userdata, r = dns_query_go(q); if (r < 0) { dns_query_free(q); - - if (r == -ESRCH) - sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); - return r; } @@ -494,10 +486,6 @@ static int bus_method_resolve_address(sd_bus_message *message, void *userdata, s r = dns_query_go(q); if (r < 0) { dns_query_free(q); - - if (r == -ESRCH) - sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); - return r; } @@ -647,10 +635,6 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd r = dns_query_go(q); if (r < 0) { dns_query_free(q); - - if (r == -ESRCH) - sd_bus_error_setf(error, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); - return r; } diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index efc407dbc6..ef6b69c768 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -411,16 +411,17 @@ int dns_cache_put( int owner_family, const union in_addr_union *owner_address) { - unsigned i; + unsigned cache_keys, i; int r; assert(c); - assert(q); - /* First, delete all matching old RRs, so that we only keep - * complete by_key in place. */ - for (i = 0; i < q->n_keys; i++) - dns_cache_remove(c, q->keys[i]); + if (q) { + /* First, if we were passed a question, delete all matching old RRs, + * so that we only keep complete by_key in place. */ + for (i = 0; i < q->n_keys; i++) + dns_cache_remove(c, q->keys[i]); + } if (!answer) return 0; @@ -435,8 +436,13 @@ int dns_cache_put( if (!IN_SET(rcode, DNS_RCODE_SUCCESS, DNS_RCODE_NXDOMAIN)) return 0; + cache_keys = answer->n_rrs; + + if (q) + cache_keys += q->n_keys; + /* Make some space for our new entries */ - dns_cache_make_space(c, answer->n_rrs + q->n_keys); + dns_cache_make_space(c, cache_keys); if (timestamp <= 0) timestamp = now(clock_boottime_or_monotonic()); @@ -448,6 +454,9 @@ int dns_cache_put( goto fail; } + if (!q) + return 0; + /* Third, add in negative entries for all keys with no RR */ for (i = 0; i < q->n_keys; i++) { DnsResourceRecord *soa = NULL; @@ -479,8 +488,11 @@ fail: /* Adding all RRs failed. Let's clean up what we already * added, just in case */ - for (i = 0; i < q->n_keys; i++) - dns_cache_remove(c, q->keys[i]); + if (q) { + for (i = 0; i < q->n_keys; i++) + dns_cache_remove(c, q->keys[i]); + } + for (i = 0; i < answer->n_rrs; i++) dns_cache_remove(c, answer->items[i].rr->key); @@ -608,3 +620,54 @@ int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_ /* There's a conflict */ return 1; } + +void dns_cache_dump(DnsCache *cache, FILE *f) { + Iterator iterator; + DnsCacheItem *i; + int r; + + if (!cache) + return; + + if (!f) + f = stdout; + + HASHMAP_FOREACH(i, cache->by_key, iterator) { + DnsCacheItem *j; + + LIST_FOREACH(by_key, j, i) { + _cleanup_free_ char *t = NULL; + + fputc('\t', f); + + if (j->rr) { + r = dns_resource_record_to_string(j->rr, &t); + if (r < 0) { + log_oom(); + continue; + } + + fputs(t, f); + fputc('\n', f); + } else { + r = dns_resource_key_to_string(j->key, &t); + if (r < 0) { + log_oom(); + continue; + } + + fputs(t, f); + fputs(" -- ", f); + fputs(j->type == DNS_CACHE_NODATA ? "NODATA" : "NXDOMAIN", f); + fputc('\n', f); + } + } + } +} + +bool dns_cache_is_empty(DnsCache *cache) { + if (!cache) + return true; + + return hashmap_isempty(cache->by_key); +} diff --git a/src/resolve/resolved-dns-cache.h b/src/resolve/resolved-dns-cache.h index fd583a775f..1225e58de4 100644 --- a/src/resolve/resolved-dns-cache.h +++ b/src/resolve/resolved-dns-cache.h @@ -43,3 +43,6 @@ int dns_cache_put(DnsCache *c, DnsQuestion *q, int rcode, DnsAnswer *answer, uns int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **answer); int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address); + +void dns_cache_dump(DnsCache *cache, FILE *f); +bool dns_cache_is_empty(DnsCache *cache); diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index e81f8a8202..5628e579ad 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -121,15 +121,15 @@ static inline uint8_t* DNS_PACKET_DATA(DnsPacket *p) { #define DNS_PACKET_ARCOUNT(p) be16toh(DNS_PACKET_HEADER(p)->arcount) #define DNS_PACKET_MAKE_FLAGS(qr, opcode, aa, tc, rd, ra, ad, cd, rcode) \ - (((uint16_t) !!qr << 15) | \ - ((uint16_t) (opcode & 15) << 11) | \ - ((uint16_t) !!aa << 10) | \ - ((uint16_t) !!tc << 9) | \ - ((uint16_t) !!rd << 8) | \ - ((uint16_t) !!ra << 7) | \ - ((uint16_t) !!ad << 5) | \ - ((uint16_t) !!cd << 4) | \ - ((uint16_t) (rcode & 15))) + (((uint16_t) !!(qr) << 15) | \ + ((uint16_t) ((opcode) & 15) << 11) | \ + ((uint16_t) !!(aa) << 10) | /* on LLMNR: c */ \ + ((uint16_t) !!(tc) << 9) | \ + ((uint16_t) !!(rd) << 8) | /* on LLMNR: t */ \ + ((uint16_t) !!(ra) << 7) | \ + ((uint16_t) !!(ad) << 5) | \ + ((uint16_t) !!(cd) << 4) | \ + ((uint16_t) ((rcode) & 15))) static inline unsigned DNS_PACKET_RRCOUNT(DnsPacket *p) { return @@ -239,11 +239,16 @@ static inline uint64_t SD_RESOLVED_FLAGS_MAKE(DnsProtocol protocol, int family) /* Converts a protocol + family into a flags field as used in queries */ - if (protocol == DNS_PROTOCOL_DNS) + switch (protocol) { + case DNS_PROTOCOL_DNS: return SD_RESOLVED_DNS; - if (protocol == DNS_PROTOCOL_LLMNR) + case DNS_PROTOCOL_LLMNR: return family == AF_INET6 ? SD_RESOLVED_LLMNR_IPV6 : SD_RESOLVED_LLMNR_IPV4; + default: + break; + } + return 0; } diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index d9f5b342b2..c0b4c8ba81 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -651,12 +651,8 @@ int dns_query_go(DnsQuery *q) { DnsTransactionState state = DNS_TRANSACTION_NO_SERVERS; dns_query_synthesize_reply(q, &state); - if (state != DNS_TRANSACTION_NO_SERVERS) { - dns_query_complete(q, state); - return 1; - } - - return -ESRCH; + dns_query_complete(q, state); + return 1; } r = dns_query_add_transaction_split(q, first); diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 57a2c7d6c1..9e6f595a1b 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -78,8 +78,7 @@ DnsScope* dns_scope_free(DnsScope *s) { dns_scope_llmnr_membership(s, false); - while ((t = s->transactions)) { - + while ((t = hashmap_steal_first(s->transactions))) { /* Abort the transaction, but make sure it is not * freed while we still look at it */ @@ -90,6 +89,8 @@ DnsScope* dns_scope_free(DnsScope *s) { dns_transaction_free(t); } + hashmap_free(s->transactions); + while ((rr = ordered_hashmap_steal_first(s->conflict_queue))) dns_resource_record_unref(rr); @@ -165,7 +166,8 @@ int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) { } else mtu = manager_find_mtu(s->manager); - if (s->protocol == DNS_PROTOCOL_DNS) { + switch (s->protocol) { + case DNS_PROTOCOL_DNS: if (DNS_PACKET_QDCOUNT(p) > 1) return -EOPNOTSUPP; @@ -179,8 +181,9 @@ int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) { if (r < 0) return r; - } else if (s->protocol == DNS_PROTOCOL_LLMNR) { + break; + case DNS_PROTOCOL_LLMNR: if (DNS_PACKET_QDCOUNT(p) > 1) return -EOPNOTSUPP; @@ -204,8 +207,12 @@ int dns_scope_emit(DnsScope *s, int fd, DnsPacket *p) { r = manager_send(s->manager, fd, ifindex, family, &addr, port, p); if (r < 0) return r; - } else + + break; + + default: return -EAFNOSUPPORT; + } return 1; } @@ -340,16 +347,16 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co if (dns_name_endswith(domain, *i) > 0) return DNS_SCOPE_YES; - if (s->protocol == DNS_PROTOCOL_DNS) { + switch (s->protocol) { + case DNS_PROTOCOL_DNS: if (dns_name_endswith(domain, "254.169.in-addr.arpa") == 0 && dns_name_endswith(domain, "0.8.e.f.ip6.arpa") == 0 && dns_name_single_label(domain) == 0) return DNS_SCOPE_MAYBE; return DNS_SCOPE_NO; - } - if (s->protocol == DNS_PROTOCOL_MDNS) { + case DNS_PROTOCOL_MDNS: if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) || (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) || (dns_name_endswith(domain, "local") > 0 && /* only resolve names ending in .local via mDNS */ @@ -358,9 +365,8 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co return DNS_SCOPE_MAYBE; return DNS_SCOPE_NO; - } - if (s->protocol == DNS_PROTOCOL_LLMNR) { + case DNS_PROTOCOL_LLMNR: if ((s->family == AF_INET && dns_name_endswith(domain, "in-addr.arpa") > 0) || (s->family == AF_INET6 && dns_name_endswith(domain, "ip6.arpa") > 0) || (dns_name_single_label(domain) > 0 && /* only resolve single label names via LLMNR */ @@ -369,9 +375,10 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co return DNS_SCOPE_MAYBE; return DNS_SCOPE_NO; - } - assert_not_reached("Unknown scope protocol"); + default: + assert_not_reached("Unknown scope protocol"); + } } int dns_scope_good_key(DnsScope *s, DnsResourceKey *key) { @@ -623,24 +630,20 @@ DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key, assert(scope); assert(key); - /* Try to find an ongoing transaction that is a equal or a - * superset of the specified question */ - - LIST_FOREACH(transactions_by_scope, t, scope->transactions) { - - /* Refuse reusing transactions that completed based on - * cached data instead of a real packet, if that's - * requested. */ - if (!cache_ok && - IN_SET(t->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE) && - !t->received) - continue; + /* Try to find an ongoing transaction that is a equal to the + * specified question */ + t = hashmap_get(scope->transactions, key); + if (!t) + return NULL; - if (dns_resource_key_equal(t->key, key) > 0) - return t; - } + /* Refuse reusing transactions that completed based on cached + * data instead of a real packet, if that's requested. */ + if (!cache_ok && + IN_SET(t->state, DNS_TRANSACTION_SUCCESS, DNS_TRANSACTION_FAILURE) && + !t->received) + return NULL; - return NULL; + return t; } static int dns_scope_make_conflict_packet( @@ -809,3 +812,35 @@ void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) { dns_scope_notify_conflict(scope, p->answer->items[i].rr); } } + +void dns_scope_dump(DnsScope *s, FILE *f) { + assert(s); + + if (!f) + f = stdout; + + fputs("[Scope protocol=", f); + fputs(dns_protocol_to_string(s->protocol), f); + + if (s->link) { + fputs(" interface=", f); + fputs(s->link->name, f); + } + + if (s->family != AF_UNSPEC) { + fputs(" family=", f); + fputs(af_to_name(s->family), f); + } + + fputs("]\n", f); + + if (!dns_zone_is_empty(&s->zone)) { + fputs("ZONE:\n", f); + dns_zone_dump(&s->zone, f); + } + + if (!dns_cache_is_empty(&s->cache)) { + fputs("CACHE:\n", f); + dns_cache_dump(&s->cache, f); + } +} diff --git a/src/resolve/resolved-dns-scope.h b/src/resolve/resolved-dns-scope.h index 47f285db47..b75f212897 100644 --- a/src/resolve/resolved-dns-scope.h +++ b/src/resolve/resolved-dns-scope.h @@ -60,7 +60,7 @@ struct DnsScope { usec_t resend_timeout; usec_t max_rtt; - LIST_HEAD(DnsTransaction, transactions); + Hashmap *transactions; LIST_FIELDS(DnsScope, scopes); }; @@ -89,3 +89,5 @@ DnsTransaction *dns_scope_find_transaction(DnsScope *scope, DnsResourceKey *key, int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr); void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p); + +void dns_scope_dump(DnsScope *s, FILE *f); diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 81156dfa45..8092bb514d 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -35,24 +35,25 @@ DnsTransaction* dns_transaction_free(DnsTransaction *t) { sd_event_source_unref(t->timeout_event_source); - dns_resource_key_unref(t->key); dns_packet_unref(t->sent); dns_packet_unref(t->received); dns_answer_unref(t->cached); - sd_event_source_unref(t->dns_event_source); - safe_close(t->dns_fd); + sd_event_source_unref(t->dns_udp_event_source); + safe_close(t->dns_udp_fd); dns_server_unref(t->server); dns_stream_free(t->stream); if (t->scope) { - LIST_REMOVE(transactions_by_scope, t->scope->transactions, t); + hashmap_remove(t->scope->transactions, t->key); if (t->id != 0) hashmap_remove(t->scope->manager->dns_transactions, UINT_TO_PTR(t->id)); } + dns_resource_key_unref(t->key); + while ((q = set_steal_first(t->queries))) set_remove(q->transactions, t); set_free(t->queries); @@ -89,14 +90,18 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) if (r < 0) return r; + r = hashmap_ensure_allocated(&s->transactions, &dns_resource_key_hash_ops); + if (r < 0) + return r; + t = new0(DnsTransaction, 1); if (!t) return -ENOMEM; - t->dns_fd = -1; - + t->dns_udp_fd = -1; t->key = dns_resource_key_ref(key); + /* Find a fresh, unused transaction id */ do random_bytes(&t->id, sizeof(t->id)); while (t->id == 0 || @@ -108,7 +113,12 @@ int dns_transaction_new(DnsTransaction **ret, DnsScope *s, DnsResourceKey *key) return r; } - LIST_PREPEND(transactions_by_scope, s->transactions, t); + r = hashmap_put(s->transactions, t->key, t); + if (r < 0) { + hashmap_remove(s->manager->dns_transactions, UINT_TO_PTR(t->id)); + return r; + } + t->scope = s; if (ret) @@ -176,9 +186,6 @@ void dns_transaction_complete(DnsTransaction *t, DnsTransactionState state) { assert(t); assert(!IN_SET(state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING)); - if (!IN_SET(t->state, DNS_TRANSACTION_NULL, DNS_TRANSACTION_PENDING)) - return; - /* Note that this call might invalidate the query. Callers * should hence not attempt to access the query or transaction * after calling this function. */ @@ -253,10 +260,12 @@ static int dns_transaction_open_tcp(DnsTransaction *t) { if (t->stream) return 0; - if (t->scope->protocol == DNS_PROTOCOL_DNS) + switch (t->scope->protocol) { + case DNS_PROTOCOL_DNS: fd = dns_scope_tcp_socket(t->scope, AF_UNSPEC, NULL, 53, &server); - else if (t->scope->protocol == DNS_PROTOCOL_LLMNR) { + break; + case DNS_PROTOCOL_LLMNR: /* When we already received a reply to this (but it was truncated), send to its sender address */ if (t->received) fd = dns_scope_tcp_socket(t->scope, t->received->family, &t->received->sender, t->received->sender_port, NULL); @@ -274,12 +283,16 @@ static int dns_transaction_open_tcp(DnsTransaction *t) { if (r == 0) return -EINVAL; if (family != t->scope->family) - return -EAFNOSUPPORT; + return -ESRCH; fd = dns_scope_tcp_socket(t->scope, family, &address, LLMNR_PORT, NULL); } - } else + + break; + + default: return -EAFNOSUPPORT; + } if (fd < 0) return fd; @@ -315,8 +328,8 @@ static void dns_transaction_next_dns_server(DnsTransaction *t) { assert(t); t->server = dns_server_unref(t->server); - t->dns_event_source = sd_event_source_unref(t->dns_event_source); - t->dns_fd = safe_close(t->dns_fd); + t->dns_udp_event_source = sd_event_source_unref(t->dns_udp_event_source); + t->dns_udp_fd = safe_close(t->dns_udp_fd); dns_scope_next_dns_server(t->scope); } @@ -335,7 +348,8 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { * should hence not attempt to access the query or transaction * after calling this function. */ - if (t->scope->protocol == DNS_PROTOCOL_LLMNR) { + switch (t->scope->protocol) { + case DNS_PROTOCOL_LLMNR: assert(t->scope->link); /* For LLMNR we will not accept any packets from other @@ -354,6 +368,14 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { dns_transaction_tentative(t, p); return; } + + break; + + case DNS_PROTOCOL_DNS: + break; + + default: + assert_not_reached("Invalid DNS protocol."); } if (t->received != p) { @@ -390,7 +412,7 @@ void dns_transaction_process_reply(DnsTransaction *t, DnsPacket *p) { break; default: - assert_not_reached("Invalid DNS protocol."); + break; } if (DNS_PACKET_TC(p)) { @@ -478,16 +500,16 @@ static int dns_transaction_emit(DnsTransaction *t) { if (fd < 0) return fd; - r = sd_event_add_io(t->scope->manager->event, &t->dns_event_source, fd, EPOLLIN, on_dns_packet, t); + r = sd_event_add_io(t->scope->manager->event, &t->dns_udp_event_source, fd, EPOLLIN, on_dns_packet, t); if (r < 0) return r; - t->dns_fd = fd; + t->dns_udp_fd = fd; fd = -1; t->server = dns_server_ref(server); } - r = dns_scope_emit(t->scope, t->dns_fd, t->sent); + r = dns_scope_emit(t->scope, t->dns_udp_fd, t->sent); if (r < 0) return r; diff --git a/src/resolve/resolved-dns-transaction.h b/src/resolve/resolved-dns-transaction.h index 4db9352a04..acf6a6f651 100644 --- a/src/resolve/resolved-dns-transaction.h +++ b/src/resolve/resolved-dns-transaction.h @@ -62,10 +62,10 @@ struct DnsTransaction { sd_event_source *timeout_event_source; unsigned n_attempts; - int dns_fd; - sd_event_source *dns_event_source; + int dns_udp_fd; + sd_event_source *dns_udp_event_source; - /* the active server */ + /* The active server */ DnsServer *server; /* TCP connection logic, if we need it */ diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index fc212f48f4..674bb6af28 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -636,3 +636,40 @@ void dns_zone_verify_all(DnsZone *zone) { dns_zone_item_verify(j); } } + +void dns_zone_dump(DnsZone *zone, FILE *f) { + Iterator iterator; + DnsZoneItem *i; + int r; + + if (!zone) + return; + + if (!f) + f = stdout; + + HASHMAP_FOREACH(i, zone->by_key, iterator) { + DnsZoneItem *j; + + LIST_FOREACH(by_key, j, i) { + _cleanup_free_ char *t = NULL; + + r = dns_resource_record_to_string(j->rr, &t); + if (r < 0) { + log_oom(); + continue; + } + + fputc('\t', f); + fputs(t, f); + fputc('\n', f); + } + } +} + +bool dns_zone_is_empty(DnsZone *zone) { + if (!zone) + return true; + + return hashmap_isempty(zone->by_key); +} diff --git a/src/resolve/resolved-dns-zone.h b/src/resolve/resolved-dns-zone.h index 71851265c6..495d17cdb1 100644 --- a/src/resolve/resolved-dns-zone.h +++ b/src/resolve/resolved-dns-zone.h @@ -78,3 +78,6 @@ int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key); void dns_zone_verify_all(DnsZone *zone); void dns_zone_item_probe_stop(DnsZoneItem *i); + +void dns_zone_dump(DnsZone *zone, FILE *f); +bool dns_zone_is_empty(DnsZone *zone); diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index fb2a06b517..13852192c4 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -430,6 +430,31 @@ static int manager_watch_hostname(Manager *m) { return 0; } +static int manager_sigusr1(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + _cleanup_free_ char *buffer = NULL; + _cleanup_fclose_ FILE *f = NULL; + Manager *m = userdata; + size_t size = 0; + DnsScope *scope; + + assert(s); + assert(si); + assert(m); + + f = open_memstream(&buffer, &size); + if (!f) + return log_oom(); + + LIST_FOREACH(scopes, scope, m->dns_scopes) + dns_scope_dump(scope, f); + + if (fflush_and_check(f) < 0) + return log_oom(); + + log_dump(LOG_INFO, buffer); + return 0; +} + int manager_new(Manager **ret) { _cleanup_(manager_freep) Manager *m = NULL; int r; @@ -480,6 +505,8 @@ int manager_new(Manager **ret) { if (r < 0) return r; + (void) sd_event_add_signal(m->event, &m->sigusr1_event_source, SIGUSR1, manager_sigusr1, m); + *ret = m; m = NULL; @@ -527,13 +554,15 @@ Manager *manager_free(Manager *m) { sd_event_source_unref(m->bus_retry_event_source); sd_bus_unref(m->bus); + sd_event_source_unref(m->sigusr1_event_source); + sd_event_unref(m->event); dns_resource_key_unref(m->llmnr_host_ipv4_key); dns_resource_key_unref(m->llmnr_host_ipv6_key); - safe_close(m->hostname_fd); sd_event_source_unref(m->hostname_event_source); + safe_close(m->hostname_fd); free(m->llmnr_hostname); free(m->mdns_hostname); diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 6f7972bbf3..fe7fe99505 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -102,6 +102,8 @@ struct Manager { /* Watch for system suspends */ sd_bus_slot *prepare_for_sleep_slot; + + sd_event_source *sigusr1_event_source; }; /* Manager */ diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c index 0af5545f8e..32e61af925 100644 --- a/src/resolve/resolved.c +++ b/src/resolve/resolved.c @@ -71,7 +71,7 @@ int main(int argc, char *argv[]) { if (r < 0) goto finish; - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, SIGUSR1, -1) >= 0); r = manager_new(&m); if (r < 0) { diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 0df0594ea3..1bcb8903f3 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -1382,7 +1382,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen if (STR_IN_SET(field, "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", - "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies")) { + "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", + "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit")) { r = parse_boolean(eq); if (r < 0) { @@ -1414,7 +1415,11 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "v", "t", u); - } else if (STR_IN_SET(field, "User", "Group", "DevicePolicy", "KillMode")) + } else if (STR_IN_SET(field, + "User", "Group", "DevicePolicy", "KillMode", + "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath", + "StandardInput", "StandardOutput", "StandardError", + "Description", "Slice", "Type")) r = sd_bus_message_append(m, "v", "s", eq); else if (streq(field, "DeviceAllow")) { diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 60144ab8a6..9a5ffb7a6c 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -34,6 +34,7 @@ #include "formats-util.h" #include "process-util.h" #include "terminal-util.h" +#include "hostname-util.h" /* up to three lines (each up to 100 characters), or 300 characters, whichever is less */ diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c index 8f66df7718..63f1e4ca6f 100644 --- a/src/shared/utmp-wtmp.c +++ b/src/shared/utmp-wtmp.c @@ -204,12 +204,13 @@ _pure_ static const char *sanitize_id(const char *id) { return id + l - sizeof(((struct utmpx*) NULL)->ut_id); } -int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) { +int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) { struct utmpx store = { .ut_type = INIT_PROCESS, .ut_pid = pid, .ut_session = sid, }; + int r; assert(id); @@ -221,7 +222,26 @@ int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line if (line) strncpy(store.ut_line, basename(line), sizeof(store.ut_line)); - return write_entry_both(&store); + r = write_entry_both(&store); + if (r < 0) + return r; + + if (ut_type == LOGIN_PROCESS || ut_type == USER_PROCESS) { + store.ut_type = LOGIN_PROCESS; + r = write_entry_both(&store); + if (r < 0) + return r; + } + + if (ut_type == USER_PROCESS) { + store.ut_type = USER_PROCESS; + strncpy(store.ut_user, user, sizeof(store.ut_user)-1); + r = write_entry_both(&store); + if (r < 0) + return r; + } + + return 0; } int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { diff --git a/src/shared/utmp-wtmp.h b/src/shared/utmp-wtmp.h index 5d26ba6fb1..e0ceb873ac 100644 --- a/src/shared/utmp-wtmp.h +++ b/src/shared/utmp-wtmp.h @@ -31,7 +31,7 @@ int utmp_put_reboot(usec_t timestamp); int utmp_put_runlevel(int runlevel, int previous); int utmp_put_dead_process(const char *id, pid_t pid, int code, int status); -int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line); +int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user); int utmp_wall( const char *message, @@ -57,7 +57,7 @@ static inline int utmp_put_runlevel(int runlevel, int previous) { static inline int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { return 0; } -static inline int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) { +static inline int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) { return 0; } static inline int utmp_wall( diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 1a9dbadbe1..3cb5f61868 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -2794,6 +2794,33 @@ static int reboot_with_logind(sd_bus *bus, enum action a) { return -EINVAL; } + if (!strv_isempty(arg_wall)) { + _cleanup_free_ char *m; + + m = strv_join(arg_wall, " "); + if (!m) + return log_oom(); + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "SetWallMessage", + &error, + NULL, + "sb", + m, + !arg_no_wall); + + if (r < 0) { + log_warning_errno(r, "Failed to set wall message, ignoring: %s", + bus_error_message(&error, r)); + sd_bus_error_free(&error); + } + } + + r = sd_bus_call_method( bus, "org.freedesktop.login1", @@ -6260,6 +6287,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_PRESET_MODE, ARG_FIRMWARE_SETUP, ARG_NOW, + ARG_MESSAGE, }; static const struct option options[] = { @@ -6304,6 +6332,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "preset-mode", required_argument, NULL, ARG_PRESET_MODE }, { "firmware-setup", no_argument, NULL, ARG_FIRMWARE_SETUP }, { "now", no_argument, NULL, ARG_NOW }, + { "message", required_argument, NULL, ARG_MESSAGE }, {} }; @@ -6588,6 +6617,11 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_now = true; break; + case ARG_MESSAGE: + if (strv_extend(&arg_wall, optarg) < 0) + return log_oom(); + break; + case '?': return -EINVAL; @@ -7356,30 +7390,20 @@ static int halt_main(sd_bus *bus) { if (!m) return log_oom(); - r = sd_bus_set_property( - b, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "WallMessage", - &error, - "s", m); - if (r < 0) { - log_warning_errno(r, "Failed to set WallMessage property in logind: %s", - bus_error_message(&error, r)); - sd_bus_error_free(&error); - } + r = sd_bus_call_method( + b, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "SetWallMessage", + &error, + NULL, + "sb", + m, + !arg_no_wall); - r = sd_bus_set_property( - b, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "EnableWallMessages", - &error, - "b", !arg_no_wall); if (r < 0) { - log_warning_errno(r, "Failed to set EnableWallMessages property in logind: %s", + log_warning_errno(r, "Failed to set wall message, ignoring: %s", bus_error_message(&error, r)); sd_bus_error_free(&error); } @@ -7537,30 +7561,20 @@ int main(int argc, char*argv[]) { } } - r = sd_bus_set_property( - b, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "WallMessage", - &error, - "s", arg_wall); - if (r < 0) { - log_warning_errno(r, "Failed to set WallMessage property in logind: %s", - bus_error_message(&error, r)); - sd_bus_error_free(&error); - } + r = sd_bus_call_method( + b, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "SetWallMessage", + &error, + NULL, + "sb", + m, + !arg_no_wall); - r = sd_bus_set_property( - b, - "org.freedesktop.login1", - "/org/freedesktop/login1", - "org.freedesktop.login1.Manager", - "EnableWallMessages", - &error, - "b", !arg_no_wall); if (r < 0) { - log_warning_errno(r, "Failed to set EnableWallMessages property in logind: %s", + log_warning_errno(r, "Failed to set wall message, ignoring: %s", bus_error_message(&error, r)); sd_bus_error_free(&error); } diff --git a/src/test/test-util.c b/src/test/test-util.c index 3c1bac4606..dff38ab6f6 100644 --- a/src/test/test-util.c +++ b/src/test/test-util.c @@ -1539,6 +1539,7 @@ static void test_extract_first_word(void) { p = original = "\""; assert_se(extract_first_word(&p, &t, NULL, 0) == 1); assert_se(streq(t, "\"")); + free(t); assert_se(isempty(p)); p = original = "\""; @@ -1548,6 +1549,7 @@ static void test_extract_first_word(void) { p = original = "\'"; assert_se(extract_first_word(&p, &t, NULL, 0) == 1); assert_se(streq(t, "\'")); + free(t); assert_se(isempty(p)); p = original = "\'"; @@ -1557,6 +1559,7 @@ static void test_extract_first_word(void) { p = original = "\'fooo"; assert_se(extract_first_word(&p, &t, NULL, 0) == 1); assert_se(streq(t, "\'fooo")); + free(t); assert_se(isempty(p)); p = original = "\'fooo"; diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c index b4ae0944eb..ead7c92b27 100644 --- a/src/udev/udev-ctrl.c +++ b/src/udev/udev-ctrl.c @@ -92,6 +92,11 @@ struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd) { uctrl->bound = true; uctrl->sock = fd; } + + /* + * FIXME: remove it as soon as we can depend on this: + * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=90c6bd34f884cd9cee21f1d152baf6c18bcac949 + */ r = setsockopt(uctrl->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); if (r < 0) log_warning_errno(errno, "could not set SO_PASSCRED: %m"); |