diff options
Diffstat (limited to 'src')
103 files changed, 1606 insertions, 670 deletions
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index a790ccd33e..d621f66aec 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -752,9 +752,9 @@ static int list_dependencies_print(const char *name, unsigned int level, unsigne char ts[FORMAT_TIMESPAN_MAX], ts2[FORMAT_TIMESPAN_MAX]; for (i = level; i != 0; i--) - printf("%s", draw_special_char(branches & (1 << (i-1)) ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE)); + printf("%s", special_glyph(branches & (1 << (i-1)) ? TREE_VERTICAL : TREE_SPACE)); - printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH)); + printf("%s", special_glyph(last ? TREE_RIGHT : TREE_BRANCH)); if (times) { if (times->time) diff --git a/src/basic/architecture.c b/src/basic/architecture.c index 8e2c2b02d2..b1c8e91f50 100644 --- a/src/basic/architecture.c +++ b/src/basic/architecture.c @@ -63,7 +63,7 @@ int uname_architecture(void) { #elif defined(__s390__) || defined(__s390x__) { "s390x", ARCHITECTURE_S390X }, { "s390", ARCHITECTURE_S390 }, -#elif defined(__sparc__) || defined(__sparc64__) +#elif defined(__sparc__) { "sparc64", ARCHITECTURE_SPARC64 }, { "sparc", ARCHITECTURE_SPARC }, #elif defined(__mips__) || defined(__mips64__) diff --git a/src/basic/architecture.h b/src/basic/architecture.h index 91ec108e04..b3e4d85906 100644 --- a/src/basic/architecture.h +++ b/src/basic/architecture.h @@ -116,7 +116,7 @@ int uname_architecture(void); #elif defined(__s390__) # define native_architecture() ARCHITECTURE_S390 # define LIB_ARCH_TUPLE "s390-linux-gnu" -#elif defined(__sparc64__) +#elif defined(__sparc__) && defined (__arch64__) # define native_architecture() ARCHITECTURE_SPARC64 # define LIB_ARCH_TUPLE "sparc64-linux-gnu" #elif defined(__sparc__) diff --git a/src/basic/io-util.c b/src/basic/io-util.c index 0037a37f2a..cc6dfa8c1b 100644 --- a/src/basic/io-util.c +++ b/src/basic/io-util.c @@ -33,6 +33,11 @@ int flush_fd(int fd) { .events = POLLIN, }; + /* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything + * read. Note that some file descriptors (notable IP sockets) will trigger POLLIN even when no data can be read + * (due to IP packet checksum mismatches), hence this function is only safe to be non-blocking if the fd used + * was set to non-blocking too. */ + for (;;) { char buf[LINE_MAX]; ssize_t l; diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c index eaad25e65b..ada0a28cd8 100644 --- a/src/basic/locale-util.c +++ b/src/basic/locale-util.c @@ -271,34 +271,35 @@ out: } -const char *draw_special_char(DrawSpecialChar ch) { - - static const char *draw_table[2][_DRAW_SPECIAL_CHAR_MAX] = { - - /* UTF-8 */ { - [DRAW_TREE_VERTICAL] = "\342\224\202 ", /* │ */ - [DRAW_TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */ - [DRAW_TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */ - [DRAW_TREE_SPACE] = " ", /* */ - [DRAW_TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */ - [DRAW_BLACK_CIRCLE] = "\342\227\217", /* ● */ - [DRAW_ARROW] = "\342\206\222", /* → */ - [DRAW_DASH] = "\342\200\223", /* – */ +const char *special_glyph(SpecialGlyph code) { + + static const char* const draw_table[2][_SPECIAL_GLYPH_MAX] = { + /* ASCII fallback */ + [false] = { + [TREE_VERTICAL] = "| ", + [TREE_BRANCH] = "|-", + [TREE_RIGHT] = "`-", + [TREE_SPACE] = " ", + [TRIANGULAR_BULLET] = ">", + [BLACK_CIRCLE] = "*", + [ARROW] = "->", + [MDASH] = "-", }, - /* ASCII fallback */ { - [DRAW_TREE_VERTICAL] = "| ", - [DRAW_TREE_BRANCH] = "|-", - [DRAW_TREE_RIGHT] = "`-", - [DRAW_TREE_SPACE] = " ", - [DRAW_TRIANGULAR_BULLET] = ">", - [DRAW_BLACK_CIRCLE] = "*", - [DRAW_ARROW] = "->", - [DRAW_DASH] = "-", - } + /* UTF-8 */ + [ true ] = { + [TREE_VERTICAL] = "\342\224\202 ", /* │ */ + [TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */ + [TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */ + [TREE_SPACE] = " ", /* */ + [TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */ + [BLACK_CIRCLE] = "\342\227\217", /* ● */ + [ARROW] = "\342\206\222", /* → */ + [MDASH] = "\342\200\223", /* – */ + }, }; - return draw_table[!is_locale_utf8()][ch]; + return draw_table[is_locale_utf8()][code]; } static const char * const locale_variable_table[_VARIABLE_LC_MAX] = { diff --git a/src/basic/locale-util.h b/src/basic/locale-util.h index b0f9679286..0630a034ab 100644 --- a/src/basic/locale-util.h +++ b/src/basic/locale-util.h @@ -55,19 +55,19 @@ void init_gettext(void); bool is_locale_utf8(void); -typedef enum DrawSpecialChar { - DRAW_TREE_VERTICAL, - DRAW_TREE_BRANCH, - DRAW_TREE_RIGHT, - DRAW_TREE_SPACE, - DRAW_TRIANGULAR_BULLET, - DRAW_BLACK_CIRCLE, - DRAW_ARROW, - DRAW_DASH, - _DRAW_SPECIAL_CHAR_MAX -} DrawSpecialChar; +typedef enum { + TREE_VERTICAL, + TREE_BRANCH, + TREE_RIGHT, + TREE_SPACE, + TRIANGULAR_BULLET, + BLACK_CIRCLE, + ARROW, + MDASH, + _SPECIAL_GLYPH_MAX +} SpecialGlyph; -const char *draw_special_char(DrawSpecialChar ch); +const char *special_glyph(SpecialGlyph code) _const_; const char* locale_variable_to_string(LocaleVariable i) _const_; LocaleVariable locale_variable_from_string(const char *s) _pure_; diff --git a/src/basic/log.c b/src/basic/log.c index d89e6f7274..3ea643b6e6 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -165,7 +165,7 @@ static int log_open_syslog(void) { goto fail; } - if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) { + if (connect(syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { safe_close(syslog_fd); /* Some legacy syslog systems still use stream @@ -177,7 +177,7 @@ static int log_open_syslog(void) { goto fail; } - if (connect(syslog_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) { + if (connect(syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { r = -errno; goto fail; } @@ -215,7 +215,7 @@ static int log_open_journal(void) { goto fail; } - if (connect(journal_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) { + if (connect(journal_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { r = -errno; goto fail; } diff --git a/src/basic/missing.h b/src/basic/missing.h index 22ea8f67cc..651e414395 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -389,6 +389,10 @@ struct btrfs_ioctl_quota_ctl_args { struct btrfs_ioctl_qgroup_limit_args) #endif +#ifndef BTRFS_IOC_QUOTA_RESCAN_WAIT +#define BTRFS_IOC_QUOTA_RESCAN_WAIT _IO(BTRFS_IOCTL_MAGIC, 46) +#endif + #ifndef BTRFS_FIRST_FREE_OBJECTID #define BTRFS_FIRST_FREE_OBJECTID 256 #endif diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index 0f38f9a0f3..c8769a54f4 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -23,6 +23,7 @@ #include <net/if.h> #include <netdb.h> #include <netinet/ip.h> +#include <poll.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> @@ -42,7 +43,9 @@ #include "socket-util.h" #include "string-table.h" #include "string-util.h" +#include "strv.h" #include "user-util.h" +#include "utf8.h" #include "util.h" int socket_address_parse(SocketAddress *a, const char *s) { @@ -794,6 +797,42 @@ static const char* const ip_tos_table[] = { DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff); +bool ifname_valid(const char *p) { + bool numeric = true; + + /* Checks whether a network interface name is valid. This is inspired by dev_valid_name() in the kernel sources + * but slightly stricter, as we only allow non-control, non-space ASCII characters in the interface name. We + * also don't permit names that only container numbers, to avoid confusion with numeric interface indexes. */ + + if (isempty(p)) + return false; + + if (strlen(p) >= IFNAMSIZ) + return false; + + if (STR_IN_SET(p, ".", "..")) + return false; + + while (*p) { + if ((unsigned char) *p >= 127U) + return false; + + if ((unsigned char) *p <= 32U) + return false; + + if (*p == ':' || *p == '/') + return false; + + numeric = numeric && (*p >= '0' && *p <= '9'); + p++; + } + + if (numeric) + return false; + + return true; +} + int getpeercred(int fd, struct ucred *ucred) { socklen_t n = sizeof(struct ucred); struct ucred u; @@ -970,3 +1009,42 @@ fallback: return (ssize_t) k; } + +int flush_accept(int fd) { + + struct pollfd pollfd = { + .fd = fd, + .events = POLLIN, + }; + int r; + + + /* Similar to flush_fd() but flushes all incoming connection by accepting them and immediately closing them. */ + + for (;;) { + int cfd; + + r = poll(&pollfd, 1, 0); + if (r < 0) { + if (errno == EINTR) + continue; + + return -errno; + + } else if (r == 0) + return 0; + + cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); + if (cfd < 0) { + if (errno == EINTR) + continue; + + if (errno == EAGAIN) + return 0; + + return -errno; + } + + close(cfd); + } +} diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index d17a2f35f8..e9230e4a9f 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -123,6 +123,8 @@ int fd_inc_rcvbuf(int fd, size_t n); int ip_tos_to_string_alloc(int i, char **s); int ip_tos_from_string(const char *s); +bool ifname_valid(const char *p); + int getpeercred(int fd, struct ucred *ucred); int getpeersec(int fd, char **ret); @@ -135,5 +137,18 @@ int receive_one_fd(int transport_fd, int flags); ssize_t next_datagram_size_fd(int fd); +int flush_accept(int fd); + #define CMSG_FOREACH(cmsg, mh) \ for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg))) + +/* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */ +#define SOCKADDR_UN_LEN(sa) \ + ({ \ + const struct sockaddr_un *_sa = &(sa); \ + assert(_sa->sun_family == AF_UNIX); \ + offsetof(struct sockaddr_un, sun_path) + \ + (_sa->sun_path[0] == 0 ? \ + 1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \ + strnlen(_sa->sun_path, sizeof(_sa->sun_path))); \ + }) diff --git a/src/basic/special.h b/src/basic/special.h index 2fd03d9f75..084d3dfa23 100644 --- a/src/basic/special.h +++ b/src/basic/special.h @@ -52,6 +52,7 @@ #define SPECIAL_LOCAL_FS_TARGET "local-fs.target" #define SPECIAL_LOCAL_FS_PRE_TARGET "local-fs-pre.target" #define SPECIAL_INITRD_FS_TARGET "initrd-fs.target" +#define SPECIAL_INITRD_ROOT_DEVICE_TARGET "initrd-root-device.target" #define SPECIAL_INITRD_ROOT_FS_TARGET "initrd-root-fs.target" #define SPECIAL_REMOTE_FS_TARGET "remote-fs.target" /* LSB's $remote_fs */ #define SPECIAL_REMOTE_FS_PRE_TARGET "remote-fs-pre.target" diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index e9baf69f6a..d0af41498f 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -266,9 +266,9 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char if (r < 0) return r; if (r > 0) - printf(" File: %s/%s/%s (%s)\n", draw_special_char(DRAW_TREE_RIGHT), path, de->d_name, v); + printf(" File: %s/%s/%s (%s)\n", special_glyph(TREE_RIGHT), path, de->d_name, v); else - printf(" File: %s/%s/%s\n", draw_special_char(DRAW_TREE_RIGHT), path, de->d_name); + printf(" File: %s/%s/%s\n", special_glyph(TREE_RIGHT), path, de->d_name); c++; } @@ -320,7 +320,7 @@ static int print_efi_option(uint16_t id, bool in_order) { printf(" ID: 0x%04X\n", id); printf(" Status: %sactive%s\n", active ? "" : "in", in_order ? ", boot-order" : ""); printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", SD_ID128_FORMAT_VAL(partition)); - printf(" File: %s%s\n", draw_special_char(DRAW_TREE_RIGHT), path); + printf(" File: %s%s\n", special_glyph(TREE_RIGHT), path); printf("\n"); return 0; @@ -1077,7 +1077,7 @@ static int bootctl_main(int argc, char*argv[]) { SD_ID128_FORMAT_VAL(loader_part_uuid)); else printf(" Partition: n/a\n"); - printf(" File: %s%s\n", draw_special_char(DRAW_TREE_RIGHT), strna(loader_path)); + printf(" File: %s%s\n", special_glyph(TREE_RIGHT), strna(loader_path)); printf("\n"); } else printf("System:\n Not booted with EFI\n"); diff --git a/src/cgroups-agent/cgroups-agent.c b/src/cgroups-agent/cgroups-agent.c index aadfba0707..d7c722ac3d 100644 --- a/src/cgroups-agent/cgroups-agent.c +++ b/src/cgroups-agent/cgroups-agent.c @@ -18,15 +18,22 @@ ***/ #include <stdlib.h> +#include <sys/socket.h> -#include "sd-bus.h" - -#include "bus-util.h" +#include "fd-util.h" #include "log.h" +#include "socket-util.h" int main(int argc, char *argv[]) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - int r; + + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/cgroups-agent", + }; + + _cleanup_close_ int fd = -1; + ssize_t n; + size_t l; if (argc != 2) { log_error("Incorrect number of arguments."); @@ -37,27 +44,22 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); - /* We send this event to the private D-Bus socket and then the - * system instance will forward this to the system bus. We do - * this to avoid an activation loop when we start dbus when we - * are called when the dbus service is shut down. */ - - r = bus_connect_system_systemd(&bus); - if (r < 0) { - /* If we couldn't connect we assume this was triggered - * while systemd got restarted/transitioned from - * initrd to the system, so let's ignore this */ - log_debug_errno(r, "Failed to get D-Bus connection: %m"); + fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); + if (fd < 0) { + log_debug_errno(errno, "Failed to allocate socket: %m"); + return EXIT_FAILURE; + } + + l = strlen(argv[1]); + + n = sendto(fd, argv[1], l, 0, &sa.sa, SOCKADDR_UN_LEN(sa.un)); + if (n < 0) { + log_debug_errno(errno, "Failed to send cgroups agent message: %m"); return EXIT_FAILURE; } - r = sd_bus_emit_signal(bus, - "/org/freedesktop/systemd1/agent", - "org.freedesktop.systemd1.Agent", - "Released", - "s", argv[1]); - if (r < 0) { - log_debug_errno(r, "Failed to send signal message on private connection: %m"); + if ((size_t) n != l) { + log_debug("Datagram size mismatch"); return EXIT_FAILURE; } diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 44106e52ea..4f1637ffe9 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -996,6 +996,7 @@ static int unit_create_cgroup( /* Keep track that this is now realized */ u->cgroup_realized = true; u->cgroup_realized_mask = target_mask; + u->cgroup_enabled_mask = enable_mask; if (u->type != UNIT_SLICE && !c->delegate) { @@ -1025,10 +1026,10 @@ int unit_attach_pids_to_cgroup(Unit *u) { return 0; } -static bool unit_has_mask_realized(Unit *u, CGroupMask target_mask) { +static bool unit_has_mask_realized(Unit *u, CGroupMask target_mask, CGroupMask enable_mask) { assert(u); - return u->cgroup_realized && u->cgroup_realized_mask == target_mask; + return u->cgroup_realized && u->cgroup_realized_mask == target_mask && u->cgroup_enabled_mask == enable_mask; } /* Check if necessary controllers and attributes for a unit are in place. @@ -1049,7 +1050,9 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) { } target_mask = unit_get_target_mask(u); - if (unit_has_mask_realized(u, target_mask)) + enable_mask = unit_get_enable_mask(u); + + if (unit_has_mask_realized(u, target_mask, enable_mask)) return 0; /* First, realize parents */ @@ -1060,7 +1063,6 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) { } /* And then do the real work */ - enable_mask = unit_get_enable_mask(u); r = unit_create_cgroup(u, target_mask, enable_mask); if (r < 0) return r; @@ -1129,7 +1131,7 @@ static void unit_queue_siblings(Unit *u) { /* If the unit doesn't need any new controllers * and has current ones realized, it doesn't need * any changes. */ - if (unit_has_mask_realized(m, unit_get_target_mask(m))) + if (unit_has_mask_realized(m, unit_get_target_mask(m), unit_get_enable_mask(m))) continue; unit_add_to_cgroup_queue(m); @@ -1208,6 +1210,7 @@ void unit_prune_cgroup(Unit *u) { u->cgroup_realized = false; u->cgroup_realized_mask = 0; + u->cgroup_enabled_mask = 0; } int unit_search_main_pid(Unit *u, pid_t *ret) { @@ -1451,7 +1454,9 @@ int manager_setup_cgroup(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to watch control group inotify object: %m"); - r = sd_event_source_set_priority(m->cgroup_inotify_event_source, SD_EVENT_PRIORITY_IDLE - 5); + /* Process cgroup empty notifications early, but after service notifications and SIGCHLD. Also + * see handling of cgroup agent notifications, for the classic cgroup hierarchy support. */ + r = sd_event_source_set_priority(m->cgroup_inotify_event_source, SD_EVENT_PRIORITY_NORMAL-5); if (r < 0) return log_error_errno(r, "Failed to set priority of inotify event source: %m"); @@ -1597,6 +1602,8 @@ int manager_notify_cgroup_empty(Manager *m, const char *cgroup) { assert(m); assert(cgroup); + log_debug("Got cgroup empty notification for: %s", cgroup); + u = manager_get_unit_by_cgroup(m, cgroup); if (!u) return 0; diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c index 97a93fb2f1..ccf7453d47 100644 --- a/src/core/dbus-job.c +++ b/src/core/dbus-job.c @@ -75,7 +75,7 @@ int bus_job_method_cancel(sd_bus_message *message, void *userdata, sd_bus_error return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ } - job_finish_and_invalidate(j, JOB_CANCELED, true); + job_finish_and_invalidate(j, JOB_CANCELED, true, false); return sd_bus_reply_method_return(message, NULL); } diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index d45f511489..86722e1162 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -642,6 +642,80 @@ static int method_set_unit_properties(sd_bus_message *message, void *userdata, s return bus_unit_method_set_properties(message, u, error); } +static int reply_unit_info(sd_bus_message *reply, Unit *u) { + _cleanup_free_ char *unit_path = NULL, *job_path = NULL; + Unit *following; + + following = unit_following(u); + + unit_path = unit_dbus_path(u); + if (!unit_path) + return -ENOMEM; + + if (u->job) { + job_path = job_dbus_path(u->job); + if (!job_path) + return -ENOMEM; + } + + return sd_bus_message_append( + reply, "(ssssssouso)", + u->id, + unit_description(u), + unit_load_state_to_string(u->load_state), + unit_active_state_to_string(unit_active_state(u)), + unit_sub_state_to_string(u), + following ? following->id : "", + unit_path, + u->job ? u->job->id : 0, + u->job ? job_type_to_string(u->job->type) : "", + job_path ? job_path : "/"); +} + +static int method_list_units_by_names(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Manager *m = userdata; + int r; + char **unit; + _cleanup_strv_free_ char **units = NULL; + + assert(message); + assert(m); + + r = sd_bus_message_read_strv(message, &units); + if (r < 0) + return r; + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(ssssssouso)"); + if (r < 0) + return r; + + STRV_FOREACH(unit, units) { + Unit *u; + + if (!unit_name_is_valid(*unit, UNIT_NAME_ANY)) + continue; + + r = manager_load_unit(m, *unit, NULL, error, &u); + if (r < 0) + return r; + + r = reply_unit_info(reply, u); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + static int method_get_unit_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; const char *name; @@ -915,14 +989,9 @@ static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_e return r; HASHMAP_FOREACH_KEY(u, k, m->units, i) { - _cleanup_free_ char *unit_path = NULL, *job_path = NULL; - Unit *following; - if (k != u->id) continue; - following = unit_following(u); - if (!strv_isempty(states) && !strv_contains(states, unit_load_state_to_string(u->load_state)) && !strv_contains(states, unit_active_state_to_string(unit_active_state(u))) && @@ -933,28 +1002,7 @@ static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_e !strv_fnmatch_or_empty(patterns, u->id, FNM_NOESCAPE)) continue; - unit_path = unit_dbus_path(u); - if (!unit_path) - return -ENOMEM; - - if (u->job) { - job_path = job_dbus_path(u->job); - if (!job_path) - return -ENOMEM; - } - - r = sd_bus_message_append( - reply, "(ssssssouso)", - u->id, - unit_description(u), - unit_load_state_to_string(u->load_state), - unit_active_state_to_string(unit_active_state(u)), - unit_sub_state_to_string(u), - following ? following->id : "", - unit_path, - u->job ? u->job->id : 0, - u->job ? job_type_to_string(u->job->type) : "", - job_path ? job_path : "/"); + r = reply_unit_info(reply, u); if (r < 0) return r; } @@ -2115,6 +2163,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_METHOD("ListUnits", NULL, "a(ssssssouso)", method_list_units, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ListUnitsFiltered", "as", "a(ssssssouso)", method_list_units_filtered, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ListUnitsByPatterns", "asas", "a(ssssssouso)", method_list_units_by_patterns, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("ListUnitsByNames", "as", "a(ssssssouso)", method_list_units_by_names, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ListJobs", NULL, "a(usssoo)", method_list_jobs, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Subscribe", NULL, NULL, method_subscribe, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Unsubscribe", NULL, NULL, method_unsubscribe, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index bb09a515f8..961340608d 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -149,7 +149,7 @@ const sd_bus_vtable bus_socket_vtable[] = { SD_BUS_PROPERTY("NAccepted", "u", bus_property_get_unsigned, offsetof(Socket, n_accepted), 0), SD_BUS_PROPERTY("FileDescriptorName", "s", property_get_fdname, 0, 0), SD_BUS_PROPERTY("SocketProtocol", "i", bus_property_get_int, offsetof(Socket, socket_protocol), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("TriggerLimitIntervalSec", "t", bus_property_get_usec, offsetof(Socket, trigger_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("TriggerLimitIntervalUSec", "t", bus_property_get_usec, offsetof(Socket, trigger_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("TriggerLimitBurst", "u", bus_property_get_unsigned, offsetof(Socket, trigger_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), diff --git a/src/core/dbus.c b/src/core/dbus.c index 263955d874..3422a02d68 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -71,28 +71,42 @@ int bus_send_queued_message(Manager *m) { return 0; } +int bus_forward_agent_released(Manager *m, const char *path) { + int r; + + assert(m); + assert(path); + + if (!MANAGER_IS_SYSTEM(m)) + return 0; + + if (!m->system_bus) + return 0; + + /* If we are running a system instance we forward the agent message on the system bus, so that the user + * instances get notified about this, too */ + + r = sd_bus_emit_signal(m->system_bus, + "/org/freedesktop/systemd1/agent", + "org.freedesktop.systemd1.Agent", + "Released", + "s", path); + if (r < 0) + return log_warning_errno(r, "Failed to propagate agent release message: %m"); + + return 1; +} + static int signal_agent_released(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - const char *cgroup, *me; Manager *m = userdata; + const char *cgroup; uid_t sender_uid; - sd_bus *bus; int r; assert(message); assert(m); - /* ignore recursive events sent by us on the system/user bus */ - bus = sd_bus_message_get_bus(message); - if (!sd_bus_is_server(bus)) { - r = sd_bus_get_unique_name(bus, &me); - if (r < 0) - return r; - - if (streq_ptr(sd_bus_message_get_sender(message), me)) - return 0; - } - /* only accept org.freedesktop.systemd1.Agent from UID=0 */ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); if (r < 0) @@ -110,16 +124,6 @@ static int signal_agent_released(sd_bus_message *message, void *userdata, sd_bus } manager_notify_cgroup_empty(m, cgroup); - - /* if running as system-instance, forward under our name */ - if (MANAGER_IS_SYSTEM(m) && m->system_bus) { - r = sd_bus_message_rewind(message, 1); - if (r >= 0) - r = sd_bus_send(m->system_bus, message, NULL); - if (r < 0) - log_warning_errno(r, "Failed to forward Released message: %m"); - } - return 0; } @@ -690,25 +694,6 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void return 0; } - if (MANAGER_IS_SYSTEM(m)) { - /* When we run as system instance we get the Released - * signal via a direct connection */ - - r = sd_bus_add_match( - bus, - NULL, - "type='signal'," - "interface='org.freedesktop.systemd1.Agent'," - "member='Released'," - "path='/org/freedesktop/systemd1/agent'", - signal_agent_released, m); - - if (r < 0) { - log_warning_errno(r, "Failed to register Released match on new connection bus: %m"); - return 0; - } - } - r = bus_setup_disconnected_match(m, bus); if (r < 0) return 0; @@ -906,8 +891,8 @@ static int bus_setup_system(Manager *m, sd_bus *bus) { assert(m); assert(bus); - /* On kdbus or if we are a user instance we get the Released message via the system bus */ - if (MANAGER_IS_USER(m) || m->kdbus_fd >= 0) { + /* if we are a user instance we get the Released message via the system bus */ + if (MANAGER_IS_USER(m)) { r = sd_bus_add_match( bus, NULL, @@ -990,7 +975,7 @@ static int bus_init_private(Manager *m) { return 0; strcpy(sa.un.sun_path, "/run/systemd/private"); - salen = offsetof(union sockaddr_union, un.sun_path) + strlen("/run/systemd/private"); + salen = SOCKADDR_UN_LEN(sa.un); } else { size_t left = sizeof(sa.un.sun_path); char *p = sa.un.sun_path; diff --git a/src/core/dbus.h b/src/core/dbus.h index e16a84fbb8..6baaffbd75 100644 --- a/src/core/dbus.h +++ b/src/core/dbus.h @@ -40,3 +40,5 @@ int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error int bus_verify_manage_unit_files_async(Manager *m, sd_bus_message *call, sd_bus_error *error); int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_error *error); int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_error *error); + +int bus_forward_agent_released(Manager *m, const char *path); diff --git a/src/core/execute.c b/src/core/execute.c index ac2ac39892..5eb3f13695 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -271,7 +271,7 @@ static int connect_journal_socket(int fd, uid_t uid, gid_t gid) { } } - r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)); + r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) r = -errno; diff --git a/src/core/job.c b/src/core/job.c index d9c5669c9f..7557874d4d 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -191,7 +191,7 @@ Job* job_install(Job *j) { if (uj) { if (job_type_is_conflicting(uj->type, j->type)) - job_finish_and_invalidate(uj, JOB_CANCELED, false); + job_finish_and_invalidate(uj, JOB_CANCELED, false, false); else { /* not conflicting, i.e. mergeable */ @@ -614,19 +614,19 @@ int job_run_and_invalidate(Job *j) { if (j) { if (r == -EALREADY) - r = job_finish_and_invalidate(j, JOB_DONE, true); + r = job_finish_and_invalidate(j, JOB_DONE, true, true); else if (r == -EBADR) - r = job_finish_and_invalidate(j, JOB_SKIPPED, true); + r = job_finish_and_invalidate(j, JOB_SKIPPED, true, false); else if (r == -ENOEXEC) - r = job_finish_and_invalidate(j, JOB_INVALID, true); + r = job_finish_and_invalidate(j, JOB_INVALID, true, false); else if (r == -EPROTO) - r = job_finish_and_invalidate(j, JOB_ASSERT, true); + r = job_finish_and_invalidate(j, JOB_ASSERT, true, false); else if (r == -EOPNOTSUPP) - r = job_finish_and_invalidate(j, JOB_UNSUPPORTED, true); + r = job_finish_and_invalidate(j, JOB_UNSUPPORTED, true, false); else if (r == -EAGAIN) job_set_state(j, JOB_WAITING); else if (r < 0) - r = job_finish_and_invalidate(j, JOB_FAILED, true); + r = job_finish_and_invalidate(j, JOB_FAILED, true, false); } return r; @@ -827,11 +827,11 @@ static void job_fail_dependencies(Unit *u, UnitDependency d) { if (!IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE)) continue; - job_finish_and_invalidate(j, JOB_DEPENDENCY, true); + job_finish_and_invalidate(j, JOB_DEPENDENCY, true, false); } } -int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { +int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool already) { Unit *u; Unit *other; JobType t; @@ -848,7 +848,9 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { log_unit_debug(u, "Job %s/%s finished, result=%s", u->id, job_type_to_string(t), job_result_to_string(result)); - job_emit_status_message(u, t, result); + /* If this job did nothing to respective unit we don't log the status message */ + if (!already) + job_emit_status_message(u, t, result); job_add_to_dbus_queue(j); @@ -923,7 +925,7 @@ static int job_dispatch_timer(sd_event_source *s, uint64_t monotonic, void *user log_unit_warning(j->unit, "Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type)); u = j->unit; - job_finish_and_invalidate(j, JOB_TIMEOUT, true); + job_finish_and_invalidate(j, JOB_TIMEOUT, true, false); failure_action(u->manager, u->job_timeout_action, u->job_timeout_reboot_arg); diff --git a/src/core/job.h b/src/core/job.h index 856b0ce829..d359e8bb3e 100644 --- a/src/core/job.h +++ b/src/core/job.h @@ -219,7 +219,7 @@ void job_add_to_dbus_queue(Job *j); int job_start_timer(Job *j); int job_run_and_invalidate(Job *j); -int job_finish_and_invalidate(Job *j, JobResult result, bool recursive); +int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool already); char *job_dbus_path(Job *j); diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 9d7329e924..cea615132a 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -732,16 +732,17 @@ int config_parse_exec( DEFINE_CONFIG_PARSE_ENUM(config_parse_service_type, service_type, ServiceType, "Failed to parse service type"); DEFINE_CONFIG_PARSE_ENUM(config_parse_service_restart, service_restart, ServiceRestart, "Failed to parse service restart specifier"); -int config_parse_socket_bindtodevice(const char* unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_socket_bindtodevice( + const char* unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { Socket *s = data; char *n; @@ -752,6 +753,11 @@ int config_parse_socket_bindtodevice(const char* unit, assert(data); if (rvalue[0] && !streq(rvalue, "*")) { + if (!ifname_valid(rvalue)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is invalid, ignoring: %s", rvalue); + return 0; + } + n = strdup(rvalue); if (!n) return log_oom(); diff --git a/src/core/manager.c b/src/core/manager.c index bd00c224f4..7838f56fd2 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -87,6 +87,7 @@ #include "watchdog.h" #define NOTIFY_RCVBUF_SIZE (8*1024*1024) +#define CGROUPS_AGENT_RCVBUF_SIZE (8*1024*1024) /* Initial delay and the interval for printing status messages about running jobs */ #define JOBS_IN_PROGRESS_WAIT_USEC (5*USEC_PER_SEC) @@ -94,6 +95,7 @@ #define JOBS_IN_PROGRESS_PERIOD_DIVISOR 3 static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); +static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int manager_dispatch_idle_pipe_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata); @@ -484,11 +486,11 @@ static int manager_setup_signals(Manager *m) { (void) sd_event_source_set_description(m->signal_event_source, "manager-signal"); - /* Process signals a bit earlier than the rest of things, but - * later than notify_fd processing, so that the notify - * processing can still figure out to which process/service a - * message belongs, before we reap the process. */ - r = sd_event_source_set_priority(m->signal_event_source, SD_EVENT_PRIORITY_NORMAL-5); + /* Process signals a bit earlier than the rest of things, but later than notify_fd processing, so that the + * notify processing can still figure out to which process/service a message belongs, before we reap the + * process. Also, process this before handling cgroup notifications, so that we always collect child exit + * status information before detecting that there's no process in a cgroup. */ + r = sd_event_source_set_priority(m->signal_event_source, SD_EVENT_PRIORITY_NORMAL-6); if (r < 0) return r; @@ -581,12 +583,12 @@ int manager_new(UnitFileScope scope, bool test_run, Manager **_m) { m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1; - m->pin_cgroupfs_fd = m->notify_fd = m->signal_fd = m->time_change_fd = - m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->cgroup_inotify_fd = -1; + m->pin_cgroupfs_fd = m->notify_fd = m->cgroups_agent_fd = m->signal_fd = m->time_change_fd = + m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->cgroup_inotify_fd = + m->ask_password_inotify_fd = -1; m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ - m->ask_password_inotify_fd = -1; m->have_ask_password = -EINVAL; /* we don't know */ m->first_boot = -1; @@ -703,7 +705,7 @@ static int manager_setup_notify(Manager *m) { (void) unlink(m->notify_socket); strncpy(sa.un.sun_path, m->notify_socket, sizeof(sa.un.sun_path)-1); - r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)); + r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); @@ -722,8 +724,8 @@ static int manager_setup_notify(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to allocate notify event source: %m"); - /* Process signals a bit earlier than SIGCHLD, so that we can - * still identify to which service an exit message belongs */ + /* Process notification messages a bit earlier than SIGCHLD, so that we can still identify to which + * service an exit message belongs. */ r = sd_event_source_set_priority(m->notify_event_source, SD_EVENT_PRIORITY_NORMAL-7); if (r < 0) return log_error_errno(r, "Failed to set priority of notify event source: %m"); @@ -734,6 +736,79 @@ static int manager_setup_notify(Manager *m) { return 0; } +static int manager_setup_cgroups_agent(Manager *m) { + + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/cgroups-agent", + }; + int r; + + /* This creates a listening socket we receive cgroups agent messages on. We do not use D-Bus for delivering + * these messages from the cgroups agent binary to PID 1, as the cgroups agent binary is very short-living, and + * each instance of it needs a new D-Bus connection. Since D-Bus connections are SOCK_STREAM/AF_UNIX, on + * overloaded systems the backlog of the D-Bus socket becomes relevant, as not more than the configured number + * of D-Bus connections may be queued until the kernel will start dropping further incoming connections, + * possibly resulting in lost cgroups agent messages. To avoid this, we'll use a private SOCK_DGRAM/AF_UNIX + * socket, where no backlog is relevant as communication may take place without an actual connect() cycle, and + * we thus won't lose messages. + * + * Note that PID 1 will forward the agent message to system bus, so that the user systemd instance may listen + * to it. The system instance hence listens on this special socket, but the user instances listen on the system + * bus for these messages. */ + + if (m->test_run) + return 0; + + if (!MANAGER_IS_SYSTEM(m)) + return 0; + + if (cg_unified() > 0) /* We don't need this anymore on the unified hierarchy */ + return 0; + + if (m->cgroups_agent_fd < 0) { + _cleanup_close_ int fd = -1; + + /* First free all secondary fields */ + m->cgroups_agent_event_source = sd_event_source_unref(m->cgroups_agent_event_source); + + fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (fd < 0) + return log_error_errno(errno, "Failed to allocate cgroups agent socket: %m"); + + fd_inc_rcvbuf(fd, CGROUPS_AGENT_RCVBUF_SIZE); + + (void) unlink(sa.un.sun_path); + + /* Only allow root to connect to this socket */ + RUN_WITH_UMASK(0077) + r = bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); + if (r < 0) + return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); + + m->cgroups_agent_fd = fd; + fd = -1; + } + + if (!m->cgroups_agent_event_source) { + r = sd_event_add_io(m->event, &m->cgroups_agent_event_source, m->cgroups_agent_fd, EPOLLIN, manager_dispatch_cgroups_agent_fd, m); + if (r < 0) + return log_error_errno(r, "Failed to allocate cgroups agent event source: %m"); + + /* Process cgroups notifications early, but after having processed service notification messages or + * SIGCHLD signals, so that a cgroup running empty is always just the last safety net of notification, + * and we collected the metadata the notification and SIGCHLD stuff offers first. Also see handling of + * cgroup inotify for the unified cgroup stuff. */ + r = sd_event_source_set_priority(m->cgroups_agent_event_source, SD_EVENT_PRIORITY_NORMAL-5); + if (r < 0) + return log_error_errno(r, "Failed to set priority of cgroups agent event source: %m"); + + (void) sd_event_source_set_description(m->cgroups_agent_event_source, "manager-cgroups-agent"); + } + + return 0; +} + static int manager_setup_kdbus(Manager *m) { _cleanup_free_ char *p = NULL; @@ -944,12 +1019,14 @@ Manager* manager_free(Manager *m) { sd_event_source_unref(m->signal_event_source); sd_event_source_unref(m->notify_event_source); + sd_event_source_unref(m->cgroups_agent_event_source); sd_event_source_unref(m->time_change_event_source); sd_event_source_unref(m->jobs_in_progress_event_source); sd_event_source_unref(m->run_queue_event_source); safe_close(m->signal_fd); safe_close(m->notify_fd); + safe_close(m->cgroups_agent_fd); safe_close(m->time_change_fd); safe_close(m->kdbus_fd); @@ -1142,6 +1219,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { if (q < 0 && r == 0) r = q; + q = manager_setup_cgroups_agent(m); + if (q < 0 && r == 0) + r = q; + /* We might have deserialized the kdbus control fd, but if we * didn't, then let's create the bus now. */ manager_setup_kdbus(m); @@ -1413,7 +1494,7 @@ void manager_clear_jobs(Manager *m) { while ((j = hashmap_first(m->jobs))) /* No need to recurse. We're cancelling all jobs. */ - job_finish_and_invalidate(j, JOB_CANCELED, false); + job_finish_and_invalidate(j, JOB_CANCELED, false, false); } static int manager_dispatch_run_queue(sd_event_source *source, void *userdata) { @@ -1479,6 +1560,35 @@ static unsigned manager_dispatch_dbus_queue(Manager *m) { return n; } +static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + Manager *m = userdata; + char buf[PATH_MAX+1]; + ssize_t n; + + n = recv(fd, buf, sizeof(buf), 0); + if (n < 0) + return log_error_errno(errno, "Failed to read cgroups agent message: %m"); + if (n == 0) { + log_error("Got zero-length cgroups agent message, ignoring."); + return 0; + } + if ((size_t) n >= sizeof(buf)) { + log_error("Got overly long cgroups agent message, ignoring."); + return 0; + } + + if (memchr(buf, 0, n)) { + log_error("Got cgroups agent message with embedded NUL byte, ignoring."); + return 0; + } + buf[n] = 0; + + manager_notify_cgroup_empty(m, buf); + bus_forward_agent_released(m, buf); + + return 0; +} + static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, const char *buf, size_t n, FDSet *fds) { _cleanup_strv_free_ char **tags = NULL; @@ -2135,11 +2245,10 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { } void manager_send_unit_plymouth(Manager *m, Unit *u) { - union sockaddr_union sa = PLYMOUTH_SOCKET; - - int n = 0; + static const union sockaddr_union sa = PLYMOUTH_SOCKET; _cleanup_free_ char *message = NULL; _cleanup_close_ int fd = -1; + int n = 0; /* Don't generate plymouth events if the service was already * started and we're just deserializing */ @@ -2165,7 +2274,7 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) { return; } - if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) { + if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { if (!IN_SET(errno, EPIPE, EAGAIN, ENOENT, ECONNREFUSED, ECONNRESET, ECONNABORTED)) log_error_errno(errno, "connect() failed: %m"); @@ -2265,6 +2374,16 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root) { fprintf(f, "notify-socket=%s\n", m->notify_socket); } + if (m->cgroups_agent_fd >= 0) { + int copy; + + copy = fdset_put_dup(fds, m->cgroups_agent_fd); + if (copy < 0) + return copy; + + fprintf(f, "cgroups-agent-fd=%i\n", copy); + } + if (m->kdbus_fd >= 0) { int copy; @@ -2432,6 +2551,17 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { free(m->notify_socket); m->notify_socket = n; + } else if (startswith(l, "cgroups-agent-fd=")) { + int fd; + + if (safe_atoi(l + 17, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) + log_debug("Failed to parse cgroups agent fd: %s", l + 10); + else { + m->cgroups_agent_event_source = sd_event_source_unref(m->cgroups_agent_event_source); + safe_close(m->cgroups_agent_fd); + m->cgroups_agent_fd = fdset_remove(fds, fd); + } + } else if (startswith(l, "kdbus-fd=")) { int fd; @@ -2552,6 +2682,10 @@ int manager_reload(Manager *m) { if (q < 0 && r >= 0) r = q; + q = manager_setup_cgroups_agent(m); + if (q < 0 && r >= 0) + r = q; + /* Third, fire things up! */ manager_coldplug(m); diff --git a/src/core/manager.h b/src/core/manager.h index f23e4056c4..6ed15c1a41 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -132,6 +132,9 @@ struct Manager { int notify_fd; sd_event_source *notify_event_source; + int cgroups_agent_fd; + sd_event_source *cgroups_agent_event_source; + int signal_fd; sd_event_source *signal_event_source; diff --git a/src/core/mount.c b/src/core/mount.c index 5a8c26b9e1..665a60bb55 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -121,6 +121,20 @@ static bool mount_is_automount(const MountParameters *p) { "x-systemd.automount\0"); } +static bool mount_state_active(MountState state) { + return IN_SET(state, + MOUNT_MOUNTING, + MOUNT_MOUNTING_DONE, + MOUNT_REMOUNTING, + MOUNT_UNMOUNTING, + MOUNT_MOUNTING_SIGTERM, + MOUNT_MOUNTING_SIGKILL, + MOUNT_UNMOUNTING_SIGTERM, + MOUNT_UNMOUNTING_SIGKILL, + MOUNT_REMOUNTING_SIGTERM, + MOUNT_REMOUNTING_SIGKILL); +} + static bool needs_quota(const MountParameters *p) { assert(p); @@ -591,16 +605,7 @@ static void mount_set_state(Mount *m, MountState state) { old_state = m->state; m->state = state; - if (state != MOUNT_MOUNTING && - state != MOUNT_MOUNTING_DONE && - state != MOUNT_REMOUNTING && - state != MOUNT_UNMOUNTING && - state != MOUNT_MOUNTING_SIGTERM && - state != MOUNT_MOUNTING_SIGKILL && - state != MOUNT_UNMOUNTING_SIGTERM && - state != MOUNT_UNMOUNTING_SIGKILL && - state != MOUNT_REMOUNTING_SIGTERM && - state != MOUNT_REMOUNTING_SIGKILL) { + if (!mount_state_active(state)) { m->timer_event_source = sd_event_source_unref(m->timer_event_source); mount_unwatch_control_pid(m); m->control_command = NULL; @@ -632,17 +637,7 @@ static int mount_coldplug(Unit *u) { if (m->control_pid > 0 && pid_is_unwaited(m->control_pid) && - IN_SET(new_state, - MOUNT_MOUNTING, - MOUNT_MOUNTING_DONE, - MOUNT_REMOUNTING, - MOUNT_UNMOUNTING, - MOUNT_MOUNTING_SIGTERM, - MOUNT_MOUNTING_SIGKILL, - MOUNT_UNMOUNTING_SIGTERM, - MOUNT_UNMOUNTING_SIGKILL, - MOUNT_REMOUNTING_SIGTERM, - MOUNT_REMOUNTING_SIGKILL)) { + mount_state_active(new_state)) { r = unit_watch_pid(UNIT(m), m->control_pid); if (r < 0) diff --git a/src/core/namespace.c b/src/core/namespace.c index b573f008b9..203d122810 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -44,6 +44,8 @@ #include "user-util.h" #include "util.h" +#define DEV_MOUNT_OPTIONS (MS_NOSUID|MS_STRICTATIME|MS_NOEXEC) + typedef enum MountMode { /* This is ordered by priority! */ INACCESSIBLE, @@ -153,7 +155,7 @@ static int mount_dev(BindMount *m) { dev = strjoina(temporary_mount, "/dev"); (void) mkdir(dev, 0755); - if (mount("tmpfs", dev, "tmpfs", MS_NOSUID|MS_STRICTATIME, "mode=755") < 0) { + if (mount("tmpfs", dev, "tmpfs", DEV_MOUNT_OPTIONS, "mode=755") < 0) { r = -errno; goto fail; } @@ -237,6 +239,8 @@ static int mount_dev(BindMount *m) { */ (void) mkdir_p_label(m->path, 0755); + /* Unmount everything in old /dev */ + umount_recursive(m->path, 0); if (mount(dev, m->path, NULL, MS_MOVE, NULL) < 0) { r = -errno; goto fail; @@ -328,9 +332,11 @@ static int make_read_only(BindMount *m) { if (IN_SET(m->mode, INACCESSIBLE, READONLY)) r = bind_remount_recursive(m->path, true); - else if (IN_SET(m->mode, READWRITE, PRIVATE_TMP, PRIVATE_VAR_TMP, PRIVATE_DEV)) + else if (IN_SET(m->mode, READWRITE, PRIVATE_TMP, PRIVATE_VAR_TMP, PRIVATE_DEV)) { r = bind_remount_recursive(m->path, false); - else + if (r == 0 && m->mode == PRIVATE_DEV) /* can be readonly but the submounts can't*/ + r = mount(NULL, m->path, NULL, MS_REMOUNT|DEV_MOUNT_OPTIONS|MS_RDONLY, NULL); + } else r = 0; if (m->ignore && r == -ENOENT) diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf index 6c504a5e69..3c64f20872 100644 --- a/src/core/org.freedesktop.systemd1.conf +++ b/src/core/org.freedesktop.systemd1.conf @@ -156,6 +156,10 @@ <allow send_destination="org.freedesktop.systemd1" send_interface="org.freedesktop.systemd1.Manager" + send_member="ListUnitsByNames"/> + + <allow send_destination="org.freedesktop.systemd1" + send_interface="org.freedesktop.systemd1.Manager" send_member="StartTransientUnit"/> <allow send_destination="org.freedesktop.systemd1" diff --git a/src/core/socket.c b/src/core/socket.c index d3d4866fe6..f6204d04bf 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -28,7 +28,6 @@ #include <unistd.h> #include <linux/sctp.h> -#include "sd-event.h" #include "alloc-util.h" #include "bus-error.h" #include "bus-util.h" @@ -38,6 +37,7 @@ #include "exit-status.h" #include "fd-util.h" #include "formats-util.h" +#include "io-util.h" #include "label.h" #include "log.h" #include "missing.h" @@ -100,7 +100,8 @@ static void socket_init(Unit *u) { s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID; - RATELIMIT_INIT(s->trigger_limit, 5*USEC_PER_SEC, 2500); + s->trigger_limit.interval = USEC_INFINITY; + s->trigger_limit.burst = (unsigned) -1; } static void socket_unwatch_control_pid(Socket *s) { @@ -328,6 +329,25 @@ static int socket_add_extras(Socket *s) { assert(s); + /* Pick defaults for the trigger limit, if nothing was explicitly configured. We pick a relatively high limit + * in Accept=yes mode, and a lower limit for Accept=no. Reason: in Accept=yes mode we are invoking accept() + * ourselves before the trigger limit can hit, thus incoming connections are taken off the socket queue quickly + * and reliably. This is different for Accept=no, where the spawned service has to take the incoming traffic + * off the queues, which it might not necessarily do. Moreover, while Accept=no services are supposed to + * process whatever is queued in one go, and thus should normally never have to be started frequently. This is + * different for Accept=yes where each connection is processed by a new service instance, and thus frequent + * service starts are typical. */ + + if (s->trigger_limit.interval == USEC_INFINITY) + s->trigger_limit.interval = 2 * USEC_PER_SEC; + + if (s->trigger_limit.burst == (unsigned) -1) { + if (s->accept) + s->trigger_limit.burst = 200; + else + s->trigger_limit.burst = 20; + } + if (have_non_accept_socket(s)) { if (!UNIT_DEREF(s->service)) { @@ -620,8 +640,8 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { if (!isempty(s->user) || !isempty(s->group)) fprintf(f, - "%sOwnerUser: %s\n" - "%sOwnerGroup: %s\n", + "%sSocketUser: %s\n" + "%sSocketGroup: %s\n", prefix, strna(s->user), prefix, strna(s->group)); @@ -670,6 +690,12 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sListenFIFO: %s\n", prefix, p->path); } + fprintf(f, + "%sTriggerLimitIntervalSec: %s\n" + "%sTriggerLimitBurst: %u\n", + prefix, format_timespan(time_string, FORMAT_TIMESPAN_MAX, s->trigger_limit.interval, USEC_PER_SEC), + prefix, s->trigger_limit.burst); + exec_context_dump(&s->exec_context, f, prefix); kill_context_dump(&s->kill_context, f, prefix); @@ -1221,6 +1247,45 @@ fail: return r; } +static int socket_determine_selinux_label(Socket *s, char **ret) { + ExecCommand *c; + int r; + + assert(s); + assert(ret); + + if (s->selinux_context_from_net) { + /* If this is requested, get label from the network label */ + + r = mac_selinux_get_our_label(ret); + if (r == -EOPNOTSUPP) + goto no_label; + + } else { + /* Otherwise, get it from the executable we are about to start */ + r = socket_instantiate_service(s); + if (r < 0) + return r; + + if (!UNIT_ISSET(s->service)) + goto no_label; + + c = SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]; + if (!c) + goto no_label; + + r = mac_selinux_get_create_label_from_exe(c->path, ret); + if (r == -EPERM || r == -EOPNOTSUPP) + goto no_label; + } + + return r; + +no_label: + *ret = NULL; + return 0; +} + static int socket_open_fds(Socket *s) { _cleanup_(mac_selinux_freep) char *label = NULL; bool know_label = false; @@ -1239,46 +1304,28 @@ static int socket_open_fds(Socket *s) { case SOCKET_SOCKET: if (!know_label) { - /* Figure out label, if we don't it know - * yet. We do it once, for the first - * socket where we need this and - * remember it for the rest. */ - - if (s->selinux_context_from_net) { - /* Get it from the network label */ - - r = mac_selinux_get_our_label(&label); - if (r < 0 && r != -EOPNOTSUPP) - goto rollback; + /* Figure out label, if we don't it know yet. We do it once, for the first socket where + * we need this and remember it for the rest. */ - } else { - /* Get it from the executable we are about to start */ - - r = socket_instantiate_service(s); - if (r < 0) - goto rollback; - - if (UNIT_ISSET(s->service) && - SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]) { - r = mac_selinux_get_create_label_from_exe(SERVICE(UNIT_DEREF(s->service))->exec_command[SERVICE_EXEC_START]->path, &label); - if (r < 0 && r != -EPERM && r != -EOPNOTSUPP) - goto rollback; - } - } + r = socket_determine_selinux_label(s, &label); + if (r < 0) + goto rollback; know_label = true; } /* Apply the socket protocol */ - switch(p->address.type) { + switch (p->address.type) { + case SOCK_STREAM: case SOCK_SEQPACKET: - if (p->socket->socket_protocol == IPPROTO_SCTP) - p->address.protocol = p->socket->socket_protocol; + if (s->socket_protocol == IPPROTO_SCTP) + p->address.protocol = s->socket_protocol; break; + case SOCK_DGRAM: - if (p->socket->socket_protocol == IPPROTO_UDPLITE) - p->address.protocol = p->socket->socket_protocol; + if (s->socket_protocol == IPPROTO_UDPLITE) + p->address.protocol = s->socket_protocol; break; } @@ -1339,8 +1386,7 @@ static int socket_open_fds(Socket *s) { } break; - case SOCKET_USB_FUNCTION: - { + case SOCKET_USB_FUNCTION: { _cleanup_free_ char *ep = NULL; ep = path_make_absolute("ep0", p->path); @@ -1423,6 +1469,34 @@ fail: return r; } +enum { + SOCKET_OPEN_NONE, + SOCKET_OPEN_SOME, + SOCKET_OPEN_ALL, +}; + +static int socket_check_open(Socket *s) { + bool have_open = false, have_closed = false; + SocketPort *p; + + assert(s); + + LIST_FOREACH(port, p, s->ports) { + if (p->fd < 0) + have_closed = true; + else + have_open = true; + + if (have_open && have_closed) + return SOCKET_OPEN_SOME; + } + + if (have_open) + return SOCKET_OPEN_ALL; + + return SOCKET_OPEN_NONE; +} + static void socket_set_state(Socket *s, SocketState state) { SocketState old_state; assert(s); @@ -1502,14 +1576,24 @@ static int socket_coldplug(Unit *u) { SOCKET_START_CHOWN, SOCKET_START_POST, SOCKET_LISTENING, - SOCKET_RUNNING, - SOCKET_STOP_PRE, - SOCKET_STOP_PRE_SIGTERM, - SOCKET_STOP_PRE_SIGKILL)) { - - r = socket_open_fds(s); - if (r < 0) - return r; + SOCKET_RUNNING)) { + + /* Originally, we used to simply reopen all sockets here that we didn't have file descriptors + * for. However, this is problematic, as we won't traverse throught the SOCKET_START_CHOWN state for + * them, and thus the UID/GID wouldn't be right. Hence, instead simply check if we have all fds open, + * and if there's a mismatch, warn loudly. */ + + r = socket_check_open(s); + if (r == SOCKET_OPEN_NONE) + log_unit_warning(UNIT(s), + "Socket unit configuration has changed while unit has been running, " + "no open socket file descriptor left. " + "The socket unit is not functional until restarted."); + else if (r == SOCKET_OPEN_SOME) + log_unit_warning(UNIT(s), + "Socket unit configuration has changed while unit has been running, " + "and some socket file descriptors have not been opened yet. " + "The socket unit is not fully functional until restarted."); } if (s->deserialized_state == SOCKET_LISTENING) { @@ -1882,6 +1966,21 @@ fail: socket_enter_dead(s, SOCKET_FAILURE_RESOURCES); } +static void flush_ports(Socket *s) { + SocketPort *p; + + /* Flush all incoming traffic, regardless if actual bytes or new connections, so that this socket isn't busy + * anymore */ + + LIST_FOREACH(port, p, s->ports) { + if (p->fd < 0) + continue; + + (void) flush_accept(p->fd); + (void) flush_fd(p->fd); + } +} + static void socket_enter_running(Socket *s, int cfd) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; @@ -1891,31 +1990,15 @@ static void socket_enter_running(Socket *s, int cfd) { assert(s); - /* We don't take connections anymore if we are supposed to - * shut down anyway */ + /* We don't take connections anymore if we are supposed to shut down anyway */ if (unit_stop_pending(UNIT(s))) { log_unit_debug(UNIT(s), "Suppressing connection request since unit stop is scheduled."); if (cfd >= 0) cfd = safe_close(cfd); - else { - /* Flush all sockets by closing and reopening them */ - socket_close_fds(s); - - r = socket_open_fds(s); - if (r < 0) { - log_unit_warning_errno(UNIT(s), r, "Failed to listen on sockets: %m"); - socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); - return; - } - - r = socket_watch_fds(s); - if (r < 0) { - log_unit_warning_errno(UNIT(s), r, "Failed to watch sockets: %m"); - socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); - } - } + else + flush_ports(s); return; } diff --git a/src/core/transaction.c b/src/core/transaction.c index d5370b2a14..e06a48a2f1 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -597,7 +597,7 @@ static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) { /* Not invalidating recursively. Avoids triggering * OnFailure= actions of dependent jobs. Also avoids * invalidating our iterator. */ - job_finish_and_invalidate(j, JOB_CANCELED, false); + job_finish_and_invalidate(j, JOB_CANCELED, false, false); } } diff --git a/src/core/unit.c b/src/core/unit.c index 04addc1f70..2fff3f2d8b 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -1249,7 +1249,7 @@ int unit_load(Unit *u) { fclose(u->transient_file); u->transient_file = NULL; - u->dropin_mtime = now(CLOCK_REALTIME); + u->fragment_mtime = now(CLOCK_REALTIME); } if (UNIT_VTABLE(u)->load) { @@ -1918,12 +1918,12 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su case JOB_VERIFY_ACTIVE: if (UNIT_IS_ACTIVE_OR_RELOADING(ns)) - job_finish_and_invalidate(u->job, JOB_DONE, true); + job_finish_and_invalidate(u->job, JOB_DONE, true, false); else if (u->job->state == JOB_RUNNING && ns != UNIT_ACTIVATING) { unexpected = true; if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true); + job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false); } break; @@ -1934,12 +1934,12 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su if (u->job->state == JOB_RUNNING) { if (ns == UNIT_ACTIVE) - job_finish_and_invalidate(u->job, reload_success ? JOB_DONE : JOB_FAILED, true); + job_finish_and_invalidate(u->job, reload_success ? JOB_DONE : JOB_FAILED, true, false); else if (ns != UNIT_ACTIVATING && ns != UNIT_RELOADING) { unexpected = true; if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true); + job_finish_and_invalidate(u->job, ns == UNIT_FAILED ? JOB_FAILED : JOB_DONE, true, false); } } @@ -1950,10 +1950,10 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su case JOB_TRY_RESTART: if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - job_finish_and_invalidate(u->job, JOB_DONE, true); + job_finish_and_invalidate(u->job, JOB_DONE, true, false); else if (u->job->state == JOB_RUNNING && ns != UNIT_DEACTIVATING) { unexpected = true; - job_finish_and_invalidate(u->job, JOB_FAILED, true); + job_finish_and_invalidate(u->job, JOB_FAILED, true, false); } break; @@ -2402,9 +2402,11 @@ int unit_set_slice(Unit *u, Unit *slice) { if (UNIT_DEREF(u->slice) == slice) return 0; - if (UNIT_ISSET(u->slice)) + /* Disallow slice changes if @u is already bound to cgroups */ + if (UNIT_ISSET(u->slice) && u->cgroup_realized) return -EBUSY; + unit_ref_unset(&u->slice); unit_ref_set(&u->slice, slice); return 1; } diff --git a/src/core/unit.h b/src/core/unit.h index f14972728e..08a927962d 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -186,6 +186,7 @@ struct Unit { /* Counterparts in the cgroup filesystem */ char *cgroup_path; CGroupMask cgroup_realized_mask; + CGroupMask cgroup_enabled_mask; CGroupMask cgroup_subtree_mask; CGroupMask cgroup_members_mask; int cgroup_inotify_wd; diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 41fc1993d5..999de63900 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -739,15 +739,16 @@ static int process_socket(int fd) { .msg_iovlen = 1, }; ssize_t n; - int l; + ssize_t l; if (!GREEDY_REALLOC(iovec, n_iovec_allocated, n_iovec + 3)) { r = log_oom(); goto finish; } - if (ioctl(fd, FIONREAD, &l) < 0) { - r = log_error_errno(errno, "FIONREAD failed: %m"); + l = next_datagram_size_fd(fd); + if (l < 0) { + r = log_error_errno(l, "Failed to determine datagram size to read: %m"); goto finish; } @@ -847,7 +848,7 @@ static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd) if (fd < 0) return log_error_errno(errno, "Failed to create coredump socket: %m"); - if (connect(fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)) < 0) + if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) return log_error_errno(errno, "Failed to connect to coredump service: %m"); for (i = 0; i < n_iovec; i++) { diff --git a/src/delta/delta.c b/src/delta/delta.c index b4f0ecff2d..f32744def2 100644 --- a/src/delta/delta.c +++ b/src/delta/delta.c @@ -105,7 +105,7 @@ static int notify_override_masked(const char *top, const char *bottom) { printf("%s%s%s %s %s %s\n", ansi_highlight_red(), "[MASKED]", ansi_normal(), - top, draw_special_char(DRAW_ARROW), bottom); + top, special_glyph(ARROW), bottom); return 1; } @@ -115,7 +115,7 @@ static int notify_override_equivalent(const char *top, const char *bottom) { printf("%s%s%s %s %s %s\n", ansi_highlight_green(), "[EQUIVALENT]", ansi_normal(), - top, draw_special_char(DRAW_ARROW), bottom); + top, special_glyph(ARROW), bottom); return 1; } @@ -125,7 +125,7 @@ static int notify_override_redirected(const char *top, const char *bottom) { printf("%s%s%s %s %s %s\n", ansi_highlight(), "[REDIRECTED]", ansi_normal(), - top, draw_special_char(DRAW_ARROW), bottom); + top, special_glyph(ARROW), bottom); return 1; } @@ -135,7 +135,7 @@ static int notify_override_overridden(const char *top, const char *bottom) { printf("%s%s%s %s %s %s\n", ansi_highlight(), "[OVERRIDDEN]", ansi_normal(), - top, draw_special_char(DRAW_ARROW), bottom); + top, special_glyph(ARROW), bottom); return 1; } @@ -145,7 +145,7 @@ static int notify_override_extended(const char *top, const char *bottom) { printf("%s%s%s %s %s %s\n", ansi_highlight(), "[EXTENDED]", ansi_normal(), - top, draw_special_char(DRAW_ARROW), bottom); + top, special_glyph(ARROW), bottom); return 1; } @@ -247,7 +247,7 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const return -ENOMEM; d = p + strlen(toppath) + 1; - log_debug("Adding at top: %s %s %s", d, draw_special_char(DRAW_ARROW), p); + log_debug("Adding at top: %s %s %s", d, special_glyph(ARROW), p); k = hashmap_put(top, d, p); if (k >= 0) { p = strdup(p); @@ -259,7 +259,7 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const return k; } - log_debug("Adding at bottom: %s %s %s", d, draw_special_char(DRAW_ARROW), p); + log_debug("Adding at bottom: %s %s %s", d, special_glyph(ARROW), p); free(hashmap_remove(bottom, d)); k = hashmap_put(bottom, d, p); if (k < 0) { @@ -283,7 +283,7 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const return -ENOMEM; log_debug("Adding to drops: %s %s %s %s %s", - unit, draw_special_char(DRAW_ARROW), basename(p), draw_special_char(DRAW_ARROW), p); + unit, special_glyph(ARROW), basename(p), special_glyph(ARROW), p); k = hashmap_put(h, basename(p), p); if (k < 0) { free(p); @@ -334,7 +334,7 @@ static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const ch if (!p) return -ENOMEM; - log_debug("Adding at top: %s %s %s", basename(p), draw_special_char(DRAW_ARROW), p); + log_debug("Adding at top: %s %s %s", basename(p), special_glyph(ARROW), p); k = hashmap_put(top, basename(p), p); if (k >= 0) { p = strdup(p); @@ -345,7 +345,7 @@ static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const ch return k; } - log_debug("Adding at bottom: %s %s %s", basename(p), draw_special_char(DRAW_ARROW), p); + log_debug("Adding at bottom: %s %s %s", basename(p), special_glyph(ARROW), p); free(hashmap_remove(bottom, basename(p))); k = hashmap_put(bottom, basename(p), p); if (k < 0) { diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 435e3805c4..3df72460ef 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -160,7 +160,7 @@ static int prompt_loop(const char *text, char **l, bool (*is_valid)(const char * _cleanup_free_ char *p = NULL; unsigned u; - r = ask_string(&p, "%s %s (empty to skip): ", draw_special_char(DRAW_TRIANGULAR_BULLET), text); + r = ask_string(&p, "%s %s (empty to skip): ", special_glyph(TRIANGULAR_BULLET), text); if (r < 0) return log_error_errno(r, "Failed to query user: %m"); @@ -371,7 +371,7 @@ static int prompt_hostname(void) { for (;;) { _cleanup_free_ char *h = NULL; - r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", draw_special_char(DRAW_TRIANGULAR_BULLET)); + r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", special_glyph(TRIANGULAR_BULLET)); if (r < 0) return log_error_errno(r, "Failed to query hostname: %m"); @@ -456,8 +456,8 @@ static int prompt_root_password(void) { print_welcome(); putchar('\n'); - msg1 = strjoina(draw_special_char(DRAW_TRIANGULAR_BULLET), " Please enter a new root password (empty to skip): "); - msg2 = strjoina(draw_special_char(DRAW_TRIANGULAR_BULLET), " Please enter new root password again: "); + msg1 = strjoina(special_glyph(TRIANGULAR_BULLET), " Please enter a new root password (empty to skip): "); + msg2 = strjoina(special_glyph(TRIANGULAR_BULLET), " Please enter new root password again: "); for (;;) { _cleanup_string_free_erase_ char *a = NULL, *b = NULL; diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c index 6f56066da8..d32e1d923e 100644 --- a/src/fsck/fsck.c +++ b/src/fsck/fsck.c @@ -262,7 +262,7 @@ static int fsck_progress_socket(void) { if (fd < 0) return log_warning_errno(errno, "socket(): %m"); - if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) { + if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { r = log_full_errno(errno == ECONNREFUSED || errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno, "Failed to connect to progress socket %s, ignoring: %m", sa.un.sun_path); safe_close(fd); diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 343e3b1817..108522873e 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -489,6 +489,7 @@ static int parse_fstab(bool initrd) { static int add_sysroot_mount(void) { _cleanup_free_ char *what = NULL; const char *opts; + int r; if (isempty(arg_root_what)) { log_debug("Could not find a root= entry on the kernel command line."); @@ -508,6 +509,13 @@ static int add_sysroot_mount(void) { opts = arg_root_options; log_debug("Found entry what=%s where=/sysroot type=%s", what, strna(arg_root_fstype)); + + if (is_device_path(what)) { + r = generator_write_initrd_root_device_deps(arg_dest, what); + if (r < 0) + return r; + } + return add_mount(what, "/sysroot", arg_root_fstype, diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index af96adec06..a4938a7c3a 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -956,6 +956,12 @@ static int add_root_mount(void) { * wait for a root device to show up. A udev rule will create * the link for us under the right name. */ + if (in_initrd()) { + r = generator_write_initrd_root_device_deps(arg_dest, "/dev/gpt-auto-root"); + if (r < 0) + return 0; + } + return add_mount( "root", "/dev/gpt-auto-root", diff --git a/src/import/importd.c b/src/import/importd.c index d2a5867a6e..956a82945c 100644 --- a/src/import/importd.c +++ b/src/import/importd.c @@ -677,7 +677,7 @@ static int manager_new(Manager **ret) { (void) mkdir_parents_label(sa.un.sun_path, 0755); (void) unlink(sa.un.sun_path); - if (bind(m->notify_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)) < 0) + if (bind(m->notify_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) return -errno; if (setsockopt(m->notify_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c index f0959b6237..5e8a3e3200 100644 --- a/src/journal/journal-send.c +++ b/src/journal/journal-send.c @@ -208,13 +208,13 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) { struct iovec *w; uint64_t *l; int i, j = 0; - struct sockaddr_un sa = { - .sun_family = AF_UNIX, - .sun_path = "/run/systemd/journal/socket", + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/socket", }; struct msghdr mh = { - .msg_name = &sa, - .msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(sa.sun_path), + .msg_name = (struct sockaddr*) &sa.sa, + .msg_namelen = SOCKADDR_UN_LEN(sa.un), }; ssize_t k; bool have_syslog_identifier = false; @@ -392,7 +392,7 @@ _public_ int sd_journal_perror(const char *message) { } _public_ int sd_journal_stream_fd(const char *identifier, int priority, int level_prefix) { - union sockaddr_union sa = { + static const union sockaddr_union sa = { .un.sun_family = AF_UNIX, .un.sun_path = "/run/systemd/journal/stdout", }; @@ -408,7 +408,7 @@ _public_ int sd_journal_stream_fd(const char *identifier, int priority, int leve if (fd < 0) return -errno; - r = connect(fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)); + r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) return -errno; diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c index a445291a5e..0a1ce205c2 100644 --- a/src/journal/journald-native.c +++ b/src/journal/journald-native.c @@ -448,24 +448,24 @@ void server_process_native_file( } int server_open_native_socket(Server*s) { + + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/socket", + }; static const int one = 1; int r; assert(s); if (s->native_fd < 0) { - union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/journal/socket", - }; - s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (s->native_fd < 0) return log_error_errno(errno, "socket() failed: %m"); - unlink(sa.un.sun_path); + (void) unlink(sa.un.sun_path); - r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)); + r = bind(s->native_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index e14d0ad980..8f82d2a838 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -1696,7 +1696,7 @@ static int server_connect_notify(Server *s) { if (sa.un.sun_path[0] == '@') sa.un.sun_path[0] = 0; - r = connect(s->notify_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(e)); + r = connect(s->notify_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) return log_error_errno(errno, "Failed to connect to notify socket: %m"); diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index 59352bcb3f..4ad16ee41c 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -700,23 +700,22 @@ fail: } int server_open_stdout_socket(Server *s) { + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/stdout", + }; int r; assert(s); if (s->stdout_fd < 0) { - union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/journal/stdout", - }; - s->stdout_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (s->stdout_fd < 0) return log_error_errno(errno, "socket() failed: %m"); - unlink(sa.un.sun_path); + (void) unlink(sa.un.sun_path); - r = bind(s->stdout_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)); + r = bind(s->stdout_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c index 5153fd0cce..0609b4b694 100644 --- a/src/journal/journald-syslog.c +++ b/src/journal/journald-syslog.c @@ -52,8 +52,7 @@ static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned .msg_iov = (struct iovec *) iovec, .msg_iovlen = n_iovec, .msg_name = (struct sockaddr*) &sa.sa, - .msg_namelen = offsetof(union sockaddr_union, un.sun_path) - + strlen("/run/systemd/journal/syslog"), + .msg_namelen = SOCKADDR_UN_LEN(sa.un), }; struct cmsghdr *cmsg; union { @@ -316,12 +315,12 @@ static void syslog_skip_date(char **buf) { } void server_process_syslog_message( - Server *s, - const char *buf, - const struct ucred *ucred, - const struct timeval *tv, - const char *label, - size_t label_len) { + Server *s, + const char *buf, + const struct ucred *ucred, + const struct timeval *tv, + const char *label, + size_t label_len) { char syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)], syslog_facility[sizeof("SYSLOG_FACILITY=") + DECIMAL_STR_MAX(int)]; @@ -365,14 +364,12 @@ void server_process_syslog_message( if (identifier) { syslog_identifier = strjoina("SYSLOG_IDENTIFIER=", identifier); - if (syslog_identifier) - IOVEC_SET_STRING(iovec[n++], syslog_identifier); + IOVEC_SET_STRING(iovec[n++], syslog_identifier); } if (pid) { syslog_pid = strjoina("SYSLOG_PID=", pid); - if (syslog_pid) - IOVEC_SET_STRING(iovec[n++], syslog_pid); + IOVEC_SET_STRING(iovec[n++], syslog_pid); } message = strjoina("MESSAGE=", buf); @@ -383,24 +380,24 @@ void server_process_syslog_message( } int server_open_syslog_socket(Server *s) { + + static const union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + .un.sun_path = "/run/systemd/journal/dev-log", + }; static const int one = 1; int r; assert(s); if (s->syslog_fd < 0) { - static const union sockaddr_union sa = { - .un.sun_family = AF_UNIX, - .un.sun_path = "/run/systemd/journal/dev-log", - }; - s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (s->syslog_fd < 0) return log_error_errno(errno, "socket() failed: %m"); - unlink(sa.un.sun_path); + (void) unlink(sa.un.sun_path); - r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)); + r = bind(s->syslog_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); @@ -437,6 +434,7 @@ int server_open_syslog_socket(Server *s) { void server_maybe_warn_forward_syslog_missed(Server *s) { usec_t n; + assert(s); if (s->n_forward_syslog_missed <= 0) diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index 99b3a1d01f..2badcdff58 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -32,6 +32,7 @@ #include "network-internal.h" #include "parse-util.h" #include "siphash24.h" +#include "socket-util.h" #include "string-util.h" #include "strv.h" #include "utf8.h" @@ -175,58 +176,19 @@ int config_parse_net_condition(const char *unit, return 0; } -int config_parse_ifname(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - char **s = data; - _cleanup_free_ char *n = NULL; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - n = strdup(rvalue); - if (!n) - return log_oom(); - - if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue); - return 0; - } - - free(*s); - if (*n) { - *s = n; - n = NULL; - } else - *s = NULL; - - return 0; -} - -int config_parse_ifnames(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_ifnames( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { char ***sv = data; - const char *word, *state; - size_t l; int r; assert(filename); @@ -234,22 +196,27 @@ int config_parse_ifnames(const char *unit, assert(rvalue); assert(data); - FOREACH_WORD(word, l, rvalue, state) { - char *n; + for (;;) { + _cleanup_free_ char *word = NULL; - n = strndup(word, l); - if (!n) - return log_oom(); + r = extract_first_word(&rvalue, &word, NULL, 0); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse interface name list: %s", rvalue); + return 0; + } + if (r == 0) + break; - if (!ascii_is_valid(n) || strlen(n) >= IFNAMSIZ) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not ASCII clean or is too long, ignoring assignment: %s", rvalue); - free(n); + if (!ifname_valid(word)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue); return 0; } - r = strv_consume(sv, n); + r = strv_push(sv, word); if (r < 0) return log_oom(); + + word = NULL; } return 0; @@ -380,28 +347,28 @@ void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size) { int deserialize_in_addrs(struct in_addr **ret, const char *string) { _cleanup_free_ struct in_addr *addresses = NULL; int size = 0; - const char *word, *state; - size_t len; assert(ret); assert(string); - FOREACH_WORD(word, len, string, state) { - _cleanup_free_ char *addr_str = NULL; + for (;;) { + _cleanup_free_ char *word = NULL; struct in_addr *new_addresses; int r; + r = extract_first_word(&string, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; + new_addresses = realloc(addresses, (size + 1) * sizeof(struct in_addr)); if (!new_addresses) return -ENOMEM; else addresses = new_addresses; - addr_str = strndup(word, len); - if (!addr_str) - return -ENOMEM; - - r = inet_pton(AF_INET, addr_str, &(addresses[size])); + r = inet_pton(AF_INET, word, &(addresses[size])); if (r <= 0) continue; @@ -431,28 +398,28 @@ void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, int deserialize_in6_addrs(struct in6_addr **ret, const char *string) { _cleanup_free_ struct in6_addr *addresses = NULL; int size = 0; - const char *word, *state; - size_t len; assert(ret); assert(string); - FOREACH_WORD(word, len, string, state) { - _cleanup_free_ char *addr_str = NULL; + for (;;) { + _cleanup_free_ char *word = NULL; struct in6_addr *new_addresses; int r; + r = extract_first_word(&string, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; + new_addresses = realloc(addresses, (size + 1) * sizeof(struct in6_addr)); if (!new_addresses) return -ENOMEM; else addresses = new_addresses; - addr_str = strndup(word, len); - if (!addr_str) - return -ENOMEM; - - r = inet_pton(AF_INET6, addr_str, &(addresses[size])); + r = inet_pton(AF_INET6, word, &(addresses[size])); if (r <= 0) continue; @@ -493,29 +460,29 @@ void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, siz int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string) { _cleanup_free_ struct sd_dhcp_route *routes = NULL; size_t size = 0, allocated = 0; - const char *word, *state; - size_t len; assert(ret); assert(ret_size); assert(ret_allocated); assert(string); - FOREACH_WORD(word, len, string, state) { - /* WORD FORMAT: dst_ip/dst_prefixlen,gw_ip */ - _cleanup_free_ char* entry = NULL; + /* WORD FORMAT: dst_ip/dst_prefixlen,gw_ip */ + for (;;) { + _cleanup_free_ char *word = NULL; char *tok, *tok_end; unsigned n; int r; - if (!GREEDY_REALLOC(routes, allocated, size + 1)) - return -ENOMEM; + r = extract_first_word(&string, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; - entry = strndup(word, len); - if (!entry) + if (!GREEDY_REALLOC(routes, allocated, size + 1)) return -ENOMEM; - tok = entry; + tok = word; /* get the subnet */ tok_end = strchr(tok, '/'); diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h index 72432774d7..5bcd577167 100644 --- a/src/libsystemd-network/network-internal.h +++ b/src/libsystemd-network/network-internal.h @@ -50,10 +50,6 @@ int config_parse_hwaddr(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_ifname(const char *unit, const char *filename, unsigned line, - const char *section, unsigned section_line, const char *lvalue, - int ltype, const char *rvalue, void *data, void *userdata); - int config_parse_ifnames(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c index 5964a01c4f..21a6b20a11 100644 --- a/src/libsystemd/sd-bus/bus-dump.c +++ b/src/libsystemd/sd-bus/bus-dump.c @@ -74,7 +74,7 @@ int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags) { "%s%s%s Type=%s%s%s Endian=%c Flags=%u Version=%u Priority=%"PRIi64, m->header->type == SD_BUS_MESSAGE_METHOD_ERROR ? ansi_highlight_red() : m->header->type == SD_BUS_MESSAGE_METHOD_RETURN ? ansi_highlight_green() : - m->header->type != SD_BUS_MESSAGE_SIGNAL ? ansi_highlight() : "", draw_special_char(DRAW_TRIANGULAR_BULLET), ansi_normal(), + m->header->type != SD_BUS_MESSAGE_SIGNAL ? ansi_highlight() : "", special_glyph(TRIANGULAR_BULLET), ansi_normal(), ansi_highlight(), bus_message_type_to_string(m->header->type), ansi_normal(), m->header->endian, m->header->flags, diff --git a/src/libsystemd/sd-bus/busctl.c b/src/libsystemd/sd-bus/busctl.c index 56bd5863a8..bfe967bfb0 100644 --- a/src/libsystemd/sd-bus/busctl.c +++ b/src/libsystemd/sd-bus/busctl.c @@ -249,8 +249,8 @@ static void print_subtree(const char *prefix, const char *path, char **l) { l++; } - vertical = strjoina(prefix, draw_special_char(DRAW_TREE_VERTICAL)); - space = strjoina(prefix, draw_special_char(DRAW_TREE_SPACE)); + vertical = strjoina(prefix, special_glyph(TREE_VERTICAL)); + space = strjoina(prefix, special_glyph(TREE_SPACE)); for (;;) { bool has_more = false; @@ -271,7 +271,7 @@ static void print_subtree(const char *prefix, const char *path, char **l) { n++; } - printf("%s%s%s\n", prefix, draw_special_char(has_more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT), *l); + printf("%s%s%s\n", prefix, special_glyph(has_more ? TREE_BRANCH : TREE_RIGHT), *l); print_subtree(has_more ? vertical : space, *l, l); l = n; @@ -1079,10 +1079,21 @@ static int message_pcap(sd_bus_message *m, FILE *f) { } static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FILE *f)) { - bool added_something = false; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; char **i; + uint32_t flags = 0; int r; + /* upgrade connection; it's not used for anything else after this call */ + r = sd_bus_message_new_method_call(bus, &message, "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus.Monitoring", "BecomeMonitor"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(message, 'a', "s"); + if (r < 0) + return bus_log_create_error(r); + STRV_FOREACH(i, argv+1) { _cleanup_free_ char *m = NULL; @@ -1095,34 +1106,38 @@ static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FIL if (!m) return log_oom(); - r = sd_bus_add_match(bus, NULL, m, NULL, NULL); + r = sd_bus_message_append_basic(message, 's', m); if (r < 0) - return log_error_errno(r, "Failed to add match: %m"); + return bus_log_create_error(r); free(m); m = strjoin("destination='", *i, "'", NULL); if (!m) return log_oom(); - r = sd_bus_add_match(bus, NULL, m, NULL, NULL); + r = sd_bus_message_append_basic(message, 's', m); if (r < 0) - return log_error_errno(r, "Failed to add match: %m"); - - added_something = true; + return bus_log_create_error(r); } STRV_FOREACH(i, arg_matches) { - r = sd_bus_add_match(bus, NULL, *i, NULL, NULL); + r = sd_bus_message_append_basic(message, 's', *i); if (r < 0) - return log_error_errno(r, "Failed to add match: %m"); - - added_something = true; + return bus_log_create_error(r); } - if (!added_something) { - r = sd_bus_add_match(bus, NULL, "", NULL, NULL); - if (r < 0) - return log_error_errno(r, "Failed to add match: %m"); + r = sd_bus_message_close_container(message); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(message, 'u', &flags); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, message, arg_timeout, &error, NULL); + if (r < 0) { + log_error("%s", bus_error_message(&error, r)); + return r; } log_info("Monitoring bus message stream."); diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index 04da94e7e3..ed5f94e136 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -836,7 +836,7 @@ static int parse_container_unix_address(sd_bus *b, const char **p, char **guid) b->sockaddr.un.sun_family = AF_UNIX; strncpy(b->sockaddr.un.sun_path, "/var/run/dbus/system_bus_socket", sizeof(b->sockaddr.un.sun_path)); - b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + strlen("/var/run/dbus/system_bus_socket"); + b->sockaddr_size = SOCKADDR_UN_LEN(b->sockaddr.un); return 0; } diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c index bd1c7f15ff..4da9dbfd63 100644 --- a/src/libsystemd/sd-daemon/sd-daemon.c +++ b/src/libsystemd/sd-daemon/sd-daemon.c @@ -458,9 +458,7 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char if (sockaddr.un.sun_path[0] == '@') sockaddr.un.sun_path[0] = 0; - msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e); - if (msghdr.msg_namelen > sizeof(struct sockaddr_un)) - msghdr.msg_namelen = sizeof(struct sockaddr_un); + msghdr.msg_namelen = SOCKADDR_UN_LEN(sockaddr.un); have_pid = pid != 0 && pid != getpid(); diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index a281f99a34..0a84d75e24 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -265,6 +265,42 @@ static int property_get_docked( return sd_bus_message_append(reply, "b", manager_is_docked_or_external_displays(m)); } +static int property_get_current_sessions( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(bus); + assert(reply); + assert(m); + + return sd_bus_message_append(reply, "t", (uint64_t) hashmap_size(m->sessions)); +} + +static int property_get_current_inhibitors( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(bus); + assert(reply); + assert(m); + + return sd_bus_message_append(reply, "t", (uint64_t) hashmap_size(m->inhibitors)); +} + static int method_get_session(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_free_ char *p = NULL; Manager *m = userdata; @@ -725,6 +761,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus m->seat0->positions[vtnr]->class != SESSION_GREETER) return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already occupied by a session"); + if (hashmap_size(m->sessions) >= m->sessions_max) + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.", m->sessions_max); + audit_session_from_pid(leader, &audit_id); if (audit_id > 0) { /* Keep our session IDs and the audit session IDs in sync */ @@ -2442,6 +2481,9 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error if (r < 0) return r; + if (hashmap_size(m->inhibitors) >= m->inhibitors_max) + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of inhibitors (%" PRIu64 ") reached, refusing further inhibitors.", m->inhibitors_max); + do { id = mfree(id); @@ -2512,6 +2554,13 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_PROPERTY("PreparingForSleep", "b", property_get_preparing, 0, 0), SD_BUS_PROPERTY("ScheduledShutdown", "(st)", property_get_scheduled_shutdown, 0, 0), SD_BUS_PROPERTY("Docked", "b", property_get_docked, 0, 0), + SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(Manager, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RuntimeDirectorySize", "t", bus_property_get_size, offsetof(Manager, runtime_dir_size), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("InhibitorsMax", "t", NULL, offsetof(Manager, inhibitors_max), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NCurrentInhibitors", "t", property_get_current_inhibitors, 0, 0), + SD_BUS_PROPERTY("SessionsMax", "t", NULL, offsetof(Manager, sessions_max), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NCurrentSessions", "t", property_get_current_sessions, 0, 0), + SD_BUS_PROPERTY("UserTasksMax", "t", NULL, offsetof(Manager, user_tasks_max), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_METHOD("GetSession", "s", "o", method_get_session, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetSessionByPID", "u", "o", method_get_session_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/login/logind-gperf.gperf b/src/login/logind-gperf.gperf index 8552c464cc..6bd08adc05 100644 --- a/src/login/logind-gperf.gperf +++ b/src/login/logind-gperf.gperf @@ -34,4 +34,6 @@ Login.IdleAction, config_parse_handle_action, 0, offsetof(Manag Login.IdleActionSec, config_parse_sec, 0, offsetof(Manager, idle_action_usec) Login.RuntimeDirectorySize, config_parse_tmpfs_size, 0, offsetof(Manager, runtime_dir_size) Login.RemoveIPC, config_parse_bool, 0, offsetof(Manager, remove_ipc) +Login.InhibitorsMax, config_parse_uint64, 0, offsetof(Manager, inhibitors_max) +Login.SessionsMax, config_parse_uint64, 0, offsetof(Manager, sessions_max) Login.UserTasksMax, config_parse_uint64, 0, offsetof(Manager, user_tasks_max) diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c index a0e3ba2b7c..6c78e0dddc 100644 --- a/src/login/logind-inhibit.c +++ b/src/login/logind-inhibit.c @@ -317,7 +317,7 @@ int inhibitor_create_fifo(Inhibitor *i) { if (r < 0) return r; - r = sd_event_source_set_priority(i->event_source, SD_EVENT_PRIORITY_IDLE); + r = sd_event_source_set_priority(i->event_source, SD_EVENT_PRIORITY_IDLE-10); if (r < 0) return r; } diff --git a/src/login/logind-seat-dbus.c b/src/login/logind-seat-dbus.c index 3cee10d009..f934a5326a 100644 --- a/src/login/logind-seat-dbus.c +++ b/src/login/logind-seat-dbus.c @@ -306,7 +306,7 @@ const sd_bus_vtable seat_vtable[] = { SD_BUS_PROPERTY("CanMultiSession", "b", property_get_can_multi_session, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("CanTTY", "b", property_get_can_tty, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("CanGraphical", "b", property_get_can_graphical, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0), SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c index 0f8862c0d9..22dea5db1f 100644 --- a/src/login/logind-session-dbus.c +++ b/src/login/logind-session-dbus.c @@ -180,6 +180,24 @@ static int property_get_idle_since_hint( return sd_bus_message_append(reply, "t", u); } +static int property_get_locked_hint( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Session *s = userdata; + + assert(bus); + assert(reply); + assert(s); + + return sd_bus_message_append(reply, "b", session_get_locked_hint(s) > 0); +} + int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error) { Session *s = userdata; int r; @@ -279,6 +297,35 @@ static int method_set_idle_hint(sd_bus_message *message, void *userdata, sd_bus_ return sd_bus_reply_method_return(message, NULL); } +static int method_set_locked_hint(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + Session *s = userdata; + uid_t uid; + int r, b; + + assert(message); + assert(s); + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_euid(creds, &uid); + if (r < 0) + return r; + + if (uid != 0 && uid != s->user->uid) + return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set locked hint"); + + session_set_locked_hint(s, b); + + return sd_bus_reply_method_return(message, NULL); +} + int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) { Session *s = userdata; const char *swho; @@ -487,12 +534,14 @@ const sd_bus_vtable session_vtable[] = { SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("LockedHint", "b", property_get_locked_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_METHOD("Terminate", NULL, NULL, bus_session_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Activate", NULL, NULL, bus_session_method_activate, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Lock", NULL, NULL, bus_session_method_lock, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Unlock", NULL, NULL, bus_session_method_lock, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetIdleHint", "b", NULL, method_set_idle_hint, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetLockedHint", "b", NULL, method_set_locked_hint, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Kill", "si", NULL, bus_session_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("TakeControl", "b", NULL, method_take_control, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ReleaseControl", NULL, NULL, method_release_control, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/login/logind-session.c b/src/login/logind-session.c index a8b1d5943d..1e0666884a 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -852,6 +852,23 @@ void session_set_idle_hint(Session *s, bool b) { manager_send_changed(s->manager, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL); } +int session_get_locked_hint(Session *s) { + assert(s); + + return s->locked_hint; +} + +void session_set_locked_hint(Session *s, bool b) { + assert(s); + + if (s->locked_hint == b) + return; + + s->locked_hint = b; + + session_send_changed(s, "LockedHint", NULL); +} + static int session_dispatch_fifo(sd_event_source *es, int fd, uint32_t revents, void *userdata) { Session *s = userdata; @@ -897,7 +914,9 @@ int session_create_fifo(Session *s) { if (r < 0) return r; - r = sd_event_source_set_priority(s->fifo_event_source, SD_EVENT_PRIORITY_IDLE); + /* Let's make sure we noticed dead sessions before we process new bus requests (which might create new + * sessions). */ + r = sd_event_source_set_priority(s->fifo_event_source, SD_EVENT_PRIORITY_NORMAL-10); if (r < 0) return r; } diff --git a/src/login/logind-session.h b/src/login/logind-session.h index e24b808474..ffb7cd2d41 100644 --- a/src/login/logind-session.h +++ b/src/login/logind-session.h @@ -105,6 +105,8 @@ struct Session { bool idle_hint; dual_timestamp idle_hint_timestamp; + bool locked_hint; + bool in_gc_queue:1; bool started:1; bool stopping:1; @@ -132,6 +134,8 @@ int session_activate(Session *s); bool session_is_active(Session *s); int session_get_idle_hint(Session *s, dual_timestamp *t); void session_set_idle_hint(Session *s, bool b); +int session_get_locked_hint(Session *s); +void session_set_locked_hint(Session *s, bool b); int session_create_fifo(Session *s); int session_start(Session *s); int session_stop(Session *s, bool force); diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c index b73f9ea69e..af6392e025 100644 --- a/src/login/logind-user-dbus.c +++ b/src/login/logind-user-dbus.c @@ -245,7 +245,7 @@ const sd_bus_vtable user_vtable[] = { SD_BUS_PROPERTY("Slice", "s", NULL, offsetof(User, slice), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Display", "(so)", property_get_display, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0), - SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Sessions", "a(so)", property_get_sessions, 0, 0), SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), diff --git a/src/login/logind.c b/src/login/logind.c index a48e2fc61e..caf149cfb7 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -62,7 +62,9 @@ static void manager_reset_config(Manager *m) { m->idle_action = HANDLE_IGNORE; m->runtime_dir_size = PAGE_ALIGN((size_t) (physical_memory() / 10)); /* 10% */ - m->user_tasks_max = UINT64_C(12288); + m->user_tasks_max = 12288; + m->sessions_max = 8192; + m->inhibitors_max = 8192; m->kill_user_processes = KILL_USER_PROCESSES; @@ -686,7 +688,7 @@ static int manager_connect_bus(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to register name: %m"); - r = sd_bus_attach_event(m->bus, m->event, 0); + r = sd_bus_attach_event(m->bus, m->event, SD_EVENT_PRIORITY_NORMAL); if (r < 0) return log_error_errno(r, "Failed to attach bus to event loop: %m"); diff --git a/src/login/logind.conf.in b/src/login/logind.conf.in index 3c96def45d..32c0844cb6 100644 --- a/src/login/logind.conf.in +++ b/src/login/logind.conf.in @@ -32,4 +32,6 @@ #IdleActionSec=30min #RuntimeDirectorySize=10% #RemoveIPC=yes +#InhibitorsMax=8192 +#SessionsMax=8192 #UserTasksMax=12288 diff --git a/src/login/logind.h b/src/login/logind.h index 6748af3c07..90431eb4b0 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -133,6 +133,8 @@ struct Manager { size_t runtime_dir_size; uint64_t user_tasks_max; + uint64_t sessions_max; + uint64_t inhibitors_max; }; int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device); diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf index 1662d4c428..c89e40457e 100644 --- a/src/login/org.freedesktop.login1.conf +++ b/src/login/org.freedesktop.login1.conf @@ -234,6 +234,10 @@ <allow send_destination="org.freedesktop.login1" send_interface="org.freedesktop.login1.Session" + send_member="SetLockedHint"/> + + <allow send_destination="org.freedesktop.login1" + send_interface="org.freedesktop.login1.Session" send_member="Kill"/> <allow send_destination="org.freedesktop.login1" diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index 40e246bb06..98dc201340 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -150,7 +150,7 @@ static int get_seat_from_display(const char *display, const char **seat, uint32_ if (fd < 0) return -errno; - if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) + if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) return -errno; r = getpeercred(fd, &ucred); diff --git a/src/login/sysfs-show.c b/src/login/sysfs-show.c index bd603e297d..29785e2f11 100644 --- a/src/login/sysfs-show.c +++ b/src/login/sysfs-show.c @@ -110,7 +110,7 @@ static int show_sysfs_one( if (!k) return -ENOMEM; - printf("%s%s%s\n", prefix, draw_special_char(lookahead ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT), k); + printf("%s%s%s\n", prefix, special_glyph(lookahead ? TREE_BRANCH : TREE_RIGHT), k); if (asprintf(&l, "%s%s:%s%s%s%s", @@ -124,13 +124,13 @@ static int show_sysfs_one( if (!k) return -ENOMEM; - printf("%s%s%s\n", prefix, lookahead ? draw_special_char(DRAW_TREE_VERTICAL) : " ", k); + printf("%s%s%s\n", prefix, lookahead ? special_glyph(TREE_VERTICAL) : " ", k); *item = next; if (*item) { _cleanup_free_ char *p = NULL; - p = strappend(prefix, lookahead ? draw_special_char(DRAW_TREE_VERTICAL) : " "); + p = strappend(prefix, lookahead ? special_glyph(TREE_VERTICAL) : " "); if (!p) return -ENOMEM; @@ -183,7 +183,7 @@ int show_sysfs(const char *seat, const char *prefix, unsigned n_columns) { if (first) show_sysfs_one(udev, seat, &first, "/", prefix, n_columns); else - printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_RIGHT), "(none)"); + printf("%s%s%s\n", prefix, special_glyph(TREE_RIGHT), "(none)"); return r; } diff --git a/src/network/networkctl.c b/src/network/networkctl.c index b22a0f648a..d2df9b7560 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -750,7 +750,7 @@ static int link_status_one( (void) sd_network_link_get_carrier_bound_to(info->ifindex, &carrier_bound_to); (void) sd_network_link_get_carrier_bound_by(info->ifindex, &carrier_bound_by); - printf("%s%s%s %i: %s\n", on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, info->ifindex, info->name); + printf("%s%s%s %i: %s\n", on_color_operational, special_glyph(BLACK_CIRCLE), off_color_operational, info->ifindex, info->name); printf(" Link File: %s\n" " Network File: %s\n" @@ -818,7 +818,7 @@ static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) { operational_state_to_color(operational_state, &on_color_operational, &off_color_operational); printf("%s%s%s State: %s%s%s\n", - on_color_operational, draw_special_char(DRAW_BLACK_CIRCLE), off_color_operational, + on_color_operational, special_glyph(BLACK_CIRCLE), off_color_operational, on_color_operational, strna(operational_state), off_color_operational); (void) dump_addresses(rtnl, " Address: ", 0); diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 8b52a1f742..4eb0d927a6 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -27,6 +27,7 @@ #include "networkd.h" #include "parse-util.h" #include "set.h" +#include "socket-util.h" #include "string-util.h" #include "utf8.h" #include "util.h" @@ -726,7 +727,8 @@ int config_parse_address(const char *unit, return 0; } -int config_parse_label(const char *unit, +int config_parse_label( + const char *unit, const char *filename, unsigned line, const char *section, @@ -736,9 +738,9 @@ int config_parse_label(const char *unit, const char *rvalue, void *data, void *userdata) { - Network *network = userdata; + _cleanup_address_free_ Address *n = NULL; - char *label; + Network *network = userdata; int r; assert(filename); @@ -751,23 +753,14 @@ int config_parse_label(const char *unit, if (r < 0) return r; - label = strdup(rvalue); - if (!label) - return log_oom(); - - if (!ascii_is_valid(label) || strlen(label) >= IFNAMSIZ) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is not ASCII clean or is too long, ignoring assignment: %s", rvalue); - free(label); + if (!ifname_valid(rvalue)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is not valid or too long, ignoring assignment: %s", rvalue); return 0; } - free(n->label); - if (*label) - n->label = label; - else { - free(label); - n->label = NULL; - } + r = free_and_strdup(&n->label, rvalue); + if (r < 0) + return log_oom(); n = NULL; diff --git a/src/network/networkd-conf.c b/src/network/networkd-conf.c index 6072c1e2de..b67a1f6d09 100644 --- a/src/network/networkd-conf.c +++ b/src/network/networkd-conf.c @@ -70,7 +70,7 @@ int config_parse_duid_rawdata( for (;;) { int n1, n2, len, r; uint32_t byte; - char *cbyte; + _cleanup_free_ char *cbyte = NULL; r = extract_first_word(&rvalue, &cbyte, ":", 0); if (r < 0) { diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index c646af1f1a..4e3f62cf51 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -131,7 +131,7 @@ static bool link_lldp_rx_enabled(Link *link) { return link->network->lldp_mode != LLDP_MODE_NO; } -static bool link_lldp_tx_enabled(Link *link) { +static bool link_lldp_emit_enabled(Link *link) { assert(link); if (link->flags & IFF_LOOPBACK) @@ -143,7 +143,7 @@ static bool link_lldp_tx_enabled(Link *link) { if (!link->network) return false; - return link->network->lldp_emit; + return link->network->lldp_emit != LLDP_EMIT_NO; } static bool link_ipv4_forward_enabled(Link *link) { @@ -491,7 +491,7 @@ static void link_free(Link *link) { sd_dhcp_client_unref(link->dhcp_client); sd_dhcp_lease_unref(link->dhcp_lease); - link_lldp_tx_stop(link); + link_lldp_emit_stop(link); free(link->lease_file); @@ -618,7 +618,7 @@ static int link_stop_clients(Link *link) { r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Discovery: %m"); } - link_lldp_tx_stop(link); + link_lldp_emit_stop(link); return r; } @@ -1411,12 +1411,12 @@ static void lldp_handler(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n (void) link_lldp_save(link); - if (link_lldp_tx_enabled(link) && event == SD_LLDP_EVENT_ADDED) { + if (link_lldp_emit_enabled(link) && event == SD_LLDP_EVENT_ADDED) { /* If we received information about a new neighbor, restart the LLDP "fast" logic */ log_link_debug(link, "Received LLDP datagram from previously unknown neighbor, restarting 'fast' LLDP transmission."); - r = link_lldp_tx_start(link); + r = link_lldp_emit_start(link); if (r < 0) log_link_warning_errno(link, r, "Failed to restart LLDP transmission: %m"); } @@ -1501,8 +1501,8 @@ static int link_acquire_conf(Link *link) { return r; } - if (link_lldp_tx_enabled(link)) { - r = link_lldp_tx_start(link); + if (link_lldp_emit_enabled(link)) { + r = link_lldp_emit_start(link); if (r < 0) return log_link_warning_errno(link, r, "Failed to start LLDP transmission: %m"); } diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 86139be557..14c4a02c7e 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -121,7 +121,7 @@ typedef struct Link { /* This is about LLDP transmission */ unsigned lldp_tx_fast; /* The LLDP txFast counter (See 802.1ab-2009, section 9.2.5.18) */ - sd_event_source *lldp_tx_event_source; + sd_event_source *lldp_emit_event_source; Hashmap *bound_by_links; Hashmap *bound_to_links; diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c index 03b694c3f1..3aa768388b 100644 --- a/src/network/networkd-lldp-tx.c +++ b/src/network/networkd-lldp-tx.c @@ -25,16 +25,14 @@ #include "fd-util.h" #include "fileio.h" #include "hostname-util.h" +#include "networkd-lldp-tx.h" +#include "networkd.h" +#include "parse-util.h" #include "random-util.h" #include "socket-util.h" #include "string-util.h" #include "unaligned.h" -#include "networkd.h" -#include "networkd-lldp-tx.h" - -#define LLDP_MULTICAST_ADDR { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e } - /* The LLDP spec calls this "txFastInit", see 9.2.5.19 */ #define LLDP_TX_FAST_INIT 4U @@ -50,6 +48,12 @@ /* The LLDP spec calls this msgFastTx, but we subtract half the jitter off it. */ #define LLDP_FAST_TX_USEC (1U * USEC_PER_SEC - LLDP_JITTER_USEC / 2) +static const struct ether_addr lldp_multicast_addr[_LLDP_EMIT_MAX] = { + [LLDP_EMIT_NEAREST_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }}, + [LLDP_EMIT_NON_TPMR_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }}, + [LLDP_EMIT_CUSTOMER_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }}, +}; + static int lldp_write_tlv_header(uint8_t **p, uint8_t id, size_t sz) { assert(p); @@ -66,6 +70,7 @@ static int lldp_write_tlv_header(uint8_t **p, uint8_t id, size_t sz) { } static int lldp_make_packet( + LLDPEmit mode, const struct ether_addr *hwaddr, const char *machine_id, const char *ifname, @@ -84,6 +89,8 @@ static int lldp_make_packet( size_t l; int r; + assert(mode > LLDP_EMIT_NO); + assert(mode < _LLDP_EMIT_MAX); assert(hwaddr); assert(machine_id); assert(ifname); @@ -132,7 +139,7 @@ static int lldp_make_packet( h = (struct ether_header*) packet; h->ether_type = htobe16(ETHERTYPE_LLDP); - memcpy(h->ether_dhost, &(struct ether_addr) { LLDP_MULTICAST_ADDR }, ETH_ALEN); + memcpy(h->ether_dhost, lldp_multicast_addr + mode, ETH_ALEN); memcpy(h->ether_shost, hwaddr, ETH_ALEN); p = (uint8_t*) packet + sizeof(struct ether_header); @@ -197,22 +204,28 @@ static int lldp_make_packet( return 0; } -static int lldp_send_packet(int ifindex, const void *packet, size_t packet_size) { +static int lldp_send_packet( + int ifindex, + const struct ether_addr *address, + const void *packet, + size_t packet_size) { union sockaddr_union sa = { .ll.sll_family = AF_PACKET, .ll.sll_protocol = htobe16(ETHERTYPE_LLDP), .ll.sll_ifindex = ifindex, .ll.sll_halen = ETH_ALEN, - .ll.sll_addr = LLDP_MULTICAST_ADDR, }; _cleanup_close_ int fd = -1; ssize_t l; assert(ifindex > 0); + assert(address); assert(packet || packet_size <= 0); + memcpy(sa.ll.sll_addr, address, ETH_ALEN); + fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC, IPPROTO_RAW); if (fd < 0) return -errno; @@ -237,6 +250,13 @@ static int link_send_lldp(Link *link) { usec_t ttl; int r; + assert(link); + + if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO) + return 0; + + assert(link->network->lldp_emit < _LLDP_EMIT_MAX); + r = sd_id128_get_machine(&machine_id); if (r < 0) return r; @@ -251,7 +271,8 @@ static int link_send_lldp(Link *link) { SD_LLDP_SYSTEM_CAPABILITIES_ROUTER : SD_LLDP_SYSTEM_CAPABILITIES_STATION; - r = lldp_make_packet(&link->mac, + r = lldp_make_packet(link->network->lldp_emit, + &link->mac, sd_id128_to_string(machine_id, machine_id_string), link->ifname, (uint16_t) ttl, @@ -264,7 +285,7 @@ static int link_send_lldp(Link *link) { if (r < 0) return r; - return lldp_send_packet(link->ifindex, packet, packet_size); + return lldp_send_packet(link->ifindex, lldp_multicast_addr + link->network->lldp_emit, packet, packet_size); } static int on_lldp_timer(sd_event_source *s, usec_t t, void *userdata) { @@ -300,12 +321,17 @@ static int on_lldp_timer(sd_event_source *s, usec_t t, void *userdata) { return 0; } -int link_lldp_tx_start(Link *link) { +int link_lldp_emit_start(Link *link) { usec_t next; int r; assert(link); + if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO) { + link_lldp_emit_stop(link); + return 0; + } + /* Starts the LLDP transmission in "fast" mode. If it is already started, turns "fast" mode back on again. */ link->lldp_tx_fast = LLDP_TX_FAST_INIT; @@ -313,22 +339,22 @@ int link_lldp_tx_start(Link *link) { next = usec_add(usec_add(now(clock_boottime_or_monotonic()), LLDP_FAST_TX_USEC), (usec_t) random_u64() % LLDP_JITTER_USEC); - if (link->lldp_tx_event_source) { + if (link->lldp_emit_event_source) { usec_t old; /* Lower the timeout, maybe */ - r = sd_event_source_get_time(link->lldp_tx_event_source, &old); + r = sd_event_source_get_time(link->lldp_emit_event_source, &old); if (r < 0) return r; if (old <= next) return 0; - return sd_event_source_set_time(link->lldp_tx_event_source, next); + return sd_event_source_set_time(link->lldp_emit_event_source, next); } else { r = sd_event_add_time( link->manager->event, - &link->lldp_tx_event_source, + &link->lldp_emit_event_source, clock_boottime_or_monotonic(), next, 0, @@ -337,14 +363,54 @@ int link_lldp_tx_start(Link *link) { if (r < 0) return r; - (void) sd_event_source_set_description(link->lldp_tx_event_source, "lldp-tx"); + (void) sd_event_source_set_description(link->lldp_emit_event_source, "lldp-tx"); } return 0; } -void link_lldp_tx_stop(Link *link) { +void link_lldp_emit_stop(Link *link) { assert(link); - link->lldp_tx_event_source = sd_event_source_unref(link->lldp_tx_event_source); + link->lldp_emit_event_source = sd_event_source_unref(link->lldp_emit_event_source); +} + +int config_parse_lldp_emit( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + LLDPEmit *emit = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) + *emit = LLDP_EMIT_NO; + else if (streq(rvalue, "nearest-bridge")) + *emit = LLDP_EMIT_NEAREST_BRIDGE; + else if (streq(rvalue, "non-tpmr-bridge")) + *emit = LLDP_EMIT_NON_TPMR_BRIDGE; + else if (streq(rvalue, "customer-bridge")) + *emit = LLDP_EMIT_CUSTOMER_BRIDGE; + else { + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse LLDP emission setting, ignoring: %s", rvalue); + return 0; + } + + *emit = r ? LLDP_EMIT_NEAREST_BRIDGE : LLDP_EMIT_NO; + } + + return 0; } diff --git a/src/network/networkd-lldp-tx.h b/src/network/networkd-lldp-tx.h index 8c7f403005..4680c9d950 100644 --- a/src/network/networkd-lldp-tx.h +++ b/src/network/networkd-lldp-tx.h @@ -21,5 +21,15 @@ #include "networkd-link.h" -int link_lldp_tx_start(Link *link); -void link_lldp_tx_stop(Link *link); +typedef enum LLDPEmit { + LLDP_EMIT_NO, + LLDP_EMIT_NEAREST_BRIDGE, + LLDP_EMIT_NON_TPMR_BRIDGE, + LLDP_EMIT_CUSTOMER_BRIDGE, + _LLDP_EMIT_MAX, +} LLDPEmit; + +int link_lldp_emit_start(Link *link); +void link_lldp_emit_stop(Link *link); + +int config_parse_lldp_emit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/network/networkd-netdev-bridge.c b/src/network/networkd-netdev-bridge.c index 3e44dd7960..4cfd00413f 100644 --- a/src/network/networkd-netdev-bridge.c +++ b/src/network/networkd-netdev-bridge.c @@ -96,6 +96,12 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_mess return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MCAST_QUERIER attribute: %m"); } + if (b->mcast_snooping >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BR_MCAST_SNOOPING, b->mcast_snooping); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MCAST_SNOOPING attribute: %m"); + } + r = sd_netlink_message_close_container(req); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); @@ -121,6 +127,7 @@ static void bridge_init(NetDev *n) { assert(b); b->mcast_querier = -1; + b->mcast_snooping = -1; } const NetDevVTable bridge_vtable = { diff --git a/src/network/networkd-netdev-bridge.h b/src/network/networkd-netdev-bridge.h index b921439f02..f2ae21fc50 100644 --- a/src/network/networkd-netdev-bridge.h +++ b/src/network/networkd-netdev-bridge.h @@ -25,6 +25,7 @@ typedef struct Bridge { NetDev meta; int mcast_querier; + int mcast_snooping; usec_t forward_delay; usec_t hello_time; diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf index 1ebd0fdf20..ba04bb0165 100644 --- a/src/network/networkd-netdev-gperf.gperf +++ b/src/network/networkd-netdev-gperf.gperf @@ -99,3 +99,4 @@ Bridge.HelloTimeSec, config_parse_sec, 0, Bridge.MaxAgeSec, config_parse_sec, 0, offsetof(Bridge, max_age) Bridge.ForwardDelaySec, config_parse_sec, 0, offsetof(Bridge, forward_delay) Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier) +Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping) diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c index d7d014f05d..851a36290c 100644 --- a/src/network/networkd-netdev.c +++ b/src/network/networkd-netdev.c @@ -656,7 +656,7 @@ static int netdev_load_one(Manager *manager, const char *filename) { if (!netdev->filename) return log_oom(); - if (!netdev->mac) { + if (!netdev->mac && netdev->kind != NETDEV_KIND_VLAN) { r = netdev_get_mac(netdev->ifname, &netdev->mac); if (r < 0) return log_error_errno(r, "Failed to generate predictable MAC address for %s: %m", netdev->ifname); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 51e750b299..4425ee4e2f 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -27,7 +27,6 @@ Match.KernelCommandLine, config_parse_net_condition, Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, match_arch) Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac) Link.MTUBytes, config_parse_iec_size, 0, offsetof(Network, mtu) -Link.IAID, config_parse_iaid, 0, offsetof(Network, iaid) Network.Description, config_parse_string, 0, offsetof(Network, description) Network.Bridge, config_parse_netdev, 0, offsetof(Network, bridge) Network.Bond, config_parse_netdev, 0, offsetof(Network, bond) @@ -43,7 +42,7 @@ Network.LinkLocalAddressing, config_parse_address_family_boolean, Network.IPv4LLRoute, config_parse_bool, 0, offsetof(Network, ipv4ll_route) Network.IPv6Token, config_parse_ipv6token, 0, offsetof(Network, ipv6_token) Network.LLDP, config_parse_lldp_mode, 0, offsetof(Network, lldp_mode) -Network.EmitLLDP, config_parse_bool, 0, offsetof(Network, lldp_emit) +Network.EmitLLDP, config_parse_lldp_emit, 0, offsetof(Network, lldp_emit) Network.Address, config_parse_address, 0, 0 Network.Gateway, config_parse_gateway, 0, 0 Network.Domains, config_parse_domains, 0, 0 @@ -89,6 +88,7 @@ DHCP.DUIDType, config_parse_duid_type, DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid) DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric) DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone) +DHCP.IAID, config_parse_iaid, 0, offsetof(Network, iaid) DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec) DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec) DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns) diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index ff2414efdd..4cd0fa4ab8 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -29,6 +29,7 @@ #include "networkd-address.h" #include "networkd-fdb.h" +#include "networkd-lldp-tx.h" #include "networkd-netdev.h" #include "networkd-route.h" #include "networkd-util.h" @@ -161,7 +162,7 @@ struct Network { DUID duid; LLDPMode lldp_mode; /* LLDP reception */ - bool lldp_emit; /* LLDP transmission */ + LLDPEmit lldp_emit; /* LLDP transmission */ LIST_HEAD(Address, static_addresses); LIST_HEAD(Route, static_routes); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 01094b20bd..43f37266d8 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -512,7 +512,7 @@ int route_configure(Route *route, Link *link, if (r < 0) return log_error_errno(r, "Could not set route table: %m"); - /* Table attribute to allow allow more than 256. */ + /* Table attribute to allow more than 256. */ r = sd_netlink_message_append_data(req, RTA_TABLE, &route->table, sizeof(route->table)); if (r < 0) return log_error_errno(r, "Could not append RTA_TABLE attribute: %m"); diff --git a/src/network/networkd.h b/src/network/networkd.h index 26d9e7d6e0..ab512f0d08 100644 --- a/src/network/networkd.h +++ b/src/network/networkd.h @@ -41,7 +41,6 @@ #include "networkd-netdev-tuntap.h" #include "networkd-netdev-veth.h" #include "networkd-netdev-vlan.h" -#include "networkd-netdev-vlan.h" #include "networkd-netdev-vxlan.h" #include "networkd-network.h" #include "networkd-util.h" diff --git a/src/network/test-networkd-conf.c b/src/network/test-networkd-conf.c index 8a62a2a567..9bd30b82c6 100644 --- a/src/network/test-networkd-conf.c +++ b/src/network/test-networkd-conf.c @@ -47,10 +47,12 @@ static void test_config_parse_duid_type(void) { static void test_config_parse_duid_rawdata_one(const char *rvalue, int ret, const DUID* expected) { DUID actual = {}; int r; + _cleanup_free_ char *d = NULL; r = config_parse_duid_rawdata("network", "filename", 1, "section", 1, "lvalue", 0, rvalue, &actual, NULL); + d = hexmem(actual.raw_data, actual.raw_data_len); log_info_errno(r, "\"%s\" → \"%s\" (%m)", - rvalue, strnull(hexmem(actual.raw_data, actual.raw_data_len))); + rvalue, strnull(d)); assert_se(r == ret); if (expected) { assert_se(actual.raw_data_len == expected->raw_data_len); diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf index 34e1310e29..2b5d452662 100644 --- a/src/nspawn/nspawn-gperf.gperf +++ b/src/nspawn/nspawn-gperf.gperf @@ -39,5 +39,6 @@ Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, Network.IPVLAN, config_parse_strv, 0, offsetof(Settings, network_ipvlan) Network.VirtualEthernet, config_parse_tristate, 0, offsetof(Settings, network_veth) Network.VirtualEthernetExtra, config_parse_veth_extra, 0, 0 -Network.Bridge, config_parse_string, 0, offsetof(Settings, network_bridge) +Network.Bridge, config_parse_ifname, 0, offsetof(Settings, network_bridge) +Network.Zone, config_parse_network_zone, 0, 0 Network.Port, config_parse_expose_port, 0, 0 diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c index f2b7e4dd79..8da47a2ca6 100644 --- a/src/nspawn/nspawn-network.c +++ b/src/nspawn/nspawn-network.c @@ -26,9 +26,12 @@ #include "alloc-util.h" #include "ether-addr-util.h" +#include "lockfile-util.h" #include "netlink-util.h" #include "nspawn-network.h" #include "siphash24.h" +#include "socket-util.h" +#include "stat-util.h" #include "string-util.h" #include "udev-util.h" #include "util.h" @@ -39,6 +42,30 @@ #define VETH_EXTRA_CONTAINER_HASH_KEY SD_ID128_MAKE(af,50,17,61,ce,f9,4d,35,84,0d,2b,20,54,be,ce,59) #define MACVLAN_HASH_KEY SD_ID128_MAKE(00,13,6d,bc,66,83,44,81,bb,0c,f9,51,1f,24,a6,6f) +static int remove_one_link(sd_netlink *rtnl, const char *name) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r; + + if (isempty(name)) + return 0; + + r = sd_rtnl_message_new_link(rtnl, &m, RTM_DELLINK, 0); + if (r < 0) + return log_error_errno(r, "Failed to allocate netlink message: %m"); + + r = sd_netlink_message_append_string(m, IFLA_IFNAME, name); + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface name: %m"); + + r = sd_netlink_call(rtnl, m, 0, NULL); + if (r == -ENODEV) /* Already gone */ + return 0; + if (r < 0) + return log_error_errno(r, "Failed to remove interface %s: %m", name); + + return 1; +} + static int generate_mac( const char *machine_name, struct ether_addr *mac, @@ -238,45 +265,149 @@ int setup_veth_extra( return 0; } -int setup_bridge(const char *veth_name, const char *bridge_name) { +static int join_bridge(sd_netlink *rtnl, const char *veth_name, const char *bridge_name) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; int r, bridge_ifi; + assert(rtnl); assert(veth_name); assert(bridge_name); bridge_ifi = (int) if_nametoindex(bridge_name); if (bridge_ifi <= 0) - return log_error_errno(errno, "Failed to resolve interface %s: %m", bridge_name); - - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); + return -errno; r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, 0); if (r < 0) - return log_error_errno(r, "Failed to allocate netlink message: %m"); + return r; r = sd_rtnl_message_link_set_flags(m, IFF_UP, IFF_UP); if (r < 0) - return log_error_errno(r, "Failed to set IFF_UP flag: %m"); + return r; r = sd_netlink_message_append_string(m, IFLA_IFNAME, veth_name); if (r < 0) - return log_error_errno(r, "Failed to add netlink interface name field: %m"); + return r; r = sd_netlink_message_append_u32(m, IFLA_MASTER, bridge_ifi); if (r < 0) - return log_error_errno(r, "Failed to add netlink master field: %m"); + return r; r = sd_netlink_call(rtnl, m, 0, NULL); if (r < 0) - return log_error_errno(r, "Failed to add veth interface to bridge: %m"); + return r; return bridge_ifi; } +static int create_bridge(sd_netlink *rtnl, const char *bridge_name) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r; + + r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0); + if (r < 0) + return r; + + r = sd_netlink_message_append_string(m, IFLA_IFNAME, bridge_name); + if (r < 0) + return r; + + r = sd_netlink_message_open_container(m, IFLA_LINKINFO); + if (r < 0) + return r; + + r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "bridge"); + if (r < 0) + return r; + + r = sd_netlink_message_close_container(m); + if (r < 0) + return r; + + r = sd_netlink_message_close_container(m); + if (r < 0) + return r; + + r = sd_netlink_call(rtnl, m, 0, NULL); + if (r < 0) + return r; + + return 0; +} + +int setup_bridge(const char *veth_name, const char *bridge_name, bool create) { + _cleanup_release_lock_file_ LockFile bridge_lock = LOCK_FILE_INIT; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + int r, bridge_ifi; + unsigned n = 0; + + assert(veth_name); + assert(bridge_name); + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + if (create) { + /* We take a system-wide lock here, so that we can safely check whether there's still a member in the + * bridge before removing it, without risking interferance from other nspawn instances. */ + + r = make_lock_file("/run/systemd/nspawn-network-zone", LOCK_EX, &bridge_lock); + if (r < 0) + return log_error_errno(r, "Failed to take network zone lock: %m"); + } + + for (;;) { + bridge_ifi = join_bridge(rtnl, veth_name, bridge_name); + if (bridge_ifi >= 0) + return bridge_ifi; + if (bridge_ifi != -ENODEV || !create || n > 10) + return log_error_errno(bridge_ifi, "Failed to add interface %s to bridge %s: %m", veth_name, bridge_name); + + /* Count attempts, so that we don't enter an endless loop here. */ + n++; + + /* The bridge doesn't exist yet. Let's create it */ + r = create_bridge(rtnl, bridge_name); + if (r < 0) + return log_error_errno(r, "Failed to create bridge interface %s: %m", bridge_name); + + /* Try again, now that the bridge exists */ + } +} + +int remove_bridge(const char *bridge_name) { + _cleanup_release_lock_file_ LockFile bridge_lock = LOCK_FILE_INIT; + _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; + const char *path; + int r; + + /* Removes the specified bridge, but only if it is currently empty */ + + if (isempty(bridge_name)) + return 0; + + r = make_lock_file("/run/systemd/nspawn-network-zone", LOCK_EX, &bridge_lock); + if (r < 0) + return log_error_errno(r, "Failed to take network zone lock: %m"); + + path = strjoina("/sys/class/net/", bridge_name, "/brif"); + + r = dir_is_empty(path); + if (r == -ENOENT) /* Already gone? */ + return 0; + if (r < 0) + return log_error_errno(r, "Can't detect if bridge %s is empty: %m", bridge_name); + if (r == 0) /* Still populated, leave it around */ + return 0; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + return remove_one_link(rtnl, bridge_name); +} + static int parse_interface(struct udev *udev, const char *name) { _cleanup_udev_device_unref_ struct udev_device *d = NULL; char ifi_str[2 + DECIMAL_STR_MAX(int)]; @@ -515,13 +646,13 @@ int veth_extra_parse(char ***l, const char *p) { r = extract_first_word(&p, &a, ":", EXTRACT_DONT_COALESCE_SEPARATORS); if (r < 0) return r; - if (r == 0 || isempty(a)) + if (r == 0 || !ifname_valid(a)) return -EINVAL; r = extract_first_word(&p, &b, ":", EXTRACT_DONT_COALESCE_SEPARATORS); if (r < 0) return r; - if (r == 0 || isempty(b)) { + if (r == 0 || !ifname_valid(b)) { free(b); b = strdup(a); if (!b) @@ -539,30 +670,6 @@ int veth_extra_parse(char ***l, const char *p) { return 0; } -static int remove_one_veth_link(sd_netlink *rtnl, const char *name) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; - int r; - - if (isempty(name)) - return 0; - - r = sd_rtnl_message_new_link(rtnl, &m, RTM_DELLINK, 0); - if (r < 0) - return log_error_errno(r, "Failed to allocate netlink message: %m"); - - r = sd_netlink_message_append_string(m, IFLA_IFNAME, name); - if (r < 0) - return log_error_errno(r, "Failed to add netlink interface name: %m"); - - r = sd_netlink_call(rtnl, m, 0, NULL); - if (r == -ENODEV) /* Already gone */ - return 0; - if (r < 0) - return log_error_errno(r, "Failed to remove veth interface %s: %m", name); - - return 1; -} - int remove_veth_links(const char *primary, char **pairs) { _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; char **a, **b; @@ -578,10 +685,10 @@ int remove_veth_links(const char *primary, char **pairs) { if (r < 0) return log_error_errno(r, "Failed to connect to netlink: %m"); - remove_one_veth_link(rtnl, primary); + remove_one_link(rtnl, primary); STRV_FOREACH_PAIR(a, b, pairs) - remove_one_veth_link(rtnl, *a); + remove_one_link(rtnl, *a); return 0; } diff --git a/src/nspawn/nspawn-network.h b/src/nspawn/nspawn-network.h index c5036ab470..3d8861e1e5 100644 --- a/src/nspawn/nspawn-network.h +++ b/src/nspawn/nspawn-network.h @@ -26,7 +26,8 @@ int setup_veth(const char *machine_name, pid_t pid, char iface_name[IFNAMSIZ], bool bridge); int setup_veth_extra(const char *machine_name, pid_t pid, char **pairs); -int setup_bridge(const char *veth_name, const char *bridge_name); +int setup_bridge(const char *veth_name, const char *bridge_name, bool create); +int remove_bridge(const char *bridge_name); int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces); int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces); diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c index b98a79fd09..5f1522cfb6 100644 --- a/src/nspawn/nspawn-settings.c +++ b/src/nspawn/nspawn-settings.c @@ -24,10 +24,11 @@ #include "nspawn-settings.h" #include "parse-util.h" #include "process-util.h" +#include "socket-util.h" +#include "string-util.h" #include "strv.h" #include "user-util.h" #include "util.h" -#include "string-util.h" int settings_load(FILE *f, const char *path, Settings **ret) { _cleanup_(settings_freep) Settings *s = NULL; @@ -96,6 +97,7 @@ Settings* settings_free(Settings *s) { strv_free(s->network_ipvlan); strv_free(s->network_veth_extra); free(s->network_bridge); + free(s->network_zone); expose_port_free_all(s->expose_ports); custom_mount_free_all(s->custom_mounts, s->n_custom_mounts); @@ -111,6 +113,7 @@ bool settings_private_network(Settings *s) { s->private_network > 0 || s->network_veth > 0 || s->network_bridge || + s->network_zone || s->network_interfaces || s->network_macvlan || s->network_ipvlan || @@ -122,7 +125,8 @@ bool settings_network_veth(Settings *s) { return s->network_veth > 0 || - s->network_bridge; + s->network_bridge || + s->network_zone; } DEFINE_CONFIG_PARSE_ENUM(config_parse_volatile_mode, volatile_mode, VolatileMode, "Failed to parse volatile mode"); @@ -319,6 +323,38 @@ int config_parse_veth_extra( return 0; } +int config_parse_network_zone( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Settings *settings = data; + _cleanup_free_ char *j = NULL; + + assert(filename); + assert(lvalue); + assert(rvalue); + + j = strappend("vz-", rvalue); + if (!ifname_valid(j)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid network zone name %s, ignoring: %m", rvalue); + return 0; + } + + free(settings->network_zone); + settings->network_zone = j; + j = NULL; + + return 0; +} + int config_parse_boot( const char *unit, const char *filename, diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index e12e91b886..1c47e37912 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -85,6 +85,7 @@ typedef struct Settings { int private_network; int network_veth; char *network_bridge; + char *network_zone; char **network_interfaces; char **network_macvlan; char **network_ipvlan; @@ -109,6 +110,7 @@ int config_parse_volatile_mode(const char *unit, const char *filename, unsigned int config_parse_bind(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_tmpfs(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_veth_extra(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_network_zone(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_boot(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_pid2(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_private_users(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 3fc6cc955c..8ec058431b 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -176,6 +176,7 @@ static char **arg_network_ipvlan = NULL; static bool arg_network_veth = false; static char **arg_network_veth_extra = NULL; static char *arg_network_bridge = NULL; +static char *arg_network_zone = NULL; static unsigned long arg_personality = PERSONALITY_INVALID; static char *arg_image = NULL; static VolatileMode arg_volatile_mode = VOLATILE_NO; @@ -234,6 +235,8 @@ static void help(void) { " Add a virtual Ethernet connection between host\n" " and container and add it to an existing bridge on\n" " the host\n" + " --network-zone=NAME Add a virtual Ethernet connection to the container,\n" + " and add it to an automatically managed bridge interface\n" " -p --port=[PROTOCOL:]HOSTPORT[:CONTAINERPORT]\n" " Expose a container IP port on the host\n" " -Z --selinux-context=SECLABEL\n" @@ -357,6 +360,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_NETWORK_MACVLAN, ARG_NETWORK_IPVLAN, ARG_NETWORK_BRIDGE, + ARG_NETWORK_ZONE, ARG_NETWORK_VETH_EXTRA, ARG_PERSONALITY, ARG_VOLATILE, @@ -404,6 +408,7 @@ static int parse_argv(int argc, char *argv[]) { { "network-veth", no_argument, NULL, 'n' }, { "network-veth-extra", required_argument, NULL, ARG_NETWORK_VETH_EXTRA}, { "network-bridge", required_argument, NULL, ARG_NETWORK_BRIDGE }, + { "network-zone", required_argument, NULL, ARG_NETWORK_ZONE }, { "personality", required_argument, NULL, ARG_PERSONALITY }, { "image", required_argument, NULL, 'i' }, { "volatile", optional_argument, NULL, ARG_VOLATILE }, @@ -466,7 +471,35 @@ static int parse_argv(int argc, char *argv[]) { arg_settings_mask |= SETTING_USER; break; + case ARG_NETWORK_ZONE: { + char *j; + + j = strappend("vz-", optarg); + if (!j) + return log_oom(); + + if (!ifname_valid(j)) { + log_error("Network zone name not valid: %s", j); + free(j); + return -EINVAL; + } + + free(arg_network_zone); + arg_network_zone = j; + + arg_network_veth = true; + arg_private_network = true; + arg_settings_mask |= SETTING_NETWORK; + break; + } + case ARG_NETWORK_BRIDGE: + + if (!ifname_valid(optarg)) { + log_error("Bridge interface name not valid: %s", optarg); + return -EINVAL; + } + r = free_and_strdup(&arg_network_bridge, optarg); if (r < 0) return log_oom(); @@ -489,6 +522,12 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_NETWORK_INTERFACE: + + if (!ifname_valid(optarg)) { + log_error("Network interface name not valid: %s", optarg); + return -EINVAL; + } + if (strv_extend(&arg_network_interfaces, optarg) < 0) return log_oom(); @@ -497,6 +536,12 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_NETWORK_MACVLAN: + + if (!ifname_valid(optarg)) { + log_error("MACVLAN network interface name not valid: %s", optarg); + return -EINVAL; + } + if (strv_extend(&arg_network_macvlan, optarg) < 0) return log_oom(); @@ -505,6 +550,12 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_NETWORK_IPVLAN: + + if (!ifname_valid(optarg)) { + log_error("IPVLAN network interface name not valid: %s", optarg); + return -EINVAL; + } + if (strv_extend(&arg_network_ipvlan, optarg) < 0) return log_oom(); @@ -1003,6 +1054,11 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } + if (arg_network_bridge && arg_network_zone) { + log_error("--network-bridge= and --network-zone= may not be combined."); + return -EINVAL; + } + if (argc > optind) { arg_parameters = strv_copy(argv + optind); if (!arg_parameters) @@ -1657,7 +1713,6 @@ static int setup_seccomp(void) { } } - /* Audit is broken in containers, much of the userspace audit hookup will fail if running inside a container. We don't @@ -3271,6 +3326,7 @@ static int load_settings(void) { (settings->private_network >= 0 || settings->network_veth >= 0 || settings->network_bridge || + settings->network_zone || settings->network_interfaces || settings->network_macvlan || settings->network_ipvlan || @@ -3301,6 +3357,10 @@ static int load_settings(void) { free(arg_network_bridge); arg_network_bridge = settings->network_bridge; settings->network_bridge = NULL; + + free(arg_network_zone); + arg_network_zone = settings->network_zone; + settings->network_zone = NULL; } } @@ -3346,7 +3406,7 @@ int main(int argc, char *argv[]) { int ret = EXIT_SUCCESS; union in_addr_union exposed = {}; _cleanup_release_lock_file_ LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT; - bool interactive; + bool interactive, veth_created = false; log_parse_environment(); log_open(); @@ -3800,14 +3860,23 @@ int main(int argc, char *argv[]) { goto finish; if (arg_network_veth) { - r = setup_veth(arg_machine, pid, veth_name, !!arg_network_bridge); + r = setup_veth(arg_machine, pid, veth_name, + arg_network_bridge || arg_network_zone); if (r < 0) goto finish; else if (r > 0) ifi = r; if (arg_network_bridge) { - r = setup_bridge(veth_name, arg_network_bridge); + /* Add the interface to a bridge */ + r = setup_bridge(veth_name, arg_network_bridge, false); + if (r < 0) + goto finish; + if (r > 0) + ifi = r; + } else if (arg_network_zone) { + /* Add the interface to a bridge, possibly creating it */ + r = setup_bridge(veth_name, arg_network_zone, true); if (r < 0) goto finish; if (r > 0) @@ -3819,6 +3888,12 @@ int main(int argc, char *argv[]) { if (r < 0) goto finish; + /* We created the primary and extra veth links now; let's remember this, so that we know to + remove them later on. Note that we don't bother with removing veth links that were created + here when their setup failed half-way, because in that case the kernel should be able to + remove them on its own, since they cannot be referenced by anything yet. */ + veth_created = true; + r = setup_macvlan(arg_machine, pid, arg_network_macvlan); if (r < 0) goto finish; @@ -3981,7 +4056,9 @@ int main(int argc, char *argv[]) { } expose_port_flush(arg_expose_ports, &exposed); + (void) remove_veth_links(veth_name, arg_network_veth_extra); + veth_created = false; } finish: @@ -4014,7 +4091,10 @@ finish: } expose_port_flush(arg_expose_ports, &exposed); - (void) remove_veth_links(veth_name, arg_network_veth_extra); + + if (veth_created) + (void) remove_veth_links(veth_name, arg_network_veth_extra); + (void) remove_bridge(arg_network_zone); free(arg_directory); free(arg_template); diff --git a/src/reply-password/reply-password.c b/src/reply-password/reply-password.c index e291758969..17eab9772e 100644 --- a/src/reply-password/reply-password.c +++ b/src/reply-password/reply-password.c @@ -26,14 +26,12 @@ #include "fd-util.h" #include "log.h" #include "macro.h" +#include "socket-util.h" #include "string-util.h" #include "util.h" static int send_on_socket(int fd, const char *socket_name, const void *packet, size_t size) { - union { - struct sockaddr sa; - struct sockaddr_un un; - } sa = { + union sockaddr_union sa = { .un.sun_family = AF_UNIX, }; @@ -43,7 +41,7 @@ static int send_on_socket(int fd, const char *socket_name, const void *packet, s strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path)); - if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) + if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) return log_error_errno(errno, "Failed to send: %m"); return 0; diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index 6805873f9e..4a4bd8d3b8 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -431,7 +431,7 @@ static int create_socket(char **name) { snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%" PRIx64, random_u64()); RUN_WITH_UMASK(0177) { - if (bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) + if (bind(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) return -errno; } diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 94d1c1d63c..8f0df84793 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -221,7 +221,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) { uint64_t u; - r = cg_cpu_shares_parse(eq, &u); + r = cg_blkio_weight_parse(eq, &u); if (r < 0) { log_error("Failed to parse %s value %s.", field, eq); return -EINVAL; @@ -347,7 +347,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen log_error("Failed to parse %s value %s.", field, weight); return -EINVAL; } - r = sd_bus_message_append(m, "v", "a(st)", path, u); + r = sd_bus_message_append(m, "v", "a(st)", 1, path, u); } } else if (streq(field, "Nice")) { @@ -1088,7 +1088,7 @@ static int dump_processes( } more = i+1 < n || cg->children; - special = draw_special_char(more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT); + special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT); fprintf(stdout, "%s%s%*"PID_PRI" %s\n", prefix, @@ -1124,14 +1124,14 @@ static int dump_processes( name++; more = i+1 < n; - special = draw_special_char(more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT); + special = special_glyph(more ? TREE_BRANCH : TREE_RIGHT); fputs(prefix, stdout); fputs(special, stdout); fputs(name, stdout); fputc('\n', stdout); - special = draw_special_char(more ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE); + special = special_glyph(more ? TREE_VERTICAL : TREE_SPACE); pp = strappend(prefix, special); if (!pp) @@ -1215,7 +1215,7 @@ static int dump_extra_processes( fprintf(stdout, "%s%s %*" PID_PRI " %s\n", prefix, - draw_special_char(DRAW_TRIANGULAR_BULLET), + special_glyph(TRIANGULAR_BULLET), width, pids[k], name); } diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c index 7539891bf2..3e451db715 100644 --- a/src/shared/cgroup-show.c +++ b/src/shared/cgroup-show.c @@ -76,9 +76,9 @@ static void show_pid_array( get_process_cmdline(pids[i], n_columns, true, &t); if (extra) - printf("%s%s ", prefix, draw_special_char(DRAW_TRIANGULAR_BULLET)); + printf("%s%s ", prefix, special_glyph(TRIANGULAR_BULLET)); else - printf("%s%s", prefix, draw_special_char(((more || i < n_pids-1) ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT))); + printf("%s%s", prefix, special_glyph(((more || i < n_pids-1) ? TREE_BRANCH : TREE_RIGHT))); printf("%*"PID_PRI" %s\n", pid_width, pids[i], strna(t)); } @@ -172,10 +172,10 @@ int show_cgroup_by_path( } if (last) { - printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_BRANCH), cg_unescape(basename(last))); + printf("%s%s%s\n", prefix, special_glyph(TREE_BRANCH), cg_unescape(basename(last))); if (!p1) { - p1 = strappend(prefix, draw_special_char(DRAW_TREE_VERTICAL)); + p1 = strappend(prefix, special_glyph(TREE_VERTICAL)); if (!p1) return -ENOMEM; } @@ -195,7 +195,7 @@ int show_cgroup_by_path( show_cgroup_one_by_path(path, prefix, n_columns, !!last, flags); if (last) { - printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_RIGHT), cg_unescape(basename(last))); + printf("%s%s%s\n", prefix, special_glyph(TREE_RIGHT), cg_unescape(basename(last))); if (!p2) { p2 = strappend(prefix, " "); diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index 1141f9964f..83be79a4f5 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -37,6 +37,7 @@ #include "path-util.h" #include "process-util.h" #include "signal-util.h" +#include "socket-util.h" #include "string-util.h" #include "strv.h" #include "syslog-util.h" @@ -873,3 +874,40 @@ int config_parse_personality( *personality = p; return 0; } + +int config_parse_ifname( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + char **s = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + *s = mfree(*s); + return 0; + } + + if (!ifname_valid(rvalue)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue); + return 0; + } + + r = free_and_strdup(s, rvalue); + if (r < 0) + return log_oom(); + + return 0; +} diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index 73fb132413..f6964e3fd4 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -125,6 +125,7 @@ int config_parse_log_facility(const char *unit, const char *filename, unsigned l int config_parse_log_level(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_signal(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_personality(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_ifname(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); #define DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg) \ int function(const char *unit, \ diff --git a/src/shared/firewall-util.c b/src/shared/firewall-util.c index ade2de7727..97865eac4a 100644 --- a/src/shared/firewall-util.c +++ b/src/shared/firewall-util.c @@ -44,6 +44,7 @@ #include "firewall-util.h" #include "in-addr-util.h" #include "macro.h" +#include "socket-util.h" DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle*, iptc_free); @@ -59,10 +60,9 @@ static int entry_fill_basics( assert(entry); - if (out_interface && strlen(out_interface) >= IFNAMSIZ) + if (out_interface && !ifname_valid(out_interface)) return -EINVAL; - - if (in_interface && strlen(in_interface) >= IFNAMSIZ) + if (in_interface && !ifname_valid(in_interface)) return -EINVAL; entry->ip.proto = protocol; diff --git a/src/shared/generator.c b/src/shared/generator.c index cd3c35cd55..70afc6a285 100644 --- a/src/shared/generator.c +++ b/src/shared/generator.c @@ -65,7 +65,7 @@ static int write_fsck_sysroot_service(const char *dir, const char *what) { "Description=File System Check on %2$s\n" "DefaultDependencies=no\n" "BindsTo=%3$s\n" - "After=%3$s local-fs-pre.target\n" + "After=initrd-root-device.target local-fs-pre.target\n" "Before=shutdown.target\n" "\n" "[Service]\n" @@ -191,3 +191,17 @@ int generator_write_timeouts( "[Unit]\nJobTimeoutSec=%s", program_invocation_short_name, timeout); } + +int generator_write_initrd_root_device_deps(const char *dir, const char *what) { + _cleanup_free_ char *unit = NULL; + int r; + + r = unit_name_from_path(what, ".device", &unit); + if (r < 0) + return log_error_errno(r, "Failed to make unit name from path: %m"); + + return write_drop_in_format(dir, SPECIAL_INITRD_ROOT_DEVICE_TARGET, 50, "root-device", + "# Automatically generated by %s\n\n" + "[Unit]\nRequires=%s\nAfter=%s", + program_invocation_short_name, unit, unit); +} diff --git a/src/shared/generator.h b/src/shared/generator.h index a734e13970..a6017c1b76 100644 --- a/src/shared/generator.h +++ b/src/shared/generator.h @@ -34,3 +34,7 @@ int generator_write_timeouts( const char *where, const char *opts, char **filtered); + +int generator_write_initrd_root_device_deps( + const char *dir, + const char *what); diff --git a/src/shared/install.c b/src/shared/install.c index cc36da1853..64d66a45d3 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -40,6 +40,7 @@ #include "hashmap.h" #include "install-printf.h" #include "install.h" +#include "locale-util.h" #include "log.h" #include "macro.h" #include "mkdir.h" @@ -116,6 +117,14 @@ bool unit_type_may_template(UnitType type) { UNIT_PATH); } +static const char *unit_file_type_table[_UNIT_FILE_TYPE_MAX] = { + [UNIT_FILE_TYPE_REGULAR] = "regular", + [UNIT_FILE_TYPE_SYMLINK] = "symlink", + [UNIT_FILE_TYPE_MASKED] = "masked", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(unit_file_type, UnitFileType); + static int in_search_path(const LookupPaths *p, const char *path) { _cleanup_free_ char *parent = NULL; char **i; @@ -328,7 +337,10 @@ void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *chang switch(changes[i].type) { case UNIT_FILE_SYMLINK: if (!quiet) - log_info("Created symlink %s, pointing to %s.", changes[i].path, changes[i].source); + log_info("Created symlink %s %s %s.", + changes[i].path, + special_glyph(ARROW), + changes[i].source); break; case UNIT_FILE_UNLINK: if (!quiet) @@ -338,6 +350,11 @@ void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *chang if (!quiet) log_info("Unit %s is masked, ignoring.", changes[i].path); break; + case UNIT_FILE_IS_DANGLING: + if (!quiet) + log_info("Unit %s is an alias to a unit that is not present, ignoring.", + changes[i].path); + break; case -EEXIST: if (changes[i].source) log_error_errno(changes[i].type, @@ -376,8 +393,6 @@ void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *chang log_error_errno(r, "Failed to %s: %m.", verb); } - - static int create_symlink( const char *old_path, const char *new_path, @@ -393,7 +408,11 @@ static int create_symlink( /* Actually create a symlink, and remember that we did. Is * smart enough to check if there's already a valid symlink in - * place. */ + * place. + * + * Returns 1 if a symlink was created or already exists and points to + * the right place, or negative on error. + */ mkdir_parents_label(new_path, 0755); @@ -418,7 +437,7 @@ static int create_symlink( } if (path_equal(dest, old_path)) - return 0; + return 1; if (!force) { unit_file_changes_add(changes, n_changes, -EEXIST, new_path, dest); @@ -1223,6 +1242,7 @@ static int unit_file_search( const LookupPaths *paths, SearchFlags flags) { + _cleanup_free_ char *template = NULL; char **p; int r; @@ -1247,24 +1267,19 @@ static int unit_file_search( return -ENOMEM; r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags); - if (r < 0) { - if (r != -ENOENT) - return r; - } else { + if (r >= 0) { info->path = path; path = NULL; return r; - } + } else if (r != -ENOENT) + return r; } if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { - /* Unit file doesn't exist, however instance * enablement was requested. We will check if it is * possible to load template unit file. */ - _cleanup_free_ char *template = NULL; - r = unit_name_template(info->name, &template); if (r < 0) return r; @@ -1277,17 +1292,16 @@ static int unit_file_search( return -ENOMEM; r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags); - if (r < 0) { - if (r != -ENOENT) - return r; - } else { + if (r >= 0) { info->path = path; path = NULL; return r; - } + } else if (r != -ENOENT) + return r; } } + log_debug("Cannot find unit %s%s%s.", info->name, template ? " or " : "", strempty(template)); return -ENOENT; } @@ -1319,6 +1333,11 @@ static int install_info_follow( return unit_file_load_or_readlink(c, i, i->path, root_dir, flags); } +/** + * Search for the unit file. If the unit name is a symlink, + * follow the symlink to the target, maybe more than once. + * Propagate the instance name if present. + */ static int install_info_traverse( UnitFileScope scope, InstallContext *c, @@ -1355,13 +1374,10 @@ static int install_info_traverse( } r = install_info_follow(c, i, paths->root_dir, flags); - if (r < 0) { + if (r == -EXDEV) { _cleanup_free_ char *buffer = NULL; const char *bn; - if (r != -EXDEV) - return r; - /* Target has a different name, create a new * install info object for that, and continue * with that. */ @@ -1388,12 +1404,15 @@ static int install_info_traverse( if (r < 0) return r; + /* Try again, with the new target we found. */ r = unit_file_search(c, i, paths, flags); - if (r < 0) - return r; + if (r == -ENOENT) + /* Translate error code to highlight this specific case */ + return -ENOLINK; } - /* Try again, with the new target we found. */ + if (r < 0) + return r; } if (ret) @@ -1689,11 +1708,17 @@ static int install_context_mark_for_removal( return r; r = install_info_traverse(scope, c, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL); - if (r < 0) + if (r == -ENOLINK) + return 0; + else if (r < 0) return r; - if (i->type != UNIT_FILE_TYPE_REGULAR) + if (i->type != UNIT_FILE_TYPE_REGULAR) { + log_debug("Unit %s has type %s, ignoring.", + i->name, + unit_file_type_to_string(i->type) ?: "invalid"); continue; + } r = mark_symlink_for_removal(remove_symlinks_to, i->name); if (r < 0) @@ -2788,6 +2813,9 @@ int unit_file_preset_all( if (r == -ERFKILL) r = unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, de->d_name, NULL); + else if (r == -ENOLINK) + r = unit_file_changes_add(changes, n_changes, + UNIT_FILE_IS_DANGLING, de->d_name, NULL); if (r < 0) return r; } @@ -2911,6 +2939,7 @@ static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] [UNIT_FILE_SYMLINK] = "symlink", [UNIT_FILE_UNLINK] = "unlink", [UNIT_FILE_IS_MASKED] = "masked", + [UNIT_FILE_IS_DANGLING] = "dangling", }; DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType); diff --git a/src/shared/install.h b/src/shared/install.h index 5812447c5b..c6aa4f6ef1 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -73,6 +73,7 @@ enum UnitFileChangeType { UNIT_FILE_SYMLINK, UNIT_FILE_UNLINK, UNIT_FILE_IS_MASKED, + UNIT_FILE_IS_DANGLING, _UNIT_FILE_CHANGE_TYPE_MAX, _UNIT_FILE_CHANGE_INVALID = INT_MIN }; diff --git a/src/socket-proxy/socket-proxyd.c b/src/socket-proxy/socket-proxyd.c index 1157a0c72e..52b4db8875 100644 --- a/src/socket-proxy/socket-proxyd.c +++ b/src/socket-proxy/socket-proxyd.c @@ -400,28 +400,19 @@ static int resolve_remote(Connection *c) { union sockaddr_union sa = {}; const char *node, *service; - socklen_t salen; int r; if (path_is_absolute(arg_remote_host)) { sa.un.sun_family = AF_UNIX; - strncpy(sa.un.sun_path, arg_remote_host, sizeof(sa.un.sun_path)-1); - sa.un.sun_path[sizeof(sa.un.sun_path)-1] = 0; - - salen = offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path); - - return connection_start(c, &sa.sa, salen); + strncpy(sa.un.sun_path, arg_remote_host, sizeof(sa.un.sun_path)); + return connection_start(c, &sa.sa, SOCKADDR_UN_LEN(sa.un)); } if (arg_remote_host[0] == '@') { sa.un.sun_family = AF_UNIX; sa.un.sun_path[0] = 0; - strncpy(sa.un.sun_path+1, arg_remote_host+1, sizeof(sa.un.sun_path)-2); - sa.un.sun_path[sizeof(sa.un.sun_path)-1] = 0; - - salen = offsetof(union sockaddr_union, un.sun_path) + 1 + strlen(sa.un.sun_path + 1); - - return connection_start(c, &sa.sa, salen); + strncpy(sa.un.sun_path+1, arg_remote_host+1, sizeof(sa.un.sun_path)-1); + return connection_start(c, &sa.sa, SOCKADDR_UN_LEN(sa.un)); } service = strrchr(arg_remote_host, ':'); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 387de025c5..0faf37d320 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -483,7 +483,7 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) { } if (circle_len > 0) - printf("%s%s%s ", on_circle, circle ? draw_special_char(DRAW_BLACK_CIRCLE) : " ", off_circle); + printf("%s%s%s ", on_circle, circle ? special_glyph(BLACK_CIRCLE) : " ", off_circle); printf("%s%-*s%s %s%-*s%s %s%-*s %-*s%s %-*s", on_active, id_len, id, off_active, @@ -1355,7 +1355,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { } else id_cols = max_id_len; - if (!arg_no_legend) + if (!arg_no_legend && c > 0) printf("%-*s %-*s\n", id_cols, "UNIT FILE", state_cols, "STATE"); @@ -1422,8 +1422,8 @@ static int list_unit_files(int argc, char *argv[], void *userdata) { n_units = hashmap_size(h); - units = new(UnitFileList, n_units); - if (!units && n_units > 0) { + units = new(UnitFileList, n_units ?: 1); /* avoid malloc(0) */ + if (!units) { unit_file_list_free(h); return log_oom(); } @@ -1519,10 +1519,9 @@ static int list_unit_files(int argc, char *argv[], void *userdata) { qsort_safe(units, c, sizeof(UnitFileList), compare_unit_file_list); output_unit_file_list(units, c); - if (install_client_side()) { + if (install_client_side()) for (unit = units; unit < units + c; unit++) free(unit->path); - } return 0; } @@ -1541,7 +1540,7 @@ static int list_dependencies_print(const char *name, int level, unsigned int bra printf("%s...\n",max_len % 2 ? "" : " "); return 0; } - printf("%s", draw_special_char(branches & (1 << i) ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE)); + printf("%s", special_glyph(branches & (1 << i) ? TREE_VERTICAL : TREE_SPACE)); } len += 2; @@ -1550,7 +1549,7 @@ static int list_dependencies_print(const char *name, int level, unsigned int bra return 0; } - printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH)); + printf("%s", special_glyph(last ? TREE_RIGHT : TREE_BRANCH)); } if (arg_full) { @@ -1711,24 +1710,25 @@ static int list_dependencies_one( const char *on; (void) get_state_one_unit(bus, *c, &active_state); + switch (active_state) { - case UNIT_ACTIVE: - case UNIT_RELOADING: - case UNIT_ACTIVATING: - on = ansi_highlight_green(); - break; - - case UNIT_INACTIVE: - case UNIT_DEACTIVATING: - on = ansi_normal(); - break; - - default: - on = ansi_highlight_red(); - break; + case UNIT_ACTIVE: + case UNIT_RELOADING: + case UNIT_ACTIVATING: + on = ansi_highlight_green(); + break; + + case UNIT_INACTIVE: + case UNIT_DEACTIVATING: + on = ansi_normal(); + break; + + default: + on = ansi_highlight_red(); + break; } - printf("%s%s%s ", on, draw_special_char(DRAW_BLACK_CIRCLE), ansi_normal()); + printf("%s%s%s ", on, special_glyph(BLACK_CIRCLE), ansi_normal()); } r = list_dependencies_print(*c, level, branches, c[1] == NULL); @@ -1965,7 +1965,7 @@ static void output_machines_list(struct machine_info *machine_infos, unsigned n) on_failed = off_failed = ""; if (circle_len > 0) - printf("%s%s%s ", on_state, circle ? draw_special_char(DRAW_BLACK_CIRCLE) : " ", off_state); + printf("%s%s%s ", on_state, circle ? special_glyph(BLACK_CIRCLE) : " ", off_state); if (m->is_host) printf("%-*s (host) %s%-*s%s %s%*" PRIu32 "%s %*" PRIu32 "\n", @@ -3528,7 +3528,7 @@ static void print_status_info( } else active_on = active_off = ""; - printf("%s%s%s %s", active_on, draw_special_char(DRAW_BLACK_CIRCLE), active_off, strna(i->id)); + printf("%s%s%s %s", active_on, special_glyph(BLACK_CIRCLE), active_off, strna(i->id)); if (i->description && !streq_ptr(i->id, i->description)) printf(" - %s", i->description); @@ -3583,7 +3583,7 @@ static void print_status_info( } printf("%s\n %s", dir, - draw_special_char(DRAW_TREE_RIGHT)); + special_glyph(TREE_RIGHT)); } last = ! (*(dropin + 1) && startswith(*(dropin + 1), dir)); @@ -4705,7 +4705,7 @@ static int show_system_status(sd_bus *bus) { } else on = off = ""; - printf("%s%s%s %s\n", on, draw_special_char(DRAW_BLACK_CIRCLE), off, arg_host ? arg_host : hn); + printf("%s%s%s %s\n", on, special_glyph(BLACK_CIRCLE), off, arg_host ? arg_host : hn); printf(" State: %s%s%s\n", on, strna(mi.state), off); diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c index 33ff3755bc..b480fdaa9c 100644 --- a/src/test/test-socket-util.c +++ b/src/test/test-socket-util.c @@ -27,6 +27,29 @@ #include "string-util.h" #include "util.h" +static void test_ifname_valid(void) { + assert(ifname_valid("foo")); + assert(ifname_valid("eth0")); + + assert(!ifname_valid("0")); + assert(!ifname_valid("99")); + assert(ifname_valid("a99")); + assert(ifname_valid("99a")); + + assert(!ifname_valid(NULL)); + assert(!ifname_valid("")); + assert(!ifname_valid(" ")); + assert(!ifname_valid(" foo")); + assert(!ifname_valid("bar\n")); + assert(!ifname_valid(".")); + assert(!ifname_valid("..")); + assert(ifname_valid("foo.bar")); + assert(!ifname_valid("x:y")); + + assert(ifname_valid("xxxxxxxxxxxxxxx")); + assert(!ifname_valid("xxxxxxxxxxxxxxxx")); +} + static void test_socket_address_parse(void) { SocketAddress a; @@ -343,10 +366,27 @@ static void test_sockaddr_equal(void) { assert_se(!sockaddr_equal(&b, &c)); } +static void test_sockaddr_un_len(void) { + static const struct sockaddr_un fs = { + .sun_family = AF_UNIX, + .sun_path = "/foo/bar/waldo", + }; + + static const struct sockaddr_un abstract = { + .sun_family = AF_UNIX, + .sun_path = "\0foobar", + }; + + assert_se(SOCKADDR_UN_LEN(fs) == offsetof(struct sockaddr_un, sun_path) + strlen(fs.sun_path)); + assert_se(SOCKADDR_UN_LEN(abstract) == offsetof(struct sockaddr_un, sun_path) + 1 + strlen(abstract.sun_path + 1)); +} + int main(int argc, char *argv[]) { log_set_max_level(LOG_DEBUG); + test_ifname_valid(); + test_socket_address_parse(); test_socket_address_parse_netlink(); test_socket_address_equal(); @@ -363,5 +403,7 @@ int main(int argc, char *argv[]) { test_sockaddr_equal(); + test_sockaddr_un_len(); + return 0; } diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index c7ded451a2..ee879c7b89 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -65,8 +65,8 @@ static int ask_password_plymouth( const char *flag_file, char ***ret) { + static const union sockaddr_union sa = PLYMOUTH_SOCKET; _cleanup_close_ int fd = -1, notify = -1; - union sockaddr_union sa = PLYMOUTH_SOCKET; _cleanup_free_ char *packet = NULL; ssize_t k; int r, n; @@ -94,7 +94,7 @@ static int ask_password_plymouth( if (fd < 0) return -errno; - r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)); + r = connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) return -errno; @@ -269,8 +269,7 @@ static int send_passwords(const char *socket_name, char **passwords) { strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path)); - r = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, - offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)); + r = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, SOCKADDR_UN_LEN(sa.un)); if (r < 0) r = log_debug_errno(errno, "sendto(): %m"); diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c index 962de22f43..f68a09d7a8 100644 --- a/src/udev/udev-ctrl.c +++ b/src/udev/udev-ctrl.c @@ -105,7 +105,7 @@ struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd) { uctrl->saddr.un.sun_family = AF_LOCAL; strscpy(uctrl->saddr.un.sun_path, sizeof(uctrl->saddr.un.sun_path), "/run/udev/control"); - uctrl->addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(uctrl->saddr.un.sun_path); + uctrl->addrlen = SOCKADDR_UN_LEN(uctrl->saddr.un); return uctrl; } diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index 8a1b824e65..08ae4aad27 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -195,9 +195,15 @@ static void font_copy_to_all_vcs(int fd) { unsigned char map8[E_TABSZ]; unsigned short map16[E_TABSZ]; struct unimapdesc unimapd; - struct unipair unipairs[USHRT_MAX]; + _cleanup_free_ struct unipair* unipairs = NULL; int i, r; + unipairs = new(struct unipair, USHRT_MAX); + if (unipairs == NULL) { + log_error("Not enough memory to copy fonts"); + return; + } + /* get active, and 16 bit mask of used VT numbers */ r = ioctl(fd, VT_GETSTATE, &vcs); if (r < 0) { |