diff options
27 files changed, 434 insertions, 193 deletions
diff --git a/.gitignore b/.gitignore index b0abc0d4e9..98eb29d657 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ *.a *.cache -*.html *.la *.lo *.log @@ -88,6 +88,14 @@ CHANGES WITH 228: from PID1's environment block into the environment block of the service. + * Timer units gained support for a new RemainAfterElapse= + setting which takes a boolean argument. It defaults on on, + exposing behaviour unchanged to previous releases. If set to + off, timer units are unloaded after they elapsed if they + cannot elapse again. This is particularly useful for + transient timer units, which shall not stay around longer + than until they first elapse. + * systemd will now bump the net.unix.max_dgram_qlen to 512 by default now (the kernel default is 16). This is beneficial for avoiding blocking on AF_UNIX/SOCK_DGRAM sockets since it @@ -71,8 +71,6 @@ Features: * install: include generator dirs in unit file search paths -* invent a better systemd-run scheme for naming scopes, that works with remoting - * rework C11 utf8.[ch] to use char32_t instead of uint32_t when referring to unicode chars, to make things more expressive. @@ -103,7 +101,7 @@ Features: * Rework systemctl's GetAll property parsing to use the generic bus_map_all_properties() API -* core/cgroup: support net_cls modules, and support automatically allocating class ids, then add support for making firewall changes depending on it, to implement a per-service firewall +* implement a per-service firewall based on net_cls * Port various tools to make use of verbs.[ch], where applicable @@ -155,8 +153,6 @@ Features: * maybe provide an API to allow migration of foreign PIDs into existing scopes. -* maybe support a new very "soft" reboot mode, that simply kills all processes, disassembles everything, flushes /run and sysvipc, and then reexecs systemd again - * man: maybe use the word "inspect" rather than "introspect"? * systemctl: if some operation fails, show log output? @@ -213,8 +209,6 @@ Features: * generator that automatically discovers btrfs subvolumes, identifies their purpose based on some xattr on them. -* timer units: actually add extra delays to timer units with high AccuracySec values, don't start them already when we are awake... - * a way for container managers to turn off getty starting via $container_headless= or so... * figure out a nice way how we can let the admin know what child/sibling unit causes cgroup membership for a specific unit @@ -736,7 +730,6 @@ Features: - Support --test based on current system state - If we show an error about a unit (such as not showing up) and it has no Description string, then show a description string generated form the reverse of unit_name_mangle(). - after deserializing sockets in socket.c we should reapply sockopts and things - - make timer units go away after they elapsed - drop PID 1 reloading, only do reexecing (difficult: Reload() currently is properly synchronous, Reexec() is weird, because we cannot delay the response properly until we are back, so instead of diff --git a/man/.gitignore b/man/.gitignore index bf5eeab938..d928e5a83f 100644 --- a/man/.gitignore +++ b/man/.gitignore @@ -1,4 +1,5 @@ /systemd.directives.xml /systemd.index.xml /*.[13578] +/*.html /custom-entities.ent diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml index 3841588820..fd03bda9cd 100644 --- a/man/systemd.timer.xml +++ b/man/systemd.timer.xml @@ -190,13 +190,12 @@ <varname>OnUnitInactiveSec=</varname> and ending the time configured with <varname>AccuracySec=</varname> later. Within this time window, the expiry time will be placed at a - host-specific, randomized but stable position that is + host-specific, randomized, but stable position that is synchronized between all local timer units. This is done in - order to distribute the wake-up time in networked - installations, as well as optimizing power consumption to - suppress unnecessary CPU wake-ups. To get best accuracy, set - this option to 1us. Note that the timer is still subject to - the timer slack configured via + order to optimize power consumption to suppress unnecessary + CPU wake-ups. To get best accuracy, set this option to + 1us. Note that the timer is still subject to the timer slack + configured via <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>'s <varname>TimerSlackNSec=</varname> setting. See <citerefentry><refentrytitle>prctl</refentrytitle><manvolnum>2</manvolnum></citerefentry> @@ -204,6 +203,38 @@ this value as high as possible and as low as necessary.</para></listitem> </varlistentry> + + <varlistentry> + <term><varname>RandomSec=</varname></term> + + <listitem><para>Delay the timer by a randomly selected, evenly + distributed amount of time between 0 and the specified time + value. Defaults to 0, indicating that no randomized delay + shall be applied. Each timer unit will determine this delay + randomly each time it is started, and the delay will simply be + added on top of the next determined elapsing time. This is + useful to stretch dispatching of similarly configured timer + events over a certain amount time, to avoid that they all fire + at the same time, possibly resulting in resource + congestion. Note the relation to + <varname>AccuracySec=</varname> above: the latter allows the + service manager to coalesce timer events within a specified + time range in order to minimize wakeups, the former does the + opposite: it stretches timer events over a time range, to make + it unlikely that they fire simultaneously. If + <varname>RandomSec=</varname> and + <varname>AccuracySec=</varname> are used in conjunction, first + the a randomized time is added, and the result is then + possibly shifted further to coalesce it with other timer + events possibly happening on the system. As mentioned above + <varname>AccuracySec=</varname> defaults to 1min and + <varname>RandomSec=</varname> to 0, thus encouraging + coalescing of timer events. In order to optimally stretch + timer events over a certain range of time, make sure to set + <varname>RandomSec=</varname> to a higher value, and + <varname>AccuracySec=1us</varname>.</para></listitem> + </varlistentry> + <varlistentry> <term><varname>Unit=</varname></term> @@ -245,11 +276,20 @@ </varlistentry> <varlistentry> - <term><varname>RemainAfterExit=</varname></term> + <term><varname>RemainAfterElapse=</varname></term> <listitem><para>Takes a boolean argument. If true, an elapsed - timer will stay loaded, and its state remains - queriable. Defaults to + timer will stay loaded, and its state remains queriable. If + false, an elapsed timer unit that cannot elapse anymore is + unloaded. Turning this off is particularly useful for + transient timer units that shall disappear after they first + elapse. Note that this setting has an effect on repeatedly + starting the a timer unit that only elapses once: if + <varname>RemainAfterElapse=</varname> is on, it will not be + started again, and is guaranteed to elapse only once. However, + if <varname>RemainAfterLeapse=</varname> is off, it might be + started again if it is already elapsed, and thus be triggered + multiple times. Defaults to <varname>yes</varname>.</para></listitem> </varlistentry> </variablelist> diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c index ea0528c6fc..c57a3cbd60 100644 --- a/src/basic/hostname-util.c +++ b/src/basic/hostname-util.c @@ -72,7 +72,7 @@ static bool hostname_valid_char(char c) { * allow_trailing_dot is true and at least two components are present * in the name. Note that due to the restricted charset and length * this call is substantially more conservative than - * dns_domain_is_valid(). + * dns_name_is_valid(). */ bool hostname_is_valid(const char *s, bool allow_trailing_dot) { unsigned n_dots = 0; diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c index 2e6c479c3a..4bee82df07 100644 --- a/src/core/dbus-timer.c +++ b/src/core/dbus-timer.c @@ -180,6 +180,7 @@ const sd_bus_vtable bus_timer_vtable[] = { BUS_PROPERTY_DUAL_TIMESTAMP("LastTriggerUSec", offsetof(Timer, last_trigger), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Timer, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("AccuracyUSec", "t", bus_property_get_usec, offsetof(Timer, accuracy_usec), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RandomUSec", "t", bus_property_get_usec, offsetof(Timer, random_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Persistent", "b", bus_property_get_bool, offsetof(Timer, persistent), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("WakeSystem", "b", bus_property_get_bool, offsetof(Timer, wake_system), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RemainAfterElapse", "b", bus_property_get_bool, offsetof(Timer, remain_after_elapse), SD_BUS_VTABLE_PROPERTY_CONST), @@ -283,6 +284,22 @@ static int bus_timer_set_transient_property( return 1; + } else if (streq(name, "RandomUSec")) { + usec_t u = 0; + + r = sd_bus_message_read(message, "t", &u); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + char time[FORMAT_TIMESPAN_MAX]; + + t->random_usec = u; + unit_write_drop_in_private_format(UNIT(t), mode, name, "RandomSec=%s\n", format_timespan(time, sizeof(time), u, USEC_PER_MSEC)); + } + + return 1; + } else if (streq(name, "WakeSystem")) { int b; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 0dbe38d7e5..c64850802e 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -347,6 +347,7 @@ Timer.Persistent, config_parse_bool, 0, Timer.WakeSystem, config_parse_bool, 0, offsetof(Timer, wake_system) Timer.RemainAfterElapse, config_parse_bool, 0, offsetof(Timer, remain_after_elapse) Timer.AccuracySec, config_parse_sec, 0, offsetof(Timer, accuracy_usec) +Timer.RandomSec, config_parse_sec, 0, offsetof(Timer, random_usec) Timer.Unit, config_parse_trigger_unit, 0, 0 m4_dnl Path.PathExists, config_parse_path_spec, 0, 0 diff --git a/src/core/timer.c b/src/core/timer.c index 0587452cfb..6b0f8e8616 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -27,6 +27,7 @@ #include "dbus-timer.h" #include "fs-util.h" #include "parse-util.h" +#include "random-util.h" #include "special.h" #include "string-table.h" #include "string-util.h" @@ -330,6 +331,28 @@ static usec_t monotonic_to_boottime(usec_t t) { return 0; } +static void add_random(Timer *t, usec_t *v) { + char s[FORMAT_TIMESPAN_MAX]; + usec_t add; + + assert(t); + assert(*v); + + if (t->random_usec == 0) + return; + if (*v == USEC_INFINITY) + return; + + add = random_u64() % t->random_usec; + + if (*v + add < *v) /* overflow */ + *v = (usec_t) -2; /* Highest possible value, that is not USEC_INFINITY */ + else + *v += add; + + log_unit_info(UNIT(t), "Adding %s random time.", format_timespan(s, sizeof(s), add, 0)); +} + static void timer_enter_waiting(Timer *t, bool initial) { bool found_monotonic = false, found_realtime = false; usec_t ts_realtime, ts_monotonic; @@ -452,6 +475,8 @@ static void timer_enter_waiting(Timer *t, bool initial) { char buf[FORMAT_TIMESPAN_MAX]; usec_t left; + add_random(t, &t->next_elapse_monotonic_or_boottime); + left = t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0; log_unit_debug(UNIT(t), "Monotonic timer elapses in %s.", format_timespan(buf, sizeof(buf), left, 0)); @@ -486,6 +511,9 @@ static void timer_enter_waiting(Timer *t, bool initial) { if (found_realtime) { char buf[FORMAT_TIMESTAMP_MAX]; + + add_random(t, &t->next_elapse_realtime); + log_unit_debug(UNIT(t), "Realtime timer elapses at %s.", format_timestamp(buf, sizeof(buf), t->next_elapse_realtime)); if (t->realtime_event_source) { diff --git a/src/core/timer.h b/src/core/timer.h index 6bc9fbed3d..0599f07818 100644 --- a/src/core/timer.h +++ b/src/core/timer.h @@ -58,6 +58,7 @@ struct Timer { Unit meta; usec_t accuracy_usec; + usec_t random_usec; LIST_HEAD(TimerValue, values); usec_t next_elapse_realtime; diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h index 69b37fc352..05bb5ae493 100644 --- a/src/libsystemd-network/dhcp-protocol.h +++ b/src/libsystemd-network/dhcp-protocol.h @@ -137,6 +137,7 @@ enum { DHCP_OPTION_REBINDING_T2_TIME = 59, DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60, DHCP_OPTION_CLIENT_IDENTIFIER = 61, + DHCP_OPTION_FQDN = 81, DHCP_OPTION_NEW_POSIX_TIMEZONE = 100, DHCP_OPTION_NEW_TZDB_TIMEZONE = 101, DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121, @@ -144,3 +145,12 @@ enum { DHCP_OPTION_PRIVATE_LAST = 254, DHCP_OPTION_END = 255, }; + +#define DHCP_MAX_FQDN_LENGTH 255 + +enum { + DHCP_FQDN_FLAG_S = (1 << 0), + DHCP_FQDN_FLAG_O = (1 << 1), + DHCP_FQDN_FLAG_E = (1 << 2), + DHCP_FQDN_FLAG_N = (1 << 3), +}; diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 137537253a..5ec0e661f7 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -34,6 +34,8 @@ #include "dhcp-internal.h" #include "dhcp-lease-internal.h" #include "dhcp-protocol.h" +#include "dns-domain.h" +#include "hostname-util.h" #include "random-util.h" #include "string-util.h" #include "util.h" @@ -298,6 +300,9 @@ int sd_dhcp_client_set_hostname(sd_dhcp_client *client, assert_return(client, -EINVAL); + if (!hostname_is_valid(hostname, false) && !dns_name_is_valid(hostname)) + return -EINVAL; + if (streq_ptr(client->hostname, hostname)) return 0; @@ -539,6 +544,24 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, return 0; } +static int client_append_fqdn_option(DHCPMessage *message, size_t optlen, size_t *optoffset, + const char *fqdn) { + uint8_t buffer[3 + DHCP_MAX_FQDN_LENGTH]; + int r; + + buffer[0] = DHCP_FQDN_FLAG_S | /* Request server to perform A RR DNS updates */ + DHCP_FQDN_FLAG_E; /* Canonical wire format */ + buffer[1] = 0; /* RCODE1 (deprecated) */ + buffer[2] = 0; /* RCODE2 (deprecated) */ + + r = dns_name_to_wire_format(fqdn, buffer + 3, sizeof(buffer) - 3); + if (r > 0) + r = dhcp_option_append(message, optlen, optoffset, 0, + DHCP_OPTION_FQDN, 3 + r, buffer); + + return r; +} + static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet, size_t len) { dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT, @@ -576,13 +599,21 @@ static int client_send_discover(sd_dhcp_client *client) { return r; } - /* it is unclear from RFC 2131 if client should send hostname in - DHCPDISCOVER but dhclient does and so we do as well - */ if (client->hostname) { - r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, - DHCP_OPTION_HOST_NAME, - strlen(client->hostname), client->hostname); + /* According to RFC 4702 "clients that send the Client FQDN option in + their messages MUST NOT also send the Host Name option". Just send + one of the two depending on the hostname type. + */ + if (dns_name_single_label(client->hostname)) { + /* it is unclear from RFC 2131 if client should send hostname in + DHCPDISCOVER but dhclient does and so we do as well + */ + r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0, + DHCP_OPTION_HOST_NAME, + strlen(client->hostname), client->hostname); + } else + r = client_append_fqdn_option(&discover->dhcp, optlen, &optoffset, + client->hostname); if (r < 0) return r; } @@ -688,9 +719,13 @@ static int client_send_request(sd_dhcp_client *client) { } if (client->hostname) { - r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, - DHCP_OPTION_HOST_NAME, - strlen(client->hostname), client->hostname); + if (dns_name_single_label(client->hostname)) + r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, + DHCP_OPTION_HOST_NAME, + strlen(client->hostname), client->hostname); + else + r = client_append_fqdn_option(&request->dhcp, optlen, &optoffset, + client->hostname); if (r < 0) return r; } diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c index 6703d87bc4..713438f212 100644 --- a/src/libsystemd-network/sd-ndisc.c +++ b/src/libsystemd-network/sd-ndisc.c @@ -32,6 +32,7 @@ #include "in-addr-util.h" #include "list.h" #include "socket-util.h" +#include "string-util.h" #define NDISC_ROUTER_SOLICITATION_INTERVAL 4 * USEC_PER_SEC #define NDISC_MAX_ROUTER_SOLICITATIONS 3 @@ -484,6 +485,7 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r int r, buflen = 0, pref, stateful; union sockaddr_union router = {}; socklen_t router_len = sizeof(router); + struct in6_addr *gw; unsigned lifetime; ssize_t len; @@ -503,9 +505,21 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r if (len < 0) { log_ndisc(nd, "Could not receive message from ICMPv6 socket: %m"); return 0; - } else if (router_len != sizeof(router.in6) && router_len != 0) { + } else if (router_len == 0) + gw = NULL; /* only happens when running the test-suite over a socketpair */ + else if (router_len != sizeof(router.in6)) { log_ndisc(nd, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)router_len); return 0; + } else + gw = &router.in6.sin6_addr; + + if (gw && !in_addr_is_link_local(AF_INET6, (const union in_addr_union*) gw)) { + _cleanup_free_ char *addr = NULL; + + (void)in_addr_to_string(AF_INET6, (const union in_addr_union*) gw, &addr); + + log_ndisc(nd, "Received RA from non-link-local address %s. Ignoring.", strna(addr)); + return 0; } if (ra->nd_ra_type != ND_ROUTER_ADVERT) @@ -544,7 +558,7 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r } if (nd->router_callback) - nd->router_callback(nd, stateful, router_len != 0 ? &router.in6.sin6_addr : NULL, lifetime, pref, nd->userdata); + nd->router_callback(nd, stateful, gw, lifetime, pref, nd->userdata); return 0; } diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 295249a50d..4af895a6fb 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -626,6 +626,9 @@ void link_check_ready(Link *link) { !link->dhcp4_configured && !link->dhcp6_configured)) return; + if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured) + return; + SET_FOREACH(a, link->addresses, i) if (!address_is_ready(a)) return; @@ -1923,7 +1926,6 @@ static int link_set_ipv6_privacy_extensions(Link *link) { static int link_set_ipv6_accept_ra(Link *link) { const char *p = NULL; - const char *v; int r; /* Make this a NOP if IPv6 is not available */ @@ -1936,16 +1938,12 @@ static int link_set_ipv6_accept_ra(Link *link) { if (!link->network) return 0; - if (link_ipv6_accept_ra_enabled(link)) - v = "1"; - else - v = "0"; - p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/accept_ra"); - r = write_string_file(p, v, WRITE_STRING_FILE_VERIFY_ON_FAILURE); + /* We handle router advertisments ourselves, tell the kernel to GTFO */ + r = write_string_file(p, "0", WRITE_STRING_FILE_VERIFY_ON_FAILURE); if (r < 0) - log_link_warning_errno(link, r, "Cannot configure kernel IPv6 accept_ra for interface: %m"); + log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface: %m"); return 0; } @@ -2006,7 +2004,6 @@ static int link_set_ipv6_hop_limit(Link *link) { return 0; } -/* static int link_drop_foreign_config(Link *link) { Address *address; Route *route; @@ -2014,6 +2011,7 @@ static int link_drop_foreign_config(Link *link) { int r; SET_FOREACH(address, link->addresses_foreign, i) { + /* we consider IPv6LL addresses to be managed by the kernel */ if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1) continue; @@ -2023,6 +2021,7 @@ static int link_drop_foreign_config(Link *link) { } SET_FOREACH(route, link->routes_foreign, i) { + /* do not touch routes managed by the kernel */ if (route->protocol == RTPROT_KERNEL) continue; @@ -2033,7 +2032,6 @@ static int link_drop_foreign_config(Link *link) { return 0; } -*/ static int link_configure(Link *link) { int r; @@ -2042,11 +2040,9 @@ static int link_configure(Link *link) { assert(link->network); assert(link->state == LINK_STATE_PENDING); -/* r = link_drop_foreign_config(link); if (r < 0) return r; -*/ r = link_set_bridge_fdb(link); if (r < 0) diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 966d729d85..ce9e513ceb 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -26,7 +26,7 @@ #include "sd-ndisc.h" #include "networkd-link.h" -/* + static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { _cleanup_link_unref_ Link *link = userdata; int r; @@ -77,6 +77,7 @@ static void ndisc_prefix_autonomous_handler(sd_ndisc *nd, const struct in6_addr if (in_addr_is_null(AF_INET6, (const union in_addr_union *) &link->network->ipv6_token) == 0) memcpy(((char *)&address->in_addr.in6) + 8, ((char *)&link->network->ipv6_token) + 8, 8); else { + /* see RFC4291 section 2.5.1 */ address->in_addr.in6.__in6_u.__u6_addr8[8] = link->mac.ether_addr_octet[0]; address->in_addr.in6.__in6_u.__u6_addr8[8] ^= 1 << 1; address->in_addr.in6.__in6_u.__u6_addr8[9] = link->mac.ether_addr_octet[1]; @@ -139,12 +140,11 @@ static void ndisc_prefix_onlink_handler(sd_ndisc *nd, const struct in6_addr *pre link->ndisc_messages ++; } -*/ static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_addr *gateway, unsigned lifetime, int pref, void *userdata) { _cleanup_route_free_ Route *route = NULL; Link *link = userdata; - /* usec_t time_now; */ + usec_t time_now; int r; assert(link); @@ -163,8 +163,6 @@ static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_a log_link_warning_errno(link, r, "Starting DHCPv6 client on NDisc request failed: %m"); } - return; -/* if (!gateway) return; @@ -191,7 +189,6 @@ static void ndisc_router_handler(sd_ndisc *nd, uint8_t flags, const struct in6_a } link->ndisc_messages ++; -*/ } static void ndisc_handler(sd_ndisc *nd, int event, void *userdata) { @@ -245,8 +242,8 @@ int ndisc_configure(Link *link) { r = sd_ndisc_set_callback(link->ndisc_router_discovery, ndisc_router_handler, - NULL, - NULL, + ndisc_prefix_onlink_handler, + ndisc_prefix_autonomous_handler, ndisc_handler, link); diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index b15370b017..873d76e40c 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -543,6 +543,7 @@ static void dns_scope_verify_conflicts(DnsScope *s, DnsPacket *p) { void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) { _cleanup_(dns_packet_unrefp) DnsPacket *reply = NULL; _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL; + DnsResourceKey *key = NULL; bool tentative = false; int r, fd; @@ -576,7 +577,10 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) { return; } - r = dns_zone_lookup(&s->zone, p->question, &answer, &soa, &tentative); + assert(p->question->n_keys == 1); + key = p->question->keys[0]; + + r = dns_zone_lookup(&s->zone, key, &answer, &soa, &tentative); if (r < 0) { log_debug_errno(r, "Failed to lookup key: %m"); return; diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 6545f6cd8a..37f47c47c0 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -626,6 +626,20 @@ int dns_transaction_go(DnsTransaction *t) { t->cached = dns_answer_unref(t->cached); t->cached_rcode = 0; + /* Check the zone, but obly if this transaction is not used + * for probing or verifying a zone item. */ + if (set_isempty(t->zone_items)) { + + r = dns_zone_lookup(&t->scope->zone, t->key, &t->cached, NULL, NULL); + if (r < 0) + return r; + if (r > 0) { + t->cached_rcode = DNS_RCODE_SUCCESS; + dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS); + return 0; + } + } + /* Check the cache, but only if this transaction is not used * for probing or verifying a zone item. */ if (set_isempty(t->zone_items)) { diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index 48dcf76daa..a021ecb93d 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -283,97 +283,76 @@ int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) { return 0; } -int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) { +int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) { _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL; - unsigned i, n_answer = 0, n_soa = 0; - bool tentative = true; + unsigned n_answer = 0; + DnsZoneItem *j, *first; + bool tentative = true, need_soa = false; int r; assert(z); - assert(q); + assert(key); assert(ret_answer); - assert(ret_soa); - if (q->n_keys <= 0) { - *ret_answer = NULL; - *ret_soa = NULL; - - if (ret_tentative) - *ret_tentative = false; + /* First iteration, count what we have */ - return 0; - } + if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) { + bool found = false, added = false; + int k; - /* First iteration, count what we have */ - for (i = 0; i < q->n_keys; i++) { - DnsZoneItem *j, *first; + /* If this is a generic match, then we have to + * go through the list by the name and look + * for everything manually */ - if (q->keys[i]->type == DNS_TYPE_ANY || - q->keys[i]->class == DNS_CLASS_ANY) { - bool found = false, added = false; - int k; + first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key)); + LIST_FOREACH(by_name, j, first) { + if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) + continue; - /* If this is a generic match, then we have to - * go through the list by the name and look - * for everything manually */ + found = true; - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])); - LIST_FOREACH(by_name, j, first) { - if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) - continue; + k = dns_resource_key_match_rr(key, j->rr); + if (k < 0) + return k; + if (k > 0) { + n_answer++; + added = true; + } - found = true; + } - k = dns_resource_key_match_rr(q->keys[i], j->rr); - if (k < 0) - return k; - if (k > 0) { - n_answer++; - added = true; - } + if (found && !added) + need_soa = true; - } + } else { + bool found = false; - if (found && !added) - n_soa++; + /* If this is a specific match, then look for + * the right key immediately */ - } else { - bool found = false; + first = hashmap_get(z->by_key, key); + LIST_FOREACH(by_key, j, first) { + if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) + continue; - /* If this is a specific match, then look for - * the right key immediately */ + found = true; + n_answer++; + } - first = hashmap_get(z->by_key, q->keys[i]); - LIST_FOREACH(by_key, j, first) { + if (!found) { + first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key)); + LIST_FOREACH(by_name, j, first) { if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) continue; - found = true; - n_answer++; - } - - if (!found) { - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])); - LIST_FOREACH(by_name, j, first) { - if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) - continue; - - n_soa++; - break; - } + need_soa = true; + break; } } } - if (n_answer <= 0 && n_soa <= 0) { - *ret_answer = NULL; - *ret_soa = NULL; - - if (ret_tentative) - *ret_tentative = false; - - return 0; - } + if (n_answer <= 0 && !need_soa) + goto return_empty; if (n_answer > 0) { answer = dns_answer_new(n_answer); @@ -381,99 +360,113 @@ int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **ret_answer, DnsAnswe return -ENOMEM; } - if (n_soa > 0) { - soa = dns_answer_new(n_soa); + if (need_soa) { + soa = dns_answer_new(1); if (!soa) return -ENOMEM; } /* Second iteration, actually add the RRs to the answers */ - for (i = 0; i < q->n_keys; i++) { - DnsZoneItem *j, *first; - - if (q->keys[i]->type == DNS_TYPE_ANY || - q->keys[i]->class == DNS_CLASS_ANY) { - bool found = false, added = false; - int k; + if (key->type == DNS_TYPE_ANY || key->class == DNS_CLASS_ANY) { + bool found = false, added = false; + int k; - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])); - LIST_FOREACH(by_name, j, first) { - if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) - continue; - - found = true; + first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key)); + LIST_FOREACH(by_name, j, first) { + if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) + continue; - if (j->state != DNS_ZONE_ITEM_PROBING) - tentative = false; + found = true; - k = dns_resource_key_match_rr(q->keys[i], j->rr); - if (k < 0) - return k; - if (k > 0) { - r = dns_answer_add(answer, j->rr, 0); - if (r < 0) - return r; + if (j->state != DNS_ZONE_ITEM_PROBING) + tentative = false; - added = true; - } - } - - if (found && !added) { - r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]), LLMNR_DEFAULT_TTL); + k = dns_resource_key_match_rr(key, j->rr); + if (k < 0) + return k; + if (k > 0) { + r = dns_answer_add(answer, j->rr, 0); if (r < 0) return r; + + added = true; } - } else { - bool found = false; + } - first = hashmap_get(z->by_key, q->keys[i]); - LIST_FOREACH(by_key, j, first) { - if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) - continue; + if (found && !added) { + r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(key), LLMNR_DEFAULT_TTL); + if (r < 0) + return r; + } + } else { + bool found = false; - found = true; + first = hashmap_get(z->by_key, key); + LIST_FOREACH(by_key, j, first) { + if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) + continue; - if (j->state != DNS_ZONE_ITEM_PROBING) - tentative = false; + found = true; - r = dns_answer_add(answer, j->rr, 0); - if (r < 0) - return r; - } + if (j->state != DNS_ZONE_ITEM_PROBING) + tentative = false; + + r = dns_answer_add(answer, j->rr, 0); + if (r < 0) + return r; + } - if (!found) { - bool add_soa = false; + if (!found) { + bool add_soa = false; - first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(q->keys[i])); - LIST_FOREACH(by_name, j, first) { - if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) - continue; + first = hashmap_get(z->by_name, DNS_RESOURCE_KEY_NAME(key)); + LIST_FOREACH(by_name, j, first) { + if (!IN_SET(j->state, DNS_ZONE_ITEM_PROBING, DNS_ZONE_ITEM_ESTABLISHED, DNS_ZONE_ITEM_VERIFYING)) + continue; - if (j->state != DNS_ZONE_ITEM_PROBING) - tentative = false; + if (j->state != DNS_ZONE_ITEM_PROBING) + tentative = false; - add_soa = true; - } + add_soa = true; + } - if (add_soa) { - r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(q->keys[i]), LLMNR_DEFAULT_TTL); - if (r < 0) - return r; - } + if (add_soa) { + r = dns_answer_add_soa(soa, DNS_RESOURCE_KEY_NAME(key), LLMNR_DEFAULT_TTL); + if (r < 0) + return r; } } } + /* If the caller sets ret_tentative to NULL, then use this as + * indication to not return tentative entries */ + + if (!ret_tentative && tentative) + goto return_empty; + *ret_answer = answer; answer = NULL; - *ret_soa = soa; - soa = NULL; + if (ret_soa) { + *ret_soa = soa; + soa = NULL; + } if (ret_tentative) *ret_tentative = tentative; return 1; + +return_empty: + *ret_answer = NULL; + + if (ret_soa) + *ret_soa = NULL; + + if (ret_tentative) + *ret_tentative = false; + + return 0; } void dns_zone_item_conflict(DnsZoneItem *i) { diff --git a/src/resolve/resolved-dns-zone.h b/src/resolve/resolved-dns-zone.h index de110f9de7..44a8624c30 100644 --- a/src/resolve/resolved-dns-zone.h +++ b/src/resolve/resolved-dns-zone.h @@ -67,7 +67,7 @@ void dns_zone_flush(DnsZone *z); int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe); void dns_zone_remove_rr(DnsZone *z, DnsResourceRecord *rr); -int dns_zone_lookup(DnsZone *z, DnsQuestion *q, DnsAnswer **answer, DnsAnswer **soa, bool *tentative); +int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, DnsAnswer **answer, DnsAnswer **soa, bool *tentative); void dns_zone_item_conflict(DnsZoneItem *i); void dns_zone_item_ready(DnsZoneItem *i); diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 73ceeba18f..38281045b8 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -1428,16 +1428,36 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen return bus_log_create_error(r); return 0; + } else if (streq(field, "EnvironmentFile")) { + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "EnvironmentFiles"); if (r < 0) - return r; + return bus_log_create_error(r); r = sd_bus_message_append(m, "v", "a(sb)", 1, eq[0] == '-' ? eq + 1 : eq, eq[0] == '-'); if (r < 0) - return r; + return bus_log_create_error(r); + + return 0; + + } else if (streq(field, "RandomSec")) { + usec_t t; + + r = parse_sec(eq, &t); + if (r < 0) + return log_error_errno(r, "Failed to parse RandomSec= parameter: %s", eq); + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "RandomUSec"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "v", "t", t); + if (r < 0) + return bus_log_create_error(r); + return 0; } @@ -1450,13 +1470,11 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges", - "SyslogLevelPrefix", "Delegate")) { + "SyslogLevelPrefix", "Delegate", "RemainAfterElapse")) { r = parse_boolean(eq); - if (r < 0) { - log_error("Failed to parse boolean assignment %s.", assignment); - return -EINVAL; - } + if (r < 0) + return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment); r = sd_bus_message_append(m, "v", "b", r); diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 7af15e0098..423ccca9cc 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -715,3 +715,37 @@ int dns_name_single_label(const char *name) { return r == 0 && *name == 0; } + +/* Encode a domain name according to RFC 1035 Section 3.1 */ +int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len) { + uint8_t *label_length; + uint8_t *out; + int r; + + assert_return(buffer, -EINVAL); + assert_return(domain, -EINVAL); + assert_return(domain[0], -EINVAL); + + out = buffer; + + do { + /* reserve a byte for label length */ + if (len == 0) + return -ENOBUFS; + len--; + label_length = out; + out++; + + /* convert and copy a single label */ + r = dns_label_unescape(&domain, (char *) out, len); + if (r < 0) + return r; + + /* fill label length, move forward */ + *label_length = r; + out += r; + len -= r; + } while (r != 0); + + return out - buffer; +} diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index 1f0d242c18..b214897440 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -67,3 +67,5 @@ int dns_name_address(const char *p, int *family, union in_addr_union *a); int dns_name_root(const char *name); int dns_name_single_label(const char *name); + +int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 51b82d57db..240fa2c551 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3360,6 +3360,7 @@ typedef struct UnitStatusInfo { usec_t inactive_enter_timestamp; bool need_daemon_reload; + bool transient; /* Service */ pid_t main_pid; @@ -3459,7 +3460,7 @@ static void print_status_info( path = i->source_path ? i->source_path : i->fragment_path; - if (i->load_error) + if (i->load_error != 0) printf(" Loaded: %s%s%s (Reason: %s)\n", on, strna(i->load_state), off, i->load_error); else if (path && !isempty(i->unit_file_state) && !isempty(i->unit_file_preset)) @@ -3475,6 +3476,9 @@ static void print_status_info( printf(" Loaded: %s%s%s\n", on, strna(i->load_state), off); + if (i->transient) + printf("Transient: yes\n"); + if (!strv_isempty(i->dropin_paths)) { _cleanup_free_ char *dir = NULL; bool last = false; @@ -3839,6 +3843,8 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo * i->condition_result = b; else if (streq(name, "AssertResult")) i->assert_result = b; + else if (streq(name, "Transient")) + i->transient = b; break; } @@ -4646,8 +4652,7 @@ static int show(int argc, char *argv[], void *userdata) { return -EINVAL; } - if (show_properties) - pager_open_if_enabled(); + pager_open_if_enabled(); if (show_status) /* Increase max number of open files to 16K if we can, we diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c index d5778748a0..407c7d8ef7 100644 --- a/src/test/test-dns-domain.c +++ b/src/test/test-dns-domain.c @@ -52,6 +52,36 @@ static void test_dns_label_unescape(void) { test_dns_label_unescape_one("foobar.", "foobar", 20, 6); } +static void test_dns_name_to_wire_format_one(const char *what, const char *expect, size_t buffer_sz, int ret) { + uint8_t buffer[buffer_sz]; + int r; + + r = dns_name_to_wire_format(what, buffer, buffer_sz); + assert_se(r == ret); + + if (r < 0) + return; + + assert_se(!memcmp(buffer, expect, r)); +} + +static void test_dns_name_to_wire_format(void) { + const char out1[] = { 3, 'f', 'o', 'o', 0 }; + const char out2[] = { 5, 'h', 'a', 'l', 'l', 'o', 3, 'f', 'o', 'o', 3, 'b', 'a', 'r', 0 }; + const char out3[] = { 4, ' ', 'f', 'o', 'o', 3, 'b', 'a', 'r', 0 }; + + test_dns_name_to_wire_format_one("", NULL, 0, -EINVAL); + + test_dns_name_to_wire_format_one("foo", out1, sizeof(out1), sizeof(out1)); + test_dns_name_to_wire_format_one("foo", out1, sizeof(out1) + 1, sizeof(out1)); + test_dns_name_to_wire_format_one("foo", out1, sizeof(out1) - 1, -ENOBUFS); + + test_dns_name_to_wire_format_one("hallo.foo.bar", out2, sizeof(out2), sizeof(out2)); + test_dns_name_to_wire_format_one("hallo.foo..bar", NULL, 32, -EINVAL); + + test_dns_name_to_wire_format_one("\\032foo.bar", out3, sizeof(out3), sizeof(out3)); +} + static void test_dns_label_unescape_suffix_one(const char *what, const char *expect1, const char *expect2, size_t buffer_sz, int ret1, int ret2) { char buffer[buffer_sz]; const char *label; @@ -300,6 +330,7 @@ int main(int argc, char *argv[]) { test_dns_name_reverse(); test_dns_name_concat(); test_dns_name_is_valid(); + test_dns_name_to_wire_format(); return 0; } diff --git a/src/test/test-siphash24.c b/src/test/test-siphash24.c index a571a95a70..c20be99350 100644 --- a/src/test/test-siphash24.c +++ b/src/test/test-siphash24.c @@ -30,7 +30,7 @@ static int do_test(const uint8_t *in, size_t len, const uint8_t *key) { unsigned i, j; out = siphash24(in, len, key); - assert_se(out == htole64(0xa129ca6149be45e5)); + assert_se(out == 0xa129ca6149be45e5); /* verify the internal state as given in the above paper */ siphash24_init(&state, key); @@ -44,7 +44,7 @@ static int do_test(const uint8_t *in, size_t len, const uint8_t *key) { assert_se(state.v2 == 0x634cb3577b01fd3d); assert_se(state.v3 == 0xa5224d6f55c7d9c8); out = siphash24_finalize(&state); - assert_se(out == htole64(0xa129ca6149be45e5)); + assert_se(out == 0xa129ca6149be45e5); assert_se(state.v0 == 0xf6bcd53893fecff1); assert_se(state.v1 == 0x54b9964c7ea0d937); assert_se(state.v2 == 0x1b38329c099bb55a); @@ -59,7 +59,7 @@ static int do_test(const uint8_t *in, size_t len, const uint8_t *key) { siphash24_compress(&in[i], j - i, &state); siphash24_compress(&in[j], len - j, &state); out = siphash24_finalize(&state); - assert_se(out == htole64(0xa129ca6149be45e5)); + assert_se(out == 0xa129ca6149be45e5); } } return 0; diff --git a/test/TEST-01-BASIC/test.sh b/test/TEST-01-BASIC/test.sh index b6b474393d..f93ea934fb 100755 --- a/test/TEST-01-BASIC/test.sh +++ b/test/TEST-01-BASIC/test.sh @@ -11,7 +11,7 @@ check_result_qemu() { mount ${LOOPDEV}p1 $TESTDIR/root [[ -e $TESTDIR/root/testok ]] && ret=0 [[ -f $TESTDIR/root/failed ]] && cp -a $TESTDIR/root/failed $TESTDIR - [[ -f $TESTDIR/root/var/log/journal ]] && cp -a $TESTDIR/root/var/log/journal $TESTDIR + cp -a $TESTDIR/root/var/log/journal $TESTDIR umount $TESTDIR/root [[ -f $TESTDIR/failed ]] && cat $TESTDIR/failed ls -l $TESTDIR/journal/*/*.journal diff --git a/test/TEST-02-CRYPTSETUP/test.sh b/test/TEST-02-CRYPTSETUP/test.sh index 2997da06ff..8ed39b118a 100755 --- a/test/TEST-02-CRYPTSETUP/test.sh +++ b/test/TEST-02-CRYPTSETUP/test.sh @@ -13,7 +13,7 @@ check_result_qemu() { [[ -f $TESTDIR/root/failed ]] && cp -a $TESTDIR/root/failed $TESTDIR cryptsetup luksOpen ${LOOPDEV}p2 varcrypt <$TESTDIR/keyfile mount /dev/mapper/varcrypt $TESTDIR/root/var - [[ -f $TESTDIR/root/var/log/journal ]] && cp -a $TESTDIR/root/var/log/journal $TESTDIR + cp -a $TESTDIR/root/var/log/journal $TESTDIR umount $TESTDIR/root/var umount $TESTDIR/root cryptsetup luksClose /dev/mapper/varcrypt |