From: Martin Pitt Date: Wed, 9 Sep 2015 05:26:01 +0000 (+0200) Subject: Imported Upstream version 226 X-Git-Tag: debian/247.3-7+deb11u1~1430^2 X-Git-Url: https://git.proxmox.com/?a=commitdiff_plain;h=d9dfd2336cec994f89bcce804d0d001a2bddefc4;p=systemd.git Imported Upstream version 226 --- diff --git a/CODING_STYLE b/CODING_STYLE index a96ddd359..f13f9becb 100644 --- a/CODING_STYLE +++ b/CODING_STYLE @@ -295,25 +295,15 @@ EXIT_FAILURE and EXIT_SUCCESS as defined by libc. - The order in which header files are included doesn't matter too - much. However, please try to include the headers of external - libraries first (these are all headers enclosed in <>), followed by - the headers of our own public headers (these are all headers - starting with "sd-"), internal utility libraries from src/shared/, - followed by the headers of the specific component. Or in other - words: - - #include - #include "sd-daemon.h" - #include "util.h" - #include "frobnicator.h" - - Where stdio.h is a public glibc API, sd-daemon.h is a public API of - our own, util.h is a utility library header from src/shared, and - frobnicator.h is an placeholder name for any systemd component. The - benefit of following this ordering is that more local definitions - are always defined after more global ones. Thus, our local - definitions will never "leak" into the global header files, possibly - altering their effect due to #ifdeffery. + much. systemd-internal headers must not rely on an include order, so + it is safe to include them in any order possible. + However, to not clutter global includes, and to make sure internal + definitions will not affect global headers, please always include the + headers of external components first (these are all headers enclosed + in <>), followed by our own exported headers (usually everything + that's prefixed by "sd-"), and then followed by internal headers. + Furthermore, in all three groups, order all includes alphabetically + so duplicate includes can easily be detected. - To implement an endless loop, use "for (;;)" rather than "while (1)". The latter is a bit ugly anyway, since you probably really diff --git a/Makefile-man.am b/Makefile-man.am index 65287371b..3b8038611 100644 --- a/Makefile-man.am +++ b/Makefile-man.am @@ -130,6 +130,7 @@ MANPAGES += \ man/systemd.kill.5 \ man/systemd.link.5 \ man/systemd.mount.5 \ + man/systemd.nspawn.5 \ man/systemd.path.5 \ man/systemd.preset.5 \ man/systemd.resource-control.5 \ @@ -1937,6 +1938,7 @@ MANPAGES_ALIAS += \ man/sd_login_monitor_get_fd.3 \ man/sd_login_monitor_get_timeout.3 \ man/sd_login_monitor_unref.3 \ + man/sd_peer_get_cgroup.3 \ man/sd_peer_get_machine_name.3 \ man/sd_peer_get_owner_uid.3 \ man/sd_peer_get_session.3 \ @@ -1944,6 +1946,7 @@ MANPAGES_ALIAS += \ man/sd_peer_get_unit.3 \ man/sd_peer_get_user_slice.3 \ man/sd_peer_get_user_unit.3 \ + man/sd_pid_get_cgroup.3 \ man/sd_pid_get_machine_name.3 \ man/sd_pid_get_owner_uid.3 \ man/sd_pid_get_slice.3 \ @@ -1981,6 +1984,7 @@ man/sd_login_monitor_get_events.3: man/sd_login_monitor_new.3 man/sd_login_monitor_get_fd.3: man/sd_login_monitor_new.3 man/sd_login_monitor_get_timeout.3: man/sd_login_monitor_new.3 man/sd_login_monitor_unref.3: man/sd_login_monitor_new.3 +man/sd_peer_get_cgroup.3: man/sd_pid_get_session.3 man/sd_peer_get_machine_name.3: man/sd_pid_get_session.3 man/sd_peer_get_owner_uid.3: man/sd_pid_get_session.3 man/sd_peer_get_session.3: man/sd_pid_get_session.3 @@ -1988,6 +1992,7 @@ man/sd_peer_get_slice.3: man/sd_pid_get_session.3 man/sd_peer_get_unit.3: man/sd_pid_get_session.3 man/sd_peer_get_user_slice.3: man/sd_pid_get_session.3 man/sd_peer_get_user_unit.3: man/sd_pid_get_session.3 +man/sd_pid_get_cgroup.3: man/sd_pid_get_session.3 man/sd_pid_get_machine_name.3: man/sd_pid_get_session.3 man/sd_pid_get_owner_uid.3: man/sd_pid_get_session.3 man/sd_pid_get_slice.3: man/sd_pid_get_session.3 @@ -2043,6 +2048,9 @@ man/sd_login_monitor_get_timeout.html: man/sd_login_monitor_new.html man/sd_login_monitor_unref.html: man/sd_login_monitor_new.html $(html-alias) +man/sd_peer_get_cgroup.html: man/sd_pid_get_session.html + $(html-alias) + man/sd_peer_get_machine_name.html: man/sd_pid_get_session.html $(html-alias) @@ -2064,6 +2072,9 @@ man/sd_peer_get_user_slice.html: man/sd_pid_get_session.html man/sd_peer_get_user_unit.html: man/sd_pid_get_session.html $(html-alias) +man/sd_pid_get_cgroup.html: man/sd_pid_get_session.html + $(html-alias) + man/sd_pid_get_machine_name.html: man/sd_pid_get_session.html $(html-alias) @@ -2372,6 +2383,7 @@ EXTRA_DIST += \ man/systemd.mount.xml \ man/systemd.netdev.xml \ man/systemd.network.xml \ + man/systemd.nspawn.xml \ man/systemd.path.xml \ man/systemd.preset.xml \ man/systemd.resource-control.xml \ diff --git a/Makefile.am b/Makefile.am index c2973c0a6..8e0448f43 100644 --- a/Makefile.am +++ b/Makefile.am @@ -42,9 +42,9 @@ LIBUDEV_CURRENT=7 LIBUDEV_REVISION=4 LIBUDEV_AGE=6 -LIBSYSTEMD_CURRENT=10 -LIBSYSTEMD_REVISION=2 -LIBSYSTEMD_AGE=10 +LIBSYSTEMD_CURRENT=11 +LIBSYSTEMD_REVISION=0 +LIBSYSTEMD_AGE=11 # The following four libraries only exist for compatibility reasons, # their version info should not be bumped anymore @@ -155,6 +155,7 @@ noinst_PROGRAMS = TESTS = endif udevlibexec_PROGRAMS = +gperf_gperf_sources = in_files = $(filter %.in,$(EXTRA_DIST)) in_in_files = $(filter %.in.in, $(in_files)) @@ -220,6 +221,7 @@ AM_CPPFLAGS = \ -I $(top_builddir)/src/journal \ -I $(top_srcdir)/src/timedate \ -I $(top_srcdir)/src/timesync \ + -I $(top_srcdir)/src/nspawn \ -I $(top_srcdir)/src/resolve \ -I $(top_builddir)/src/resolve \ -I $(top_srcdir)/src/systemd \ @@ -2776,11 +2778,31 @@ systemd_cgtop_LDADD = \ # ------------------------------------------------------------------------------ systemd_nspawn_SOURCES = \ src/nspawn/nspawn.c \ + src/nspawn/nspawn-settings.c \ + src/nspawn/nspawn-settings.h \ + src/nspawn/nspawn-mount.c \ + src/nspawn/nspawn-mount.h \ + src/nspawn/nspawn-network.c \ + src/nspawn/nspawn-network.h \ + src/nspawn/nspawn-expose-ports.c \ + src/nspawn/nspawn-expose-ports.h \ + src/nspawn/nspawn-cgroup.c \ + src/nspawn/nspawn-cgroup.h \ + src/nspawn/nspawn-register.c \ + src/nspawn/nspawn-register.h \ + src/nspawn/nspawn-setuid.c \ + src/nspawn/nspawn-setuid.h \ src/core/mount-setup.c \ src/core/mount-setup.h \ src/core/loopback-setup.c \ src/core/loopback-setup.h +nodist_systemd_nspawn_SOURCES = \ + src/nspawn/nspawn-gperf.c + +gperf_gperf_sources += \ + src/nspawn/nspawn-gperf.gperf + systemd_nspawn_CFLAGS = \ $(AM_CFLAGS) \ $(BLKID_CFLAGS) \ @@ -3486,7 +3508,7 @@ nodist_libudev_core_la_SOURCES = \ src/udev/keyboard-keys-to-name.h \ src/udev/net/link-config-gperf.c -gperf_gperf_sources = \ +gperf_gperf_sources += \ src/udev/net/link-config-gperf.gperf libudev_core_la_CFLAGS = \ @@ -5167,41 +5189,48 @@ libnetworkd_core_la_SOURCES = \ src/libsystemd-network/network-internal.h \ src/network/networkd.h \ src/network/networkd-link.h \ + src/network/networkd-link.c \ src/network/networkd-netdev.h \ - src/network/networkd-netdev-tunnel.h \ - src/network/networkd-netdev-veth.h \ - src/network/networkd-netdev-vxlan.h \ - src/network/networkd-netdev-vlan.h \ - src/network/networkd-netdev-macvlan.h \ - src/network/networkd-netdev-ipvlan.h \ - src/network/networkd-netdev-dummy.h \ - src/network/networkd-netdev-tuntap.h \ - src/network/networkd-netdev-bond.h \ - src/network/networkd-netdev-bridge.h \ src/network/networkd-netdev.c \ + src/network/networkd-netdev-tunnel.h \ src/network/networkd-netdev-tunnel.c \ + src/network/networkd-netdev-veth.h \ src/network/networkd-netdev-veth.c \ + src/network/networkd-netdev-vxlan.h \ src/network/networkd-netdev-vxlan.c \ + src/network/networkd-netdev-vlan.h \ src/network/networkd-netdev-vlan.c \ + src/network/networkd-netdev-macvlan.h \ src/network/networkd-netdev-macvlan.c \ + src/network/networkd-netdev-ipvlan.h \ src/network/networkd-netdev-ipvlan.c \ + src/network/networkd-netdev-dummy.h \ src/network/networkd-netdev-dummy.c \ + src/network/networkd-netdev-tuntap.h \ src/network/networkd-netdev-tuntap.c \ + src/network/networkd-netdev-bond.h \ src/network/networkd-netdev-bond.c \ + src/network/networkd-netdev-bridge.h \ src/network/networkd-netdev-bridge.c \ - src/network/networkd-link.c \ src/network/networkd-link-bus.c \ src/network/networkd-ipv4ll.c \ src/network/networkd-dhcp4.c \ src/network/networkd-dhcp6.c \ + src/network/networkd-network.h \ src/network/networkd-network.c \ src/network/networkd-network-bus.c \ + src/network/networkd-address.h \ src/network/networkd-address.c \ + src/network/networkd-route.h \ src/network/networkd-route.c \ src/network/networkd-manager.c \ src/network/networkd-manager-bus.c \ + src/network/networkd-fdb.h \ src/network/networkd-fdb.c \ - src/network/networkd-address-pool.c + src/network/networkd-address-pool.h \ + src/network/networkd-address-pool.c \ + src/network/networkd-util.h \ + src/network/networkd-util.c nodist_libnetworkd_core_la_SOURCES = \ src/network/networkd-network-gperf.c \ diff --git a/NEWS b/NEWS index 1baa9aa11..6803c6588 100644 --- a/NEWS +++ b/NEWS @@ -1,27 +1,195 @@ systemd System and Service Manager -CHANGES WITH 225: +CHANGES WITH 226: + + * The DHCP implementation of systemd-networkd gained a set of + new features: + + - The DHCP server now supports emitting DNS and NTP + information. It may be enabled and configured via + EmitDNS=, DNS=, EmitNTP=, and NTP=. If transmission of DNS + and NTP information is enabled, but no servers are + configured, the corresponding uplink information (if there + is any) is propagated. + + - Server and client now support transmission and reception + of timezone information. It can be configured via the + newly introduced network options UseTimezone=, + EmitTimezone=, and Timezone=. Transmission of timezone + information is enabled between host and containers by + default now: the container will change its local timezone + to what the host has set. + + - Lease timeouts can now be configured via + MaxLeaseTimeSec= and DefaultLeaseTimeSec=. + + - The DHCP server improved on the stability of + leases. Clients are more likely to get the same lease + information back, even if the server loses state. + + - The DHCP server supports two new configuration options to + control the lease address pool metrics, PoolOffset= and + PoolSize=. + + * The encapsulation limit of tunnels in systemd-networkd may + now be configured via 'EncapsulationLimit='. It allows + modifying the maximum additional levels of encapsulation + that are permitted to be prepended to a packet. + + * systemd now supports the concept of user buses replacing + session buses, if used with dbus-1.10 (and enabled via dbus + --enable-user-session). It previously only supported this on + kdbus-enabled systems, and this release expands this to + 'dbus-daemon' systems. + + * systemd-networkd now supports predictable interface names + for virtio devices. + + * systemd now optionally supports the new Linux kernel + "unified" control group hierarchy. If enabled via the kernel + command-line option 'systemd.unified_cgroup_hierarchy=1', + systemd will try to mount the unified cgroup hierarchy + directly on /sys/fs/cgroup. If not enabled, or not + available, systemd will fall back to the legacy cgroup + hierarchy setup, as before. Host system and containers can + mix and match legacy and unified hierarchies as they + wish. nspawn understands the $UNIFIED_CROUP_HIERARCHY + environment variable to individually select the hierarchy to + use for executed containers. By default, nspawn will use the + unified hierarchy for the containers if the host uses the + unified hierarchy, and the legacy hierarchy otherwise. + Please note that at this point the unified hierarchy is an + experimental kernel feature and is likely to change in one + of the next kernel releases. Therefore, it should not be + enabled by default in downstream distributions yet. The + minimum required kernel version for the unified hierarchy to + work is 4.2. Note that when the unified hierarchy is used + for the first time delegated access to controllers is + safe. Because of this systemd-nspawn containers will get + access to controllers now, as will systemd user + sessions. This means containers and user sessions may now + manage their own resources, partitioning up what the system + grants them. + + * A new special scope unit "init.scope" has been introduced + that encapsulates PID 1 of the system. It may be used to + determine resource usage and enforce resource limits on PID + 1 itself. PID 1 hence moved out of the root of the control + group tree. + + * The cgtop tool gained support for filtering out kernel + threads when counting tasks in a control group. Also, the + count of processes is now recursively summed up by + default. Two options -k and --recursive= have been added to + revert to old behaviour. The tool has also been updated to + work correctly in containers now. + + * systemd-nspawn's --bind= and --bind-ro= options have been + extended to allow creation of non-recursive bind mounts. + + * libsystemd gained two new calls sd_pid_get_cgroup() and + sd_peer_get_cgroup() which return the control group path of + a process or peer of a connected AF_UNIX socket. This + function call is particularly useful when implementing + delegated subtrees support in the control group hierarchy. + + * The "sd-event" event loop API of libsystemd now supports + correct dequeuing of real-time signals, without losing + signal events. + + * When systemd requests a PolicyKit decision when managing + units it will now add additional fields to the request, + including unit name and desired operation. This enables more + powerful PolicyKit policies, that make decisions depending + on these parameters. + + * nspawn learnt support for .nspawn settings files, that may + accompany the image files or directories of containers, and + may contain additional settings for the container. This is + an alternative to configuring container parameters via the + nspawn command line. + + Contributions from: Cristian Rodríguez, Daniel Mack, David + Herrmann, Eugene Yakubovich, Evgeny Vereshchagin, Filipe + Brandenburger, Hans de Goede, Jan Alexander Steffens, Jan + Synacek, Kay Sievers, Lennart Poettering, Mangix, Marcel + Holtmann, Martin Pitt, Michael Biebl, Michael Chapman, Michal + Sekletar, Peter Hutterer, Piotr Drąg, reverendhomer, Robin + Hack, Susant Sahani, Sylvain Pasche, Thomas Hindoe Paaboel + Andersen, Tom Gundersen, Torstein Husebø + + -- Berlin, 2015-09-08 - * machinectl gained a new verb 'shell' which opens a fresh shell on the - target machine. It is similar to 'login', but spawns the shell - directly. The pseudo machine '.host' now refers to the local host and - is used by default. Hence, 'machinectl shell' can be used as - replacement for 'su' which spawns the session as a fresh systemd - unit. +CHANGES WITH 225: - * systemd-networkd learned to cope with private-zone DHCP options and - allows other programs to query the values. + * machinectl gained a new verb 'shell' which opens a fresh + shell on the target container or the host. It is similar to + the existing 'login' command of machinectl, but spawns the + shell directly without prompting for username or + password. The pseudo machine '.host' now refers to the local + host and is used by default. Hence, 'machinectl shell' can + be used as replacement for 'su -' which spawns a session as + a fresh systemd unit in a way that is fully isolated from + the originating session. + + * systemd-networkd learned to cope with private-zone DHCP + options and allows other programs to query the values. + + * SELinux access control when enabling/disabling units is no + longer enforced with this release. The previous + implementation was incorrect, and a new corrected + implementation is not yet available. As unit file operations + are still protected via PolicyKit and D-Bus policy this is + not a security problem. Yet, distributions which care about + optimal SELinux support should probably not stabilize on + this release. + + * sd-bus gained support for matches of type "arg0has=", that + test for membership of strings in string arrays sent in bus + messages. - Contributions from: Alastair Hughes, Alex Crawford, Daniel Mack, David - Herrmann, Dimitri John Ledkov, Eric Kostrowski, Evgeny Vereshchagin, - Felipe Sateler, HATAYAMA Daisuke, Jan Pokorný, Jan Synacek, Johnny - Robeson, Karel Zak, Kay Sievers, Kefeng Wang, Lennart Poettering, Major - Hayden, Marcel Holtmann, Markus Elfring, Martin Mikkelsen, Martin Pitt, - Matt Turner, Maxim Mikityanskiy, Michael Biebl, Namhyung Kim, Nicolas - Cornu, Owen W. Taylor, Patrik Flykt, Peter Hutterer, reverendhomer, - Richard Maw, Ronny Chevalier, Seth Jennings, Stef Walter, Susant Sahani, - Thomas Blume, Thomas Hindoe Paaboel Andersen, Thomas Meyer, Tom - Gundersen, Vincent Batts, WaLyong Cho, Zbigniew Jędrzejewski-Szmek + * systemd-resolved now dumps the contents of its DNS and LLMNR + caches to the logs on reception of the SIGUSR1 signal. This + is useful to debug DNS behaviour. + + * The coredumpctl tool gained a new --directory= option to + operate on journal files in a specific directory. + + * "systemctl reboot" and related commands gained a new + "--message=" option which may be used to set a free-text + wall message when shutting down or rebooting the + system. This message is also logged, which is useful for + figuring out the reason for a reboot or shutdown a + posteriori. + + * The "systemd-resolve-host" tool's -i switch now takes + network interface numbers as alternative to interface names. + + * A new unit file setting for services has been introduced: + UtmpMode= allows configuration of how precisely systemd + handles utmp and wtmp entries for the service if this is + enabled. This allows writing services that appear similar to + user sessions in the output of the "w", "who", "last" and + "lastlog" tools. + + * systemd-resolved will now locally synthesize DNS resource + records for the "localhost" and "gateway" domains as well as + the local hostname. This should ensure that clients querying + RRs via resolved will get similar results as those going via + NSS, if nss-myhostname is enabled. + + Contributions from: Alastair Hughes, Alex Crawford, Daniel + Mack, David Herrmann, Dimitri John Ledkov, Eric Kostrowski, + Evgeny Vereshchagin, Felipe Sateler, HATAYAMA Daisuke, Jan + Pokorný, Jan Synacek, Johnny Robeson, Karel Zak, Kay Sievers, + Kefeng Wang, Lennart Poettering, Major Hayden, Marcel + Holtmann, Markus Elfring, Martin Mikkelsen, Martin Pitt, Matt + Turner, Maxim Mikityanskiy, Michael Biebl, Namhyung Kim, + Nicolas Cornu, Owen W. Taylor, Patrik Flykt, Peter Hutterer, + reverendhomer, Richard Maw, Ronny Chevalier, Seth Jennings, + Stef Walter, Susant Sahani, Thomas Blume, Thomas Hindoe + Paaboel Andersen, Thomas Meyer, Tom Gundersen, Vincent Batts, + WaLyong Cho, Zbigniew Jędrzejewski-Szmek -- Berlin, 2015-08-27 @@ -30,9 +198,9 @@ CHANGES WITH 224: * The systemd-efi-boot-generator functionality was merged into systemd-gpt-auto-generator. - * systemd-networkd now supports Group Policy for vxlan devices. It can - be enabled via the new boolean configuration option called - 'GroupPolicyExtension='. + * systemd-networkd now supports Group Policy for vxlan + devices. It can be enabled via the new boolean configuration + option called 'GroupPolicyExtension='. Contributions from: Andreas Kempf, Christian Hesse, Daniel Mack, David Herrmann, Herman Fries, Johannes Nixdorf, Kay Sievers, Lennart diff --git a/README b/README index d84bf8ea6..27cc9a016 100644 --- a/README +++ b/README @@ -36,8 +36,8 @@ LICENSE: - except src/udev/* which is (currently still) GPLv2, GPLv2+ REQUIREMENTS: - Linux kernel >= 3.7 - Linux kernel >= 3.8 for Smack support + Linux kernel >= 3.11 + Linux kernel >= 4.2 for unified cgroup hierarchy support Kernel Config Options: CONFIG_DEVTMPFS diff --git a/TODO b/TODO index ce50f1655..4fdecebd0 100644 --- a/TODO +++ b/TODO @@ -26,16 +26,14 @@ External: Features: +* sd-event: maybe add support for inotify events + * PID 1 should send out sd_notify("WATCHDOG=1") messages (for usage in the --user mode, and when run via nspawn) * nspawn should send out sd_notify("WATCHDOG=1") messages * nspawn should optionally support receiving WATCHDOG=1 messages from its payload PID 1... -* introduce "machinectl shell" that is like systemd-run -M foo /bin/bash -t but also adds PAMName=login - -* allow loging into host with "machinectl login". - * consider throwing a warning if a service declares it wants to be "Before=" a .device unit. * "systemctl edit" should know a mode to create a new unit file @@ -65,10 +63,6 @@ Features: * logind: follow PropertiesChanged state more closely, to deal with quick logouts and relogins -* change to KillMode=mixed by default - -* introduce argv0contains= - * invent a better systemd-run scheme for naming scopes, that works with remoting * add journalctl -H that talks via ssh to a remote peer and passes through binary logs data @@ -209,8 +203,6 @@ Features: * "machinectl list-images" should show os-release data, as well as machine-info data (including deployment level) -* nspawn: when start a container "foobar" look for its configuration in a file "foobar.nspawn" in /etc/systemd/nspawn/ as well as next to the actualy directory or image to boot - * Port various tools to make use of verbs.[ch], where applicable * "machinectl history" @@ -256,8 +248,6 @@ Features: * figure out when we can use the coarse timers -* sd-resolve: drop res_query wrapping, people should call via the bus to resolved instead - * add "systemctl start -v foobar.service" that shows logs of a service while the start command runs. This is non-trivial to do without races though, since we should flush out all journal messages before @@ -322,26 +312,22 @@ Features: (throughout the codebase, not only PID1) * networkd: - - make DHCP server IP range configurable, including only with a single IP address - - dhcp server: try to assign stable IP addresses based on client's MAC address - add LLDP client side support - the DHCP lease data (such as NTP/DNS) is still made available when a carrier is lost on a link. It should be removed instantly. - expose in the API the following bits: - option 15, domain name and/or option 119, search list - option 12, host name and/or option 81, fqdn - - option 100, 101, timezone - option 123, 144, geolocation - option 252, configure http proxy (PAC/wpad) - - networkd's dhcp server should transparently pass on the DNS and - NTP server list it got from user configuration and its dhcp client - to clients. It should also pass on its own timezone information. - provide a way to define a per-network interface default metric value for all routes to it. possibly a second default for DHCP routes. - allow Name= to be specified repeatedly in the [Match] section. Maybe also support Name=foo*|bar*|baz ? - duplicate address check for static IPs (like ARPCHECK in network-scripts) - allow DUID/IAID to be customized, see issue #394. + - support configuration option for TSO (tcp segmentation offload) + - networkd: whenever uplink info changes, make DHCP server send out FORCERENEW * resolved: - put networkd events and rtnl events at a higher priority, so that @@ -473,7 +459,6 @@ Features: * sd-bus: - EBADSLT handling - - change argv list matching logic - GetAllProperties() on a non-existing object does not result in a failure currently - kdbus: process fd=-1 for incoming msgs - port to sd-resolve for connecting to TCP dbus servers @@ -895,7 +880,6 @@ Features: * dhcp: - figure out how much we can increase Maximum Message Size - - export timezone information - support RFC4702 (pass FQDN) * dhcp6: diff --git a/configure.ac b/configure.ac index d9ab3624d..2024939ad 100644 --- a/configure.ac +++ b/configure.ac @@ -20,7 +20,7 @@ AC_PREREQ([2.64]) AC_INIT([systemd], - [225], + [226], [http://github.com/systemd/systemd/issues], [systemd], [http://www.freedesktop.org/wiki/Software/systemd]) @@ -39,9 +39,14 @@ AM_SILENT_RULES([yes]) AC_CANONICAL_HOST AC_DEFINE_UNQUOTED([CANONICAL_HOST], "$host", [Canonical host string.]) -AC_CHECK_TOOLS([AR], [gcc-ar ar], [:]) -AC_CHECK_TOOLS([NM], [gcc-nm nm], [:]) -AC_CHECK_TOOLS([RANLIB], [gcc-ranlib ranlib], [:]) +AC_PROG_CC_C99 + +AX_COMPILER_VENDOR +AS_IF([test "x$ax_cv_c_compiler_vendor" = "xgnu"], [ + AC_CHECK_TOOLS([AR], [gcc-ar ar], [:]) + AC_CHECK_TOOLS([NM], [gcc-nm nm], [:]) + AC_CHECK_TOOLS([RANLIB], [gcc-ranlib ranlib], [:]) +]) LT_PREREQ(2.2) LT_INIT([disable-static]) @@ -87,8 +92,6 @@ AC_PROG_SED AC_PROG_GREP AC_PROG_AWK -AC_PROG_CC_C99 - AC_PATH_PROG([M4], [m4]) AC_PATH_PROG([XSLTPROC], [xsltproc]) @@ -1552,7 +1555,6 @@ AC_MSG_RESULT([ SysV init scripts: ${SYSTEM_SYSVINIT_PATH} SysV rc?.d directories: ${SYSTEM_SYSVRCND_PATH} Build Python: ${PYTHON} - sphinx binary: ${SPHINX_BUILD} PAM modules dir: ${with_pamlibdir} PAM configuration dir: ${with_pamconfdir} D-Bus policy dir: ${with_dbuspolicydir} diff --git a/hwdb/20-bluetooth-vendor-product.hwdb b/hwdb/20-bluetooth-vendor-product.hwdb index 5835d9a10..5a7f28729 100644 --- a/hwdb/20-bluetooth-vendor-product.hwdb +++ b/hwdb/20-bluetooth-vendor-product.hwdb @@ -1898,3 +1898,36 @@ bluetooth:v0275* bluetooth:v0276* ID_VENDOR_FROM_DATABASE=E.G.O. Elektro-Gertebau GmbH + +bluetooth:v0277* + ID_VENDOR_FROM_DATABASE=bewhere inc + +bluetooth:v0278* + ID_VENDOR_FROM_DATABASE=Johnson Outdoors Inc + +bluetooth:v0279* + ID_VENDOR_FROM_DATABASE=steute Schaltgerate GmbH & Co. KG + +bluetooth:v027A* + ID_VENDOR_FROM_DATABASE=Ekomini inc. + +bluetooth:v027B* + ID_VENDOR_FROM_DATABASE=DEFA AS + +bluetooth:v027C* + ID_VENDOR_FROM_DATABASE=Aseptika Ltd + +bluetooth:v027D* + ID_VENDOR_FROM_DATABASE=HUAWEI Technologies Co., Ltd. ( 华为技术有限公司 ) + +bluetooth:v027E* + ID_VENDOR_FROM_DATABASE=HabitAware, LLC + +bluetooth:v027F* + ID_VENDOR_FROM_DATABASE=ruwido austria gmbh + +bluetooth:v0280* + ID_VENDOR_FROM_DATABASE=ITEC corporation + +bluetooth:v0281* + ID_VENDOR_FROM_DATABASE=StoneL diff --git a/hwdb/70-mouse.hwdb b/hwdb/70-mouse.hwdb index a8bf055ba..5a9034040 100644 --- a/hwdb/70-mouse.hwdb +++ b/hwdb/70-mouse.hwdb @@ -124,6 +124,10 @@ mouse:usb:v04f2p0963:name:Chicony 2.4G Multimedia Wireless Kit: # Dell ########################################## +# Dell MUAR DEL7 +mouse:usb:v413cp3012:name:Dell Dell USB Optical Mouse: + MOUSE_DPI=400@166 + # Dell USB Laser Mouse mouse:usb:v046dpc063:name:DELL DELL USB Laser Mouse: MOUSE_DPI=1000@125 @@ -173,6 +177,10 @@ mouse:usb:v093ap2510:name:PIXART USB OPTICAL MOUSE: mouse:usb:v17efp6019:name:Lenovo Optical USB Mouse: MOUSE_DPI=1000@125 +# Lenovo M-U0025-O +mouse:usb:v17efp6019:name:Logitech Lenovo USB Optical Mouse: + MOUSE_DPI=1000@166 + # ThinkPad USB Laser Mouse mouse:usb:v17efp6044:name:ThinkPad USB Laser Mouse: MOUSE_DPI=1200@125 @@ -181,6 +189,13 @@ mouse:usb:v17efp6044:name:ThinkPad USB Laser Mouse: mouse:usb:v17efp6050:name:Lenovo Precision USB Mouse: MOUSE_DPI=1200@127 +# Lenovo MOBGUL +mouse:usb:v17efp601d:name:Primax Lenovo Laser Mouse: +# Lenovo MOBGULA +mouse:usb:v17efp6045:name:Lenovo USB Laser Mouse: + MOUSE_DPI=1600@125 + + ########################################## # Logitech ########################################## @@ -220,6 +235,8 @@ mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:400a: MOUSE_DPI=600@166 MOUSE_WHEEL_CLICK_ANGLE=20 +# Logitech MX400 Performance Laser Mouse +mouse:usb:v046dpc043:name:Logitech USB-PS/2 Optical Mouse: # Logitech MX1000 Laser Cordless Mouse mouse:usb:v046dpc50e:name:Logitech USB RECEIVER: # Logitech Cordless Click! Plus @@ -234,16 +251,16 @@ mouse:usb:v046dpc01e:name:Logitech USB-PS/2 Optical Mouse: # Logitech, Inc. RX 250 Optical Mouse mouse:usb:v046dpc050:name:Logitech USB-PS/2 Optical Mouse: - MOUSE_DPI=800@142 + MOUSE_DPI=1000@142 # Logitech Wireless Mouse M185 mouse:usb:v046dp4008:name:Logitech M185: mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:4008: +# Logitech Wireless Mouse M510 +mouse:usb:v046dp1025:name:Logitech M510: # Logitech M705 (marathon mouse) mouse:usb:v046dp101b:name:Logitech M705: mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:101b: -# Logitech Performance MX -mouse:usb:v046dp101a:name:Logitech Performance MX: MOUSE_DPI=800@166 # Logitech MX Revolution @@ -274,14 +291,24 @@ mouse:usb:v046dp101d:name:Logitech M505: mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:101d: MOUSE_DPI=900@166 +# Logitech Cordless Desktop Wave Mouse +mouse:usb:v046dpc517:name:Logitech USB Receiver: + MOUSE_DPI=950@125 + # Logitech RX1000 Laser Mouse mouse:usb:v046dpc046:name:Logitech USB Optical Mouse: # Logitech M100 Optical Mouse mouse:usb:v046dpc05a:name:Logitech USB Optical Mouse: # Logitech USB Laser Mouse M-U0011-O rebranded as "terra Laser" mouse:usb:v046dpc065:name:Logitech USB Laser Mouse: +# Logitech V500 Cordless Notebook Mouse +mouse:usb:v046dpc510:name:Logitech USB Receiver: MOUSE_DPI=1000@125 +# Logitech V220 Cordless Optical Mouse +mouse:usb:v046dpc51b:name:Logitech USB Receiver: +# Logitech Performance MX +mouse:usb:v046dp101a:name:Logitech Performance MX: # Logitech MX Master mouse:usb:v046dp4041:name:Logitech MX Master: MOUSE_DPI=1000@166 @@ -296,6 +323,10 @@ mouse:usb:v046dpc06b:name:Logitech G700 Laser Mouse: mouse:usb:v046dpc531:name:Logitech USB Receiver: MOUSE_DPI=*1000@500 3800@500 500@1000 1500@1000 2000@1000 +# Logitech Wireless Mouse M310 +mouse:usb:v046dp1024:name:Logitech M310: + MOUSE_DPI=1100@168 + # Logitech USB Laser Mouse M-UAS144 [LS1 Laser Mouse] mouse:usb:v046dpc062:name:Logitech USB Laser Mouse: # Logitech USB Laser Mouse M-U0007 @@ -321,6 +352,10 @@ mouse:usb:v046dpc52b:name:Logitech Unifying Device. Wireless PID:4026: mouse:usb:v046dpc068:name:Logitech G500: MOUSE_DPI=*1600@500 2600@500 3600@500 +# Logitech MX1000 Laser Cordless Mouse +mouse:bluetooth:v046dpb003:name:Logitech MX1000 mouse: + MOUSE_DPI=800@80 + # Logitech Ultrathin Touch Mouse mouse:bluetooth:v046dpb00d:name:Ultrathin Touch Mouse: MOUSE_DPI=1000@1000 @@ -347,6 +382,8 @@ mouse:usb:v045ep0040:name:Microsoft Microsoft 3-Button Mouse with IntelliEye(TM) mouse:usb:v045ep0745:name:Microsoft Microsoft® 2.4GHz Transceiver v6.0: MOUSE_DPI=800@142 +# Microsoft Wireless Mobile Mouse 4000 +mouse:usb:v045ep0745:name:Microsoft Microsoft® Nano Transceiver v2.0: # Microsoft Sculpt Ergonomic Mouse mouse:usb:v045ep07a5:name:Microsoft Microsoft® 2.4GHz Transceiver v9.0: MOUSE_DPI=1000@142 @@ -363,6 +400,15 @@ mouse:bluetooth:v045ep0702:name:Microsoft Wireless Laser Mouse 8000: mouse:bluetooth:v045ep07f3:name:Arc Touch Mouse SE: MOUSE_DPI=1000@2000 +########################################## +# Mionix +########################################## + +#Mionix Avior 7000 +mouse:usb:v22d4p1308:name:Laview Technology Mionix Avior 7000: + MOUSE_DPI=400@1000 *1600@1000 7000@1000 + MOUSE_WHEEL_CLICK_ANGLE=15 + ########################################## # Oklick ########################################## diff --git a/hwdb/70-pointingstick.hwdb b/hwdb/70-pointingstick.hwdb index 775ec7fea..8e674b112 100644 --- a/hwdb/70-pointingstick.hwdb +++ b/hwdb/70-pointingstick.hwdb @@ -88,6 +88,8 @@ evdev:name:*DualPoint Stick:dmi:bvn*:bvr*:bd*:svnDellInc.:pnLatitudeE6400*:pvr* # Lenovo ######################################### +# Lenovo Thinkpad X230 +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX230:* # Lenovo Thinkpad X230 tablet evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX230Tablet:* # Lenovo Thinkpad X240 @@ -96,6 +98,8 @@ evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadX240 evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT440s:* # Lenovo Thinkpad T540p evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT540p:* +# Lenovo Thinkpad T550 / W550s +evdev:name:TPPS/2 IBM TrackPoint:dmi:bvn*:bvr*:bd*:svnLENOVO:pn*:pvrThinkPadT550:* POINTINGSTICK_SENSITIVITY=200 POINTINGSTICK_CONST_ACCEL=1.0 diff --git a/m4/ax_compiler_vendor.m4 b/m4/ax_compiler_vendor.m4 new file mode 100644 index 000000000..39ca3c0f3 --- /dev/null +++ b/m4/ax_compiler_vendor.m4 @@ -0,0 +1,87 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_compiler_vendor.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_COMPILER_VENDOR +# +# DESCRIPTION +# +# Determine the vendor of the C/C++ compiler, e.g., gnu, intel, ibm, sun, +# hp, borland, comeau, dec, cray, kai, lcc, metrowerks, sgi, microsoft, +# watcom, etc. The vendor is returned in the cache variable +# $ax_cv_c_compiler_vendor for C and $ax_cv_cxx_compiler_vendor for C++. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2008 Matteo Frigo +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 15 + +AC_DEFUN([AX_COMPILER_VENDOR], +[AC_CACHE_CHECK([for _AC_LANG compiler vendor], ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor, + dnl Please add if possible support to ax_compiler_version.m4 + [# note: don't check for gcc first since some other compilers define __GNUC__ + vendors="intel: __ICC,__ECC,__INTEL_COMPILER + ibm: __xlc__,__xlC__,__IBMC__,__IBMCPP__ + pathscale: __PATHCC__,__PATHSCALE__ + clang: __clang__ + cray: _CRAYC + fujitsu: __FUJITSU + gnu: __GNUC__ + sun: __SUNPRO_C,__SUNPRO_CC + hp: __HP_cc,__HP_aCC + dec: __DECC,__DECCXX,__DECC_VER,__DECCXX_VER + borland: __BORLANDC__,__CODEGEARC__,__TURBOC__ + comeau: __COMO__ + kai: __KCC + lcc: __LCC__ + sgi: __sgi,sgi + microsoft: _MSC_VER + metrowerks: __MWERKS__ + watcom: __WATCOMC__ + portland: __PGI + tcc: __TINYC__ + unknown: UNKNOWN" + for ventest in $vendors; do + case $ventest in + *:) vendor=$ventest; continue ;; + *) vencpp="defined("`echo $ventest | sed 's/,/) || defined(/g'`")" ;; + esac + AC_COMPILE_IFELSE([AC_LANG_PROGRAM(,[ + #if !($vencpp) + thisisanerror; + #endif + ])], [break]) + done + ax_cv_[]_AC_LANG_ABBREV[]_compiler_vendor=`echo $vendor | cut -d: -f1` + ]) +]) diff --git a/man/networkctl.xml b/man/networkctl.xml index 388afbed9..46dab58d6 100644 --- a/man/networkctl.xml +++ b/man/networkctl.xml @@ -87,6 +87,7 @@ + diff --git a/man/nss-myhostname.xml b/man/nss-myhostname.xml index b7b7e1b55..4481fdf8c 100644 --- a/man/nss-myhostname.xml +++ b/man/nss-myhostname.xml @@ -111,8 +111,8 @@ Here's an example /etc/nsswitch.conf file, that enables myhostname correctly: -passwd: compat -group: compat +passwd: compat mymachines +group: compat mymachines shadow: compat hosts: files resolve mymachines myhostname diff --git a/man/nss-resolve.xml b/man/nss-resolve.xml index dd402b359..7d291b83c 100644 --- a/man/nss-resolve.xml +++ b/man/nss-resolve.xml @@ -82,8 +82,8 @@ Here's an example /etc/nsswitch.conf file, that enables resolve correctly: -passwd: compat -group: compat +passwd: compat mymachines +group: compat mymachines shadow: compat hosts: files resolve mymachines myhostname diff --git a/man/os-release.xml b/man/os-release.xml index 4ca2e5970..d2e259820 100644 --- a/man/os-release.xml +++ b/man/os-release.xml @@ -214,10 +214,11 @@ CPE_NAME= - A CPE name for the operating system, following - the Common + A CPE name for the operating system, in URI + binding syntax, following the + Common Platform Enumeration Specification as proposed by the - MITRE Corporation. This field is optional. Example: + NIST. This field is optional. Example: CPE_NAME="cpe:/o:fedoraproject:fedora:17" diff --git a/man/sd_get_seats.xml b/man/sd_get_seats.xml index 4390d36eb..f1981f7ea 100644 --- a/man/sd_get_seats.xml +++ b/man/sd_get_seats.xml @@ -115,6 +115,29 @@ errno-style error code. + + + Errors + + Returned errors may indicate the following problems: + + + + + -EINVAL + + An input parameter was invalid (out of range, + or NULL, where that's not accepted). + + + + -ENOMEM + + Memory allocation failed. + + + + Notes diff --git a/man/sd_login_monitor_new.xml b/man/sd_login_monitor_new.xml index a7b47a320..a8854dd59 100644 --- a/man/sd_login_monitor_new.xml +++ b/man/sd_login_monitor_new.xml @@ -161,20 +161,20 @@ is no timeout to wait for this will fill in (uint64_t) -1 instead. Note that poll() takes a relative timeout in milliseconds rather than an absolute timeout - in microseconds. To convert the absolute 'us' timeout into + in microseconds. To convert the absolute 'µs' timeout into relative 'ms', use code like the following: uint64_t t; int msec; sd_login_monitor_get_timeout(m, &t); if (t == (uint64_t) -1) - msec = -1; + msec = -1; else { - struct timespec ts; - uint64_t n; - clock_getttime(CLOCK_MONOTONIC, &ts); - n = (uint64_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000; - msec = t > n ? (int) ((t - n + 999) / 1000) : 0; + struct timespec ts; + uint64_t n; + clock_getttime(CLOCK_MONOTONIC, &ts); + n = (uint64_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000; + msec = t > n ? (int) ((t - n + 999) / 1000) : 0; } The code above does not do any error checking for brevity's @@ -203,6 +203,29 @@ else { always returns NULL. + + Errors + + Returned errors may indicate the following problems: + + + + + -EINVAL + + An input parameter was invalid (out of range, + or NULL, where that's not accepted). The specified category to + watch is not known. + + + + -ENOMEM + + Memory allocation failed. + + + + Notes diff --git a/man/sd_machine_get_class.xml b/man/sd_machine_get_class.xml index 5b881ccea..9ad7f3fc6 100644 --- a/man/sd_machine_get_class.xml +++ b/man/sd_machine_get_class.xml @@ -56,7 +56,7 @@ int sd_machine_get_class const char* machine - char *class + char **class @@ -98,6 +98,35 @@ code. + + Errors + + Returned errors may indicate the following problems: + + + + + -ENXIO + + The specified machine does not exist or is currently not running. + + + + + -EINVAL + + An input parameter was invalid (out of range, + or NULL, where that's not accepted). + + + + -ENOMEM + + Memory allocation failed. + + + + Notes diff --git a/man/sd_pid_get_session.xml b/man/sd_pid_get_session.xml index 9c6706caf..035effcaa 100644 --- a/man/sd_pid_get_session.xml +++ b/man/sd_pid_get_session.xml @@ -1,4 +1,4 @@ - + @@ -50,6 +50,7 @@ sd_pid_get_machine_name sd_pid_get_slice sd_pid_get_user_slice + sd_pid_get_cgroup sd_peer_get_session sd_peer_get_unit sd_peer_get_user_unit @@ -57,6 +58,7 @@ sd_peer_get_machine_name sd_peer_get_slice sd_peer_get_user_slice + sd_peer_get_cgroup Determine session, unit, owner of a session, container/VM or slice of a specific PID or socket peer @@ -108,6 +110,12 @@ char **slice + + int sd_pid_get_cgroup + pid_t pid + char **cgroup + + int sd_peer_get_session int fd @@ -149,6 +157,12 @@ int fd char **slice + + + int sd_peer_get_cgroup + int fd + char **cgroup + @@ -163,7 +177,7 @@ processes, user processes that are shared between multiple sessions of the same user, or kernel threads). For processes not being part of a login session this function will fail with - -ENXIO. The returned string needs to be freed with the libc + -ENODATA. The returned string needs to be freed with the libc free3 call after use. @@ -175,9 +189,9 @@ paths. Note that not all processes are part of a system unit/service (e.g. user processes, or kernel threads). For processes not being part of a systemd system unit this function - will fail with -ENXIO (More specifically: this call will not work - for kernel threads.) The returned string needs to be freed with - the libc free3 call after use. @@ -194,7 +208,7 @@ multiple login sessions of the same user, where sd_pid_get_session() will fail. For processes not being part of a login session and not being a shared process - of a user this function will fail with -ENXIO. + of a user this function will fail with -ENODATA. sd_pid_get_machine_name() may be used to determine the name of the VM or container is a member of. The @@ -203,7 +217,7 @@ free3 call after use. For processes not part of a VM or containers this - function fails with -ENXIO. + function fails with -ENODATA. sd_pid_get_slice() may be used to determine the slice unit the process is a member of. See @@ -217,6 +231,17 @@ returns the user slice (as managed by the user's systemd instance) of a process. + sd_pid_get_cgroup() returns the control + group path of the specified process, relative to the root of the + hierarchy. Returns the path without trailing slash, except for + processes located in the root control group, where "/" is + returned. To find the actual control group path in the file system + the returned path needs to be prefixed with + /sys/fs/cgroup/ (if the unified control group + setup is used), or + /sys/fs/cgroup/HIERARCHY/ + (if the legacy multi-hierarchy control group setup is used). + If the pid parameter of any of these functions is passed as 0, the operation is executed for the calling process. @@ -226,13 +251,14 @@ sd_peer_get_user_unit(), sd_peer_get_owner_uid(), sd_peer_get_machine_name(), - sd_peer_get_slice() and - sd_peer_get_user_slice() calls operate - similar to their PID counterparts, but operate on a connected - AF_UNIX socket and retrieve information about the connected peer - process. Note that these fields are retrieved via - /proc, and hence are not suitable for - authorization purposes, as they are subject to races. + sd_peer_get_slice(), + sd_peer_get_user_slice() and + sd_peer_get_cgroup() calls operate similar to + their PID counterparts, but operate on a connected AF_UNIX socket + and retrieve information about the connected peer process. Note + that these fields are retrieved via /proc, + and hence are not suitable for authorization purposes, as they are + subject to races. @@ -251,7 +277,22 @@ - -ENXIO + -ESRCH + + The specified PID does not refer to a running + process. + + + + + -BADF + + The specified socket file descriptor was + invalid. + + + + -ENODATA Given field is not specified for the described process or peer. @@ -259,11 +300,10 @@ - -ESRCH + -EINVAL - The specified PID does not refer to a running - process. - + An input parameter was invalid (out of range, + or NULL, where that's not accepted). diff --git a/man/sd_seat_get_active.xml b/man/sd_seat_get_active.xml index 3c57ec9ea..4d3e0822e 100644 --- a/man/sd_seat_get_active.xml +++ b/man/sd_seat_get_active.xml @@ -148,6 +148,43 @@ errno-style error code. + + Errors + + Returned errors may indicate the following problems: + + + + + -ENODATA + + Given field is not specified for the described + seat. + + + + + -ENXIO + + The specified seat is unknown. + + + + + -EINVAL + + An input parameter was invalid (out of range, + or NULL, where that's not accepted). + + + + -ENOMEM + + Memory allocation failed. + + + + Notes diff --git a/man/sd_session_is_active.xml b/man/sd_session_is_active.xml index 4ca3a6c15..7de952378 100644 --- a/man/sd_session_is_active.xml +++ b/man/sd_session_is_active.xml @@ -289,6 +289,43 @@ negative errno-style error code. + + Errors + + Returned errors may indicate the following problems: + + + + + -ENXIO + + The specified session does not exist. + + + + + -ENODATA + + Given field is not specified for the described + session. + + + + + -EINVAL + + An input parameter was invalid (out of range, + or NULL, where that's not accepted). + + + + -ENOMEM + + Memory allocation failed. + + + + Notes diff --git a/man/sd_uid_get_state.xml b/man/sd_uid_get_state.xml index b158f3528..13ddf08c6 100644 --- a/man/sd_uid_get_state.xml +++ b/man/sd_uid_get_state.xml @@ -169,6 +169,45 @@ errno-style error code. + + Errors + + Returned errors may indicate the following problems: + + + + + -ENODATA + + Given field is not specified for the described + user. + + + + + -ENXIO + + The specified seat is unknown. + + + + + -EINVAL + + An input parameter was invalid (out of range, + or NULL, where that's not accepted). This is also returned if + the passed user ID is 0xFFFF or 0xFFFFFFFF, which are + undefined on Linux. + + + + -ENOMEM + + Memory allocation failed. + + + + Notes diff --git a/man/standard-conf.xml b/man/standard-conf.xml index 004f53f70..ffc6f7629 100644 --- a/man/standard-conf.xml +++ b/man/standard-conf.xml @@ -30,7 +30,9 @@ the vendor, the recommended way is to place a symlink to /dev/null in the configuration directory in /etc/, with the same filename as the vendor - configuration file. + configuration file. If the vendor configuration file is included in + the initrd image, the image has to be regenerated. + diff --git a/man/systemctl.xml b/man/systemctl.xml index 20d143741..37ba4ab6d 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -918,6 +918,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service RequiresOverridable=, Requisite=, RequisiteOverridable=, + ConsistsOf=, Wants=, BindsTo= dependencies. If no unit is specified, default.target is implied. diff --git a/man/systemd-cgtop.xml b/man/systemd-cgtop.xml index d4b041a1f..0e0ea3ba7 100644 --- a/man/systemd-cgtop.xml +++ b/man/systemd-cgtop.xml @@ -1,4 +1,4 @@ - + @@ -64,10 +64,10 @@ regular intervals (by default every 1s), similar in style to top1. - If systemd-cgtop is not connected to a tty, no - column headers are printed and the default is to only run one iteration. - The --iterations argument, if given, is still honored. - This mode is suitable for scripting. + If systemd-cgtop is not connected to a + tty, no column headers are printed and the default is to only run + one iteration. The --iterations= argument, if + given, is honored. This mode is suitable for scripting. Resource usage is only accounted for control groups in the relevant hierarchy, i.e. CPU usage is only accounted for control @@ -104,6 +104,7 @@ + Order by control group path name. @@ -111,25 +112,28 @@ + - Order by number of tasks in control group - (i.e. threads and processes). + Order by number of processes in control group. + Order by CPU load. + Order by memory usage. + Order by disk I/O load. @@ -140,7 +144,7 @@ Run in "batch" mode: do not accept input and run until the iteration limit set with - is exhausted or until killed. + is exhausted or until killed. This mode could be useful for sending output from systemd-cgtop to other programs or to a file. @@ -155,12 +159,45 @@ numbers. + + + + + Controls whether the CPU usage is shown as + percentage or time. By default the CPU usage is shown as + percentage. This setting may also be toggled at runtime by + pressing the % key. + + + + + + Include kernel threads when counting tasks in + control groups. By default, kernel threads are not included in + the count. This setting may also be toggled at runtime by + pressing the k key. + + + + + + Controls whether the number of tasks shown for + a control group shall include all tasks that are contained in + any of the child control groups as well. Takes a boolean + argument, defaults to yes. If enabled the + tasks in child control groups are included, if disabled only + the tasks in the control group itself are counted. This + setting may also be toggled at runtime by pressing the + r key. + + - Perform only this many iterations. A value of 0 - indicates that the program should run indefinitely. + Perform only this many iterations. A value of + 0 indicates that the program should run + indefinitely. @@ -168,10 +205,11 @@ Specify refresh delay in seconds (or if one of - ms, - us, + ms, us, min is specified as unit in this time - unit). + unit). This setting may also be increased and decreased at + runtime by pressing the + and + - keys. @@ -191,7 +229,6 @@ - Keys @@ -200,49 +237,72 @@ - h + h Shows a short help text. - SPACE + Immediately refresh output. - q + q Terminate the program. - - p - t - c - m - i + p + t + c + m + i Sort the control groups by path, number of - tasks, CPU load, memory usage, or IO load, respectively. - + tasks, CPU load, memory usage, or IO load, respectively. This + setting may also be controlled using the + command line + switch. - % + % Toggle between showing CPU time as time or - percentage. + percentage. This setting may also be controlled using the + command line switch. - + - - + + + - Increase or decrease refresh delay, - respectively. + respectively. This setting may also be controlled using the + command line + switch. + + + + k + + Toggle between including or excluding kernel + threads in control group task counts. This setting may also be + controlled using the command line + switch. + + + + r + + Toggle between recursively including or + excluding tasks in child control groups in control group task + counts. This setting may also be controlled using the + command line + switch. diff --git a/man/systemd-detect-virt.xml b/man/systemd-detect-virt.xml index 40755a24d..9ea9141d4 100644 --- a/man/systemd-detect-virt.xml +++ b/man/systemd-detect-virt.xml @@ -88,7 +88,7 @@ - VM + VM qemu QEMU software virtualization @@ -133,6 +133,11 @@ User-mode Linux + + parallels + Parallels Desktop, Parallels Server + + container openvz diff --git a/man/systemd-gpt-auto-generator.xml b/man/systemd-gpt-auto-generator.xml index 27ec72c98..f569ea3cd 100644 --- a/man/systemd-gpt-auto-generator.xml +++ b/man/systemd-gpt-auto-generator.xml @@ -69,7 +69,7 @@ units are explicitly configured (for example, listed in fstab5), - the units this generator creates are overriden, but additional + the units this generator creates are overridden, but additional automatic dependencies might be created. This generator will only look for root partitions on the diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index 496674925..bc5dacd98 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -1,4 +1,4 @@ - + @@ -576,12 +576,15 @@ Bind mount a file or directory from the host - into the container. Either takes a path argument -- in which + into the container. Takes one of: a path argument -- in which case the specified path will be mounted from the host to the same path in the container --, or a colon-separated pair of paths -- in which case the first specified path is the source in the host, and the second path is the destination in the - container. Backslash escapes are interpreted so + container --, or a colon-separated triple of source path, + destination path and mount options. Mount options are comma + separated and currently only "rbind" and "norbind" + are allowed. Defaults to "rbind". Backslash escapes are interpreted so \: may be used to embed colons in either path. This option may be specified multiple times for creating multiple independent bind mount points. The @@ -745,34 +748,86 @@ - =MODE + + MODE Boots the container in volatile mode. When no mode parameter is passed or when mode is specified as - yes full volatile mode is enabled. This + full volatile mode is enabled. This means the root directory is mounted as mostly unpopulated tmpfs instance, and /usr from the OS tree is mounted into it, read-only (the system thus starts up with read-only OS resources, but pristine state and configuration, any changes to the either are lost on shutdown). When the mode parameter - is specified as state the OS tree is + is specified as the OS tree is mounted read-only, but /var is mounted as tmpfs instance into it (the system thus starts up with read-only OS resources and configuration, but pristine state, any changes to the latter are lost on shutdown). When the mode parameter is specified as - no (the default) the whole OS tree is made + (the default) the whole OS tree is made available writable. - Note that setting this to yes or - state will only work correctly with + Note that setting this to or + will only work correctly with operating systems in the container that can boot up with only /usr mounted, and are able to populate /var automatically, as needed. + + MODE + + Controls whether + systemd-nspawn shall search for and use + additional per-container settings from + .nspawn files. Takes a boolean or the + special values or + . + + If enabled (the default) a settings file named after the + machine (as specified with the + setting, or derived from the directory or image file name) + with the suffix .nspawn is searched in + /etc/systemd/nspawn/ and + /run/systemd/nspawn/. If it is found + there, its settings are read and used. If it is not found + there it is subsequently searched in the same directory as the + image file or in the immediate parent of the root directory of + the container. In this case, if the file is found its settings + will be also read and used, but potentially unsafe settings + are ignored. Note that in both these cases settings on the + command line take precedence over the corresponding settings + from loaded .nspawn files, if both are + specified. Unsafe settings are considered all settings that + elevate the container's privileges or grant access to + additional resources such as files or directories of the + host. For details about the format and contents of + .nspawn files consult + systemd.nspawn5. + + If this option is set to the + file is searched, read and used the same way, however the order of + precedence is reversed: settings read from the + .nspawn file will take precedence over + the corresponding command line options, if both are + specified. + + If this option is set to the + file is searched, read and used the same way, but regardless + if found in /etc/systemd/nspawn/, + /run/systemd/nspawn/ or next to the image + file or container root directory, all settings will take + effect, however command line arguments still take precedence + over corresponding settings. + + If disabled no .nspawn file is read + and no settings except the ones on the command line are in + effect. + + @@ -856,6 +911,7 @@ See Also systemd1, + systemd.nspawn5, chroot1, dnf8, yum8, diff --git a/man/systemd-path.xml b/man/systemd-path.xml index dfc75ee0f..4f790d2cd 100644 --- a/man/systemd-path.xml +++ b/man/systemd-path.xml @@ -64,9 +64,9 @@ When invoked without arguments a list of known paths and their current values is shown. When at least one argument is - passed the path with this is name is queried and its value shown. + passed the path with this name is queried and its value shown. The variables whose name begins with search- - don't refer to individual paths, but instead a to a list of + don't refer to individual paths, but instead to a list of colon-separated search paths, in their order of precedence. diff --git a/man/systemd-resolved.service.xml b/man/systemd-resolved.service.xml index 27662456e..96dc4f662 100644 --- a/man/systemd-resolved.service.xml +++ b/man/systemd-resolved.service.xml @@ -61,15 +61,22 @@ resolver and an LLMNR resolver and responder. It also generates /run/systemd/resolve/resolv.conf for compatibility which may be symlinked from - /etc/resolv.conf. + /etc/resolv.conf. The glibc NSS module + nss-resolve8 + is necessary to allow libc's NSS resolver functions to resolve + host names via systemd-resolved. The DNS servers contacted are determined from the global - settings in - resolved.conf5, - the per-link static settings in .network - files, and the per-link dynamic settings received over DHCP. See + settings in /etc/systemd/resolved.conf, the + per-link static settings in /etc/systemd/network/*.network files, + and the per-link dynamic settings received over DHCP. See + resolved.conf5 + and systemd.network5 - for more details. + for details. To improve compatibility + /etc/resolv.conf is read in order to discover + configured system DNS servers, however only if it is not a symlink + to /run/systemd/resolve/resolv.conf (see above). systemd-resolved synthesizes DNS RRs for the following cases: @@ -137,6 +144,7 @@ systemd1, resolved.conf5, + nss-resolve8, systemd.network5, systemd-networkd.service8 diff --git a/man/systemd-run.xml b/man/systemd-run.xml index 80db14870..b220e0dce 100644 --- a/man/systemd-run.xml +++ b/man/systemd-run.xml @@ -112,6 +112,13 @@ The following options are understood: + + + + Do not query the user for authentication for + privileged operations. + + diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index 2680627a7..05bbad7f6 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -534,6 +534,19 @@ + + EncapsulationLimit= + + The Tunnel Encapsulation Limit option specifies how many additional + levels of encapsulation are permitted to be prepended to the packet. + For example, a Tunnel Encapsulation Limit option containing a limit + value of zero means that a packet carrying that option may not enter + another tunnel before exiting the current tunnel. + (see RFC 2473). + The valid range is 0-255 and none. Defaults to 4. + + + Mode= diff --git a/man/systemd.network.xml b/man/systemd.network.xml index e8a164d22..2fb473349 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -1,4 +1,4 @@ - + @@ -223,7 +223,7 @@ DHCP= - Enables DHCPv4 and/or DHCPv6 support. Accepts + Enables DHCPv4 and/or DHCPv6 client support. Accepts yes, no, ipv4, or ipv6. @@ -235,9 +235,10 @@ DHCPServer= - A boolean. Enables a basic DHCPv4 server on the - device. Mostly useful for handing out leases to container - instances. + A boolean. Enables DHCPv4 server support. Defaults + to no. Further settings for the DHCP + server may be set in the [DHCPServer] + section described below. @@ -541,7 +542,9 @@ [DHCP] Section Options - The [DHCP] section accepts the following keys: + The [DHCP] section configures the + DHCPv4 and DHCP6 client, if it is enabled with the + DHCP= setting described above: @@ -552,7 +555,8 @@ any statically configured ones. This corresponds to the - option in resolv.conf5. + option in resolv.conf5. @@ -582,7 +586,7 @@ UseHostname= When true (the default), the hostname received from - the DHCP server will be used as the transient hostname. + the DHCP server will be set as the transient hostname of the system @@ -615,6 +619,15 @@ table with metric of 1024. + + + UseTimezone= + + When true, the timezone received from the + DHCP server will be set as as timezone of the local + system. Defaults to no. + + CriticalConnection= @@ -658,8 +671,111 @@ DHCP server. - + + + + + + [DHCPServer] Section Options + The [DHCPServer] section contains + settings for the DHCP server, if enabled via the + DHCPServer= option described above: + + + + + PoolOffset= + PoolSize= + + Configures the pool of addresses to hand out. The pool + is a contiguous sequence of IP addresses in the subnet configured for + the server address, which does not include the subnet nor the broadcast + address. PoolOffset= takes the offset of the pool + from the start of subnet, or zero to use the default value. + PoolSize= takes the number of IP addresses in the + pool or zero to use the default value. By default the pool starts at + the first address after the subnet address and takes up the rest of + the subnet, excluding the broadcast address. If the pool includes + the server address (the default), this is reserved and not handed + out to clients. + + + + DefaultLeaseTimeSec= + MaxLeaseTimeSec= + + Control the default and maximum DHCP lease + time to pass to clients. These settings take time values in seconds or + another common time unit, depending on the suffix. The default + lease time is used for clients that did not ask for a specific + lease time. If a client asks for a lease time longer than the + maximum lease time it is automatically shortened to the + specified time. The default lease time defaults to 1h, the + maximum lease time to 12h. Shorter lease times are beneficial + if the configuration data in DHCP leases changes frequently + and clients shall learn the new settings with shorter + latencies. Longer lease times reduce the generated DHCP + network traffic. + + + + EmitDNS= + DNS= + + Configures whether the DHCP leases handed out + to clients shall contain DNS server information. The + EmitDNS= setting takes a boolean argument + and defaults to yes. The DNS servers to + pass to clients may be configured with the + DNS= option, which takes a list of IPv4 + addresses. If the EmitDNS= option is + enabled but no servers configured the servers are + automatically propagated from an "uplink" interface that has + appropriate servers set. The "uplink" interface is determined + by the default route of the system with the highest + priority. Note that this information is acquired at the time + the lease is handed out, and does not take uplink interfaces + into account that acquire DNS or NTP server information at a + later point. DNS server propagation does not take + /etc/resolv.conf into account. Also, note + that the leases are not refreshed if uplink network + configuration changes. To ensure clients regularly acquire the + most current uplink DNS server information it is thus + advisable to shorten the DHCP lease time via + MaxLeaseTimeSec= described + above. + + + EmitNTP= + NTP= + + Similar to the EmitDNS= and + DNS= settings described above these + settings configure whether and what NTP server information + shall be emitted as part of the DHCP lease. The same syntax, + propagation semantics and defaults apply as for + EmitDNS= and + DNS=. + + + + EmitTimezone= + Timezone= + + Configures whether the DHCP leases handed out + to clients shall contain timezone information. The + EmitTimezone= setting takes a boolean + argument and defaults to yes. The + Timezone= setting takes a timezone string + (such as Europe/Berlin or + UTC) to pass to clients. If no explicit + timezone is set the system timezone of the local host is + propagated, as determined by the + /etc/localtime symlink. + + + diff --git a/man/systemd.nspawn.xml b/man/systemd.nspawn.xml new file mode 100644 index 000000000..7bfafb424 --- /dev/null +++ b/man/systemd.nspawn.xml @@ -0,0 +1,383 @@ + + +%entities; +]> + + + + + + + systemd.nspawn + systemd + + + + Developer + Lennart + Poettering + lennart@poettering.net + + + + + + systemd.nspawn + 5 + + + + systemd.nspawn + Container settings + + + + /etc/systemd/nspawn/machine.nspawn + /run/systemd/nspawn/machine.nspawn + /var/lib/machines/machine.nspawn + + + + Description + + An nspawn container settings file (suffix + .nspawn) encodes additional runtime + information about a local container, and is searched, read and + used by + systemd-nspawn1 + when starting a container. Files of this type are named after the + containers they define settings for. They are optional, and only + required for containers whose execution environment shall differ + from the defaults. Files of this type mostly contain settings that + may also be set on the systemd-nspawn command + line, and make it easier to persistently attach specific settings + to specific containers. The syntax of these files is inspired by + .desktop files following the XDG + Desktop Entry Specification, which are in turn inspired by + Microsoft Windows .ini files. + + Boolean arguments used in these settings files can be + written in various formats. For positive settings the strings + , , + and are equivalent. For negative settings, the + strings , , + and are + equivalent. + + Empty lines and lines starting with # or ; are + ignored. This may be used for commenting. Lines ending + in a backslash are concatenated with the following + line while reading and the backslash is replaced by a + space character. This may be used to wrap long lines. + + + + + <filename>.nspawn</filename> File Discovery + + Files are searched by appending the + .nspawn suffix to the machine name of the + container, as specified with the + switch of systemd-nspawn, or derived from the + directory or image file name. This file is first searched in + /etc/systemd/nspawn/ and + /run/systemd/nspawn/. If found in these + directories its settings are read and all of them take full effect + (but are possibly overridden by corresponding command line + arguments). If not found the file will then be searched next to + the image file or in the immediate parent of the root directory of + the container. If the file is found there only a subset of the + settings will take effect however. All settings that possibly + elevate privileges or grant additional access to resources of the + host (such as files or directories) are ignored. To which options + this applies is documented below. + + Persistent settings file created and maintained by the + administrator (and thus trusted) should be placed in + /etc/systemd/nspawn/, while automatically + downloaded (and thus potentially untrusted) settings files are + placed in /var/lib/machines/ instead (next to + the container images), where their security impact is limited. In + order to add privileged settings to .nspawn + files acquired from the image vendor it is recommended to copy the + settings files into /etc/systemd/nspawn/ and + edit them there, so that the privileged options become + available. The precise algorithm how the files are searched and + interpreted may be configured with + systemd-nspawn's + switch, see + systemd-nspawn1 + for details. + + + + [Exec] Section Options + + Settings files may include an [Exec] + section, which carries various execution parameters: + + + + + Boot= + + Takes a boolean argument, defaults to off. If + enabled systemd-nspawn will automatically + search for an init executable and invoke + it. In this case the specified parameters using + Parameters= are passed as additional + arguments to the init process. This + setting corresponds to the switch on + the systemd-nspawn command + line. + + + + Parameters= + + Takes a space separated list of + arguments. This is either a command line, beginning with the + binary name to execute, or – if Boot= is + enabled – the list of arguments to pass to the init + process. This setting corresponds to the command line + parameters passed on the systemd-nspawn + command line. + + + + Environment= + + Takes an environment variable assignment + consisting of key and value, separated by + =. Sets an environment variable for the + main process invoked in the container. This setting may be + used multiple times to set multiple environment variables. It + corresponds to the command line + switch. + + + + User= + + Takes a UNIX user name. Specifies the user + name to invoke the main process of the container as. This user + must be known in the container's user database. This + corresponds to the command line + switch. + + + + Capability= + DropCapability= + + Takes a space separated list of Linux process + capabilities (see + capabilities7 + for details). The Capability= setting + specifies additional capabilities to pass on top of the + default set of capabilities. The + DropCapability= setting specifies + capabilities to drop from the default set. These settings + correspond to the and + command line + switches. Note that Capability= is a + privileged setting, and only takes effect in + .nspawn files in + /etc/systemd/nspawn/ and + /run/system/nspawn/ (see above). On the + other hand DropCapability= takes effect in + all cases. + + + + Personality= + + Configures the kernel personality for the + container. This is equivalent to the + switch. + + + + MachineID= + + Configures the 128bit machine ID (UUID) to pass to + the container. This is equivalent to the + command line switch. This option is + privileged (see above). + + + + + + [Files] Section Options + + Settings files may include a [Files] + section, which carries various parameters configuring the file + system of the container: + + + + + ReadOnly= + + Takes a boolean argument, defaults to off. If + specified the container will be run with a read-only file + system. This setting corresponds to the + command line + switch. + + + + Volatile= + + Takes a boolean argument, or the special value + state. This configures whether to run the + container with volatile state and/or configuration. This + option is equivalent to , see + systemd-nspawn1 + for details about the specific options + supported. + + + + Bind= + BindReadOnly= + + Adds a bind mount from the host into the + container. Takes a single path, a pair of two paths separated + by a colon, or a triplet of two paths plus an option string + separated by colons. This option may be used multiple times to + configure multiple bind mounts. This option is equivalent to + the command line switches and + , see + systemd-nspawn1 + for details about the specific options supported. This setting + is privileged (see above). + + + + TemporaryFileSystem= + + Adds a tmpfs mount to the + container. Takes a path or a pair of path and option string, + separated by a colon. This option may be used multiple times to + configure multiple tmpfs mounts. This + option is equivalent to the command line switch + , see + systemd-nspawn1 + for details about the specific options supported. This setting + is privileged (see above). + + + + + + [Network] Section Options + + Settings files may include a [Network] + section, which carries various parameters configuring the network + connectivity of the container: + + + + + Private= + + Takes a boolean argument, defaults to off. If + enabled the container will run in its own network namespace + and not share network interfaces and configuration with the + host. This setting corresponds to the + command line + switch. + + + + VirtualEthernet= + + Takes a boolean argument. Configures whether + to create a virtual ethernet connection + (veth) between host and the container. This + setting implies Private=yes. This setting + corresponds to the command + line switch. This option is privileged (see + above). + + + + Interface= + + Takes a space separated list of interfaces to + add to the container. This option corresponds to the + command line switch and + implies Private=yes. This option is + privileged (see above). + + + + MACVLAN= + IPVLAN= + + Takes a space separated list of interfaces to + add MACLVAN or IPVLAN interfaces to, which are then added to + the container. These options correspond to the + and + command line switches and + imply Private=yes. These options are + privileged (see above). + + + + Bridge= + + Takes an interface name. This setting implies + VirtualEthernet=yes and + Private=yes and has the effect that the + host side of the created virtual Ethernet link is connected to + the specified bridge interface. This option corresponds to the + command line switch. This + option is privileged (see above). + + + + Port= + + Exposes a TCP or UDP port of the container on + the host. This option corresponds to the + command line switch, see + systemd-nspawn1 + for the precise syntax of the argument this option takes. This + option is privileged (see above). + + + + + + See Also + + systemd1, + systemd-nspawn1, + systemd.directives7 + + + + diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index 407f6d32e..ea58580bb 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -256,7 +256,7 @@ - Unit Load Path + Unit File Load Path Unit files are loaded from a set of paths determined during compilation, described in the two tables below. Unit files found diff --git a/network/80-container-host0.network b/network/80-container-host0.network index a778ecfef..b65cc6acb 100644 --- a/network/80-container-host0.network +++ b/network/80-container-host0.network @@ -12,3 +12,6 @@ Name=host0 [Network] DHCP=yes LinkLocalAddressing=yes + +[DHCP] +UseTimezone=yes diff --git a/po/POTFILES.in b/po/POTFILES.in index b4c1121d1..f33c53fb4 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -5,3 +5,4 @@ src/locale/org.freedesktop.locale1.policy.in src/login/org.freedesktop.login1.policy.in src/machine/org.freedesktop.machine1.policy.in src/timedate/org.freedesktop.timedate1.policy.in +src/core/dbus-unit.c diff --git a/po/pl.po b/po/pl.po index ff8053f17..d3e2ae841 100644 --- a/po/pl.po +++ b/po/pl.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-06-18 00:53+0200\n" -"PO-Revision-Date: 2015-04-12 01:44+0200\n" +"POT-Creation-Date: 2015-09-06 20:39+0200\n" +"PO-Revision-Date: 2015-09-06 20:40+0200\n" "Last-Translator: Piotr Drąg \n" "Language-Team: Polish \n" "Language: pl\n" @@ -430,6 +430,14 @@ msgstr "" "Wymagane jest uwierzytelnienie, aby wskazać oprogramowaniu sprzętowemu, że " "należy uruchomić interfejs ustawień." +#: ../src/login/org.freedesktop.login1.policy.in.h:55 +msgid "Set a wall message" +msgstr "Ustawienie komunikatu wall" + +#: ../src/login/org.freedesktop.login1.policy.in.h:56 +msgid "Authentication is required to set a wall message" +msgstr "Wymagane jest uwierzytelnienie, aby ustawić komunikat wall" + #: ../src/machine/org.freedesktop.machine1.policy.in.h:1 msgid "Log into a local container" msgstr "Logowanie do lokalnego kontenera" @@ -440,21 +448,68 @@ msgstr "" "Wymagane jest uwierzytelnienie, aby zalogować się do lokalnego kontenera." #: ../src/machine/org.freedesktop.machine1.policy.in.h:3 +msgid "Log into the local host" +msgstr "Logowanie do lokalnego komputera" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:4 +msgid "Authentication is required to log into the local host." +msgstr "" +"Wymagane jest uwierzytelnienie, aby zalogować się do lokalnego komputera." + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:5 +msgid "Acquire a shell in a local container" +msgstr "Uzyskanie powłoki w lokalnym kontenerze" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:6 +msgid "Authentication is required to acquire a shell in a local container." +msgstr "" +"Wymagane jest uwierzytelnienie, aby uzyskać powłokę w lokalnym kontenerze." + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:7 +msgid "Acquire a shell on the local host" +msgstr "Uzyskanie powłoki na lokalnym komputerze" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:8 +msgid "Authentication is required to acquire a shell on the local host." +msgstr "" +"Wymagane jest uwierzytelnienie, aby uzyskać powłokę na lokalnym komputerze." + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:9 +msgid "Acquire a pseudo TTY in a local container" +msgstr "Uzyskanie pseudo-TTY w lokalnym kontenerze" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:10 +msgid "" +"Authentication is required to acquire a pseudo TTY in a local container." +msgstr "" +"Wymagane jest uwierzytelnienie, aby uzyskać pseudo-TTY w lokalnym kontenerze." + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:11 +msgid "Acquire a pseudo TTY on the local host" +msgstr "Uzyskanie pseudo-TTY na lokalnym komputerze" + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:12 +msgid "Authentication is required to acquire a pseudo TTY on the local host." +msgstr "" +"Wymagane jest uwierzytelnienie, aby uzyskać pseudo-TTY na lokalnym " +"komputerze." + +#: ../src/machine/org.freedesktop.machine1.policy.in.h:13 msgid "Manage local virtual machines and containers" msgstr "Zarządzanie lokalnymi maszynami wirtualnymi i kontenerami" -#: ../src/machine/org.freedesktop.machine1.policy.in.h:4 +#: ../src/machine/org.freedesktop.machine1.policy.in.h:14 msgid "" "Authentication is required to manage local virtual machines and containers." msgstr "" "Wymagane jest uwierzytelnienie, aby zarządzać lokalnymi maszynami " "wirtualnymi i kontenerami." -#: ../src/machine/org.freedesktop.machine1.policy.in.h:5 +#: ../src/machine/org.freedesktop.machine1.policy.in.h:15 msgid "Manage local virtual machine and container images" msgstr "Zarządzanie lokalnymi obrazami maszyn wirtualnych i kontenerów" -#: ../src/machine/org.freedesktop.machine1.policy.in.h:6 +#: ../src/machine/org.freedesktop.machine1.policy.in.h:16 msgid "" "Authentication is required to manage local virtual machine and container " "images." @@ -502,6 +557,40 @@ msgstr "" "Wymagane jest uwierzytelnienie, aby kontrolować, czy włączyć synchronizację " "czasu przez sieć." +#: ../src/core/dbus-unit.c:428 +msgid "Authentication is required to start '$(unit)'." +msgstr "Wymagane jest uwierzytelnienie, aby uruchomić jednostkę „$(unit)”." + +#: ../src/core/dbus-unit.c:429 +msgid "Authentication is required to stop '$(unit)'." +msgstr "Wymagane jest uwierzytelnienie, aby zatrzymać jednostkę „$(unit)”." + +#: ../src/core/dbus-unit.c:430 +msgid "Authentication is required to reload '$(unit)'." +msgstr "" +"Wymagane jest uwierzytelnienie, aby ponownie wczytać jednostkę „$(unit)”." + +#: ../src/core/dbus-unit.c:431 ../src/core/dbus-unit.c:432 +msgid "Authentication is required to restart '$(unit)'." +msgstr "" +"Wymagane jest uwierzytelnienie, aby ponownie uruchomić jednostkę „$(unit)”." + +#: ../src/core/dbus-unit.c:535 +msgid "Authentication is required to kill '$(unit)'." +msgstr "" +"Wymagane jest uwierzytelnienie, aby wymusić wyłączenie jednostki „$(unit)”." + +#: ../src/core/dbus-unit.c:565 +msgid "Authentication is required to reset the \"failed\" state of '$(unit)'." +msgstr "" +"Wymagane jest uwierzytelnienie, aby przywrócić stan „failed” (niepowodzenia) " +"jednostki „$(unit)”." + +#: ../src/core/dbus-unit.c:597 +msgid "Authentication is required to set properties on '$(unit)'." +msgstr "" +"Wymagane jest uwierzytelnienie, aby ustawić właściwości jednostki „$(unit)”." + #~ msgid "Press Ctrl+C to cancel all filesystem checks in progress" #~ msgstr "" #~ "Naciśnięcie klawiszy Ctrl+C anuluje wszystkie trwające procesy " diff --git a/shell-completion/bash/networkctl b/shell-completion/bash/networkctl new file mode 100644 index 000000000..7ca2aa5a8 --- /dev/null +++ b/shell-completion/bash/networkctl @@ -0,0 +1,70 @@ +# networkctl(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word () { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done + return 1 +} + +__get_links() { + networkctl list --no-legend --no-pager --all | awk '{ print $2 }' | sort -u +} + +_networkctl() { + local i verb comps + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local -A OPTS=( + [STANDALONE]='-a --all -h --help --version --no-pager --no-legend' + [ARG]='' + ) + + local -A VERBS=( + [STANDALONE]='list lldp' + [LINKS]='status' + ) + + _init_completion || return + + for ((i=0; i < COMP_CWORD; i++)); do + if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]} && + ! __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG]}; then + verb=${COMP_WORDS[i]} + break + fi + done + + if [[ "$cur" = -* ]]; then + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) + return 0 + fi + + if [[ -z $verb ]]; then + comps=${VERBS[*]} + elif __contains_word "$verb" ${VERBS[STANDALONE]}; then + comps='' + elif __contains_word "$verb" ${VERBS[LINKS]}; then + comps=$( __get_links ) + fi + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _networkctl networkctl diff --git a/shell-completion/bash/systemctl.in b/shell-completion/bash/systemctl.in index 0bcd49f92..4d63e2870 100644 --- a/shell-completion/bash/systemctl.in +++ b/shell-completion/bash/systemctl.in @@ -85,6 +85,12 @@ __get_masked_units () { __systemctl $1 list-unit-files \ | { while read -r a b c ; do [[ $b == "masked" ]] && echo " $a"; done; }; } __get_all_unit_files () { { __systemctl $1 list-unit-files; } | { while read -r a b; do echo " $a"; done; }; } +__get_machines() { + local a b + (machinectl list-images --no-legend --no-pager; machinectl list --no-legend --no-pager) | \ + { while read a b; do echo " $a"; done; } | sort -u; +} + _systemctl () { local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} local i verb comps mode @@ -92,8 +98,10 @@ _systemctl () { local -A OPTS=( [STANDALONE]='--all -a --reverse --after --before --defaults --failed --force -f --full -l --global --help -h --no-ask-password --no-block --no-legend --no-pager --no-reload --no-wall - --quiet -q --privileged -P --system --user --version --runtime --recursive -r --firmware-setup' - [ARG]='--host -H --kill-who --property -p --signal -s --type -t --state --job-mode --root' + --quiet -q --privileged -P --system --user --version --runtime --recursive -r --firmware-setup + --show-types -i --ignore-inhibitors --plain' + [ARG]='--host -H --kill-who --property -p --signal -s --type -t --state --job-mode --root + --preset-mode -n --lines -o --output -M --machine' ) if __contains_word "--user" ${COMP_WORDS[*]}; then @@ -112,7 +120,7 @@ _systemctl () { ;; --state) comps='loaded not-found stub - active inactive + active inactive failed dead elapsed exited listening mounted plugged running waiting' ;; --job-mode) @@ -132,6 +140,16 @@ _systemctl () { --property|-p) comps=$(__systemd_properties $mode) ;; + --preset-mode) + comps='full enable-only disable-only' + ;; + --output|-o) + comps='short short-iso short-precise short-monotonic verbose export json + json-pretty json-sse cat' + ;; + --machine|-M) + comps=$( __get_machines ) + ;; esac COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) return 0 diff --git a/shell-completion/bash/systemd-analyze b/shell-completion/bash/systemd-analyze index 00947029c..7a5f46ba1 100644 --- a/shell-completion/bash/systemd-analyze +++ b/shell-completion/bash/systemd-analyze @@ -35,8 +35,8 @@ _systemd_analyze() { local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} local -A OPTS=( - [STANDALONE]='--help --version --system --user --from-pattern --to-pattern --order --require --no-pager' - [ARG]='-H --host -M --machine --fuzz --man' + [STANDALONE]='--help --version --system --user --order --require --no-pager --man' + [ARG]='-H --host -M --machine --fuzz --from-pattern --to-pattern ' ) local -A VERBS=( @@ -102,7 +102,7 @@ _systemd_analyze() { elif __contains_word "$verb" ${VERBS[VERIFY]}; then if [[ $cur = -* ]]; then - comps='--help --version --system --user --no-man' + comps='--help --version --system --user --man' else comps=$( compgen -A file -- "$cur" ) compopt -o filenames diff --git a/shell-completion/bash/systemd-path b/shell-completion/bash/systemd-path new file mode 100644 index 000000000..cdaf29794 --- /dev/null +++ b/shell-completion/bash/systemd-path @@ -0,0 +1,60 @@ +# systemd-path(1) completion -*- shell-script -*- +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word () { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done + return 1 +} + +__get_names() { + systemd-path | cut -d: -f1 | sort -u +} + +_systemd_path() { + local comps + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local -A OPTS=( + [STANDALONE]='-h --help --version' + [ARG]='--suffix' + ) + + _init_completion || return + + if __contains_word "$prev" ${OPTS[ARG]}; then + case $prev in + --suffix) + comps='' + ;; + esac + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + fi + + if [[ "$cur" = -* ]]; then + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) + return 0 + fi + + comps=$( __get_names ) + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _systemd_path systemd-path diff --git a/shell-completion/bash/systemd-run b/shell-completion/bash/systemd-run index 712655caf..a94867751 100644 --- a/shell-completion/bash/systemd-run +++ b/shell-completion/bash/systemd-run @@ -34,10 +34,16 @@ _systemd_run() { local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} local OPTS='-h --help --version --user --system --scope --unit --description --slice -r --remain-after-exit --send-sighup -H --host -M --machine --service-type - --uid --gid --nice --setenv -p --property' + --on-active --on-boot --on-startup --on-unit-active --on-unit-inactive + --on-calendar --timer-property -t --pty -q --quiet --no-block + --uid --gid --nice --setenv -p --property --no-ask-password' local mode=--system local i + local opts_with_values=( + --unit --description --slice --service-type -H --host -M --machine -p --property --on-active + --on-boot --on-startup --on-unit-active --on-unit-inactive --on-calendar --timer-property + ) for (( i=1; i <= COMP_CWORD; i++ )); do if [[ ${COMP_WORDS[i]} != -* ]]; then local root_command=${COMP_WORDS[i]} @@ -47,11 +53,11 @@ _systemd_run() { [[ ${COMP_WORDS[i]} == "--user" ]] && mode=--user - [[ $i -lt $COMP_CWORD && ${COMP_WORDS[i]} == @(--unit|--description|--slice|--service-type|-H|--host|-M|--machine|-p|--property) ]] && ((i++)) + [[ $i -lt $COMP_CWORD && " ${opts_with_values[@]} " =~ " ${COMP_WORDS[i]} " ]] && ((i++)) done case "$prev" in - --unit|--description) + --unit|--description|--on-active|--on-boot|--on-startup|--on-unit-active|--on-unit-inactive|--on-calendar) # argument required but no completions available return ;; @@ -89,6 +95,11 @@ _systemd_run() { -M|--machine) local comps=$( __get_machines ) + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + ;; + --timer-property) + local comps='AccuracySec= WakeSystem=' COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) return 0 ;; diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index db1e7f3f3..ab7fb5326 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -1092,12 +1092,59 @@ static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[]) { return 0; } +static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) { + _cleanup_strv_free_ char **expanded_patterns = NULL; + char **pattern; + int r; + + STRV_FOREACH(pattern, patterns) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *unit = NULL, *unit_id = NULL; + + if (strv_extend(&expanded_patterns, *pattern) < 0) + return log_oom(); + + if (string_is_glob(*pattern)) + continue; + + unit = unit_dbus_path_from_name(*pattern); + if (!unit) + return log_oom(); + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + unit, + "org.freedesktop.systemd1.Unit", + "Id", + &error, + &unit_id); + if (r < 0) + return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r)); + + if (!streq(*pattern, unit_id)) { + if (strv_extend(&expanded_patterns, unit_id) < 0) + return log_oom(); + } + } + + *ret = expanded_patterns; + expanded_patterns = NULL; /* do not free */ + + return 0; +} + static int dot(sd_bus *bus, char* patterns[]) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_strv_free_ char **expanded_patterns = NULL; int r; UnitInfo u; + r = expand_patterns(bus, patterns, &expanded_patterns); + if (r < 0) + return r; + r = sd_bus_call_method( bus, "org.freedesktop.systemd1", @@ -1120,7 +1167,7 @@ static int dot(sd_bus *bus, char* patterns[]) { while ((r = bus_parse_unit_info(reply, &u)) > 0) { - r = graph_one(bus, &u, patterns); + r = graph_one(bus, &u, expanded_patterns); if (r < 0) return r; } diff --git a/src/basic/audit.c b/src/basic/audit.c index 54148fcf1..1f593aa81 100644 --- a/src/basic/audit.c +++ b/src/basic/audit.c @@ -36,6 +36,11 @@ int audit_session_from_pid(pid_t pid, uint32_t *id) { assert(id); + /* We don't convert ENOENT to ESRCH here, since we can't + * really distuingish between "audit is not available in the + * kernel" and "the process does not exist", both which will + * result in ENOENT. */ + p = procfs_file_alloca(pid, "sessionid"); r = read_one_line_file(p, &s); @@ -47,7 +52,7 @@ int audit_session_from_pid(pid_t pid, uint32_t *id) { return r; if (u == AUDIT_SESSION_INVALID || u <= 0) - return -ENXIO; + return -ENODATA; *id = u; return 0; @@ -68,6 +73,8 @@ int audit_loginuid_from_pid(pid_t pid, uid_t *uid) { return r; r = parse_uid(s, &u); + if (r == -ENXIO) /* the UID was -1 */ + return -ENODATA; if (r < 0) return r; diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 6b3162a35..f661a54d9 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -29,7 +29,6 @@ #include #include -#include "cgroup-util.h" #include "set.h" #include "macro.h" #include "util.h" @@ -41,6 +40,7 @@ #include "special.h" #include "mkdir.h" #include "login-util.h" +#include "cgroup-util.h" int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) { _cleanup_free_ char *fs = NULL; @@ -113,7 +113,7 @@ int cg_read_subgroup(DIR *d, char **fn) { assert(d); assert(fn); - FOREACH_DIRENT(de, d, return -errno) { + FOREACH_DIRENT_ALL(de, d, return -errno) { char *b; if (de->d_type != DT_DIR) @@ -187,7 +187,7 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo if (ignore_self && pid == my_pid) continue; - if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid)) + if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid)) continue; /* If we haven't killed this process yet, kill @@ -197,7 +197,7 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo ret = -errno; } else { if (sigcont && sig != SIGKILL) - kill(pid, SIGCONT); + (void) kill(pid, SIGCONT); if (ret == 0) ret = 1; @@ -205,7 +205,7 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo done = false; - r = set_put(s, LONG_TO_PTR(pid)); + r = set_put(s, PID_TO_PTR(pid)); if (r < 0) { if (ret >= 0) return r; @@ -233,7 +233,7 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo int cg_kill_recursive(const char *controller, const char *path, int sig, bool sigcont, bool ignore_self, bool rem, Set *s) { _cleanup_set_free_ Set *allocated_set = NULL; _cleanup_closedir_ DIR *d = NULL; - int r, ret = 0; + int r, ret; char *fn; assert(path); @@ -264,7 +264,7 @@ int cg_kill_recursive(const char *controller, const char *path, int sig, bool si return -ENOMEM; r = cg_kill_recursive(controller, p, sig, sigcont, ignore_self, rem, s); - if (ret >= 0 && r != 0) + if (r != 0 && ret >= 0) ret = r; } @@ -318,7 +318,15 @@ int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char if (ignore_self && pid == my_pid) continue; - if (set_get(s, LONG_TO_PTR(pid)) == LONG_TO_PTR(pid)) + if (set_get(s, PID_TO_PTR(pid)) == PID_TO_PTR(pid)) + continue; + + /* Ignore kernel threads. Since they can only + * exist in the root cgroup, we only check for + * them there. */ + if (cfrom && + (isempty(pfrom) || path_equal(pfrom, "/")) && + is_kernel_thread(pid) > 0) continue; r = cg_attach(cto, pto, pid); @@ -330,7 +338,7 @@ int cg_migrate(const char *cfrom, const char *pfrom, const char *cto, const char done = false; - r = set_put(s, LONG_TO_PTR(pid)); + r = set_put(s, PID_TO_PTR(pid)); if (r < 0) { if (ret >= 0) return r; @@ -382,12 +390,8 @@ int cg_migrate_recursive( p = strjoin(pfrom, "/", fn, NULL); free(fn); - if (!p) { - if (ret >= 0) - return -ENOMEM; - - return ret; - } + if (!p) + return -ENOMEM; r = cg_migrate_recursive(cfrom, p, cto, pto, ignore_self, rem); if (r != 0 && ret >= 0) @@ -428,114 +432,174 @@ int cg_migrate_recursive_fallback( /* This didn't work? Then let's try all prefixes of the destination */ PATH_FOREACH_PREFIX(prefix, pto) { - r = cg_migrate_recursive(cfrom, pfrom, cto, prefix, ignore_self, rem); - if (r >= 0) - break; + int q; + + q = cg_migrate_recursive(cfrom, pfrom, cto, prefix, ignore_self, rem); + if (q >= 0) + return q; } } - return 0; + return r; } -static const char *normalize_controller(const char *controller) { +static const char *controller_to_dirname(const char *controller) { + const char *e; assert(controller); - if (startswith(controller, "name=")) - return controller + 5; - else - return controller; + /* Converts a controller name to the directory name below + * /sys/fs/cgroup/ we want to mount it to. Effectively, this + * just cuts off the name= prefixed used for named + * hierarchies, if it is specified. */ + + e = startswith(controller, "name="); + if (e) + return e; + + return controller; } -static int join_path(const char *controller, const char *path, const char *suffix, char **fs) { +static int join_path_legacy(const char *controller, const char *path, const char *suffix, char **fs) { + const char *dn; char *t = NULL; - if (!isempty(controller)) { - if (!isempty(path) && !isempty(suffix)) - t = strjoin("/sys/fs/cgroup/", controller, "/", path, "/", suffix, NULL); - else if (!isempty(path)) - t = strjoin("/sys/fs/cgroup/", controller, "/", path, NULL); - else if (!isempty(suffix)) - t = strjoin("/sys/fs/cgroup/", controller, "/", suffix, NULL); - else - t = strappend("/sys/fs/cgroup/", controller); - } else { - if (!isempty(path) && !isempty(suffix)) - t = strjoin(path, "/", suffix, NULL); - else if (!isempty(path)) - t = strdup(path); - else - return -EINVAL; - } + assert(fs); + assert(controller); + + dn = controller_to_dirname(controller); + + if (isempty(path) && isempty(suffix)) + t = strappend("/sys/fs/cgroup/", dn); + else if (isempty(path)) + t = strjoin("/sys/fs/cgroup/", dn, "/", suffix, NULL); + else if (isempty(suffix)) + t = strjoin("/sys/fs/cgroup/", dn, "/", path, NULL); + else + t = strjoin("/sys/fs/cgroup/", dn, "/", path, "/", suffix, NULL); + if (!t) + return -ENOMEM; + + *fs = t; + return 0; +} + +static int join_path_unified(const char *path, const char *suffix, char **fs) { + char *t; + + assert(fs); + if (isempty(path) && isempty(suffix)) + t = strdup("/sys/fs/cgroup"); + else if (isempty(path)) + t = strappend("/sys/fs/cgroup/", suffix); + else if (isempty(suffix)) + t = strappend("/sys/fs/cgroup/", path); + else + t = strjoin("/sys/fs/cgroup/", path, "/", suffix, NULL); if (!t) return -ENOMEM; - *fs = path_kill_slashes(t); + *fs = t; return 0; } int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) { - const char *p; - static thread_local bool good = false; + int unified, r; assert(fs); - if (controller && !cg_controller_is_valid(controller)) - return -EINVAL; + if (!controller) { + char *t; - if (_unlikely_(!good)) { - int r; + /* If no controller is specified, we return the path + * *below* the controllers, without any prefix. */ - r = path_is_mount_point("/sys/fs/cgroup", 0); - if (r < 0) - return r; - if (r == 0) - return -ENOENT; + if (!path && !suffix) + return -EINVAL; - /* Cache this to save a few stat()s */ - good = true; + if (!suffix) + t = strdup(path); + else if (!path) + t = strdup(suffix); + else + t = strjoin(path, "/", suffix, NULL); + if (!t) + return -ENOMEM; + + *fs = path_kill_slashes(t); + return 0; } - p = controller ? normalize_controller(controller) : NULL; + if (!cg_controller_is_valid(controller)) + return -EINVAL; + + unified = cg_unified(); + if (unified < 0) + return unified; + + if (unified > 0) + r = join_path_unified(path, suffix, fs); + else + r = join_path_legacy(controller, path, suffix, fs); + if (r < 0) + return r; - return join_path(p, path, suffix, fs); + path_kill_slashes(*fs); + return 0; } -static int check_hierarchy(const char *p) { - const char *cc; +static int controller_is_accessible(const char *controller) { + int unified; - assert(p); + assert(controller); - if (!filename_is_valid(p)) - return 0; + /* Checks whether a specific controller is accessible, + * i.e. its hierarchy mounted. In the unified hierarchy all + * controllers are considered accessible, except for the named + * hierarchies */ - /* Check if this controller actually really exists */ - cc = strjoina("/sys/fs/cgroup/", p); - if (laccess(cc, F_OK) < 0) - return -errno; + if (!cg_controller_is_valid(controller)) + return -EINVAL; + + unified = cg_unified(); + if (unified < 0) + return unified; + if (unified > 0) { + /* We don't support named hierarchies if we are using + * the unified hierarchy. */ + + if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) + return 0; + + if (startswith(controller, "name=")) + return -EOPNOTSUPP; + + } else { + const char *cc, *dn; + + dn = controller_to_dirname(controller); + cc = strjoina("/sys/fs/cgroup/", dn); + + if (laccess(cc, F_OK) < 0) + return -errno; + } return 0; } int cg_get_path_and_check(const char *controller, const char *path, const char *suffix, char **fs) { - const char *p; int r; + assert(controller); assert(fs); - if (!cg_controller_is_valid(controller)) - return -EINVAL; - - /* Normalize the controller syntax */ - p = normalize_controller(controller); - - /* Check if this controller actually really exists */ - r = check_hierarchy(p); + /* Check if the specified controller is actually accessible */ + r = controller_is_accessible(controller); if (r < 0) return r; - return join_path(p, path, suffix, fs); + return cg_get_path(controller, path, suffix, fs); } static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct FTW *ftwbuf) { @@ -549,7 +613,7 @@ static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct if (ftwbuf->level < 1) return 0; - rmdir(path); + (void) rmdir(path); return 0; } @@ -564,8 +628,14 @@ int cg_trim(const char *controller, const char *path, bool delete_root) { return r; errno = 0; - if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) - r = errno ? -errno : -EIO; + if (nftw(fs, trim_cb, 64, FTW_DEPTH|FTW_MOUNT|FTW_PHYS) != 0) { + if (errno == ENOENT) + r = 0; + else if (errno != 0) + r = -errno; + else + r = -EIO; + } if (delete_root) { if (rmdir(fs) < 0 && errno != ENOENT) @@ -575,20 +645,6 @@ int cg_trim(const char *controller, const char *path, bool delete_root) { return r; } -int cg_delete(const char *controller, const char *path) { - _cleanup_free_ char *parent = NULL; - int r; - - assert(path); - - r = path_get_parent(path, &parent); - if (r < 0) - return r; - - r = cg_migrate_recursive(controller, path, controller, parent, false, true); - return r == -ENOENT ? 0 : r; -} - int cg_create(const char *controller, const char *path) { _cleanup_free_ char *fs = NULL; int r; @@ -664,13 +720,15 @@ int cg_attach_fallback(const char *controller, const char *path, pid_t pid) { * the destination */ PATH_FOREACH_PREFIX(prefix, path) { - r = cg_attach(controller, prefix, pid); - if (r >= 0) - break; + int q; + + q = cg_attach(controller, prefix, pid); + if (q >= 0) + return q; } } - return 0; + return r; } int cg_set_group_access( @@ -683,7 +741,8 @@ int cg_set_group_access( _cleanup_free_ char *fs = NULL; int r; - assert(path); + if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID) + return 0; if (mode != MODE_INVALID) mode &= 0777; @@ -703,7 +762,7 @@ int cg_set_task_access( gid_t gid) { _cleanup_free_ char *fs = NULL, *procs = NULL; - int r; + int r, unified; assert(path); @@ -721,77 +780,88 @@ int cg_set_task_access( if (r < 0) return r; + unified = cg_unified(); + if (unified < 0) + return unified; + if (unified) + return 0; + /* Compatibility, Always keep values for "tasks" in sync with * "cgroup.procs" */ - r = cg_get_path(controller, path, "tasks", &procs); - if (r < 0) - return r; + if (cg_get_path(controller, path, "tasks", &procs) >= 0) + (void) chmod_and_chown(procs, mode, uid, gid); - return chmod_and_chown(procs, mode, uid, gid); + return 0; } int cg_pid_get_path(const char *controller, pid_t pid, char **path) { _cleanup_fclose_ FILE *f = NULL; char line[LINE_MAX]; const char *fs; - size_t cs; + size_t cs = 0; + int unified; assert(path); assert(pid >= 0); - if (controller) { - if (!cg_controller_is_valid(controller)) - return -EINVAL; + unified = cg_unified(); + if (unified < 0) + return unified; + if (unified == 0) { + if (controller) { + if (!cg_controller_is_valid(controller)) + return -EINVAL; + } else + controller = SYSTEMD_CGROUP_CONTROLLER; - controller = normalize_controller(controller); - } else - controller = SYSTEMD_CGROUP_CONTROLLER; + cs = strlen(controller); + } fs = procfs_file_alloca(pid, "cgroup"); - f = fopen(fs, "re"); if (!f) return errno == ENOENT ? -ESRCH : -errno; - cs = strlen(controller); - FOREACH_LINE(line, f, return -errno) { - char *l, *p, *e; - size_t k; - const char *word, *state; - bool found = false; + char *e, *p; truncate_nl(line); - l = strchr(line, ':'); - if (!l) - continue; - - l++; - e = strchr(l, ':'); - if (!e) - continue; + if (unified) { + e = startswith(line, "0:"); + if (!e) + continue; - *e = 0; + e = strchr(e, ':'); + if (!e) + continue; + } else { + char *l; + size_t k; + const char *word, *state; + bool found = false; + + l = strchr(line, ':'); + if (!l) + continue; - FOREACH_WORD_SEPARATOR(word, k, l, ",", state) { + l++; + e = strchr(l, ':'); + if (!e) + continue; - if (k == cs && memcmp(word, controller, cs) == 0) { - found = true; - break; + *e = 0; + FOREACH_WORD_SEPARATOR(word, k, l, ",", state) { + if (k == cs && memcmp(word, controller, cs) == 0) { + found = true; + break; + } } - if (k == 5 + cs && - memcmp(word, "name=", 5) == 0 && - memcmp(word+5, controller, cs) == 0) { - found = true; - break; - } + if (!found) + continue; } - if (!found) - continue; - p = strdup(e + 1); if (!p) return -ENOMEM; @@ -800,16 +870,22 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) { return 0; } - return -ENOENT; + return -ENODATA; } int cg_install_release_agent(const char *controller, const char *agent) { _cleanup_free_ char *fs = NULL, *contents = NULL; - char *sc; - int r; + const char *sc; + int r, unified; assert(agent); + unified = cg_unified(); + if (unified < 0) + return unified; + if (unified) /* doesn't apply to unified hierarchy */ + return -EOPNOTSUPP; + r = cg_get_path(controller, NULL, "release_agent", &fs); if (r < 0) return r; @@ -819,11 +895,11 @@ int cg_install_release_agent(const char *controller, const char *agent) { return r; sc = strstrip(contents); - if (sc[0] == 0) { + if (isempty(sc)) { r = write_string_file(fs, agent, 0); if (r < 0) return r; - } else if (!streq(sc, agent)) + } else if (!path_equal(sc, agent)) return -EEXIST; fs = mfree(fs); @@ -853,7 +929,13 @@ int cg_install_release_agent(const char *controller, const char *agent) { int cg_uninstall_release_agent(const char *controller) { _cleanup_free_ char *fs = NULL; - int r; + int r, unified; + + unified = cg_unified(); + if (unified < 0) + return unified; + if (unified) /* Doesn't apply to unified hierarchy */ + return -EOPNOTSUPP; r = cg_get_path(controller, NULL, "notify_on_release", &fs); if (r < 0) @@ -876,73 +958,92 @@ int cg_uninstall_release_agent(const char *controller) { return 0; } -int cg_is_empty(const char *controller, const char *path, bool ignore_self) { +int cg_is_empty(const char *controller, const char *path) { _cleanup_fclose_ FILE *f = NULL; - pid_t pid = 0, self_pid; - bool found = false; + pid_t pid; int r; assert(path); r = cg_enumerate_processes(controller, path, &f); + if (r == -ENOENT) + return 1; if (r < 0) - return r == -ENOENT ? 1 : r; - - self_pid = getpid(); - - while ((r = cg_read_pid(f, &pid)) > 0) { - - if (ignore_self && pid == self_pid) - continue; - - found = true; - break; - } + return r; + r = cg_read_pid(f, &pid); if (r < 0) return r; - return !found; + return r == 0; } -int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self) { - _cleanup_closedir_ DIR *d = NULL; - char *fn; - int r; +int cg_is_empty_recursive(const char *controller, const char *path) { + int unified, r; assert(path); - r = cg_is_empty(controller, path, ignore_self); - if (r <= 0) - return r; + /* The root cgroup is always populated */ + if (controller && (isempty(path) || path_equal(path, "/"))) + return false; - r = cg_enumerate_subgroups(controller, path, &d); - if (r < 0) - return r == -ENOENT ? 1 : r; + unified = cg_unified(); + if (unified < 0) + return unified; - while ((r = cg_read_subgroup(d, &fn)) > 0) { - _cleanup_free_ char *p = NULL; + if (unified > 0) { + _cleanup_free_ char *populated = NULL, *t = NULL; - p = strjoin(path, "/", fn, NULL); - free(fn); - if (!p) - return -ENOMEM; + /* On the unified hierarchy we can check empty state + * via the "cgroup.populated" attribute. */ + + r = cg_get_path(controller, path, "cgroup.populated", &populated); + if (r < 0) + return r; - r = cg_is_empty_recursive(controller, p, ignore_self); + r = read_one_line_file(populated, &t); + if (r == -ENOENT) + return 1; + if (r < 0) + return r; + + return streq(t, "0"); + } else { + _cleanup_closedir_ DIR *d = NULL; + char *fn; + + r = cg_is_empty(controller, path); if (r <= 0) return r; - } - if (r < 0) - return r; + r = cg_enumerate_subgroups(controller, path, &d); + if (r == -ENOENT) + return 1; + if (r < 0) + return r; - return 1; + while ((r = cg_read_subgroup(d, &fn)) > 0) { + _cleanup_free_ char *p = NULL; + + p = strjoin(path, "/", fn, NULL); + free(fn); + if (!p) + return -ENOMEM; + + r = cg_is_empty_recursive(controller, p); + if (r <= 0) + return r; + } + if (r < 0) + return r; + + return true; + } } int cg_split_spec(const char *spec, char **controller, char **path) { - const char *e; char *t = NULL, *u = NULL; - _cleanup_free_ char *v = NULL; + const char *e; assert(spec); @@ -970,7 +1071,7 @@ int cg_split_spec(const char *spec, char **controller, char **path) { return -EINVAL; if (controller) { - t = strdup(normalize_controller(spec)); + t = strdup(spec); if (!t) return -ENOMEM; @@ -983,10 +1084,7 @@ int cg_split_spec(const char *spec, char **controller, char **path) { return 0; } - v = strndup(spec, e-spec); - if (!v) - return -ENOMEM; - t = strdup(normalize_controller(v)); + t = strndup(spec, e-spec); if (!t) return -ENOMEM; if (!cg_controller_is_valid(t)) { @@ -994,13 +1092,9 @@ int cg_split_spec(const char *spec, char **controller, char **path) { return -EINVAL; } - if (streq(e+1, "")) { - u = strdup("/"); - if (!u) { - free(t); - return -ENOMEM; - } - } else { + if (isempty(e+1)) + u = NULL; + else { u = strdup(e+1); if (!u) { free(t); @@ -1054,7 +1148,7 @@ int cg_mangle_path(const char *path, char **result) { if (r < 0) return r; - return cg_get_path(c ? c : SYSTEMD_CGROUP_CONTROLLER, p ? p : "/", NULL, result); + return cg_get_path(c ?: SYSTEMD_CGROUP_CONTROLLER, p ?: "/", NULL, result); } int cg_get_root_path(char **path) { @@ -1067,7 +1161,11 @@ int cg_get_root_path(char **path) { if (r < 0) return r; - e = endswith(p, "/" SPECIAL_SYSTEM_SLICE); + e = endswith(p, "/" SPECIAL_INIT_SCOPE); + if (!e) + e = endswith(p, "/" SPECIAL_SYSTEM_SLICE); /* legacy */ + if (!e) + e = endswith(p, "/system"); /* even more legacy */ if (e) *e = 0; @@ -1095,7 +1193,7 @@ int cg_shift_path(const char *cgroup, const char *root, const char **shifted) { } p = path_startswith(cgroup, root); - if (p) + if (p && p > cgroup) *shifted = p - 1; else *shifted = cgroup; @@ -1359,17 +1457,15 @@ int cg_pid_get_user_unit(pid_t pid, char **unit) { } int cg_path_get_machine_name(const char *path, char **machine) { - _cleanup_free_ char *u = NULL, *sl = NULL; + _cleanup_free_ char *u = NULL; + const char *sl; int r; r = cg_path_get_unit(path, &u); if (r < 0) return r; - sl = strjoin("/run/systemd/machines/unit:", u, NULL); - if (!sl) - return -ENOMEM; - + sl = strjoina("/run/systemd/machines/unit:", u); return readlink_malloc(sl, machine); } @@ -1562,31 +1658,38 @@ char *cg_escape(const char *p) { p[0] == '.' || streq(p, "notify_on_release") || streq(p, "release_agent") || - streq(p, "tasks")) + streq(p, "tasks") || + startswith(p, "cgroup.")) need_prefix = true; else { const char *dot; dot = strrchr(p, '.'); if (dot) { + CGroupController c; + size_t l = dot - p; - if (dot - p == 6 && memcmp(p, "cgroup", 6) == 0) - need_prefix = true; - else { - char *n; + for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { + const char *n; + + n = cgroup_controller_to_string(c); - n = strndupa(p, dot - p); + if (l != strlen(n)) + continue; - if (check_hierarchy(n) >= 0) - need_prefix = true; + if (memcmp(p, n, l) != 0) + continue; + + need_prefix = true; + break; } } } if (need_prefix) return strappend("_", p); - else - return strdup(p); + + return strdup(p); } char *cg_unescape(const char *p) { @@ -1719,17 +1822,9 @@ int cg_get_attribute(const char *controller, const char *path, const char *attri return read_one_line_file(p, ret); } -static const char mask_names[] = - "cpu\0" - "cpuacct\0" - "blkio\0" - "memory\0" - "devices\0"; - -int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path) { - CGroupControllerMask bit = 1; - const char *n; - int r; +int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) { + CGroupController c; + int r, unified; /* This one will create a cgroup in our private tree, but also * duplicate it in the trees specified in mask, and remove it @@ -1740,69 +1835,82 @@ int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask ma if (r < 0) return r; - /* Then, do the same in the other hierarchies */ - NULSTR_FOREACH(n, mask_names) { + /* If we are in the unified hierarchy, we are done now */ + unified = cg_unified(); + if (unified < 0) + return unified; + if (unified > 0) + return 0; + + /* Otherwise, do the same in the other hierarchies */ + for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { + CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c); + const char *n; + + n = cgroup_controller_to_string(c); + if (mask & bit) - cg_create(n, path); + (void) cg_create(n, path); else if (supported & bit) - cg_trim(n, path, true); - - bit <<= 1; + (void) cg_trim(n, path, true); } return 0; } -int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) { - CGroupControllerMask bit = 1; - const char *n; - int r; +int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) { + CGroupController c; + int r, unified; r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid); if (r < 0) return r; - NULSTR_FOREACH(n, mask_names) { + unified = cg_unified(); + if (unified < 0) + return unified; + if (unified > 0) + return 0; - if (supported & bit) { - const char *p = NULL; + for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { + CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c); + const char *p = NULL; - if (path_callback) - p = path_callback(bit, userdata); + if (!(supported & bit)) + continue; - if (!p) - p = path; + if (path_callback) + p = path_callback(bit, userdata); - cg_attach_fallback(n, p, pid); - } + if (!p) + p = path; - bit <<= 1; + (void) cg_attach_fallback(cgroup_controller_to_string(c), p, pid); } return 0; } -int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) { +int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) { Iterator i; void *pidp; int r = 0; SET_FOREACH(pidp, pids, i) { - pid_t pid = PTR_TO_LONG(pidp); + pid_t pid = PTR_TO_PID(pidp); int q; q = cg_attach_everywhere(supported, path, pid, path_callback, userdata); - if (q < 0) + if (q < 0 && r >= 0) r = q; } return r; } -int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) { - CGroupControllerMask bit = 1; - const char *n; - int r; +int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) { + CGroupController c; + int r = 0, unified; if (!path_equal(from, to)) { r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, false, true); @@ -1810,56 +1918,127 @@ int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, cons return r; } - NULSTR_FOREACH(n, mask_names) { - if (supported & bit) { - const char *p = NULL; + unified = cg_unified(); + if (unified < 0) + return unified; + if (unified > 0) + return r; - if (to_callback) - p = to_callback(bit, userdata); + for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { + CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c); + const char *p = NULL; - if (!p) - p = to; + if (!(supported & bit)) + continue; - cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, n, p, false, false); - } + if (to_callback) + p = to_callback(bit, userdata); - bit <<= 1; + if (!p) + p = to; + + (void) cg_migrate_recursive_fallback(SYSTEMD_CGROUP_CONTROLLER, to, cgroup_controller_to_string(c), p, false, false); } return 0; } -int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root) { - CGroupControllerMask bit = 1; - const char *n; - int r; +int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) { + CGroupController c; + int r, unified; r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root); if (r < 0) return r; - NULSTR_FOREACH(n, mask_names) { - if (supported & bit) - cg_trim(n, path, delete_root); + unified = cg_unified(); + if (unified < 0) + return unified; + if (unified > 0) + return r; + + for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { + CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c); - bit <<= 1; + if (!(supported & bit)) + continue; + + (void) cg_trim(cgroup_controller_to_string(c), path, delete_root); } return 0; } -CGroupControllerMask cg_mask_supported(void) { - CGroupControllerMask bit = 1, mask = 0; - const char *n; +int cg_mask_supported(CGroupMask *ret) { + CGroupMask mask = 0; + int r, unified; + + /* Determines the mask of supported cgroup controllers. Only + * includes controllers we can make sense of and that are + * actually accessible. */ + + unified = cg_unified(); + if (unified < 0) + return unified; + if (unified > 0) { + _cleanup_free_ char *root = NULL, *controllers = NULL, *path = NULL; + const char *c; - NULSTR_FOREACH(n, mask_names) { - if (check_hierarchy(n) >= 0) - mask |= bit; + /* In the unified hierarchy we can read the supported + * and accessible controllers from a the top-level + * cgroup attribute */ - bit <<= 1; + r = cg_get_root_path(&root); + if (r < 0) + return r; + + r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, root, "cgroup.controllers", &path); + if (r < 0) + return r; + + r = read_one_line_file(path, &controllers); + if (r < 0) + return r; + + c = controllers; + for (;;) { + _cleanup_free_ char *n = NULL; + CGroupController v; + + r = extract_first_word(&c, &n, NULL, 0); + if (r < 0) + return r; + if (r == 0) + break; + + v = cgroup_controller_from_string(n); + if (v < 0) + continue; + + mask |= CGROUP_CONTROLLER_TO_MASK(v); + } + + /* Currently, we only support the memory controller in + * the unified hierarchy, mask everything else off. */ + mask &= CGROUP_MASK_MEMORY; + + } else { + CGroupController c; + + /* In the legacy hierarchy, we check whether which + * hierarchies are mounted. */ + + for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { + const char *n; + + n = cgroup_controller_to_string(c); + if (controller_is_accessible(n) >= 0) + mask |= CGROUP_CONTROLLER_TO_MASK(c); + } } - return mask; + *ret = mask; + return 0; } int cg_kernel_controllers(Set *controllers) { @@ -1869,6 +2048,11 @@ int cg_kernel_controllers(Set *controllers) { assert(controllers); + /* Determines the full list of kernel-known controllers. Might + * include controllers we don't actually support, arbitrary + * named hierarchies and controllers that aren't currently + * accessible (because not mounted). */ + f = fopen("/proc/cgroups", "re"); if (!f) { if (errno == ENOENT) @@ -1889,7 +2073,7 @@ int cg_kernel_controllers(Set *controllers) { if (feof(f)) break; - if (ferror(f) && errno) + if (ferror(f) && errno != 0) return -errno; return -EBADMSG; @@ -1900,7 +2084,7 @@ int cg_kernel_controllers(Set *controllers) { continue; } - if (!filename_is_valid(controller)) { + if (!cg_controller_is_valid(controller)) { free(controller); return -EBADMSG; } @@ -1912,3 +2096,122 @@ int cg_kernel_controllers(Set *controllers) { return 0; } + +static thread_local int unified_cache = -1; + +int cg_unified(void) { + struct statfs fs; + + /* Checks if we support the unified hierarchy. Returns an + * error when the cgroup hierarchies aren't mounted yet or we + * have any other trouble determining if the unified hierarchy + * is supported. */ + + if (unified_cache >= 0) + return unified_cache; + + if (statfs("/sys/fs/cgroup/", &fs) < 0) + return -errno; + + if (F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC)) + unified_cache = true; + else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) + unified_cache = false; + else + return -ENOEXEC; + + return unified_cache; +} + +void cg_unified_flush(void) { + unified_cache = -1; +} + +int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) { + _cleanup_free_ char *fs = NULL; + CGroupController c; + int r, unified; + + assert(p); + + if (supported == 0) + return 0; + + unified = cg_unified(); + if (unified < 0) + return unified; + if (!unified) /* on the legacy hiearchy there's no joining of controllers defined */ + return 0; + + r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs); + if (r < 0) + return r; + + for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) { + CGroupMask bit = CGROUP_CONTROLLER_TO_MASK(c); + const char *n; + + if (!(supported & bit)) + continue; + + n = cgroup_controller_to_string(c); + { + char s[1 + strlen(n) + 1]; + + s[0] = mask & bit ? '+' : '-'; + strcpy(s + 1, n); + + r = write_string_file(fs, s, 0); + if (r < 0) + log_debug_errno(r, "Failed to enable controller %s for %s (%s): %m", n, p, fs); + } + } + + return 0; +} + +bool cg_is_unified_wanted(void) { + static thread_local int wanted = -1; + int r, unified; + + /* If the hierarchy is already mounted, then follow whatever + * was chosen for it. */ + unified = cg_unified(); + if (unified >= 0) + return unified; + + /* Otherwise, let's see what the kernel command line has to + * say. Since checking that is expensive, let's cache the + * result. */ + if (wanted >= 0) + return wanted; + + r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy", NULL); + if (r > 0) + return (wanted = true); + else { + _cleanup_free_ char *value = NULL; + + r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy=", &value); + if (r < 0) + return false; + if (r == 0) + return (wanted = false); + + return (wanted = parse_boolean(value) > 0); + } +} + +bool cg_is_legacy_wanted(void) { + return !cg_is_unified_wanted(); +} + +static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = { + [CGROUP_CONTROLLER_CPU] = "cpu", + [CGROUP_CONTROLLER_CPUACCT] = "cpuacct", + [CGROUP_CONTROLLER_BLKIO] = "blkio", + [CGROUP_CONTROLLER_MEMORY] = "memory", + [CGROUP_CONTROLLER_DEVICE] = "devices", +}; + +DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController); diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h index fd72e9e5c..6fd6d8059 100644 --- a/src/basic/cgroup-util.h +++ b/src/basic/cgroup-util.h @@ -28,15 +28,28 @@ #include "set.h" #include "def.h" +/* An enum of well known cgroup controllers */ +typedef enum CGroupController { + CGROUP_CONTROLLER_CPU, + CGROUP_CONTROLLER_CPUACCT, + CGROUP_CONTROLLER_BLKIO, + CGROUP_CONTROLLER_MEMORY, + CGROUP_CONTROLLER_DEVICE, + _CGROUP_CONTROLLER_MAX, + _CGROUP_CONTROLLER_INVALID = -1, +} CGroupController; + +#define CGROUP_CONTROLLER_TO_MASK(c) (1 << (c)) + /* A bit mask of well known cgroup controllers */ -typedef enum CGroupControllerMask { - CGROUP_CPU = 1, - CGROUP_CPUACCT = 2, - CGROUP_BLKIO = 4, - CGROUP_MEMORY = 8, - CGROUP_DEVICE = 16, - _CGROUP_CONTROLLER_MASK_ALL = 31 -} CGroupControllerMask; +typedef enum CGroupMask { + CGROUP_MASK_CPU = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPU), + CGROUP_MASK_CPUACCT = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_CPUACCT), + CGROUP_MASK_BLKIO = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BLKIO), + CGROUP_MASK_MEMORY = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_MEMORY), + CGROUP_MASK_DEVICE = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_DEVICE), + _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1 +} CGroupMask; /* * General rules: @@ -77,7 +90,6 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path); int cg_trim(const char *controller, const char *path, bool delete_root); int cg_rmdir(const char *controller, const char *path); -int cg_delete(const char *controller, const char *path); int cg_create(const char *controller, const char *path); int cg_attach(const char *controller, const char *path, pid_t pid); @@ -93,8 +105,8 @@ int cg_set_task_access(const char *controller, const char *path, mode_t mode, ui int cg_install_release_agent(const char *controller, const char *agent); int cg_uninstall_release_agent(const char *controller); -int cg_is_empty(const char *controller, const char *path, bool ignore_self); -int cg_is_empty_recursive(const char *controller, const char *path, bool ignore_self); +int cg_is_empty(const char *controller, const char *path); +int cg_is_empty_recursive(const char *controller, const char *path); int cg_get_root_path(char **path); @@ -126,14 +138,24 @@ bool cg_controller_is_valid(const char *p); int cg_slice_to_path(const char *unit, char **ret); -typedef const char* (*cg_migrate_callback_t)(CGroupControllerMask mask, void *userdata); +typedef const char* (*cg_migrate_callback_t)(CGroupMask mask, void *userdata); -int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path); -int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid, cg_migrate_callback_t callback, void *userdata); -int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata); -int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata); -int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root); +int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path); +int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t callback, void *userdata); +int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata); +int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata); +int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root); +int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p); -CGroupControllerMask cg_mask_supported(void); +int cg_mask_supported(CGroupMask *ret); int cg_kernel_controllers(Set *controllers); + +int cg_unified(void); +void cg_unified_flush(void); + +bool cg_is_unified_wanted(void); +bool cg_is_legacy_wanted(void); + +const char* cgroup_controller_to_string(CGroupController c) _const_; +CGroupController cgroup_controller_from_string(const char *s) _pure_; diff --git a/src/basic/conf-files.h b/src/basic/conf-files.h index 3169a907f..d8aebc5e5 100644 --- a/src/basic/conf-files.h +++ b/src/basic/conf-files.h @@ -22,7 +22,6 @@ along with systemd; If not, see . ***/ - -int conf_files_list(char ***strv, const char *suffix, const char *root, const char *dir, ...); -int conf_files_list_strv(char ***strv, const char *suffix, const char *root, const char* const* dirs); -int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, const char *dirs); +int conf_files_list(char ***ret, const char *suffix, const char *root, const char *dir, ...); +int conf_files_list_strv(char ***ret, const char *suffix, const char *root, const char* const* dirs); +int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, const char *dirs); diff --git a/src/basic/copy.c b/src/basic/copy.c index 33427c6a7..cc5faa80a 100644 --- a/src/basic/copy.c +++ b/src/basic/copy.c @@ -30,7 +30,7 @@ #define COPY_BUFFER_SIZE (16*1024) int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) { - bool try_sendfile = true; + bool try_sendfile = true, try_splice = true; int r; assert(fdf >= 0); @@ -69,7 +69,23 @@ int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) { } else if (n == 0) /* EOF */ break; else if (n > 0) - /* Succcess! */ + /* Success! */ + goto next; + } + + /* The try splice, unless we already tried */ + if (try_splice) { + n = splice(fdf, NULL, fdt, NULL, m, 0); + if (n < 0) { + if (errno != EINVAL && errno != ENOSYS) + return -errno; + + try_splice = false; + /* use fallback below */ + } else if (n == 0) /* EOF */ + break; + else if (n > 0) + /* Success! */ goto next; } diff --git a/src/basic/def.h b/src/basic/def.h index 5aaba1fe8..7c4161eb7 100644 --- a/src/basic/def.h +++ b/src/basic/def.h @@ -35,7 +35,7 @@ * the watchdog pings will keep the loop busy. */ #define DEFAULT_EXIT_USEC (30*USEC_PER_SEC) -#define SYSTEMD_CGROUP_CONTROLLER "systemd" +#define SYSTEMD_CGROUP_CONTROLLER "name=systemd" #define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT #define SIGNALS_IGNORE SIGPIPE diff --git a/src/basic/macro.h b/src/basic/macro.h index 627d768b7..cbc3ca97b 100644 --- a/src/basic/macro.h +++ b/src/basic/macro.h @@ -298,6 +298,9 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { #define PTR_TO_GID(p) ((gid_t) (((uintptr_t) (p))-1)) #define GID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1)) +#define PTR_TO_PID(p) ((pid_t) ((uintptr_t) p)) +#define PID_TO_PTR(p) ((void*) ((uintptr_t) p)) + #define memzero(x,l) (memset((x), 0, (l))) #define zero(x) (memzero(&(x), sizeof(x))) diff --git a/src/basic/missing.h b/src/basic/missing.h index 34ab0254d..dc1f244d4 100644 --- a/src/basic/missing.h +++ b/src/basic/missing.h @@ -492,6 +492,14 @@ struct btrfs_ioctl_quota_ctl_args { #define BTRFS_SUPER_MAGIC 0x9123683E #endif +#ifndef CGROUP_SUPER_MAGIC +#define CGROUP_SUPER_MAGIC 0x27e0eb +#endif + +#ifndef TMPFS_MAGIC +#define TMPFS_MAGIC 0x01021994 +#endif + #ifndef MS_MOVE #define MS_MOVE 8192 #endif diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 61f188467..cff2d2a03 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -181,10 +181,10 @@ int is_kernel_thread(pid_t pid) { bool eof; FILE *f; - if (pid == 0) + if (pid == 0 || pid == 1) /* pid 1, and we ourselves certainly aren't a kernel thread */ return 0; - assert(pid > 0); + assert(pid > 1); p = procfs_file_alloca(pid, "cmdline"); f = fopen(p, "re"); diff --git a/src/basic/refcnt.h b/src/basic/refcnt.h index 0502c20a2..8a39d69fe 100644 --- a/src/basic/refcnt.h +++ b/src/basic/refcnt.h @@ -21,7 +21,9 @@ along with systemd; If not, see . ***/ -/* A type-safe atomic refcounter */ +/* A type-safe atomic refcounter. + * + * DO NOT USE THIS UNLESS YOU ACTUALLY CARE ABOUT THREAD SAFETY! */ typedef struct { volatile unsigned _value; diff --git a/src/basic/ring.h b/src/basic/ring.h index a7c44d1b5..dbd629638 100644 --- a/src/basic/ring.h +++ b/src/basic/ring.h @@ -50,7 +50,6 @@ int ring_push(Ring *r, const void *u8, size_t size); void ring_pull(Ring *r, size_t size); /* return size of occupied buffer in bytes */ -static inline size_t ring_get_size(Ring *r) -{ +static inline size_t ring_get_size(Ring *r) { return r->used; } diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c index 7c58985cd..a39a0f775 100644 --- a/src/basic/selinux-util.c +++ b/src/basic/selinux-util.c @@ -199,11 +199,11 @@ int mac_selinux_get_create_label_from_exe(const char *exe, char **label) { if (!mac_selinux_use()) return -EOPNOTSUPP; - r = getcon(&mycon); + r = getcon_raw(&mycon); if (r < 0) return -errno; - r = getfilecon(exe, &fcon); + r = getfilecon_raw(exe, &fcon); if (r < 0) return -errno; @@ -225,7 +225,7 @@ int mac_selinux_get_our_label(char **label) { if (!mac_selinux_use()) return -EOPNOTSUPP; - r = getcon(label); + r = getcon_raw(label); if (r < 0) return -errno; #endif @@ -249,7 +249,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char * if (!mac_selinux_use()) return -EOPNOTSUPP; - r = getcon(&mycon); + r = getcon_raw(&mycon); if (r < 0) return -errno; @@ -260,7 +260,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char * if (!exec_label) { /* If there is no context set for next exec let's use context of target executable */ - r = getfilecon(exe, &fcon); + r = getfilecon_raw(exe, &fcon); if (r < 0) return -errno; } diff --git a/src/basic/set.h b/src/basic/set.h index 51e40d3a6..4554ef2d4 100644 --- a/src/basic/set.h +++ b/src/basic/set.h @@ -28,12 +28,14 @@ Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); #define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS) -static inline void set_free(Set *s) { +static inline Set *set_free(Set *s) { internal_hashmap_free(HASHMAP_BASE(s)); + return NULL; } -static inline void set_free_free(Set *s) { +static inline Set *set_free_free(Set *s) { internal_hashmap_free_free(HASHMAP_BASE(s)); + return NULL; } /* no set_free_free_free */ diff --git a/src/basic/special.h b/src/basic/special.h index e51310eb6..f30458f25 100644 --- a/src/basic/special.h +++ b/src/basic/special.h @@ -115,3 +115,6 @@ #define SPECIAL_USER_SLICE "user.slice" #define SPECIAL_MACHINE_SLICE "machine.slice" #define SPECIAL_ROOT_SLICE "-.slice" + +/* The scope unit systemd itself lives in. */ +#define SPECIAL_INIT_SCOPE "init.scope" diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index cf55263bb..c5ef5ab0d 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -1074,3 +1074,22 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) { return 0; } + +int ptsname_namespace(int pty, char **ret) { + int no = -1, r; + + /* Like ptsname(), but doesn't assume that the path is + * accessible in the local namespace. */ + + r = ioctl(pty, TIOCGPTN, &no); + if (r < 0) + return -errno; + + if (no < 0) + return -EIO; + + if (asprintf(ret, "/dev/pts/%i", no) < 0) + return -ENOMEM; + + return 0; +} diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h index 188714f22..b9a3809a6 100644 --- a/src/basic/terminal-util.h +++ b/src/basic/terminal-util.h @@ -107,3 +107,5 @@ int get_ctty(pid_t, dev_t *_devnr, char **r); int getttyname_malloc(int fd, char **r); int getttyname_harder(int fd, char **r); + +int ptsname_namespace(int pty, char **ret); diff --git a/src/basic/time-util.c b/src/basic/time-util.c index e278196c9..531931f6e 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -26,6 +26,7 @@ #include "util.h" #include "time-util.h" +#include "path-util.h" #include "strv.h" usec_t now(clockid_t clock_id) { @@ -36,6 +37,14 @@ usec_t now(clockid_t clock_id) { return timespec_load(&ts); } +nsec_t now_nsec(clockid_t clock_id) { + struct timespec ts; + + assert_se(clock_gettime(clock_id, &ts) == 0); + + return timespec_load_nsec(&ts); +} + dual_timestamp* dual_timestamp_get(dual_timestamp *ts) { assert(ts); @@ -129,6 +138,18 @@ usec_t timespec_load(const struct timespec *ts) { (usec_t) ts->tv_nsec / NSEC_PER_USEC; } +nsec_t timespec_load_nsec(const struct timespec *ts) { + assert(ts); + + if (ts->tv_sec == (time_t) -1 && + ts->tv_nsec == (long) -1) + return NSEC_INFINITY; + + return + (nsec_t) ts->tv_sec * NSEC_PER_SEC + + (nsec_t) ts->tv_nsec; +} + struct timespec *timespec_store(struct timespec *ts, usec_t u) { assert(ts); @@ -971,7 +992,10 @@ bool timezone_is_valid(const char *name) { const char *p, *t; struct stat st; - if (!name || *name == 0 || *name == '/') + if (isempty(name)) + return false; + + if (name[0] == '/') return false; for (p = name; *p; p++) { @@ -1021,3 +1045,30 @@ clockid_t clock_boottime_or_monotonic(void) { return clock; } + +int get_timezone(char **tz) { + _cleanup_free_ char *t = NULL; + const char *e; + char *z; + int r; + + r = readlink_malloc("/etc/localtime", &t); + if (r < 0) + return r; /* returns EINVAL if not a symlink */ + + e = path_startswith(t, "/usr/share/zoneinfo/"); + if (!e) + e = path_startswith(t, "../usr/share/zoneinfo/"); + if (!e) + return -EINVAL; + + if (!timezone_is_valid(e)) + return -EINVAL; + + z = strdup(e); + if (!z) + return -ENOMEM; + + *tz = z; + return 0; +} diff --git a/src/basic/time-util.h b/src/basic/time-util.h index 2aba04221..de881e8fe 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -70,6 +70,7 @@ typedef struct dual_timestamp { #define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) { 0ULL, 0ULL }) usec_t now(clockid_t clock); +nsec_t now_nsec(clockid_t clock); dual_timestamp* dual_timestamp_get(dual_timestamp *ts); dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u); @@ -87,6 +88,8 @@ struct timespec *timespec_store(struct timespec *ts, usec_t u); usec_t timeval_load(const struct timeval *tv) _pure_; struct timeval *timeval_store(struct timeval *tv, usec_t u); +nsec_t timespec_load_nsec(const struct timespec *ts) _pure_; + char *format_timestamp(char *buf, size_t l, usec_t t); char *format_timestamp_utc(char *buf, size_t l, usec_t t); char *format_timestamp_us(char *buf, size_t l, usec_t t); @@ -110,3 +113,5 @@ bool timezone_is_valid(const char *name); clockid_t clock_boottime_or_monotonic(void); #define xstrftime(buf, fmt, tm) assert_se(strftime(buf, ELEMENTSOF(buf), fmt, tm) > 0) + +int get_timezone(char **timezone); diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c index fa530da45..8742ee757 100644 --- a/src/basic/unit-name.c +++ b/src/basic/unit-name.c @@ -586,6 +586,42 @@ int unit_name_from_dbus_path(const char *path, char **name) { return 0; } +const char* unit_dbus_interface_from_type(UnitType t) { + + static const char *const table[_UNIT_TYPE_MAX] = { + [UNIT_SERVICE] = "org.freedesktop.systemd1.Service", + [UNIT_SOCKET] = "org.freedesktop.systemd1.Socket", + [UNIT_BUSNAME] = "org.freedesktop.systemd1.BusName", + [UNIT_TARGET] = "org.freedesktop.systemd1.Target", + [UNIT_SNAPSHOT] = "org.freedesktop.systemd1.Snapshot", + [UNIT_DEVICE] = "org.freedesktop.systemd1.Device", + [UNIT_MOUNT] = "org.freedesktop.systemd1.Mount", + [UNIT_AUTOMOUNT] = "org.freedesktop.systemd1.Automount", + [UNIT_SWAP] = "org.freedesktop.systemd1.Swap", + [UNIT_TIMER] = "org.freedesktop.systemd1.Timer", + [UNIT_PATH] = "org.freedesktop.systemd1.Path", + [UNIT_SLICE] = "org.freedesktop.systemd1.Slice", + [UNIT_SCOPE] = "org.freedesktop.systemd1.Scope", + }; + + if (t < 0) + return NULL; + if (t >= _UNIT_TYPE_MAX) + return NULL; + + return table[t]; +} + +const char *unit_dbus_interface_from_name(const char *name) { + UnitType t; + + t = unit_name_to_type(name); + if (t < 0) + return NULL; + + return unit_dbus_interface_from_type(t); +} + static char *do_escape_mangle(const char *f, UnitNameMangle allow_globs, char *t) { const char *valid_chars; @@ -787,7 +823,7 @@ static const char* const unit_type_table[_UNIT_TYPE_MAX] = { [UNIT_TIMER] = "timer", [UNIT_PATH] = "path", [UNIT_SLICE] = "slice", - [UNIT_SCOPE] = "scope" + [UNIT_SCOPE] = "scope", }; DEFINE_STRING_TABLE_LOOKUP(unit_type, UnitType); diff --git a/src/basic/unit-name.h b/src/basic/unit-name.h index b2043d087..28b3a555f 100644 --- a/src/basic/unit-name.h +++ b/src/basic/unit-name.h @@ -152,6 +152,9 @@ int unit_name_to_path(const char *name, char **ret); char *unit_dbus_path_from_name(const char *name); int unit_name_from_dbus_path(const char *path, char **name); +const char* unit_dbus_interface_from_type(UnitType t); +const char *unit_dbus_interface_from_name(const char *name); + typedef enum UnitNameMangle { UNIT_NAME_NOGLOB, UNIT_NAME_GLOB, diff --git a/src/basic/util.c b/src/basic/util.c index f752595ca..86aacad30 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -373,6 +373,19 @@ int parse_pid(const char *s, pid_t* ret_pid) { return 0; } +bool uid_is_valid(uid_t uid) { + + /* Some libc APIs use UID_INVALID as special placeholder */ + if (uid == (uid_t) 0xFFFFFFFF) + return false; + + /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */ + if (uid == (uid_t) 0xFFFF) + return false; + + return true; +} + int parse_uid(const char *s, uid_t* ret_uid) { unsigned long ul = 0; uid_t uid; @@ -389,13 +402,11 @@ int parse_uid(const char *s, uid_t* ret_uid) { if ((unsigned long) uid != ul) return -ERANGE; - /* Some libc APIs use UID_INVALID as special placeholder */ - if (uid == (uid_t) 0xFFFFFFFF) - return -ENXIO; - - /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */ - if (uid == (uid_t) 0xFFFF) - return -ENXIO; + if (!uid_is_valid(uid)) + return -ENXIO; /* we return ENXIO instead of EINVAL + * here, to make it easy to distuingish + * invalid numeric uids invalid + * strings. */ if (ret_uid) *ret_uid = uid; @@ -4273,7 +4284,7 @@ bool is_locale_utf8(void) { /* Check result, but ignore the result if C was set * explicitly. */ cached_answer = - streq(set, "C") && + STR_IN_SET(set, "C", "POSIX") && !getenv("LC_ALL") && !getenv("LC_CTYPE") && !getenv("LANG"); @@ -5754,40 +5765,39 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra switch (state) { case START: - if (c == 0) { - if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) - if (!GREEDY_REALLOC(s, allocated, sz+1)) - return -ENOMEM; + if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) + if (!GREEDY_REALLOC(s, allocated, sz+1)) + return -ENOMEM; + + if (c == 0) goto finish_force_terminate; - } else if (strchr(separators, c)) { + else if (strchr(separators, c)) { if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) { - if (!GREEDY_REALLOC(s, allocated, sz+1)) - return -ENOMEM; (*p) ++; goto finish_force_next; } break; } + /* We found a non-blank character, so we will always + * want to return a string (even if it is empty), + * allocate it here. */ + if (!GREEDY_REALLOC(s, allocated, sz+1)) + return -ENOMEM; + state = VALUE; /* fallthrough */ case VALUE: if (c == 0) goto finish_force_terminate; - else if (c == '\'' && (flags & EXTRACT_QUOTES)) { - if (!GREEDY_REALLOC(s, allocated, sz+1)) - return -ENOMEM; - + else if (c == '\'' && (flags & EXTRACT_QUOTES)) state = SINGLE_QUOTE; - } else if (c == '\\') + else if (c == '\\') state = VALUE_ESCAPE; - else if (c == '\"' && (flags & EXTRACT_QUOTES)) { - if (!GREEDY_REALLOC(s, allocated, sz+1)) - return -ENOMEM; - + else if (c == '\"' && (flags & EXTRACT_QUOTES)) state = DOUBLE_QUOTE; - } else if (strchr(separators, c)) { + else if (strchr(separators, c)) { if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) { (*p) ++; goto finish_force_next; @@ -5891,8 +5901,6 @@ end_escape: case SEPARATOR: if (c == 0) goto finish_force_terminate; - if (flags & EXTRACT_DONT_COALESCE_SEPARATORS) - goto finish_force_next; if (!strchr(separators, c)) goto finish; break; @@ -6098,6 +6106,9 @@ int openpt_in_namespace(pid_t pid, int flags) { if (master < 0) _exit(EXIT_FAILURE); + if (unlockpt(master) < 0) + _exit(EXIT_FAILURE); + cmsg = CMSG_FIRSTHDR(&mh); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; diff --git a/src/basic/util.h b/src/basic/util.h index 1ead7b541..0fafebd52 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -83,7 +83,7 @@ int strcmp_ptr(const char *a, const char *b) _pure_; #define newdup(t, p, n) ((t*) memdup_multiply(p, sizeof(t), (n))) -#define malloc0(n) (calloc((n), 1)) +#define malloc0(n) (calloc(1, (n))) static inline void *mfree(void *memory) { free(memory); @@ -154,7 +154,10 @@ int parse_size(const char *t, off_t base, off_t *size); int parse_boolean(const char *v) _pure_; int parse_pid(const char *s, pid_t* ret_pid); int parse_uid(const char *s, uid_t* ret_uid); -#define parse_gid(s, ret_uid) parse_uid(s, ret_uid) +#define parse_gid(s, ret_gid) parse_uid(s, ret_gid) + +bool uid_is_valid(uid_t uid); +#define gid_is_valid(gid) uid_is_valid(gid) int safe_atou(const char *s, unsigned *ret_u); int safe_atoi(const char *s, int *ret_i); @@ -363,6 +366,9 @@ int fd_is_temporary_fs(int fd); int pipe_eof(int fd); +DEFINE_TRIVIAL_CLEANUP_FUNC(cpu_set_t*, CPU_FREE); +#define _cleanup_cpu_free_ _cleanup_(CPU_FREEp) + cpu_set_t* cpu_set_malloc(unsigned *ncpus); #define xsprintf(buf, fmt, ...) assert_se((size_t) snprintf(buf, ELEMENTSOF(buf), fmt, __VA_ARGS__) < ELEMENTSOF(buf)) @@ -561,6 +567,7 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, void *arg); #define _(String) gettext (String) +#define N_(String) String void init_gettext(void); bool is_locale_utf8(void); diff --git a/src/basic/virt.c b/src/basic/virt.c index a8d26716a..4a4bebd52 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -156,7 +156,8 @@ static int detect_vm_dmi(const char **_id) { "VMW\0" "vmware\0" "innotek GmbH\0" "oracle\0" "Xen\0" "xen\0" - "Bochs\0" "bochs\0"; + "Bochs\0" "bochs\0" + "Parallels\0" "parallels\0"; unsigned i; for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) { @@ -244,8 +245,9 @@ int detect_vm(const char **id) { r = detect_vm_dmi(&_id); /* kvm with and without Virtualbox */ + /* Parallels exports KVMKVMKVM leaf */ if (streq_ptr(_id_cpuid, "kvm")) { - if (r > 0 && streq(_id, "oracle")) + if (r > 0 && (streq(_id, "oracle") || streq(_id, "parallels"))) goto finish; _id = _id_cpuid; diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 4ac193e22..38b79da88 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -70,16 +70,14 @@ typedef struct { BOOLEAN no_editor; } Config; -static VOID cursor_left(UINTN *cursor, UINTN *first) -{ +static VOID cursor_left(UINTN *cursor, UINTN *first) { if ((*cursor) > 0) (*cursor)--; else if ((*first) > 0) (*first)--; } -static VOID cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len) -{ +static VOID cursor_right(UINTN *cursor, UINTN *first, UINTN x_max, UINTN len) { if ((*cursor)+1 < x_max) (*cursor)++; else if ((*first) + (*cursor) < len) @@ -856,13 +854,11 @@ static VOID config_entry_free(ConfigEntry *entry) { FreePool(entry->options); } -static BOOLEAN is_digit(CHAR16 c) -{ +static BOOLEAN is_digit(CHAR16 c) { return (c >= '0') && (c <= '9'); } -static UINTN c_order(CHAR16 c) -{ +static UINTN c_order(CHAR16 c) { if (c == '\0') return 0; if (is_digit(c)) @@ -873,8 +869,7 @@ static UINTN c_order(CHAR16 c) return c + 0x10000; } -static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2) -{ +static INTN str_verscmp(CHAR16 *s1, CHAR16 *s2) { CHAR16 *os1 = s1; CHAR16 *os2 = s2; diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c index b8d1d2cca..4fb642e7b 100644 --- a/src/cgls/cgls.c +++ b/src/cgls/cgls.c @@ -54,7 +54,7 @@ static void help(void) { " -a --all Show all groups, including empty\n" " -l --full Do not ellipsize output\n" " -k Include kernel threads in output\n" - " -M --machine Show container\n" + " -M --machine= Show container\n" , program_invocation_short_name); } @@ -123,146 +123,161 @@ static int parse_argv(int argc, char *argv[]) { return 1; } -int main(int argc, char *argv[]) { - int r = 0, retval = EXIT_FAILURE; - int output_flags; - _cleanup_free_ char *root = NULL; +static int get_cgroup_root(char **ret) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL; + _cleanup_free_ char *unit = NULL, *path = NULL; + const char *m; + int r; + + if (!arg_machine) { + r = cg_get_root_path(ret); + if (r < 0) + return log_error_errno(r, "Failed to get root control group path: %m"); + + return 0; + } + + m = strjoina("/run/systemd/machines/", arg_machine); + r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL); + if (r < 0) + return log_error_errno(r, "Failed to load machine data: %m"); + + path = unit_dbus_path_from_name(unit); + if (!path) + return log_oom(); + + r = bus_open_transport(BUS_TRANSPORT_LOCAL, NULL, false, &bus); + if (r < 0) + return log_error_errno(r, "Failed to create bus connection: %m"); + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + path, + unit_dbus_interface_from_name(unit), + "ControlGroup", + &error, + ret); + if (r < 0) + return log_error_errno(r, "Failed to query unit control group path: %s", bus_error_message(&error, r)); + + return 0; +} + +int main(int argc, char *argv[]) { + int r, output_flags; log_parse_environment(); log_open(); r = parse_argv(argc, argv); - if (r < 0) - goto finish; - else if (r == 0) { - retval = EXIT_SUCCESS; + if (r <= 0) goto finish; - } if (!arg_no_pager) { r = pager_open(false); - if (r > 0) { - if (arg_full == -1) - arg_full = true; - } + if (r > 0 && arg_full < 0) + arg_full = true; } output_flags = arg_all * OUTPUT_SHOW_ALL | (arg_full > 0) * OUTPUT_FULL_WIDTH; - r = bus_open_transport(BUS_TRANSPORT_LOCAL, NULL, false, &bus); - if (r < 0) { - log_error_errno(r, "Failed to create bus connection: %m"); - goto finish; - } - if (optind < argc) { + _cleanup_free_ char *root = NULL; int i; + r = get_cgroup_root(&root); + if (r < 0) + goto finish; + for (i = optind; i < argc; i++) { int q; - fprintf(stdout, "%s:\n", argv[i]); - fflush(stdout); + if (path_startswith(argv[i], "/sys/fs/cgroup")) { - if (arg_machine) - root = strjoin("machine/", arg_machine, "/", argv[i], NULL); - else - root = strdup(argv[i]); - if (!root) - return log_oom(); + printf("Directory %s:\n", argv[i]); + fflush(stdout); + + q = show_cgroup_by_path(argv[i], NULL, 0, arg_kernel_threads, output_flags); + } else { + _cleanup_free_ char *c = NULL, *p = NULL, *j = NULL; + const char *controller, *path; + + r = cg_split_spec(argv[i], &c, &p); + if (r < 0) { + log_error_errno(r, "Failed to split argument %s: %m", argv[i]); + goto finish; + } + + controller = c ?: SYSTEMD_CGROUP_CONTROLLER; + if (p) { + j = strjoin(root, "/", p, NULL); + if (!j) { + r = log_oom(); + goto finish; + } + + path_kill_slashes(j); + path = j; + } else + path = root; + + if (cg_unified() > 0) + printf("Control group %s:\n", path); + else + printf("Controller %s; control group %s:\n", controller, path); + fflush(stdout); + + q = show_cgroup(controller, path, NULL, 0, arg_kernel_threads, output_flags); + } - q = show_cgroup_by_path(root, NULL, 0, - arg_kernel_threads, output_flags); if (q < 0) r = q; } } else { - _cleanup_free_ char *p; + bool done = false; - p = get_current_dir_name(); - if (!p) { - log_error_errno(errno, "Cannot determine current working directory: %m"); - goto finish; - } + if (!arg_machine) { + _cleanup_free_ char *cwd = NULL; - if (path_startswith(p, "/sys/fs/cgroup") && !arg_machine) { - printf("Working Directory %s:\n", p); - r = show_cgroup_by_path(p, NULL, 0, - arg_kernel_threads, output_flags); - } else { - if (arg_machine) { - char *m; - const char *cgroup; - _cleanup_free_ char *unit = NULL; - _cleanup_free_ char *path = NULL; - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - - m = strjoina("/run/systemd/machines/", arg_machine); - r = parse_env_file(m, NEWLINE, "SCOPE", &unit, NULL); - if (r < 0) { - log_error_errno(r, "Failed to get machine path: %m"); - goto finish; - } - - path = unit_dbus_path_from_name(unit); - if (!path) { - log_oom(); - goto finish; - } + cwd = get_current_dir_name(); + if (!cwd) { + r = log_error_errno(errno, "Cannot determine current working directory: %m"); + goto finish; + } - r = sd_bus_get_property( - bus, - "org.freedesktop.systemd1", - path, - endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service", - "ControlGroup", - &error, - &reply, - "s"); + if (path_startswith(cwd, "/sys/fs/cgroup")) { + printf("Working directory %s:\n", cwd); + fflush(stdout); - if (r < 0) { - log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r)); - goto finish; - } - - r = sd_bus_message_read(reply, "s", &cgroup); - if (r < 0) { - bus_log_parse_error(r); - goto finish; - } + r = show_cgroup_by_path(cwd, NULL, 0, arg_kernel_threads, output_flags); + done = true; + } + } - root = strdup(cgroup); - if (!root) { - log_oom(); - goto finish; - } + if (!done) { + _cleanup_free_ char *root = NULL; - } else - r = cg_get_root_path(&root); - if (r < 0) { - log_error_errno(r, "Failed to get %s path: %m", - arg_machine ? "machine" : "root"); + r = get_cgroup_root(&root); + if (r < 0) goto finish; - } - r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, - arg_kernel_threads, output_flags); + printf("Control group %s:\n", isempty(root) ? "/" : root); + fflush(stdout); + + r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, root, NULL, 0, arg_kernel_threads, output_flags); } } - if (r < 0) { - log_error_errno(r, "Failed to list cgroup tree %s: %m", root); - retval = EXIT_FAILURE; - } else - retval = EXIT_SUCCESS; + if (r < 0) + log_error_errno(r, "Failed to list cgroup tree: %m"); finish: pager_close(); - return retval; + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index f953c9e62..1c94bea31 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -19,7 +19,6 @@ along with systemd; If not, see . ***/ -#define __STDC_FORMAT_MACROS #include #include #include @@ -31,6 +30,7 @@ #include "path-util.h" #include "terminal-util.h" +#include "process-util.h" #include "util.h" #include "hashmap.h" #include "cgroup-util.h" @@ -48,23 +48,25 @@ typedef struct Group { unsigned n_tasks; unsigned cpu_iteration; - uint64_t cpu_usage; - struct timespec cpu_timestamp; + nsec_t cpu_usage; + nsec_t cpu_timestamp; double cpu_fraction; uint64_t memory; unsigned io_iteration; uint64_t io_input, io_output; - struct timespec io_timestamp; + nsec_t io_timestamp; uint64_t io_input_bps, io_output_bps; } Group; static unsigned arg_depth = 3; -static unsigned arg_iterations = (unsigned)-1; +static unsigned arg_iterations = (unsigned) -1; static bool arg_batch = false; static bool arg_raw = false; static usec_t arg_delay = 1*USEC_PER_SEC; +static bool arg_kernel_threads = false; +static bool arg_recursive = true; static enum { ORDER_PATH, @@ -108,12 +110,16 @@ static const char *maybe_format_bytes(char *buf, size_t l, bool is_valid, off_t return format_bytes(buf, l, t); } -static int process(const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration) { +static int process( + const char *controller, + const char *path, + Hashmap *a, + Hashmap *b, + unsigned iteration, + Group **ret) { + Group *g; int r; - FILE *f = NULL; - pid_t pid; - unsigned n; assert(controller); assert(path); @@ -142,104 +148,111 @@ static int process(const char *controller, const char *path, Hashmap *a, Hashmap r = hashmap_move_one(a, b, path); if (r < 0) return r; + g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false; } } - /* Regardless which controller, let's find the maximum number - * of processes in any of it */ + if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { + _cleanup_fclose_ FILE *f = NULL; + pid_t pid; - r = cg_enumerate_processes(controller, path, &f); - if (r < 0) - return r; + r = cg_enumerate_processes(controller, path, &f); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; - n = 0; - while (cg_read_pid(f, &pid) > 0) - n++; - fclose(f); + g->n_tasks = 0; + while (cg_read_pid(f, &pid) > 0) { - if (n > 0) { - if (g->n_tasks_valid) - g->n_tasks = MAX(g->n_tasks, n); - else - g->n_tasks = n; + if (!arg_kernel_threads && is_kernel_thread(pid) > 0) + continue; - g->n_tasks_valid = true; - } + g->n_tasks++; + } - if (streq(controller, "cpuacct")) { + if (g->n_tasks > 0) + g->n_tasks_valid = true; + + } else if (streq(controller, "cpuacct") && cg_unified() <= 0) { + _cleanup_free_ char *p = NULL, *v = NULL; uint64_t new_usage; - char *p, *v; - struct timespec ts; + nsec_t timestamp; r = cg_get_path(controller, path, "cpuacct.usage", &p); if (r < 0) return r; r = read_one_line_file(p, &v); - free(p); + if (r == -ENOENT) + return 0; if (r < 0) return r; r = safe_atou64(v, &new_usage); - free(v); if (r < 0) return r; - assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); + timestamp = now_nsec(CLOCK_MONOTONIC); - if (g->cpu_iteration == iteration - 1) { - uint64_t x, y; + if (g->cpu_iteration == iteration - 1 && + (nsec_t) new_usage > g->cpu_usage) { - x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) - - ((uint64_t) g->cpu_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->cpu_timestamp.tv_nsec); + nsec_t x, y; - y = new_usage - g->cpu_usage; + x = timestamp - g->cpu_timestamp; + if (x < 1) + x = 1; - if (y > 0) { - g->cpu_fraction = (double) y / (double) x; - g->cpu_valid = true; - } + y = (nsec_t) new_usage - g->cpu_usage; + g->cpu_fraction = (double) y / (double) x; + g->cpu_valid = true; } - g->cpu_usage = new_usage; - g->cpu_timestamp = ts; + g->cpu_usage = (nsec_t) new_usage; + g->cpu_timestamp = timestamp; g->cpu_iteration = iteration; } else if (streq(controller, "memory")) { - char *p, *v; + _cleanup_free_ char *p = NULL, *v = NULL; - r = cg_get_path(controller, path, "memory.usage_in_bytes", &p); + if (cg_unified() <= 0) + r = cg_get_path(controller, path, "memory.usage_in_bytes", &p); + else + r = cg_get_path(controller, path, "memory.current", &p); if (r < 0) return r; r = read_one_line_file(p, &v); - free(p); + if (r == -ENOENT) + return 0; if (r < 0) return r; r = safe_atou64(v, &g->memory); - free(v); if (r < 0) return r; if (g->memory > 0) g->memory_valid = true; - } else if (streq(controller, "blkio")) { - char *p; + } else if (streq(controller, "blkio") && cg_unified() <= 0) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *p = NULL; uint64_t wr = 0, rd = 0; - struct timespec ts; + nsec_t timestamp; r = cg_get_path(controller, path, "blkio.io_service_bytes", &p); if (r < 0) return r; f = fopen(p, "re"); - free(p); - - if (!f) + if (!f) { + if (errno == ENOENT) + return 0; return -errno; + } for (;;) { char line[LINE_MAX], *l; @@ -269,20 +282,26 @@ static int process(const char *controller, const char *path, Hashmap *a, Hashmap *q += k; } - fclose(f); - - assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0); + timestamp = now_nsec(CLOCK_MONOTONIC); if (g->io_iteration == iteration - 1) { uint64_t x, yr, yw; - x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) - - ((uint64_t) g->io_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->io_timestamp.tv_nsec); + x = (uint64_t) (timestamp - g->io_timestamp); + if (x < 1) + x = 1; - yr = rd - g->io_input; - yw = wr - g->io_output; + if (rd > g->io_input) + yr = rd - g->io_input; + else + yr = 0; + + if (wr > g->io_output) + yw = wr - g->io_output; + else + yw = 0; - if (g->io_input > 0 || g->io_output > 0) { + if (yr > 0 || yw > 0) { g->io_input_bps = (yr * 1000000000ULL) / x; g->io_output_bps = (yw * 1000000000ULL) / x; g->io_valid = true; @@ -291,10 +310,13 @@ static int process(const char *controller, const char *path, Hashmap *a, Hashmap g->io_input = rd; g->io_output = wr; - g->io_timestamp = ts; + g->io_timestamp = timestamp; g->io_iteration = iteration; } + if (ret) + *ret = g; + return 0; } @@ -304,9 +326,11 @@ static int refresh_one( Hashmap *a, Hashmap *b, unsigned iteration, - unsigned depth) { + unsigned depth, + Group **ret) { - DIR *d = NULL; + _cleanup_closedir_ DIR *d = NULL; + Group *ours; int r; assert(controller); @@ -316,83 +340,100 @@ static int refresh_one( if (depth > arg_depth) return 0; - r = process(controller, path, a, b, iteration); + r = process(controller, path, a, b, iteration, &ours); if (r < 0) return r; r = cg_enumerate_subgroups(controller, path, &d); - if (r < 0) { - if (r == -ENOENT) - return 0; - + if (r == -ENOENT) + return 0; + if (r < 0) return r; - } for (;;) { - char *fn, *p; + _cleanup_free_ char *fn = NULL, *p = NULL; + Group *child = NULL; r = cg_read_subgroup(d, &fn); - if (r <= 0) - goto finish; + if (r < 0) + return r; + if (r == 0) + break; p = strjoin(path, "/", fn, NULL); - free(fn); - - if (!p) { - r = -ENOMEM; - goto finish; - } + if (!p) + return -ENOMEM; path_kill_slashes(p); - r = refresh_one(controller, p, a, b, iteration, depth + 1); - free(p); - + r = refresh_one(controller, p, a, b, iteration, depth + 1, &child); if (r < 0) - goto finish; + return r; + + if (arg_recursive && + child && + child->n_tasks_valid && + streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { + + /* Recursively sum up processes */ + + if (ours->n_tasks_valid) + ours->n_tasks += child->n_tasks; + else { + ours->n_tasks = child->n_tasks; + ours->n_tasks_valid = true; + } + } } -finish: - if (d) - closedir(d); + if (ret) + *ret = ours; - return r; + return 1; } -static int refresh(Hashmap *a, Hashmap *b, unsigned iteration) { +static int refresh(const char *root, Hashmap *a, Hashmap *b, unsigned iteration) { int r; assert(a); - r = refresh_one("name=systemd", "/", a, b, iteration, 0); + r = refresh_one(SYSTEMD_CGROUP_CONTROLLER, root, a, b, iteration, 0, NULL); if (r < 0) - if (r != -ENOENT) - return r; - r = refresh_one("cpuacct", "/", a, b, iteration, 0); + return r; + r = refresh_one("cpuacct", root, a, b, iteration, 0, NULL); if (r < 0) - if (r != -ENOENT) - return r; - r = refresh_one("memory", "/", a, b, iteration, 0); + return r; + r = refresh_one("memory", root, a, b, iteration, 0, NULL); if (r < 0) - if (r != -ENOENT) - return r; - - r = refresh_one("blkio", "/", a, b, iteration, 0); + return r; + r = refresh_one("blkio", root, a, b, iteration, 0, NULL); if (r < 0) - if (r != -ENOENT) - return r; + return r; + return 0; } static int group_compare(const void*a, const void *b) { const Group *x = *(Group**)a, *y = *(Group**)b; - if (path_startswith(y->path, x->path)) - return -1; - if (path_startswith(x->path, y->path)) - return 1; + if (arg_order != ORDER_TASKS || arg_recursive) { + /* Let's make sure that the parent is always before + * the child. Except when ordering by tasks and + * recursive summing is off, since that is actually + * not accumulative for all children. */ + + if (path_startswith(y->path, x->path)) + return -1; + if (path_startswith(x->path, y->path)) + return 1; + } + + switch (arg_order) { + + case ORDER_PATH: + break; - if (arg_order == ORDER_CPU) { + case ORDER_CPU: if (arg_cpu_type == CPU_PERCENT) { if (x->cpu_valid && y->cpu_valid) { if (x->cpu_fraction > y->cpu_fraction) @@ -409,10 +450,10 @@ static int group_compare(const void*a, const void *b) { else if (x->cpu_usage < y->cpu_usage) return 1; } - } - if (arg_order == ORDER_TASKS) { + break; + case ORDER_TASKS: if (x->n_tasks_valid && y->n_tasks_valid) { if (x->n_tasks > y->n_tasks) return -1; @@ -422,9 +463,10 @@ static int group_compare(const void*a, const void *b) { return -1; else if (y->n_tasks_valid) return 1; - } - if (arg_order == ORDER_MEMORY) { + break; + + case ORDER_MEMORY: if (x->memory_valid && y->memory_valid) { if (x->memory > y->memory) return -1; @@ -434,9 +476,10 @@ static int group_compare(const void*a, const void *b) { return -1; else if (y->memory_valid) return 1; - } - if (arg_order == ORDER_IO) { + break; + + case ORDER_IO: if (x->io_valid && y->io_valid) { if (x->io_input_bps + x->io_output_bps > y->io_input_bps + y->io_output_bps) return -1; @@ -448,13 +491,13 @@ static int group_compare(const void*a, const void *b) { return 1; } - return strcmp(x->path, y->path); + return path_compare(x->path, y->path); } #define ON ANSI_HIGHLIGHT_ON #define OFF ANSI_HIGHLIGHT_OFF -static int display(Hashmap *a) { +static void display(Hashmap *a) { Iterator i; Group *g; Group **array; @@ -481,9 +524,10 @@ static int display(Hashmap *a) { for (j = 0; j < n; j++) { unsigned cputlen, pathtlen; - format_timespan(buffer, sizeof(buffer), (nsec_t) (array[j]->cpu_usage / NSEC_PER_USEC), 0); + format_timespan(buffer, sizeof(buffer), (usec_t) (array[j]->cpu_usage / NSEC_PER_USEC), 0); cputlen = strlen(buffer); maxtcpu = MAX(maxtcpu, cputlen); + pathtlen = strlen(array[j]->path); maxtpath = MAX(maxtpath, pathtlen); } @@ -503,7 +547,7 @@ static int display(Hashmap *a) { path_columns = 10; printf("%s%-*s%s %s%7s%s %s%s%s %s%8s%s %s%8s%s %s%8s%s\n\n", - arg_order == ORDER_PATH ? ON : "", path_columns, "Path", + arg_order == ORDER_PATH ? ON : "", path_columns, "Control Group", arg_order == ORDER_PATH ? OFF : "", arg_order == ORDER_TASKS ? ON : "", "Tasks", arg_order == ORDER_TASKS ? OFF : "", @@ -519,16 +563,17 @@ static int display(Hashmap *a) { path_columns = maxtpath; for (j = 0; j < n; j++) { - char *p; + _cleanup_free_ char *ellipsized = NULL; + const char *path; if (on_tty() && j + 5 > rows) break; g = array[j]; - p = ellipsize(g->path, path_columns, 33); - printf("%-*s", path_columns, p ? p : g->path); - free(p); + path = isempty(g->path) ? "/" : g->path; + ellipsized = ellipsize(path, path_columns, 33); + printf("%-*s", path_columns, ellipsized ?: path); if (g->n_tasks_valid) printf(" %7u", g->n_tasks); @@ -541,7 +586,7 @@ static int display(Hashmap *a) { else fputs(" -", stdout); } else - printf(" %*s", maxtcpu, format_timespan(buffer, sizeof(buffer), (nsec_t) (g->cpu_usage / NSEC_PER_USEC), 0)); + printf(" %*s", maxtcpu, format_timespan(buffer, sizeof(buffer), (usec_t) (g->cpu_usage / NSEC_PER_USEC), 0)); printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->memory_valid, g->memory)); printf(" %8s", maybe_format_bytes(buffer, sizeof(buffer), g->io_valid, g->io_input_bps)); @@ -549,22 +594,23 @@ static int display(Hashmap *a) { putchar('\n'); } - - return 0; } static void help(void) { printf("%s [OPTIONS...]\n\n" "Show top control groups by their resource usage.\n\n" " -h --help Show this help\n" - " --version Print version and exit\n" - " -p Order by path\n" - " -t Order by number of tasks\n" - " -c Order by CPU load\n" - " -m Order by memory load\n" - " -i Order by IO load\n" + " --version Show package version\n" + " -p --order=path Order by path\n" + " -t --order=tasks Order by number of tasks\n" + " -c --order=cpu Order by CPU load (default)\n" + " -m --order=memory Order by memory load\n" + " -i --order=io Order by IO load\n" " -r --raw Provide raw (not human-readable) numbers\n" - " --cpu[=TYPE] Show CPU usage as time or percentage (default)\n" + " --cpu=percentage Show CPU usage as percentage (default)\n" + " --cpu=time Show CPU usage as time\n" + " -k Include kernel threads in task count\n" + " --recursive=BOOL Sum up task count recursively\n" " -d --delay=DELAY Delay between updates\n" " -n --iterations=N Run for N iterations before exiting\n" " -b --batch Run in batch mode, accepting no input\n" @@ -577,28 +623,31 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, ARG_DEPTH, - ARG_CPU_TYPE + ARG_CPU_TYPE, + ARG_ORDER, + ARG_RECURSIVE, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "delay", required_argument, NULL, 'd' }, - { "iterations", required_argument, NULL, 'n' }, - { "batch", no_argument, NULL, 'b' }, - { "raw", no_argument, NULL, 'r' }, - { "depth", required_argument, NULL, ARG_DEPTH }, - { "cpu", optional_argument, NULL, ARG_CPU_TYPE}, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "delay", required_argument, NULL, 'd' }, + { "iterations", required_argument, NULL, 'n' }, + { "batch", no_argument, NULL, 'b' }, + { "raw", no_argument, NULL, 'r' }, + { "depth", required_argument, NULL, ARG_DEPTH }, + { "cpu", optional_argument, NULL, ARG_CPU_TYPE }, + { "order", required_argument, NULL, ARG_ORDER }, + { "recursive", required_argument, NULL, ARG_RECURSIVE }, {} }; - int c; - int r; + int c, r; assert(argc >= 1); assert(argv); - while ((c = getopt_long(argc, argv, "hptcmin:brd:", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "hptcmin:brd:k", options, NULL)) >= 0) switch (c) { @@ -613,13 +662,17 @@ static int parse_argv(int argc, char *argv[]) { case ARG_CPU_TYPE: if (optarg) { - if (strcmp(optarg, "time") == 0) + if (streq(optarg, "time")) arg_cpu_type = CPU_TIME; - else if (strcmp(optarg, "percentage") == 0) + else if (streq(optarg, "percentage")) arg_cpu_type = CPU_PERCENT; - else + else { + log_error("Unknown argument to --cpu=: %s", optarg); return -EINVAL; - } + } + } else + arg_cpu_type = CPU_TIME; + break; case ARG_DEPTH: @@ -677,6 +730,37 @@ static int parse_argv(int argc, char *argv[]) { arg_order = ORDER_IO; break; + case ARG_ORDER: + if (streq(optarg, "path")) + arg_order = ORDER_PATH; + else if (streq(optarg, "tasks")) + arg_order = ORDER_TASKS; + else if (streq(optarg, "cpu")) + arg_order = ORDER_CPU; + else if (streq(optarg, "memory")) + arg_order = ORDER_MEMORY; + else if (streq(optarg, "io")) + arg_order = ORDER_IO; + else { + log_error("Invalid argument to --order=: %s", optarg); + return -EINVAL; + } + break; + + case 'k': + arg_kernel_threads = true; + break; + + case ARG_RECURSIVE: + r = parse_boolean(optarg); + if (r < 0) { + log_error("Failed to parse --recursive= argument: %s", optarg); + return r; + } + + arg_recursive = r; + break; + case '?': return -EINVAL; @@ -698,6 +782,7 @@ int main(int argc, char *argv[]) { unsigned iteration = 0; usec_t last_refresh = 0; bool quit = false, immediate_refresh = false; + _cleanup_free_ char *root = NULL; log_parse_environment(); log_open(); @@ -706,6 +791,12 @@ int main(int argc, char *argv[]) { if (r <= 0) goto finish; + r = cg_get_root_path(&root); + if (r < 0) { + log_error_errno(r, "Failed to get root control group path: %m"); + goto finish; + } + a = hashmap_new(&string_hash_ops); b = hashmap_new(&string_hash_ops); if (!a || !b) { @@ -715,7 +806,7 @@ int main(int argc, char *argv[]) { signal(SIGWINCH, columns_lines_cache_reset); - if (arg_iterations == (unsigned)-1) + if (arg_iterations == (unsigned) -1) arg_iterations = on_tty() ? 0 : 1; while (!quit) { @@ -728,9 +819,11 @@ int main(int argc, char *argv[]) { if (t >= last_refresh + arg_delay || immediate_refresh) { - r = refresh(a, b, iteration++); - if (r < 0) + r = refresh(root, a, b, iteration++); + if (r < 0) { + log_error_errno(r, "Failed to refresh: %m"); goto finish; + } group_hashmap_clear(b); @@ -742,9 +835,7 @@ int main(int argc, char *argv[]) { immediate_refresh = false; } - r = display(b); - if (r < 0) - goto finish; + display(b); if (arg_iterations && iteration >= arg_iterations) break; @@ -753,11 +844,10 @@ int main(int argc, char *argv[]) { fputs("\n", stdout); fflush(stdout); - if (arg_batch) { - usleep(last_refresh + arg_delay - t); - } else { - r = read_one_char(stdin, &key, - last_refresh + arg_delay - t, NULL); + if (arg_batch) + (void) usleep(last_refresh + arg_delay - t); + else { + r = read_one_char(stdin, &key, last_refresh + arg_delay - t, NULL); if (r == -ETIMEDOUT) continue; if (r < 0) { @@ -808,6 +898,20 @@ int main(int argc, char *argv[]) { arg_cpu_type = arg_cpu_type == CPU_TIME ? CPU_PERCENT : CPU_TIME; break; + case 'k': + arg_kernel_threads = !arg_kernel_threads; + fprintf(stdout, "\nCounting kernel threads: %s.", yes_no(arg_kernel_threads)); + fflush(stdout); + sleep(1); + break; + + case 'r': + arg_recursive = !arg_recursive; + fprintf(stdout, "\nRecursive task counting: %s", yes_no(arg_recursive)); + fflush(stdout); + sleep(1); + break; + case '+': if (arg_delay < USEC_PER_SEC) arg_delay += USEC_PER_MSEC*250; @@ -836,14 +940,17 @@ int main(int argc, char *argv[]) { case 'h': fprintf(stdout, "\t<" ON "p" OFF "> By path; <" ON "t" OFF "> By tasks; <" ON "c" OFF "> By CPU; <" ON "m" OFF "> By memory; <" ON "i" OFF "> By I/O\n" - "\t<" ON "+" OFF "> Increase delay; <" ON "-" OFF "> Decrease delay; <" ON "%%" OFF "> Toggle time\n" - "\t<" ON "q" OFF "> Quit; <" ON "SPACE" OFF "> Refresh"); + "\t<" ON "+" OFF "> Inc. delay; <" ON "-" OFF "> Dec. delay; <" ON "%%" OFF "> Toggle time; <" ON "SPACE" OFF "> Refresh\n" + "\t<" ON "k" OFF "> Count kernel threads; <" ON "r" OFF "> Count recursively; <" ON "q" OFF "> Quit"); fflush(stdout); sleep(3); break; default: - fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key); + if (key < ' ') + fprintf(stdout, "\nUnknown key '\\x%x'. Ignoring.", key); + else + fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key); fflush(stdout); sleep(1); break; @@ -856,10 +963,5 @@ finish: group_hashmap_free(a); group_hashmap_free(b); - if (r < 0) { - log_error_errno(r, "Exiting with failure: %m"); - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/core/automount.c b/src/core/automount.c index 4af381b4b..b8171ddad 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -1075,7 +1075,6 @@ const UnitVTable automount_vtable = { .reset_failed = automount_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Automount", .bus_vtable = bus_automount_vtable, .shutdown = automount_shutdown, diff --git a/src/core/busname.c b/src/core/busname.c index 9530a8731..d3c128223 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -1058,7 +1058,6 @@ const UnitVTable busname_vtable = { .supported = busname_supported, - .bus_interface = "org.freedesktop.systemd1.BusName", .bus_vtable = bus_busname_vtable, .status_message_formats = { diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 6474e08bd..9a025cf92 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -283,7 +283,7 @@ fail: return -errno; } -void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const char *path, ManagerState state) { +void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, ManagerState state) { bool is_root; int r; @@ -304,7 +304,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const cha * cgroup trees (assuming we are running in a container then), * and missing cgroups, i.e. EROFS and ENOENT. */ - if ((mask & CGROUP_CPU) && !is_root) { + if ((mask & CGROUP_MASK_CPU) && !is_root) { char buf[MAX(DECIMAL_STR_MAX(unsigned long), DECIMAL_STR_MAX(usec_t)) + 1]; sprintf(buf, "%lu\n", @@ -331,7 +331,7 @@ void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const cha "Failed to set cpu.cfs_quota_us on %s: %m", path); } - if (mask & CGROUP_BLKIO) { + if (mask & CGROUP_MASK_BLKIO) { char buf[MAX3(DECIMAL_STR_MAX(unsigned long)+1, DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(unsigned long)*1, DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1)]; @@ -381,21 +381,30 @@ void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const cha } } - if ((mask & CGROUP_MEMORY) && !is_root) { + if ((mask & CGROUP_MASK_MEMORY) && !is_root) { if (c->memory_limit != (uint64_t) -1) { char buf[DECIMAL_STR_MAX(uint64_t) + 1]; sprintf(buf, "%" PRIu64 "\n", c->memory_limit); - r = cg_set_attribute("memory", path, "memory.limit_in_bytes", buf); - } else - r = cg_set_attribute("memory", path, "memory.limit_in_bytes", "-1"); + + if (cg_unified() <= 0) + r = cg_set_attribute("memory", path, "memory.limit_in_bytes", buf); + else + r = cg_set_attribute("memory", path, "memory.max", buf); + + } else { + if (cg_unified() <= 0) + r = cg_set_attribute("memory", path, "memory.limit_in_bytes", "-1"); + else + r = cg_set_attribute("memory", path, "memory.max", "max"); + } if (r < 0) log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to set memory.limit_in_bytes on %s: %m", path); + "Failed to set memory.limit_in_bytes/memory.max on %s: %m", path); } - if ((mask & CGROUP_DEVICE) && !is_root) { + if ((mask & CGROUP_MASK_DEVICE) && !is_root) { CGroupDeviceAllow *a; /* Changing the devices list of a populated cgroup @@ -459,8 +468,8 @@ void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const cha } } -CGroupControllerMask cgroup_context_get_mask(CGroupContext *c) { - CGroupControllerMask mask = 0; +CGroupMask cgroup_context_get_mask(CGroupContext *c) { + CGroupMask mask = 0; /* Figure out which controllers we need */ @@ -468,52 +477,62 @@ CGroupControllerMask cgroup_context_get_mask(CGroupContext *c) { c->cpu_shares != (unsigned long) -1 || c->startup_cpu_shares != (unsigned long) -1 || c->cpu_quota_per_sec_usec != USEC_INFINITY) - mask |= CGROUP_CPUACCT | CGROUP_CPU; + mask |= CGROUP_MASK_CPUACCT | CGROUP_MASK_CPU; if (c->blockio_accounting || c->blockio_weight != (unsigned long) -1 || c->startup_blockio_weight != (unsigned long) -1 || c->blockio_device_weights || c->blockio_device_bandwidths) - mask |= CGROUP_BLKIO; + mask |= CGROUP_MASK_BLKIO; if (c->memory_accounting || c->memory_limit != (uint64_t) -1) - mask |= CGROUP_MEMORY; + mask |= CGROUP_MASK_MEMORY; if (c->device_allow || c->device_policy != CGROUP_AUTO) - mask |= CGROUP_DEVICE; + mask |= CGROUP_MASK_DEVICE; return mask; } -CGroupControllerMask unit_get_cgroup_mask(Unit *u) { +CGroupMask unit_get_own_mask(Unit *u) { CGroupContext *c; + /* Returns the mask of controllers the unit needs for itself */ + c = unit_get_cgroup_context(u); if (!c) return 0; /* If delegation is turned on, then turn on all cgroups, - * unless the process we fork into it is known to drop - * privileges anyway, and shouldn't get access to the - * controllers anyway. */ + * unless we are on the legacy hierarchy and the process we + * fork into it is known to drop privileges, and hence + * shouldn't get access to the controllers. + * + * Note that on the unified hierarchy it is safe to delegate + * controllers to unprivileged services. */ if (c->delegate) { ExecContext *e; e = unit_get_exec_context(u); - if (!e || exec_context_maintains_privileges(e)) - return _CGROUP_CONTROLLER_MASK_ALL; + if (!e || + exec_context_maintains_privileges(e) || + cg_unified() > 0) + return _CGROUP_MASK_ALL; } return cgroup_context_get_mask(c); } -CGroupControllerMask unit_get_members_mask(Unit *u) { +CGroupMask unit_get_members_mask(Unit *u) { assert(u); + /* Returns the mask of controllers all of the unit's children + * require, merged */ + if (u->cgroup_members_mask_valid) return u->cgroup_members_mask; @@ -532,7 +551,7 @@ CGroupControllerMask unit_get_members_mask(Unit *u) { continue; u->cgroup_members_mask |= - unit_get_cgroup_mask(member) | + unit_get_own_mask(member) | unit_get_members_mask(member); } } @@ -541,19 +560,52 @@ CGroupControllerMask unit_get_members_mask(Unit *u) { return u->cgroup_members_mask; } -CGroupControllerMask unit_get_siblings_mask(Unit *u) { +CGroupMask unit_get_siblings_mask(Unit *u) { assert(u); + /* Returns the mask of controllers all of the unit's siblings + * require, i.e. the members mask of the unit's parent slice + * if there is one. */ + if (UNIT_ISSET(u->slice)) return unit_get_members_mask(UNIT_DEREF(u->slice)); - return unit_get_cgroup_mask(u) | unit_get_members_mask(u); + return unit_get_own_mask(u) | unit_get_members_mask(u); +} + +CGroupMask unit_get_subtree_mask(Unit *u) { + + /* Returns the mask of this subtree, meaning of the group + * itself and its children. */ + + return unit_get_own_mask(u) | unit_get_members_mask(u); } -CGroupControllerMask unit_get_target_mask(Unit *u) { - CGroupControllerMask mask; +CGroupMask unit_get_target_mask(Unit *u) { + CGroupMask mask; + + /* This returns the cgroup mask of all controllers to enable + * for a specific cgroup, i.e. everything it needs itself, + * plus all that its children need, plus all that its siblings + * need. This is primarily useful on the legacy cgroup + * hierarchy, where we need to duplicate each cgroup in each + * hierarchy that shall be enabled for it. */ - mask = unit_get_cgroup_mask(u) | unit_get_members_mask(u) | unit_get_siblings_mask(u); + mask = unit_get_own_mask(u) | unit_get_members_mask(u) | unit_get_siblings_mask(u); + mask &= u->manager->cgroup_supported; + + return mask; +} + +CGroupMask unit_get_enable_mask(Unit *u) { + CGroupMask mask; + + /* This returns the cgroup mask of all controllers to enable + * for the children of a specific cgroup. This is primarily + * useful for the unified cgroup hierarchy, where each cgroup + * controls which controllers are enabled for its children. */ + + mask = unit_get_members_mask(u); mask &= u->manager->cgroup_supported; return mask; @@ -562,13 +614,13 @@ CGroupControllerMask unit_get_target_mask(Unit *u) { /* Recurse from a unit up through its containing slices, propagating * mask bits upward. A unit is also member of itself. */ void unit_update_cgroup_members_masks(Unit *u) { - CGroupControllerMask m; + CGroupMask m; bool more; assert(u); /* Calculate subtree mask */ - m = unit_get_cgroup_mask(u) | unit_get_members_mask(u); + m = unit_get_subtree_mask(u); /* See if anything changed from the previous invocation. If * not, we're done. */ @@ -608,7 +660,7 @@ void unit_update_cgroup_members_masks(Unit *u) { } } -static const char *migrate_callback(CGroupControllerMask mask, void *userdata) { +static const char *migrate_callback(CGroupMask mask, void *userdata) { Unit *u = userdata; assert(mask != 0); @@ -626,7 +678,115 @@ static const char *migrate_callback(CGroupControllerMask mask, void *userdata) { return NULL; } -static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) { +char *unit_default_cgroup_path(Unit *u) { + _cleanup_free_ char *escaped = NULL, *slice = NULL; + int r; + + assert(u); + + if (unit_has_name(u, SPECIAL_ROOT_SLICE)) + return strdup(u->manager->cgroup_root); + + if (UNIT_ISSET(u->slice) && !unit_has_name(UNIT_DEREF(u->slice), SPECIAL_ROOT_SLICE)) { + r = cg_slice_to_path(UNIT_DEREF(u->slice)->id, &slice); + if (r < 0) + return NULL; + } + + escaped = cg_escape(u->id); + if (!escaped) + return NULL; + + if (slice) + return strjoin(u->manager->cgroup_root, "/", slice, "/", escaped, NULL); + else + return strjoin(u->manager->cgroup_root, "/", escaped, NULL); +} + +int unit_set_cgroup_path(Unit *u, const char *path) { + _cleanup_free_ char *p = NULL; + int r; + + assert(u); + + if (path) { + p = strdup(path); + if (!p) + return -ENOMEM; + } else + p = NULL; + + if (streq_ptr(u->cgroup_path, p)) + return 0; + + if (p) { + r = hashmap_put(u->manager->cgroup_unit, p, u); + if (r < 0) + return r; + } + + unit_release_cgroup(u); + + u->cgroup_path = p; + p = NULL; + + return 1; +} + +int unit_watch_cgroup(Unit *u) { + _cleanup_free_ char *populated = NULL; + int r; + + assert(u); + + if (!u->cgroup_path) + return 0; + + if (u->cgroup_inotify_wd >= 0) + return 0; + + /* Only applies to the unified hierarchy */ + r = cg_unified(); + if (r < 0) + return log_unit_error_errno(u, r, "Failed detect wether the unified hierarchy is used: %m"); + if (r == 0) + return 0; + + /* Don't watch the root slice, it's pointless. */ + if (unit_has_name(u, SPECIAL_ROOT_SLICE)) + return 0; + + r = hashmap_ensure_allocated(&u->manager->cgroup_inotify_wd_unit, &trivial_hash_ops); + if (r < 0) + return log_oom(); + + r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.populated", &populated); + if (r < 0) + return log_oom(); + + u->cgroup_inotify_wd = inotify_add_watch(u->manager->cgroup_inotify_fd, populated, IN_MODIFY); + if (u->cgroup_inotify_wd < 0) { + + if (errno == ENOENT) /* If the directory is already + * gone we don't need to track + * it, so this is not an error */ + return 0; + + return log_unit_error_errno(u, errno, "Failed to add inotify watch descriptor for control group %s: %m", u->cgroup_path); + } + + r = hashmap_put(u->manager->cgroup_inotify_wd_unit, INT_TO_PTR(u->cgroup_inotify_wd), u); + if (r < 0) + return log_unit_error_errno(u, r, "Failed to add inotify watch descriptor to hash map: %m"); + + return 0; +} + +static int unit_create_cgroup( + Unit *u, + CGroupMask target_mask, + CGroupMask enable_mask) { + CGroupContext *c; int r; @@ -643,25 +803,29 @@ static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) { if (!path) return log_oom(); - r = hashmap_put(u->manager->cgroup_unit, path, u); - if (r < 0) { - log_error(r == -EEXIST ? "cgroup %s exists already: %s" : "hashmap_put failed for %s: %s", path, strerror(-r)); - return r; - } - if (r > 0) { - u->cgroup_path = path; - path = NULL; - } + r = unit_set_cgroup_path(u, path); + if (r == -EEXIST) + return log_unit_error_errno(u, r, "Control group %s exists already.", path); + if (r < 0) + return log_unit_error_errno(u, r, "Failed to set unit's control group path to %s: %m", path); } /* First, create our own group */ - r = cg_create_everywhere(u->manager->cgroup_supported, mask, u->cgroup_path); + r = cg_create_everywhere(u->manager->cgroup_supported, target_mask, u->cgroup_path); + if (r < 0) + return log_unit_error_errno(u, r, "Failed to create cgroup %s: %m", u->cgroup_path); + + /* Start watching it */ + (void) unit_watch_cgroup(u); + + /* Enable all controllers we need */ + r = cg_enable_everywhere(u->manager->cgroup_supported, enable_mask, u->cgroup_path); if (r < 0) - return log_error_errno(r, "Failed to create cgroup %s: %m", u->cgroup_path); + log_unit_warning_errno(u, r, "Failed to enable controllers on cgroup %s, ignoring: %m", u->cgroup_path); /* Keep track that this is now realized */ u->cgroup_realized = true; - u->cgroup_realized_mask = mask; + u->cgroup_realized_mask = target_mask; if (u->type != UNIT_SLICE && !c->delegate) { @@ -670,7 +834,7 @@ static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) { * for slice and delegation units. */ r = cg_migrate_everywhere(u->manager->cgroup_supported, u->cgroup_path, u->cgroup_path, migrate_callback, u); if (r < 0) - log_warning_errno(r, "Failed to migrate cgroup from to %s: %m", u->cgroup_path); + log_unit_warning_errno(u, r, "Failed to migrate cgroup from to %s, ignoring: %m", u->cgroup_path); } return 0; @@ -691,10 +855,10 @@ int unit_attach_pids_to_cgroup(Unit *u) { return 0; } -static bool unit_has_mask_realized(Unit *u, CGroupControllerMask mask) { +static bool unit_has_mask_realized(Unit *u, CGroupMask target_mask) { assert(u); - return u->cgroup_realized && u->cgroup_realized_mask == mask; + return u->cgroup_realized && u->cgroup_realized_mask == target_mask; } /* Check if necessary controllers and attributes for a unit are in place. @@ -704,7 +868,7 @@ static bool unit_has_mask_realized(Unit *u, CGroupControllerMask mask) { * * Returns 0 on success and < 0 on failure. */ static int unit_realize_cgroup_now(Unit *u, ManagerState state) { - CGroupControllerMask mask; + CGroupMask target_mask, enable_mask; int r; assert(u); @@ -714,9 +878,8 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) { u->in_cgroup_queue = false; } - mask = unit_get_target_mask(u); - - if (unit_has_mask_realized(u, mask)) + target_mask = unit_get_target_mask(u); + if (unit_has_mask_realized(u, target_mask)) return 0; /* First, realize parents */ @@ -727,12 +890,13 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) { } /* And then do the real work */ - r = unit_create_cgroups(u, mask); + enable_mask = unit_get_enable_mask(u); + r = unit_create_cgroup(u, target_mask, enable_mask); if (r < 0) return r; /* Finally, apply the necessary attributes. */ - cgroup_context_apply(unit_get_cgroup_context(u), mask, u->cgroup_path, state); + cgroup_context_apply(unit_get_cgroup_context(u), target_mask, u->cgroup_path, state); return 0; } @@ -759,7 +923,7 @@ unsigned manager_dispatch_cgroup_queue(Manager *m) { r = unit_realize_cgroup_now(i, state); if (r < 0) - log_warning_errno(r, "Failed to realize cgroups for queued unit %s: %m", i->id); + log_warning_errno(r, "Failed to realize cgroups for queued unit %s, ignoring: %m", i->id); n++; } @@ -806,12 +970,9 @@ static void unit_queue_siblings(Unit *u) { } int unit_realize_cgroup(Unit *u) { - CGroupContext *c; - assert(u); - c = unit_get_cgroup_context(u); - if (!c) + if (!UNIT_HAS_CGROUP_CONTEXT(u)) return 0; /* So, here's the deal: when realizing the cgroups for this @@ -832,39 +993,67 @@ int unit_realize_cgroup(Unit *u) { return unit_realize_cgroup_now(u, manager_state(u->manager)); } -void unit_destroy_cgroup_if_empty(Unit *u) { +void unit_release_cgroup(Unit *u) { + assert(u); + + /* Forgets all cgroup details for this cgroup */ + + if (u->cgroup_path) { + (void) hashmap_remove(u->manager->cgroup_unit, u->cgroup_path); + u->cgroup_path = mfree(u->cgroup_path); + } + + if (u->cgroup_inotify_wd >= 0) { + if (inotify_rm_watch(u->manager->cgroup_inotify_fd, u->cgroup_inotify_wd) < 0) + log_unit_debug_errno(u, errno, "Failed to remove cgroup inotify watch %i for %s, ignoring", u->cgroup_inotify_wd, u->id); + + (void) hashmap_remove(u->manager->cgroup_inotify_wd_unit, INT_TO_PTR(u->cgroup_inotify_wd)); + u->cgroup_inotify_wd = -1; + } +} + +void unit_prune_cgroup(Unit *u) { int r; + bool is_root_slice; assert(u); + /* Removes the cgroup, if empty and possible, and stops watching it. */ + if (!u->cgroup_path) return; - r = cg_trim_everywhere(u->manager->cgroup_supported, u->cgroup_path, !unit_has_name(u, SPECIAL_ROOT_SLICE)); + is_root_slice = unit_has_name(u, SPECIAL_ROOT_SLICE); + + r = cg_trim_everywhere(u->manager->cgroup_supported, u->cgroup_path, !is_root_slice); if (r < 0) { - log_debug_errno(r, "Failed to destroy cgroup %s: %m", u->cgroup_path); + log_debug_errno(r, "Failed to destroy cgroup %s, ignoring: %m", u->cgroup_path); return; } - hashmap_remove(u->manager->cgroup_unit, u->cgroup_path); + if (is_root_slice) + return; + + unit_release_cgroup(u); - free(u->cgroup_path); - u->cgroup_path = NULL; u->cgroup_realized = false; u->cgroup_realized_mask = 0; } -pid_t unit_search_main_pid(Unit *u) { +int unit_search_main_pid(Unit *u, pid_t *ret) { _cleanup_fclose_ FILE *f = NULL; pid_t pid = 0, npid, mypid; + int r; assert(u); + assert(ret); if (!u->cgroup_path) - return 0; + return -ENXIO; - if (cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &f) < 0) - return 0; + r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &f); + if (r < 0) + return r; mypid = getpid(); while (cg_read_pid(f, &npid) > 0) { @@ -877,90 +1066,274 @@ pid_t unit_search_main_pid(Unit *u) { if (get_parent_of_pid(npid, &ppid) >= 0 && ppid != mypid) continue; - if (pid != 0) { + if (pid != 0) /* Dang, there's more than one daemonized PID in this group, so we don't know what process is the main process. */ - pid = 0; - break; - } + + return -ENODATA; pid = npid; } - return pid; + *ret = pid; + return 0; +} + +static int unit_watch_pids_in_path(Unit *u, const char *path) { + _cleanup_closedir_ DIR *d = NULL; + _cleanup_fclose_ FILE *f = NULL; + int ret = 0, r; + + assert(u); + assert(path); + + r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, path, &f); + if (r < 0) + ret = r; + else { + pid_t pid; + + while ((r = cg_read_pid(f, &pid)) > 0) { + r = unit_watch_pid(u, pid); + if (r < 0 && ret >= 0) + ret = r; + } + + if (r < 0 && ret >= 0) + ret = r; + } + + r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, path, &d); + if (r < 0) { + if (ret >= 0) + ret = r; + } else { + char *fn; + + while ((r = cg_read_subgroup(d, &fn)) > 0) { + _cleanup_free_ char *p = NULL; + + p = strjoin(path, "/", fn, NULL); + free(fn); + + if (!p) + return -ENOMEM; + + r = unit_watch_pids_in_path(u, p); + if (r < 0 && ret >= 0) + ret = r; + } + + if (r < 0 && ret >= 0) + ret = r; + } + + return ret; +} + +int unit_watch_all_pids(Unit *u) { + assert(u); + + /* Adds all PIDs from our cgroup to the set of PIDs we + * watch. This is a fallback logic for cases where we do not + * get reliable cgroup empty notifications: we try to use + * SIGCHLD as replacement. */ + + if (!u->cgroup_path) + return -ENOENT; + + if (cg_unified() > 0) /* On unified we can use proper notifications */ + return 0; + + return unit_watch_pids_in_path(u, u->cgroup_path); +} + +int unit_notify_cgroup_empty(Unit *u) { + int r; + + assert(u); + + if (!u->cgroup_path) + return 0; + + r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path); + if (r <= 0) + return r; + + unit_add_to_gc_queue(u); + + if (UNIT_VTABLE(u)->notify_cgroup_empty) + UNIT_VTABLE(u)->notify_cgroup_empty(u); + + return 0; +} + +static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + Manager *m = userdata; + + assert(s); + assert(fd >= 0); + assert(m); + + for (;;) { + union inotify_event_buffer buffer; + struct inotify_event *e; + ssize_t l; + + l = read(fd, &buffer, sizeof(buffer)); + if (l < 0) { + if (errno == EINTR || errno == EAGAIN) + return 0; + + return log_error_errno(errno, "Failed to read control group inotify events: %m"); + } + + FOREACH_INOTIFY_EVENT(e, buffer, l) { + Unit *u; + + if (e->wd < 0) + /* Queue overflow has no watch descriptor */ + continue; + + if (e->mask & IN_IGNORED) + /* The watch was just removed */ + continue; + + u = hashmap_get(m->cgroup_inotify_wd_unit, INT_TO_PTR(e->wd)); + if (!u) /* Not that inotify might deliver + * events for a watch even after it + * was removed, because it was queued + * before the removal. Let's ignore + * this here safely. */ + continue; + + (void) unit_notify_cgroup_empty(u); + } + } } int manager_setup_cgroup(Manager *m) { _cleanup_free_ char *path = NULL; - int r; + CGroupController c; + int r, unified; + char *e; assert(m); /* 1. Determine hierarchy */ - free(m->cgroup_root); - m->cgroup_root = NULL; - + m->cgroup_root = mfree(m->cgroup_root); r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &m->cgroup_root); if (r < 0) return log_error_errno(r, "Cannot determine cgroup we are running in: %m"); - /* LEGACY: Already in /system.slice? If so, let's cut this - * off. This is to support live upgrades from older systemd - * versions where PID 1 was moved there. */ - if (m->running_as == MANAGER_SYSTEM) { - char *e; + /* Chop off the init scope, if we are already located in it */ + e = endswith(m->cgroup_root, "/" SPECIAL_INIT_SCOPE); + /* LEGACY: Also chop off the system slice if we are in + * it. This is to support live upgrades from older systemd + * versions where PID 1 was moved there. Also see + * cg_get_root_path(). */ + if (!e && m->running_as == MANAGER_SYSTEM) { e = endswith(m->cgroup_root, "/" SPECIAL_SYSTEM_SLICE); if (!e) - e = endswith(m->cgroup_root, "/system"); - if (e) - *e = 0; + e = endswith(m->cgroup_root, "/system"); /* even more legacy */ } + if (e) + *e = 0; /* And make sure to store away the root value without trailing * slash, even for the root dir, so that we can easily prepend * it everywhere. */ - if (streq(m->cgroup_root, "/")) - m->cgroup_root[0] = 0; + while ((e = endswith(m->cgroup_root, "/"))) + *e = 0; /* 2. Show data */ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, NULL, &path); if (r < 0) return log_error_errno(r, "Cannot find cgroup mount point: %m"); - log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path); + unified = cg_unified(); + if (unified < 0) + return log_error_errno(r, "Couldn't determine if we are running in the unified hierarchy: %m"); + if (unified > 0) + log_debug("Unified cgroup hierarchy is located at %s.", path); + else + log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path); + if (!m->test_run) { + const char *scope_path; /* 3. Install agent */ - if (m->running_as == MANAGER_SYSTEM) { + if (unified) { + + /* In the unified hierarchy we can can get + * cgroup empty notifications via inotify. */ + + m->cgroup_inotify_event_source = sd_event_source_unref(m->cgroup_inotify_event_source); + safe_close(m->cgroup_inotify_fd); + + m->cgroup_inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (m->cgroup_inotify_fd < 0) + return log_error_errno(errno, "Failed to create control group inotify object: %m"); + + r = sd_event_add_io(m->event, &m->cgroup_inotify_event_source, m->cgroup_inotify_fd, EPOLLIN, on_cgroup_inotify_event, 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); + if (r < 0) + return log_error_errno(r, "Failed to set priority of inotify event source: %m"); + + (void) sd_event_source_set_description(m->cgroup_inotify_event_source, "cgroup-inotify"); + + } else if (m->running_as == MANAGER_SYSTEM) { + + /* On the legacy hierarchy we only get + * notifications via cgroup agents. (Which + * isn't really reliable, since it does not + * generate events when control groups with + * children run empty. */ + r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH); if (r < 0) log_warning_errno(r, "Failed to install release agent, ignoring: %m"); else if (r > 0) log_debug("Installed release agent."); - else + else if (r == 0) log_debug("Release agent already installed."); } - /* 4. Make sure we are in the root cgroup */ - r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, 0); + /* 4. Make sure we are in the special "init.scope" unit in the root slice. */ + scope_path = strjoina(m->cgroup_root, "/" SPECIAL_INIT_SCOPE); + r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, scope_path, 0); + if (r < 0) + return log_error_errno(r, "Failed to create %s control group: %m", scope_path); + + /* also, move all other userspace processes remaining + * in the root cgroup into that scope. */ + r = cg_migrate(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, SYSTEMD_CGROUP_CONTROLLER, scope_path, false); if (r < 0) - return log_error_errno(r, "Failed to create root cgroup hierarchy: %m"); + log_warning_errno(r, "Couldn't move remaining userspace processes, ignoring: %m"); /* 5. And pin it, so that it cannot be unmounted */ safe_close(m->pin_cgroupfs_fd); - m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK); if (m->pin_cgroupfs_fd < 0) return log_error_errno(errno, "Failed to open pin file: %m"); /* 6. Always enable hierarchical support if it exists... */ - cg_set_attribute("memory", "/", "memory.use_hierarchy", "1"); + if (!unified) + (void) cg_set_attribute("memory", "/", "memory.use_hierarchy", "1"); } /* 7. Figure out which controllers are supported */ - m->cgroup_supported = cg_mask_supported(); + r = cg_mask_supported(&m->cgroup_supported); + if (r < 0) + return log_error_errno(r, "Failed to determine supported controllers: %m"); + + for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) + log_debug("Controller '%s' supported: %s", cgroup_controller_to_string(c), yes_no(m->cgroup_supported & c)); return 0; } @@ -971,12 +1344,16 @@ void manager_shutdown_cgroup(Manager *m, bool delete) { /* We can't really delete the group, since we are in it. But * let's trim it. */ if (delete && m->cgroup_root) - cg_trim(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, false); + (void) cg_trim(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, false); + + m->cgroup_inotify_wd_unit = hashmap_free(m->cgroup_inotify_wd_unit); + + m->cgroup_inotify_event_source = sd_event_source_unref(m->cgroup_inotify_event_source); + m->cgroup_inotify_fd = safe_close(m->cgroup_inotify_fd); m->pin_cgroupfs_fd = safe_close(m->pin_cgroupfs_fd); - free(m->cgroup_root); - m->cgroup_root = NULL; + m->cgroup_root = mfree(m->cgroup_root); } Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) { @@ -995,8 +1372,8 @@ Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) { char *e; e = strrchr(p, '/'); - if (e == p || !e) - return NULL; + if (!e || e == p) + return hashmap_get(m->cgroup_unit, SPECIAL_ROOT_SLICE); *e = 0; @@ -1006,13 +1383,13 @@ Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) { } } -Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) { +Unit *manager_get_unit_by_pid_cgroup(Manager *m, pid_t pid) { _cleanup_free_ char *cgroup = NULL; int r; assert(m); - if (pid <= 1) + if (pid <= 0) return NULL; r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup); @@ -1022,9 +1399,30 @@ Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) { return manager_get_unit_by_cgroup(m, cgroup); } +Unit *manager_get_unit_by_pid(Manager *m, pid_t pid) { + Unit *u; + + assert(m); + + if (pid <= 0) + return NULL; + + if (pid == 1) + return hashmap_get(m->units, SPECIAL_INIT_SCOPE); + + u = hashmap_get(m->watch_pids1, PID_TO_PTR(pid)); + if (u) + return u; + + u = hashmap_get(m->watch_pids2, PID_TO_PTR(pid)); + if (u) + return u; + + return manager_get_unit_by_pid_cgroup(m, pid); +} + int manager_notify_cgroup_empty(Manager *m, const char *cgroup) { Unit *u; - int r; assert(m); assert(cgroup); @@ -1033,15 +1431,7 @@ int manager_notify_cgroup_empty(Manager *m, const char *cgroup) { if (!u) return 0; - r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true); - if (r <= 0) - return r; - - if (UNIT_VTABLE(u)->notify_cgroup_empty) - UNIT_VTABLE(u)->notify_cgroup_empty(u); - - unit_add_to_gc_queue(u); - return 0; + return unit_notify_cgroup_empty(u); } int unit_get_memory_current(Unit *u, uint64_t *ret) { @@ -1054,10 +1444,13 @@ int unit_get_memory_current(Unit *u, uint64_t *ret) { if (!u->cgroup_path) return -ENODATA; - if ((u->cgroup_realized_mask & CGROUP_MEMORY) == 0) + if ((u->cgroup_realized_mask & CGROUP_MASK_MEMORY) == 0) return -ENODATA; - r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v); + if (cg_unified() <= 0) + r = cg_get_attribute("memory", u->cgroup_path, "memory.usage_in_bytes", &v); + else + r = cg_get_attribute("memory", u->cgroup_path, "memory.current", &v); if (r == -ENOENT) return -ENODATA; if (r < 0) @@ -1077,7 +1470,7 @@ static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) { if (!u->cgroup_path) return -ENODATA; - if ((u->cgroup_realized_mask & CGROUP_CPUACCT) == 0) + if ((u->cgroup_realized_mask & CGROUP_MASK_CPUACCT) == 0) return -ENODATA; r = cg_get_attribute("cpuacct", u->cgroup_path, "cpuacct.usage", &v); @@ -1127,6 +1520,18 @@ int unit_reset_cpu_usage(Unit *u) { return 0; } +bool unit_cgroup_delegate(Unit *u) { + CGroupContext *c; + + assert(u); + + c = unit_get_cgroup_context(u); + if (!c) + return false; + + return c->delegate; +} + static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = { [CGROUP_AUTO] = "auto", [CGROUP_CLOSED] = "closed", diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 869ddae8c..438f5bf50 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -96,22 +96,32 @@ struct CGroupContext { void cgroup_context_init(CGroupContext *c); void cgroup_context_done(CGroupContext *c); void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix); -void cgroup_context_apply(CGroupContext *c, CGroupControllerMask mask, const char *path, ManagerState state); +void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, ManagerState state); -CGroupControllerMask cgroup_context_get_mask(CGroupContext *c); +CGroupMask cgroup_context_get_mask(CGroupContext *c); void cgroup_context_free_device_allow(CGroupContext *c, CGroupDeviceAllow *a); void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w); void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b); -CGroupControllerMask unit_get_cgroup_mask(Unit *u); -CGroupControllerMask unit_get_siblings_mask(Unit *u); -CGroupControllerMask unit_get_members_mask(Unit *u); -CGroupControllerMask unit_get_target_mask(Unit *u); +CGroupMask unit_get_own_mask(Unit *u); +CGroupMask unit_get_siblings_mask(Unit *u); +CGroupMask unit_get_members_mask(Unit *u); +CGroupMask unit_get_subtree_mask(Unit *u); + +CGroupMask unit_get_target_mask(Unit *u); +CGroupMask unit_get_enable_mask(Unit *u); void unit_update_cgroup_members_masks(Unit *u); + +char *unit_default_cgroup_path(Unit *u); +int unit_set_cgroup_path(Unit *u, const char *path); + int unit_realize_cgroup(Unit *u); -void unit_destroy_cgroup_if_empty(Unit *u); +void unit_release_cgroup(Unit *u); +void unit_prune_cgroup(Unit *u); +int unit_watch_cgroup(Unit *u); + int unit_attach_pids_to_cgroup(Unit *u); int manager_setup_cgroup(Manager *m); @@ -120,15 +130,20 @@ void manager_shutdown_cgroup(Manager *m, bool delete); unsigned manager_dispatch_cgroup_queue(Manager *m); Unit *manager_get_unit_by_cgroup(Manager *m, const char *cgroup); +Unit *manager_get_unit_by_pid_cgroup(Manager *m, pid_t pid); Unit* manager_get_unit_by_pid(Manager *m, pid_t pid); -pid_t unit_search_main_pid(Unit *u); - -int manager_notify_cgroup_empty(Manager *m, const char *group); +int unit_search_main_pid(Unit *u, pid_t *ret); +int unit_watch_all_pids(Unit *u); int unit_get_memory_current(Unit *u, uint64_t *ret); int unit_get_cpu_usage(Unit *u, nsec_t *ret); int unit_reset_cpu_usage(Unit *u); +bool unit_cgroup_delegate(Unit *u); + +int unit_notify_cgroup_empty(Unit *u); +int manager_notify_cgroup_empty(Manager *m, const char *group); + const char* cgroup_device_policy_to_string(CGroupDevicePolicy i) _const_; CGroupDevicePolicy cgroup_device_policy_from_string(const char *s) _pure_; diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c index 9dcc51f24..e8fd44e29 100644 --- a/src/core/dbus-cgroup.c +++ b/src/core/dbus-cgroup.c @@ -228,7 +228,7 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->cpu_accounting = b; - u->cgroup_realized_mask &= ~CGROUP_CPUACCT; + u->cgroup_realized_mask &= ~CGROUP_MASK_CPUACCT; unit_write_drop_in_private(u, mode, name, b ? "CPUAccounting=yes" : "CPUAccounting=no"); } @@ -252,7 +252,7 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->cpu_shares = ul; - u->cgroup_realized_mask &= ~CGROUP_CPU; + u->cgroup_realized_mask &= ~CGROUP_MASK_CPU; unit_write_drop_in_private_format(u, mode, name, "CPUShares=%lu", ul); } @@ -276,7 +276,7 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->startup_cpu_shares = ul; - u->cgroup_realized_mask &= ~CGROUP_CPU; + u->cgroup_realized_mask &= ~CGROUP_MASK_CPU; unit_write_drop_in_private_format(u, mode, name, "StartupCPUShares=%lu", ul); } @@ -294,7 +294,7 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->cpu_quota_per_sec_usec = u64; - u->cgroup_realized_mask &= ~CGROUP_CPU; + u->cgroup_realized_mask &= ~CGROUP_MASK_CPU; unit_write_drop_in_private_format(u, mode, "CPUQuota", "CPUQuota=%0.f%%", (double) (c->cpu_quota_per_sec_usec / 10000)); } @@ -309,7 +309,7 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->blockio_accounting = b; - u->cgroup_realized_mask &= ~CGROUP_BLKIO; + u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO; unit_write_drop_in_private(u, mode, name, b ? "BlockIOAccounting=yes" : "BlockIOAccounting=no"); } @@ -333,7 +333,7 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->blockio_weight = ul; - u->cgroup_realized_mask &= ~CGROUP_BLKIO; + u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO; unit_write_drop_in_private_format(u, mode, name, "BlockIOWeight=%lu", ul); } @@ -357,7 +357,7 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->startup_blockio_weight = ul; - u->cgroup_realized_mask &= ~CGROUP_BLKIO; + u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO; unit_write_drop_in_private_format(u, mode, name, "StartupBlockIOWeight=%lu", ul); } @@ -427,7 +427,7 @@ int bus_cgroup_set_property( cgroup_context_free_blockio_device_bandwidth(c, a); } - u->cgroup_realized_mask &= ~CGROUP_BLKIO; + u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO; f = open_memstream(&buf, &size); if (!f) @@ -510,7 +510,7 @@ int bus_cgroup_set_property( cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights); } - u->cgroup_realized_mask &= ~CGROUP_BLKIO; + u->cgroup_realized_mask &= ~CGROUP_MASK_BLKIO; f = open_memstream(&buf, &size); if (!f) @@ -535,7 +535,7 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->memory_accounting = b; - u->cgroup_realized_mask &= ~CGROUP_MEMORY; + u->cgroup_realized_mask &= ~CGROUP_MASK_MEMORY; unit_write_drop_in_private(u, mode, name, b ? "MemoryAccounting=yes" : "MemoryAccounting=no"); } @@ -550,7 +550,7 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->memory_limit = limit; - u->cgroup_realized_mask &= ~CGROUP_MEMORY; + u->cgroup_realized_mask &= ~CGROUP_MASK_MEMORY; unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit); } @@ -572,7 +572,7 @@ int bus_cgroup_set_property( char *buf; c->device_policy = p; - u->cgroup_realized_mask &= ~CGROUP_DEVICE; + u->cgroup_realized_mask &= ~CGROUP_MASK_DEVICE; buf = strjoina("DevicePolicy=", policy); unit_write_drop_in_private(u, mode, name, buf); @@ -651,7 +651,7 @@ int bus_cgroup_set_property( cgroup_context_free_device_allow(c, c->device_allow); } - u->cgroup_realized_mask &= ~CGROUP_DEVICE; + u->cgroup_realized_mask &= ~CGROUP_MASK_DEVICE; f = open_memstream(&buf, &size); if (!f) diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 0a9effda7..f9275ed93 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -25,6 +25,7 @@ #include "cgroup-util.h" #include "strv.h" #include "bus-common-errors.h" +#include "special.h" #include "dbus.h" #include "dbus-unit.h" @@ -390,6 +391,29 @@ static int property_get_load_error( return sd_bus_message_append(reply, "(ss)", e.name, e.message); } +static int bus_verify_manage_units_async_full( + Unit *u, + const char *verb, + int capability, + const char *polkit_message, + sd_bus_message *call, + sd_bus_error *error) { + + const char *details[9] = { + "unit", u->id, + "verb", verb, + }; + + if (polkit_message) { + details[4] = "polkit.message"; + details[5] = polkit_message; + details[6] = "polkit.gettext_domain"; + details[7] = GETTEXT_PACKAGE; + } + + return bus_verify_polkit_async(call, capability, "org.freedesktop.systemd1.manage-units", details, false, UID_INVALID, &u->manager->polkit_registry, error); +} + int bus_unit_method_start_generic( sd_bus_message *message, Unit *u, @@ -399,6 +423,14 @@ int bus_unit_method_start_generic( const char *smode; JobMode mode; + _cleanup_free_ char *verb = NULL; + static const char *const polkit_message_for_job[_JOB_TYPE_MAX] = { + [JOB_START] = N_("Authentication is required to start '$(unit)'."), + [JOB_STOP] = N_("Authentication is required to stop '$(unit)'."), + [JOB_RELOAD] = N_("Authentication is required to reload '$(unit)'."), + [JOB_RESTART] = N_("Authentication is required to restart '$(unit)'."), + [JOB_TRY_RESTART] = N_("Authentication is required to restart '$(unit)'."), + }; int r; assert(message); @@ -417,7 +449,20 @@ int bus_unit_method_start_generic( if (mode < 0) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s invalid", smode); - r = bus_verify_manage_units_async(u->manager, message, error); + if (reload_if_possible) + verb = strjoin("reload-or-", job_type_to_string(job_type), NULL); + else + verb = strdup(job_type_to_string(job_type)); + if (!verb) + return -ENOMEM; + + r = bus_verify_manage_units_async_full( + u, + verb, + CAP_SYS_ADMIN, + job_type < _JOB_TYPE_MAX ? polkit_message_for_job[job_type] : NULL, + message, + error); if (r < 0) return r; if (r == 0) @@ -483,7 +528,13 @@ int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error * if (signo <= 0 || signo >= _NSIG) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range."); - r = bus_verify_manage_units_async_for_kill(u->manager, message, error); + r = bus_verify_manage_units_async_full( + u, + "kill", + CAP_KILL, + N_("Authentication is required to kill '$(unit)'."), + message, + error); if (r < 0) return r; if (r == 0) @@ -507,7 +558,13 @@ int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus if (r < 0) return r; - r = bus_verify_manage_units_async(u->manager, message, error); + r = bus_verify_manage_units_async_full( + u, + "reset-failed", + CAP_SYS_ADMIN, + N_("Authentication is required to reset the \"failed\" state of '$(unit)'."), + message, + error); if (r < 0) return r; if (r == 0) @@ -533,7 +590,13 @@ int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_b if (r < 0) return r; - r = bus_verify_manage_units_async(u->manager, message, error); + r = bus_verify_manage_units_async_full( + u, + "set-property", + CAP_SYS_ADMIN, + N_("Authentication is required to set properties on '$(unit)'."), + message, + error); if (r < 0) return r; if (r == 0) @@ -783,7 +846,7 @@ static int send_changed_signal(sd_bus *bus, void *userdata) { r = sd_bus_emit_properties_changed_strv( bus, p, - UNIT_VTABLE(u)->bus_interface, + unit_dbus_interface_from_type(u->type), NULL); if (r < 0) return r; @@ -965,38 +1028,41 @@ static int bus_unit_set_transient_property( return 1; - } else if (streq(name, "Slice") && unit_get_cgroup_context(u)) { + } else if (streq(name, "Slice")) { + Unit *slice; const char *s; + if (!UNIT_HAS_CGROUP_CONTEXT(u)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "The slice property is only available for units with control groups."); + if (u->type == UNIT_SLICE) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Slice may not be set for slice units."); + if (unit_has_name(u, SPECIAL_INIT_SCOPE)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot set slice for init.scope"); + r = sd_bus_message_read(message, "s", &s); if (r < 0) return r; - if (!unit_name_is_valid(s, UNIT_NAME_PLAIN) || !endswith(s, ".slice")) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid slice name %s", s); + if (!unit_name_is_valid(s, UNIT_NAME_PLAIN)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name '%s'", s); - if (isempty(s)) { - if (mode != UNIT_CHECK) { - unit_ref_unset(&u->slice); - unit_remove_drop_in(u, mode, name); - } - } else { - Unit *slice; + r = manager_load_unit(u->manager, s, NULL, error, &slice); + if (r < 0) + return r; + + if (slice->type != UNIT_SLICE) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit name '%s' is not a slice", s); - r = manager_load_unit(u->manager, s, NULL, error, &slice); + if (mode != UNIT_CHECK) { + r = unit_set_slice(u, slice); if (r < 0) return r; - if (slice->type != UNIT_SLICE) - return -EINVAL; - - if (mode != UNIT_CHECK) { - unit_ref_set(&u->slice, slice); - unit_write_drop_in_private_format(u, mode, name, "Slice=%s\n", s); - } + unit_write_drop_in_private_format(u, mode, name, "Slice=%s\n", s); } return 1; + } else if (STR_IN_SET(name, "Requires", "RequiresOverridable", "Requisite", "RequisiteOverridable", diff --git a/src/core/dbus.c b/src/core/dbus.c index 44bf5cab2..0a2180c6a 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -356,7 +356,7 @@ static int bus_unit_interface_find(sd_bus *bus, const char *path, const char *in if (r <= 0) return r; - if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface)) + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) return 0; *found = u; @@ -378,10 +378,10 @@ static int bus_unit_cgroup_find(sd_bus *bus, const char *path, const char *inter if (r <= 0) return r; - if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface)) + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) return 0; - if (!unit_get_cgroup_context(u)) + if (!UNIT_HAS_CGROUP_CONTEXT(u)) return 0; *found = u; @@ -404,7 +404,7 @@ static int bus_cgroup_context_find(sd_bus *bus, const char *path, const char *in if (r <= 0) return r; - if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface)) + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) return 0; c = unit_get_cgroup_context(u); @@ -431,7 +431,7 @@ static int bus_exec_context_find(sd_bus *bus, const char *path, const char *inte if (r <= 0) return r; - if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface)) + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) return 0; c = unit_get_exec_context(u); @@ -458,7 +458,7 @@ static int bus_kill_context_find(sd_bus *bus, const char *path, const char *inte if (r <= 0) return r; - if (!streq_ptr(interface, UNIT_VTABLE(u)->bus_interface)) + if (!streq_ptr(interface, unit_dbus_interface_from_type(u->type))) return 0; c = unit_get_kill_context(u); @@ -555,30 +555,34 @@ static int bus_setup_api_vtables(Manager *m, sd_bus *bus) { return log_error_errno(r, "Failed to add job enumerator: %m"); for (t = 0; t < _UNIT_TYPE_MAX; t++) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, unit_vtable[t]->bus_vtable, bus_unit_interface_find, m); + const char *interface; + + assert_se(interface = unit_dbus_interface_from_type(t)); + + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, unit_vtable[t]->bus_vtable, bus_unit_interface_find, m); if (r < 0) - return log_error_errno(r, "Failed to register type specific vtable for %s: %m", unit_vtable[t]->bus_interface); + return log_error_errno(r, "Failed to register type specific vtable for %s: %m", interface); if (unit_vtable[t]->cgroup_context_offset > 0) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_unit_cgroup_vtable, bus_unit_cgroup_find, m); + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_unit_cgroup_vtable, bus_unit_cgroup_find, m); if (r < 0) - return log_error_errno(r, "Failed to register control group unit vtable for %s: %m", unit_vtable[t]->bus_interface); + return log_error_errno(r, "Failed to register control group unit vtable for %s: %m", interface); - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_cgroup_vtable, bus_cgroup_context_find, m); + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_cgroup_vtable, bus_cgroup_context_find, m); if (r < 0) - return log_error_errno(r, "Failed to register control group vtable for %s: %m", unit_vtable[t]->bus_interface); + return log_error_errno(r, "Failed to register control group vtable for %s: %m", interface); } if (unit_vtable[t]->exec_context_offset > 0) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_exec_vtable, bus_exec_context_find, m); + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_exec_vtable, bus_exec_context_find, m); if (r < 0) - return log_error_errno(r, "Failed to register execute vtable for %s: %m", unit_vtable[t]->bus_interface); + return log_error_errno(r, "Failed to register execute vtable for %s: %m", interface); } if (unit_vtable[t]->kill_context_offset > 0) { - r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_kill_vtable, bus_kill_context_find, m); + r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", interface, bus_kill_vtable, bus_kill_context_find, m); if (r < 0) - return log_error_errno(r, "Failed to register kill vtable for %s: %m", unit_vtable[t]->bus_interface); + return log_error_errno(r, "Failed to register kill vtable for %s: %m", interface); } } @@ -1194,22 +1198,17 @@ int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l) { } int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error) { - return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-units", false, UID_INVALID, &m->polkit_registry, error); -} - -/* Same as bus_verify_manage_unit_async(), but checks for CAP_KILL instead of CAP_SYS_ADMIN */ -int bus_verify_manage_units_async_for_kill(Manager *m, sd_bus_message *call, sd_bus_error *error) { - return bus_verify_polkit_async(call, CAP_KILL, "org.freedesktop.systemd1.manage-units", false, UID_INVALID, &m->polkit_registry, error); + return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-units", NULL, false, UID_INVALID, &m->polkit_registry, error); } int bus_verify_manage_unit_files_async(Manager *m, sd_bus_message *call, sd_bus_error *error) { - return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-unit-files", false, UID_INVALID, &m->polkit_registry, error); + return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.manage-unit-files", NULL, false, UID_INVALID, &m->polkit_registry, error); } int bus_verify_reload_daemon_async(Manager *m, sd_bus_message *call, sd_bus_error *error) { - return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.reload-daemon", false, UID_INVALID, &m->polkit_registry, error); + return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.reload-daemon", NULL, false, UID_INVALID, &m->polkit_registry, error); } int bus_verify_set_environment_async(Manager *m, sd_bus_message *call, sd_bus_error *error) { - return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.set-environment", false, UID_INVALID, &m->polkit_registry, error); + return bus_verify_polkit_async(call, CAP_SYS_ADMIN, "org.freedesktop.systemd1.set-environment", NULL, false, UID_INVALID, &m->polkit_registry, error); } diff --git a/src/core/dbus.h b/src/core/dbus.h index 483272206..4f06ad11c 100644 --- a/src/core/dbus.h +++ b/src/core/dbus.h @@ -37,7 +37,6 @@ int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l); int bus_foreach_bus(Manager *m, sd_bus_track *subscribed2, int (*send_message)(sd_bus *bus, void *userdata), void *userdata); int bus_verify_manage_units_async(Manager *m, sd_bus_message *call, sd_bus_error *error); -int bus_verify_manage_units_async_for_kill(Manager *m, sd_bus_message *call, sd_bus_error *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); diff --git a/src/core/device.c b/src/core/device.c index e7efcf0f0..3f574b183 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -849,7 +849,6 @@ const UnitVTable device_vtable = { .active_state = device_active_state, .sub_state_to_string = device_sub_state_to_string, - .bus_interface = "org.freedesktop.systemd1.Device", .bus_vtable = bus_device_vtable, .following = device_following, diff --git a/src/core/execute.h b/src/core/execute.h index 8d14fe23d..a750246a8 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -214,7 +214,7 @@ struct ExecParameters { bool apply_tty_stdin; bool confirm_spawn; bool selinux_context_net; - CGroupControllerMask cgroup_supported; + CGroupMask cgroup_supported; const char *cgroup_path; bool cgroup_delegate; const char *runtime_prefix; diff --git a/src/core/killall.c b/src/core/killall.c index 2a9d72c90..ee5d38856 100644 --- a/src/core/killall.c +++ b/src/core/killall.c @@ -108,7 +108,7 @@ static void wait_for_children(Set *pids, sigset_t *mask) { return; } - set_remove(pids, ULONG_TO_PTR(pid)); + (void) set_remove(pids, PID_TO_PTR(pid)); } /* Now explicitly check who might be remaining, who @@ -117,7 +117,7 @@ static void wait_for_children(Set *pids, sigset_t *mask) { /* We misuse getpgid as a check whether a * process still exists. */ - if (getpgid((pid_t) PTR_TO_ULONG(p)) >= 0) + if (getpgid(PTR_TO_PID(p)) >= 0) continue; if (errno != ESRCH) @@ -179,7 +179,7 @@ static int killall(int sig, Set *pids, bool send_sighup) { if (kill(pid, sig) >= 0) { if (pids) { - r = set_put(pids, ULONG_TO_PTR(pid)); + r = set_put(pids, PID_TO_PTR(pid)); if (r < 0) log_oom(); } diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 60b97722d..edd55b9e4 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -106,7 +106,7 @@ m4_define(`KILL_CONTEXT_CONFIG_ITEMS', `$1.SendSIGKILL, config_parse_bool, 0, offsetof($1, kill_context.send_sigkill) $1.SendSIGHUP, config_parse_bool, 0, offsetof($1, kill_context.send_sighup) $1.KillMode, config_parse_kill_mode, 0, offsetof($1, kill_context.kill_mode) -$1.KillSignal, config_parse_kill_signal, 0, offsetof($1, kill_context.kill_signal)' +$1.KillSignal, config_parse_signal, 0, offsetof($1, kill_context.kill_signal)' )m4_dnl m4_define(`CGROUP_CONTEXT_CONFIG_ITEMS', `$1.Slice, config_parse_unit_slice, 0, 0 diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index b3bf8bdb4..542bf8eb2 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -1048,7 +1048,7 @@ int config_parse_bounding_set(const char *unit, cap = capability_from_name(t); if (cap < 0) { - log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse capability in bounding set, ignoring: %s", t); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse capability in bounding set, ignoring: %s", t); continue; } @@ -1143,39 +1143,8 @@ int config_parse_sysv_priority(const char *unit, #endif DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode"); - DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); -int config_parse_kill_signal(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - int *sig = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(sig); - - r = signal_from_string_try_harder(rvalue); - if (r <= 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to parse kill signal, ignoring: %s", rvalue); - return 0; - } - - *sig = r; - return 0; -} - int config_parse_exec_mount_flags(const char *unit, const char *filename, unsigned line, @@ -2602,7 +2571,7 @@ int config_parse_unit_slice( void *userdata) { _cleanup_free_ char *k = NULL; - Unit *u = userdata, *slice; + Unit *u = userdata, *slice = NULL; int r; assert(filename); @@ -2611,29 +2580,23 @@ int config_parse_unit_slice( assert(u); r = unit_name_printf(u, rvalue, &k); - if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); - if (!k) { - k = strdup(rvalue); - if (!k) - return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers on %s. Ignoring.", rvalue); + return 0; } r = manager_load_unit(u->manager, k, NULL, NULL, &slice); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to load slice unit %s. Ignoring.", k); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load slice unit %s. Ignoring.", k); return 0; } - if (slice->type != UNIT_SLICE) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Slice unit %s is not a slice. Ignoring.", k); + r = unit_set_slice(u, slice); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to assign slice %s to unit %s. Ignoring.", slice->id, u->id); return 0; } - unit_ref_set(&u->slice, slice); return 0; } @@ -3027,36 +2990,6 @@ int config_parse_job_mode_isolate( return 0; } -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) { - - unsigned long *personality = data, p; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(personality); - - p = personality_from_string(rvalue); - if (p == PERSONALITY_INVALID) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to parse personality, ignoring: %s", rvalue); - return 0; - } - - *personality = p; - return 0; -} - int config_parse_runtime_directory( const char *unit, const char *filename, @@ -3603,6 +3536,11 @@ int unit_load_fragment(Unit *u) { assert(u->load_state == UNIT_STUB); assert(u->id); + if (u->transient) { + u->load_state = UNIT_LOADED; + return 0; + } + /* First, try to find the unit under its id. We always look * for unit files in the default directories, to make it easy * to override things by placing things in /etc/systemd/system */ @@ -3721,7 +3659,7 @@ void unit_dump_config_items(FILE *f) { { config_parse_sysv_priority, "SYSVPRIORITY" }, #endif { config_parse_kill_mode, "KILLMODE" }, - { config_parse_kill_signal, "SIGNAL" }, + { config_parse_signal, "SIGNAL" }, { config_parse_socket_listen, "SOCKET [...]" }, { config_parse_socket_bind, "SOCKETBIND" }, { config_parse_socket_bindtodevice, "NETWORKINTERFACE" }, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index fcca2b022..1d128716c 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -92,7 +92,6 @@ int config_parse_blockio_bandwidth(const char *unit, const char *filename, unsig int config_parse_job_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_job_mode_isolate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_exec_selinux_context(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_exec_apparmor_profile(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_exec_smack_process_label(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_address_families(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/core/main.c b/src/core/main.c index 87b97aa88..4cd2b08c3 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -433,25 +433,28 @@ static int config_parse_cpu_affinity2( void *data, void *userdata) { - const char *word, *state; - size_t l; - cpu_set_t *c = NULL; + const char *whole_rvalue = rvalue; + _cleanup_cpu_free_ cpu_set_t *c = NULL; unsigned ncpus = 0; assert(filename); assert(lvalue); assert(rvalue); - FOREACH_WORD_QUOTED(word, l, rvalue, state) { - char *t; - int r; + for (;;) { + _cleanup_free_ char *word = NULL; unsigned cpu; + int r; - if (!(t = strndup(word, l))) - return log_oom(); + r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue); + return r; + } + if (r == 0) + break; - r = safe_atou(t, &cpu); - free(t); + r = safe_atou(word, &cpu); if (!c) if (!(c = cpu_set_malloc(&ncpus))) @@ -460,23 +463,19 @@ static int config_parse_cpu_affinity2( if (r < 0 || cpu >= ncpus) { log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse CPU affinity '%s'", rvalue); - CPU_FREE(c); return -EBADMSG; } CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c); } - if (!isempty(state)) + if (!isempty(rvalue)) log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Trailing garbage, ignoring."); - if (c) { + if (c) if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0) log_warning("Failed to set CPU affinity: %m"); - CPU_FREE(c); - } - return 0; } @@ -538,9 +537,8 @@ static int config_parse_join_controllers(const char *unit, void *data, void *userdata) { + const char *whole_rvalue = rvalue; unsigned n = 0; - const char *word, *state; - size_t length; assert(filename); assert(lvalue); @@ -548,16 +546,22 @@ static int config_parse_join_controllers(const char *unit, free_join_controllers(); - FOREACH_WORD_QUOTED(word, length, rvalue, state) { - char *s, **l; - - s = strndup(word, length); - if (!s) - return log_oom(); + for (;;) { + _cleanup_free_ char *word = NULL; + char **l; + int r; - l = strv_split(s, ","); - free(s); + r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue); + return r; + } + if (r == 0) + break; + l = strv_split(word, ","); + if (!l) + log_oom(); strv_uniq(l); if (strv_length(l) <= 1) { @@ -617,7 +621,7 @@ static int config_parse_join_controllers(const char *unit, arg_join_controllers = t; } } - if (!isempty(state)) + if (!isempty(rvalue)) log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Trailing garbage, ignoring."); @@ -1391,8 +1395,7 @@ int main(int argc, char *argv[]) { /* clear the kernel timestamp, * because we are not PID 1 */ - kernel_timestamp.monotonic = 0ULL; - kernel_timestamp.realtime = 0ULL; + kernel_timestamp = DUAL_TIMESTAMP_NULL; } /* Initialize default unit */ @@ -2044,7 +2047,7 @@ finish: * kernel; at this point, we will not listen to the * signals anyway */ if (detect_container(NULL) <= 0) - cg_uninstall_release_agent(SYSTEMD_CGROUP_CONTROLLER); + (void) cg_uninstall_release_agent(SYSTEMD_CGROUP_CONTROLLER); execve(SYSTEMD_SHUTDOWN_BINARY_PATH, (char **) command_line, env_block); log_error_errno(errno, "Failed to execute shutdown binary, %s: %m", diff --git a/src/core/manager.c b/src/core/manager.c index ecea89c37..fc10ddb5d 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -250,8 +250,8 @@ static int manager_dispatch_ask_password_fd(sd_event_source *source, static void manager_close_ask_password(Manager *m) { assert(m); - m->ask_password_inotify_fd = safe_close(m->ask_password_inotify_fd); m->ask_password_event_source = sd_event_source_unref(m->ask_password_event_source); + m->ask_password_inotify_fd = safe_close(m->ask_password_inotify_fd); m->have_ask_password = -EINVAL; } @@ -568,11 +568,14 @@ int manager_new(ManagerRunningAs running_as, 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->utab_inotify_fd = -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->utab_inotify_fd = + m->cgroup_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; m->test_run = test_run; @@ -1582,19 +1585,19 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t /* Notify every unit that might be interested, but try * to avoid notifying the same one multiple times. */ - u1 = manager_get_unit_by_pid(m, ucred->pid); + u1 = manager_get_unit_by_pid_cgroup(m, ucred->pid); if (u1) { manager_invoke_notify_message(m, u1, ucred->pid, buf, n, fds); found = true; } - u2 = hashmap_get(m->watch_pids1, LONG_TO_PTR(ucred->pid)); + u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(ucred->pid)); if (u2 && u2 != u1) { manager_invoke_notify_message(m, u2, ucred->pid, buf, n, fds); found = true; } - u3 = hashmap_get(m->watch_pids2, LONG_TO_PTR(ucred->pid)); + u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(ucred->pid)); if (u3 && u3 != u2 && u3 != u1) { manager_invoke_notify_message(m, u3, ucred->pid, buf, n, fds); found = true; @@ -1660,13 +1663,13 @@ static int manager_dispatch_sigchld(Manager *m) { /* And now figure out the unit this belongs * to, it might be multiple... */ - u1 = manager_get_unit_by_pid(m, si.si_pid); + u1 = manager_get_unit_by_pid_cgroup(m, si.si_pid); if (u1) invoke_sigchld_event(m, u1, &si); - u2 = hashmap_get(m->watch_pids1, LONG_TO_PTR(si.si_pid)); + u2 = hashmap_get(m->watch_pids1, PID_TO_PTR(si.si_pid)); if (u2 && u2 != u1) invoke_sigchld_event(m, u2, &si); - u3 = hashmap_get(m->watch_pids2, LONG_TO_PTR(si.si_pid)); + u3 = hashmap_get(m->watch_pids2, PID_TO_PTR(si.si_pid)); if (u3 && u3 != u2 && u3 != u1) invoke_sigchld_event(m, u3, &si); } @@ -2721,7 +2724,7 @@ void manager_check_finished(Manager *m) { SET_FOREACH(u, m->startup_units, i) if (u->cgroup_path) - cgroup_context_apply(unit_get_cgroup_context(u), unit_get_cgroup_mask(u), u->cgroup_path, manager_state(m)); + cgroup_context_apply(unit_get_cgroup_context(u), unit_get_own_mask(u), u->cgroup_path, manager_state(m)); } static int create_generator_dir(Manager *m, char **generator, const char *name) { @@ -2998,12 +3001,14 @@ void manager_set_first_boot(Manager *m, bool b) { if (m->running_as != MANAGER_SYSTEM) return; - m->first_boot = b; + if (m->first_boot != (int) b) { + if (b) + (void) touch("/run/systemd/first-boot"); + else + (void) unlink("/run/systemd/first-boot"); + } - if (m->first_boot) - touch("/run/systemd/first-boot"); - else - unlink("/run/systemd/first-boot"); + m->first_boot = b; } void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) { diff --git a/src/core/manager.h b/src/core/manager.h index 1e01f2bde..9956cb770 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -215,16 +215,22 @@ struct Manager { /* Data specific to the cgroup subsystem */ Hashmap *cgroup_unit; - CGroupControllerMask cgroup_supported; + CGroupMask cgroup_supported; char *cgroup_root; - int gc_marker; - unsigned n_in_gc_queue; + /* Notifications from cgroups, when the unified hierarchy is + * used is done via inotify. */ + int cgroup_inotify_fd; + sd_event_source *cgroup_inotify_event_source; + Hashmap *cgroup_inotify_wd_unit; /* Make sure the user cannot accidentally unmount our cgroup * file system */ int pin_cgroupfs_fd; + int gc_marker; + unsigned n_in_gc_queue; + /* Flags */ ManagerRunningAs running_as; ManagerExitCode exit_code:5; @@ -233,7 +239,6 @@ struct Manager { bool dispatching_dbus_queue:1; bool taint_usr:1; - bool first_boot:1; bool test_run:1; @@ -295,6 +300,8 @@ struct Manager { const char *unit_log_field; const char *unit_log_format_string; + + int first_boot; }; int manager_new(ManagerRunningAs running_as, bool test_run, Manager **m); diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c index 1782d4072..c6f356991 100644 --- a/src/core/mount-setup.c +++ b/src/core/mount-setup.c @@ -93,12 +93,14 @@ static const MountPoint mount_table[] = { #endif { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, NULL, MNT_FATAL|MNT_IN_CONTAINER }, + { "cgroup", "/sys/fs/cgroup", "cgroup", "__DEVEL__sane_behavior", MS_NOSUID|MS_NOEXEC|MS_NODEV, + cg_is_unified_wanted, MNT_FATAL|MNT_IN_CONTAINER }, { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, - NULL, MNT_FATAL|MNT_IN_CONTAINER }, + cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER }, { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV, - NULL, MNT_IN_CONTAINER }, + cg_is_legacy_wanted, MNT_IN_CONTAINER }, { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, - NULL, MNT_FATAL|MNT_IN_CONTAINER }, + cg_is_legacy_wanted, MNT_FATAL|MNT_IN_CONTAINER }, { "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL, MNT_NONE }, #ifdef ENABLE_EFI @@ -217,6 +219,9 @@ int mount_cgroup_controllers(char ***join_controllers) { _cleanup_set_free_free_ Set *controllers = NULL; int r; + if (!cg_is_legacy_wanted()) + return 0; + /* Mount all available cgroup controllers that are built into the kernel. */ controllers = set_new(&string_hash_ops); diff --git a/src/core/mount.c b/src/core/mount.c index c0d1cdfbd..2b81d17b9 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -521,7 +521,7 @@ static int mount_add_extras(Mount *m) { if (r < 0) return r; - r = unit_add_default_slice(u, &m->cgroup_context); + r = unit_set_default_slice(u); if (r < 0) return r; @@ -1871,7 +1871,6 @@ const UnitVTable mount_vtable = { .reset_failed = mount_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Mount", .bus_vtable = bus_mount_vtable, .bus_set_property = bus_mount_set_property, .bus_commit_properties = bus_mount_commit_properties, diff --git a/src/core/path.c b/src/core/path.c index 20995d920..e9111d061 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -770,6 +770,5 @@ const UnitVTable path_vtable = { .reset_failed = path_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Path", .bus_vtable = bus_path_vtable }; diff --git a/src/core/scope.c b/src/core/scope.c index ab1769b46..35cf0621b 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -22,12 +22,13 @@ #include #include -#include "unit.h" -#include "scope.h" #include "log.h" -#include "dbus-scope.h" +#include "strv.h" #include "special.h" #include "unit-name.h" +#include "unit.h" +#include "scope.h" +#include "dbus-scope.h" #include "load-dropin.h" static const UnitActiveState state_translation_table[_SCOPE_STATE_MAX] = { @@ -136,7 +137,9 @@ static int scope_verify(Scope *s) { if (UNIT(s)->load_state != UNIT_LOADED) return 0; - if (set_isempty(UNIT(s)->pids) && UNIT(s)->manager->n_reloading <= 0) { + if (set_isempty(UNIT(s)->pids) && + !manager_is_reloading_or_reexecuting(UNIT(s)->manager) && + !unit_has_name(UNIT(s), SPECIAL_INIT_SCOPE)) { log_unit_error(UNIT(s), "Scope has no PIDs. Refusing."); return -EINVAL; } @@ -151,7 +154,7 @@ static int scope_load(Unit *u) { assert(s); assert(u->load_state == UNIT_STUB); - if (!u->transient && UNIT(s)->manager->n_reloading <= 0) + if (!u->transient && !manager_is_reloading_or_reexecuting(u->manager)) return -ENOENT; u->load_state = UNIT_LOADED; @@ -164,7 +167,7 @@ static int scope_load(Unit *u) { if (r < 0) return r; - r = unit_add_default_slice(u, &s->cgroup_context); + r = unit_set_default_slice(u); if (r < 0) return r; @@ -279,6 +282,9 @@ static int scope_start(Unit *u) { assert(s); + if (unit_has_name(u, SPECIAL_INIT_SCOPE)) + return -EPERM; + if (s->state == SCOPE_FAILED) return -EPERM; @@ -289,7 +295,7 @@ static int scope_start(Unit *u) { assert(s->state == SCOPE_DEAD); - if (!u->transient && UNIT(s)->manager->n_reloading <= 0) + if (!u->transient && !manager_is_reloading_or_reexecuting(u->manager)) return -ENOENT; (void) unit_realize_cgroup(u); @@ -396,7 +402,7 @@ static bool scope_check_gc(Unit *u) { if (u->cgroup_path) { int r; - r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true); + r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path); if (r <= 0) return true; } @@ -464,6 +470,9 @@ static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *user int scope_abandon(Scope *s) { assert(s); + if (unit_has_name(UNIT(s), SPECIAL_INIT_SCOPE)) + return -EPERM; + if (!IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED)) return -ESTALE; @@ -499,6 +508,48 @@ _pure_ static const char *scope_sub_state_to_string(Unit *u) { return scope_state_to_string(SCOPE(u)->state); } +static int scope_enumerate(Manager *m) { + Unit *u; + int r; + + assert(m); + + /* Let's unconditionally add the "init.scope" special unit + * that encapsulates PID 1. Note that PID 1 already is in the + * cgroup for this, we hence just need to allocate the object + * for it and that's it. */ + + u = manager_get_unit(m, SPECIAL_INIT_SCOPE); + if (!u) { + u = unit_new(m, sizeof(Scope)); + if (!u) + return log_oom(); + + r = unit_add_name(u, SPECIAL_INIT_SCOPE); + if (r < 0) { + unit_free(u); + return log_error_errno(r, "Failed to add init.scope name"); + } + } + + u->transient = true; + u->default_dependencies = false; + u->no_gc = true; + SCOPE(u)->deserialized_state = SCOPE_RUNNING; + SCOPE(u)->kill_context.kill_signal = SIGRTMIN+14; + + /* Prettify things, if we can. */ + if (!u->description) + u->description = strdup("System and Service Manager"); + if (!u->documentation) + (void) strv_extend(&u->documentation, "man:systemd(1)"); + + unit_add_to_load_queue(u); + unit_add_to_dbus_queue(u); + + return 0; +} + static const char* const scope_state_table[_SCOPE_STATE_MAX] = { [SCOPE_DEAD] = "dead", [SCOPE_RUNNING] = "running", @@ -561,10 +612,11 @@ const UnitVTable scope_vtable = { .notify_cgroup_empty = scope_notify_cgroup_empty_event, - .bus_interface = "org.freedesktop.systemd1.Scope", .bus_vtable = bus_scope_vtable, .bus_set_property = bus_scope_set_property, .bus_commit_properties = bus_scope_commit_properties, - .can_transient = true + .can_transient = true, + + .enumerate = scope_enumerate, }; diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c index f920c2e2c..40ca0c616 100644 --- a/src/core/selinux-access.c +++ b/src/core/selinux-access.c @@ -246,7 +246,7 @@ int mac_selinux_generic_access_check( if (path) { /* Get the file context of the unit file */ - r = getfilecon(path, &fcon); + r = getfilecon_raw(path, &fcon); if (r < 0) { r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get file context on %s.", path); goto finish; @@ -254,7 +254,7 @@ int mac_selinux_generic_access_check( tclass = "service"; } else { - r = getcon(&fcon); + r = getcon_raw(&fcon); if (r < 0) { r = sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to get current context."); goto finish; diff --git a/src/core/selinux-setup.c b/src/core/selinux-setup.c index a4678500e..e5b457643 100644 --- a/src/core/selinux-setup.c +++ b/src/core/selinux-setup.c @@ -34,6 +34,7 @@ #include "log.h" #ifdef HAVE_SELINUX +_printf_(2,3) static int null_log(int type, const char *fmt, ...) { return 0; } diff --git a/src/core/service.c b/src/core/service.c index b790ec98b..292fe50de 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -556,7 +556,7 @@ static int service_add_extras(Service *s) { if (r < 0) return r; - r = unit_add_default_slice(UNIT(s), &s->cgroup_context); + r = unit_set_default_slice(UNIT(s)); if (r < 0) return r; @@ -767,7 +767,7 @@ static int service_load_pid_file(Service *s, bool may_warn) { } static int service_search_main_pid(Service *s) { - pid_t pid; + pid_t pid = 0; int r; assert(s); @@ -782,9 +782,9 @@ static int service_search_main_pid(Service *s) { assert(s->main_pid <= 0); - pid = unit_search_main_pid(UNIT(s)); - if (pid <= 0) - return -ENOENT; + r = unit_search_main_pid(UNIT(s), &pid); + if (r < 0) + return r; log_unit_debug(UNIT(s), "Main PID guessed: "PID_FMT, pid); r = service_set_main_pid(s, pid); @@ -860,7 +860,7 @@ static void service_set_state(Service *s, ServiceState state) { /* For the inactive states unit_notify() will trim the cgroup, * but for exit we have to do that ourselves... */ if (state == SERVICE_EXITED && UNIT(s)->manager->n_reloading <= 0) - unit_destroy_cgroup_if_empty(UNIT(s)); + unit_prune_cgroup(UNIT(s)); /* For remain_after_exit services, let's see if we can "release" the * hold on the console, since unit_notify() only does that in case of @@ -1269,7 +1269,7 @@ static int cgroup_good(Service *s) { if (!UNIT(s)->cgroup_path) return 0; - r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path, true); + r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path); if (r < 0) return r; @@ -1520,18 +1520,33 @@ fail: service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); } +static bool service_good(Service *s) { + int main_pid_ok; + assert(s); + + if (s->type == SERVICE_DBUS && !s->bus_name_good) + return false; + + main_pid_ok = main_pid_good(s); + if (main_pid_ok > 0) /* It's alive */ + return true; + if (main_pid_ok == 0) /* It's dead */ + return false; + + /* OK, we don't know anything about the main PID, maybe + * because there is none. Let's check the control group + * instead. */ + + return cgroup_good(s) != 0; +} + static void service_enter_running(Service *s, ServiceResult f) { - int main_pid_ok, cgroup_ok; assert(s); if (f != SERVICE_SUCCESS) s->result = f; - main_pid_ok = main_pid_good(s); - cgroup_ok = cgroup_good(s); - - if ((main_pid_ok > 0 || (main_pid_ok < 0 && cgroup_ok != 0)) && - (s->bus_name_good || s->type != SERVICE_DBUS)) { + if (service_good(s)) { /* If there are any queued up sd_notify() * notifications, process them now */ @@ -2629,7 +2644,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { break; } } else - service_search_main_pid(s); + (void) service_search_main_pid(s); service_enter_start_post(s); break; @@ -2651,7 +2666,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { break; } } else - service_search_main_pid(s); + (void) service_search_main_pid(s); service_enter_running(s, SERVICE_SUCCESS); break; @@ -2659,7 +2674,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { case SERVICE_RELOAD: if (f == SERVICE_SUCCESS) { service_load_pid_file(s, true); - service_search_main_pid(s); + (void) service_search_main_pid(s); } s->reload_result = f; @@ -3214,7 +3229,6 @@ const UnitVTable service_vtable = { .bus_name_owner_change = service_bus_name_owner_change, - .bus_interface = "org.freedesktop.systemd1.Service", .bus_vtable = bus_service_vtable, .bus_set_property = bus_service_set_property, .bus_commit_properties = bus_service_commit_properties, diff --git a/src/core/slice.c b/src/core/slice.c index 064eb5d93..b41446206 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -21,12 +21,13 @@ #include -#include "unit.h" -#include "slice.h" #include "log.h" -#include "dbus-slice.h" +#include "strv.h" #include "special.h" #include "unit-name.h" +#include "unit.h" +#include "slice.h" +#include "dbus-slice.h" static const UnitActiveState state_translation_table[_SLICE_STATE_MAX] = { [SLICE_DEAD] = UNIT_INACTIVE, @@ -252,6 +253,40 @@ _pure_ static const char *slice_sub_state_to_string(Unit *u) { return slice_state_to_string(SLICE(u)->state); } +static int slice_enumerate(Manager *m) { + Unit *u; + int r; + + assert(m); + + u = manager_get_unit(m, SPECIAL_ROOT_SLICE); + if (!u) { + u = unit_new(m, sizeof(Slice)); + if (!u) + return log_oom(); + + r = unit_add_name(u, SPECIAL_ROOT_SLICE); + if (r < 0) { + unit_free(u); + return log_error_errno(r, "Failed to add -.slice name"); + } + } + + u->default_dependencies = false; + u->no_gc = true; + SLICE(u)->deserialized_state = SLICE_ACTIVE; + + if (!u->description) + u->description = strdup("Root Slice"); + if (!u->documentation) + (void) strv_extend(&u->documentation, "man:systemd.special(7)"); + + unit_add_to_load_queue(u); + unit_add_to_dbus_queue(u); + + return 0; +} + static const char* const slice_state_table[_SLICE_STATE_MAX] = { [SLICE_DEAD] = "dead", [SLICE_ACTIVE] = "active" @@ -289,11 +324,12 @@ const UnitVTable slice_vtable = { .active_state = slice_active_state, .sub_state_to_string = slice_sub_state_to_string, - .bus_interface = "org.freedesktop.systemd1.Slice", .bus_vtable = bus_slice_vtable, .bus_set_property = bus_slice_set_property, .bus_commit_properties = bus_slice_commit_properties, + .enumerate = slice_enumerate, + .status_message_formats = { .finished_start_job = { [JOB_DONE] = "Created slice %s.", diff --git a/src/core/snapshot.c b/src/core/snapshot.c index 9518e21f3..336ff20f8 100644 --- a/src/core/snapshot.c +++ b/src/core/snapshot.c @@ -302,6 +302,5 @@ const UnitVTable snapshot_vtable = { .active_state = snapshot_active_state, .sub_state_to_string = snapshot_sub_state_to_string, - .bus_interface = "org.freedesktop.systemd1.Snapshot", .bus_vtable = bus_snapshot_vtable }; diff --git a/src/core/socket.c b/src/core/socket.c index a38705747..1014fad62 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -345,7 +345,7 @@ static int socket_add_extras(Socket *s) { if (r < 0) return r; - r = unit_add_default_slice(u, &s->cgroup_context); + r = unit_set_default_slice(u); if (r < 0) return r; } @@ -839,7 +839,7 @@ static void socket_apply_socket_options(Socket *s, int fd) { if (s->keep_alive_cnt) { int value = s->keep_alive_cnt; - if (setsockopt(fd, SOL_SOCKET, TCP_KEEPCNT, &value, sizeof(value)) < 0) + if (setsockopt(fd, SOL_TCP, TCP_KEEPCNT, &value, sizeof(value)) < 0) log_unit_warning_errno(UNIT(s), errno, "TCP_KEEPCNT failed: %m"); } @@ -2709,7 +2709,6 @@ const UnitVTable socket_vtable = { .reset_failed = socket_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Socket", .bus_vtable = bus_socket_vtable, .bus_set_property = bus_socket_set_property, .bus_commit_properties = bus_socket_commit_properties, diff --git a/src/core/swap.c b/src/core/swap.c index 0bc3827ff..4f3ddc9f0 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -326,7 +326,7 @@ static int swap_load(Unit *u) { if (r < 0) return r; - r = unit_add_default_slice(u, &s->cgroup_context); + r = unit_set_default_slice(u); if (r < 0) return r; @@ -1485,7 +1485,6 @@ const UnitVTable swap_vtable = { .reset_failed = swap_reset_failed, - .bus_interface = "org.freedesktop.systemd1.Swap", .bus_vtable = bus_swap_vtable, .bus_set_property = bus_swap_set_property, .bus_commit_properties = bus_swap_commit_properties, diff --git a/src/core/target.c b/src/core/target.c index b492a7c4c..f714cb31c 100644 --- a/src/core/target.c +++ b/src/core/target.c @@ -221,7 +221,6 @@ const UnitVTable target_vtable = { .active_state = target_active_state, .sub_state_to_string = target_sub_state_to_string, - .bus_interface = "org.freedesktop.systemd1.Target", .bus_vtable = bus_target_vtable, .status_message_formats = { diff --git a/src/core/timer.c b/src/core/timer.c index 89758c6b1..eb6567bbf 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -772,7 +772,6 @@ const UnitVTable timer_vtable = { .reset_failed = timer_reset_failed, .time_change = timer_time_change, - .bus_interface = "org.freedesktop.systemd1.Timer", .bus_vtable = bus_timer_vtable, .bus_set_property = bus_timer_set_property, diff --git a/src/core/unit.c b/src/core/unit.c index 43a5ca106..1aa0321b2 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -28,27 +28,28 @@ #include "sd-id128.h" #include "sd-messages.h" #include "set.h" -#include "unit.h" #include "macro.h" #include "strv.h" #include "path-util.h" -#include "load-fragment.h" -#include "load-dropin.h" #include "log.h" -#include "unit-name.h" -#include "dbus-unit.h" -#include "special.h" #include "cgroup-util.h" #include "missing.h" #include "mkdir.h" #include "fileio-label.h" -#include "bus-common-errors.h" -#include "dbus.h" -#include "execute.h" -#include "dropin.h" #include "formats-util.h" #include "process-util.h" +#include "virt.h" +#include "bus-common-errors.h" #include "bus-util.h" +#include "dropin.h" +#include "unit-name.h" +#include "special.h" +#include "unit.h" +#include "load-fragment.h" +#include "load-dropin.h" +#include "dbus.h" +#include "dbus-unit.h" +#include "execute.h" const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = { [UNIT_SERVICE] = &service_vtable, @@ -90,6 +91,7 @@ Unit *unit_new(Manager *m, size_t size) { u->unit_file_state = _UNIT_FILE_STATE_INVALID; u->unit_file_preset = -1; u->on_failure_job_mode = JOB_REPLACE; + u->cgroup_inotify_wd = -1; RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16); @@ -405,17 +407,17 @@ static void unit_remove_transient(Unit *u) { return; if (u->fragment_path) - unlink(u->fragment_path); + (void) unlink(u->fragment_path); STRV_FOREACH(i, u->dropin_paths) { _cleanup_free_ char *p = NULL; int r; - unlink(*i); + (void) unlink(*i); r = path_get_parent(*i, &p); if (r >= 0) - rmdir(p); + (void) rmdir(p); } } @@ -524,10 +526,7 @@ void unit_free(Unit *u) { if (u->in_cgroup_queue) LIST_REMOVE(cgroup_queue, u->manager->cgroup_queue, u); - if (u->cgroup_path) { - hashmap_remove(u->manager->cgroup_unit, u->cgroup_path); - free(u->cgroup_path); - } + unit_release_cgroup(u); manager_update_failed_units(u->manager, u, false); set_remove(u->manager->startup_units, u); @@ -1122,7 +1121,7 @@ static int unit_add_target_dependencies(Unit *u) { static int unit_add_slice_dependencies(Unit *u) { assert(u); - if (!unit_get_cgroup_context(u)) + if (!UNIT_HAS_CGROUP_CONTEXT(u)) return 0; if (UNIT_ISSET(u->slice)) @@ -1417,6 +1416,7 @@ int unit_start(Unit *u) { assert(u); + /* Units that aren't loaded cannot be started */ if (u->load_state != UNIT_LOADED) return -EINVAL; @@ -1445,6 +1445,15 @@ int unit_start(Unit *u) { return -EPROTO; } + /* Units of types that aren't supported cannot be + * started. Note that we do this test only after the condition + * checks, so that we rather return condition check errors + * (which are usually not considered a true failure) than "not + * supported" errors (which are considered a failure). + */ + if (!unit_supported(u)) + return -EOPNOTSUPP; + /* Forward to the main object, if we aren't it. */ following = unit_following(u); if (following) { @@ -1452,9 +1461,6 @@ int unit_start(Unit *u) { return unit_start(following); } - if (!unit_supported(u)) - return -EOPNOTSUPP; - /* If it is stopped, but we cannot start it, then fail */ if (!UNIT_VTABLE(u)->start) return -EBADR; @@ -1473,6 +1479,12 @@ int unit_start(Unit *u) { bool unit_can_start(Unit *u) { assert(u); + if (u->load_state != UNIT_LOADED) + return false; + + if (!unit_supported(u)) + return false; + return !!UNIT_VTABLE(u)->start; } @@ -1800,7 +1812,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su /* Make sure the cgroup is always removed when we become inactive */ if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - unit_destroy_cgroup_if_empty(u); + unit_prune_cgroup(u); /* Note that this doesn't apply to RemainAfterExit services exiting * successfully, since there's no change of state in that case. Which is @@ -1996,16 +2008,16 @@ int unit_watch_pid(Unit *u, pid_t pid) { if (r < 0) return r; - r = hashmap_put(u->manager->watch_pids1, LONG_TO_PTR(pid), u); + r = hashmap_put(u->manager->watch_pids1, PID_TO_PTR(pid), u); if (r == -EEXIST) { r = hashmap_ensure_allocated(&u->manager->watch_pids2, NULL); if (r < 0) return r; - r = hashmap_put(u->manager->watch_pids2, LONG_TO_PTR(pid), u); + r = hashmap_put(u->manager->watch_pids2, PID_TO_PTR(pid), u); } - q = set_put(u->pids, LONG_TO_PTR(pid)); + q = set_put(u->pids, PID_TO_PTR(pid)); if (q < 0) return q; @@ -2016,81 +2028,18 @@ void unit_unwatch_pid(Unit *u, pid_t pid) { assert(u); assert(pid >= 1); - hashmap_remove_value(u->manager->watch_pids1, LONG_TO_PTR(pid), u); - hashmap_remove_value(u->manager->watch_pids2, LONG_TO_PTR(pid), u); - set_remove(u->pids, LONG_TO_PTR(pid)); + (void) hashmap_remove_value(u->manager->watch_pids1, PID_TO_PTR(pid), u); + (void) hashmap_remove_value(u->manager->watch_pids2, PID_TO_PTR(pid), u); + (void) set_remove(u->pids, PID_TO_PTR(pid)); } void unit_unwatch_all_pids(Unit *u) { assert(u); while (!set_isempty(u->pids)) - unit_unwatch_pid(u, PTR_TO_LONG(set_first(u->pids))); + unit_unwatch_pid(u, PTR_TO_PID(set_first(u->pids))); - set_free(u->pids); - u->pids = NULL; -} - -static int unit_watch_pids_in_path(Unit *u, const char *path) { - _cleanup_closedir_ DIR *d = NULL; - _cleanup_fclose_ FILE *f = NULL; - int ret = 0, r; - - assert(u); - assert(path); - - /* Adds all PIDs from a specific cgroup path to the set of PIDs we watch. */ - - r = cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, path, &f); - if (r >= 0) { - pid_t pid; - - while ((r = cg_read_pid(f, &pid)) > 0) { - r = unit_watch_pid(u, pid); - if (r < 0 && ret >= 0) - ret = r; - } - if (r < 0 && ret >= 0) - ret = r; - - } else if (ret >= 0) - ret = r; - - r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, path, &d); - if (r >= 0) { - char *fn; - - while ((r = cg_read_subgroup(d, &fn)) > 0) { - _cleanup_free_ char *p = NULL; - - p = strjoin(path, "/", fn, NULL); - free(fn); - - if (!p) - return -ENOMEM; - - r = unit_watch_pids_in_path(u, p); - if (r < 0 && ret >= 0) - ret = r; - } - if (r < 0 && ret >= 0) - ret = r; - - } else if (ret >= 0) - ret = r; - - return ret; -} - -int unit_watch_all_pids(Unit *u) { - assert(u); - - /* Adds all PIDs from our cgroup to the set of PIDs we watch */ - - if (!u->cgroup_path) - return -ENOENT; - - return unit_watch_pids_in_path(u, u->cgroup_path); + u->pids = set_free(u->pids); } void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2) { @@ -2102,7 +2051,7 @@ void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2) { /* Cleans dead PIDs from our list */ SET_FOREACH(e, u->pids, i) { - pid_t pid = PTR_TO_LONG(e); + pid_t pid = PTR_TO_PID(e); if (pid == except1 || pid == except2) continue; @@ -2399,39 +2348,49 @@ char *unit_dbus_path(Unit *u) { return unit_dbus_path_from_name(u->id); } -char *unit_default_cgroup_path(Unit *u) { - _cleanup_free_ char *escaped = NULL, *slice = NULL; - int r; - +int unit_set_slice(Unit *u, Unit *slice) { assert(u); + assert(slice); - if (unit_has_name(u, SPECIAL_ROOT_SLICE)) - return strdup(u->manager->cgroup_root); + /* Sets the unit slice if it has not been set before. Is extra + * careful, to only allow this for units that actually have a + * cgroup context. Also, we don't allow to set this for slices + * (since the parent slice is derived from the name). Make + * sure the unit we set is actually a slice. */ - if (UNIT_ISSET(u->slice) && !unit_has_name(UNIT_DEREF(u->slice), SPECIAL_ROOT_SLICE)) { - r = cg_slice_to_path(UNIT_DEREF(u->slice)->id, &slice); - if (r < 0) - return NULL; - } + if (!UNIT_HAS_CGROUP_CONTEXT(u)) + return -EOPNOTSUPP; - escaped = cg_escape(u->id); - if (!escaped) - return NULL; + if (u->type == UNIT_SLICE) + return -EINVAL; - if (slice) - return strjoin(u->manager->cgroup_root, "/", slice, "/", escaped, NULL); - else - return strjoin(u->manager->cgroup_root, "/", escaped, NULL); + if (unit_active_state(u) != UNIT_INACTIVE) + return -EBUSY; + + if (slice->type != UNIT_SLICE) + return -EINVAL; + + if (unit_has_name(u, SPECIAL_INIT_SCOPE) && + !unit_has_name(slice, SPECIAL_ROOT_SLICE)) + return -EPERM; + + if (UNIT_DEREF(u->slice) == slice) + return 0; + + if (UNIT_ISSET(u->slice)) + return -EBUSY; + + unit_ref_set(&u->slice, slice); + return 1; } -int unit_add_default_slice(Unit *u, CGroupContext *c) { +int unit_set_default_slice(Unit *u) { _cleanup_free_ char *b = NULL; const char *slice_name; Unit *slice; int r; assert(u); - assert(c); if (UNIT_ISSET(u->slice)) return 0; @@ -2463,7 +2422,7 @@ int unit_add_default_slice(Unit *u, CGroupContext *c) { slice_name = b; } else slice_name = - u->manager->running_as == MANAGER_SYSTEM + u->manager->running_as == MANAGER_SYSTEM && !unit_has_name(u, SPECIAL_INIT_SCOPE) ? SPECIAL_SYSTEM_SLICE : SPECIAL_ROOT_SLICE; @@ -2471,8 +2430,7 @@ int unit_add_default_slice(Unit *u, CGroupContext *c) { if (r < 0) return r; - unit_ref_set(&u->slice, slice); - return 0; + return unit_set_slice(u, slice); } const char *unit_slice_name(Unit *u) { @@ -2673,40 +2631,6 @@ void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) { fprintf(f, "%s=%s\n", key, value); } -static int unit_set_cgroup_path(Unit *u, const char *path) { - _cleanup_free_ char *p = NULL; - int r; - - assert(u); - - if (path) { - p = strdup(path); - if (!p) - return -ENOMEM; - } else - p = NULL; - - if (streq_ptr(u->cgroup_path, p)) - return 0; - - if (p) { - r = hashmap_put(u->manager->cgroup_unit, p, u); - if (r < 0) - return r; - } - - if (u->cgroup_path) { - log_unit_debug(u, "Changing cgroup path from %s to %s.", u->cgroup_path, strna(p)); - hashmap_remove(u->manager->cgroup_unit, u->cgroup_path); - free(u->cgroup_path); - } - - u->cgroup_path = p; - p = NULL; - - return 0; -} - int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { ExecRuntime **rt = NULL; size_t offset; @@ -2837,6 +2761,8 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { if (r < 0) log_unit_debug_errno(u, r, "Failed to set cgroup path %s, ignoring: %m", v); + (void) unit_watch_cgroup(u); + continue; } else if (streq(l, "cgroup-realized")) { int b; @@ -3080,13 +3006,13 @@ static Set *unit_pid_set(pid_t main_pid, pid_t control_pid) { /* Exclude the main/control pids from being killed via the cgroup */ if (main_pid > 0) { - r = set_put(pid_set, LONG_TO_PTR(main_pid)); + r = set_put(pid_set, PID_TO_PTR(main_pid)); if (r < 0) goto fail; } if (control_pid > 0) { - r = set_put(pid_set, LONG_TO_PTR(control_pid)); + r = set_put(pid_set, PID_TO_PTR(control_pid)); if (r < 0) goto fail; } @@ -3108,17 +3034,17 @@ int unit_kill_common( int r = 0; - if (who == KILL_MAIN && main_pid <= 0) { + if (who == KILL_MAIN) { if (main_pid < 0) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type)); - else + else if (main_pid == 0) return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill"); } - if (who == KILL_CONTROL && control_pid <= 0) { + if (who == KILL_CONTROL) { if (control_pid < 0) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no control processes", unit_type_to_string(u->type)); - else + else if (control_pid == 0) return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); } @@ -3141,7 +3067,7 @@ int unit_kill_common( if (!pid_set) return -ENOMEM; - q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set); + q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, false, false, pid_set); if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT) r = q; } @@ -3317,6 +3243,8 @@ ExecRuntime *unit_get_exec_runtime(Unit *u) { } static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient, char **dir) { + assert(u); + if (u->manager->running_as == MANAGER_USER) { int r; @@ -3324,9 +3252,9 @@ static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient, r = user_config_home(dir); else r = user_runtime_dir(dir); - if (r == 0) return -ENOENT; + return r; } @@ -3340,8 +3268,7 @@ static int unit_drop_in_dir(Unit *u, UnitSetPropertiesMode mode, bool transient, return 0; } -static int unit_drop_in_file(Unit *u, - UnitSetPropertiesMode mode, const char *name, char **p, char **q) { +static int unit_drop_in_file(Unit *u, UnitSetPropertiesMode mode, const char *name, char **p, char **q) { _cleanup_free_ char *dir = NULL; int r; @@ -3475,40 +3402,17 @@ int unit_remove_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name) { } int unit_make_transient(Unit *u) { - int r; - assert(u); + if (!UNIT_VTABLE(u)->can_transient) + return -EOPNOTSUPP; + u->load_state = UNIT_STUB; u->load_error = 0; u->transient = true; + u->fragment_path = mfree(u->fragment_path); - free(u->fragment_path); - u->fragment_path = NULL; - - if (u->manager->running_as == MANAGER_USER) { - _cleanup_free_ char *c = NULL; - - r = user_runtime_dir(&c); - if (r < 0) - return r; - if (r == 0) - return -ENOENT; - - u->fragment_path = strjoin(c, "/", u->id, NULL); - if (!u->fragment_path) - return -ENOMEM; - - mkdir_p(c, 0755); - } else { - u->fragment_path = strappend("/run/systemd/system/", u->id); - if (!u->fragment_path) - return -ENOMEM; - - mkdir_p("/run/systemd/system", 0755); - } - - return write_string_file_atomic_label(u->fragment_path, "# Transient stub"); + return 0; } int unit_kill_context( @@ -3519,7 +3423,8 @@ int unit_kill_context( pid_t control_pid, bool main_pid_alien) { - int sig, wait_for_exit = false, r; + bool wait_for_exit = false; + int sig, r; assert(u); assert(c); @@ -3548,13 +3453,13 @@ int unit_kill_context( _cleanup_free_ char *comm = NULL; get_process_comm(main_pid, &comm); - log_unit_warning_errno(u, r, "Failed to kill main process " PID_FMT " (%s): %m", main_pid, strna(comm)); + log_unit_warning_errno(u, r, "Failed to kill main process " PID_FMT " (%s), ignoring: %m", main_pid, strna(comm)); } else { if (!main_pid_alien) wait_for_exit = true; - if (c->send_sighup && k != KILL_KILL) - kill(main_pid, SIGHUP); + if (c->send_sighup && k == KILL_TERMINATE) + (void) kill(main_pid, SIGHUP); } } @@ -3565,16 +3470,17 @@ int unit_kill_context( _cleanup_free_ char *comm = NULL; get_process_comm(control_pid, &comm); - log_unit_warning_errno(u, r, "Failed to kill control process " PID_FMT " (%s): %m", control_pid, strna(comm)); + log_unit_warning_errno(u, r, "Failed to kill control process " PID_FMT " (%s), ignoring: %m", control_pid, strna(comm)); } else { wait_for_exit = true; - if (c->send_sighup && k != KILL_KILL) - kill(control_pid, SIGHUP); + if (c->send_sighup && k == KILL_TERMINATE) + (void) kill(control_pid, SIGHUP); } } - if ((c->kill_mode == KILL_CONTROL_GROUP || (c->kill_mode == KILL_MIXED && k == KILL_KILL)) && u->cgroup_path) { + if (u->cgroup_path && + (c->kill_mode == KILL_CONTROL_GROUP || (c->kill_mode == KILL_MIXED && k == KILL_KILL))) { _cleanup_set_free_ Set *pid_set = NULL; /* Exclude the main/control pids from being killed via the cgroup */ @@ -3582,21 +3488,30 @@ int unit_kill_context( if (!pid_set) return -ENOMEM; - r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, sig, true, true, false, pid_set); + r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, sig, true, k != KILL_TERMINATE, false, pid_set); if (r < 0) { if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) - log_unit_warning_errno(u, r, "Failed to kill control group: %m"); - } else if (r > 0) { + log_unit_warning_errno(u, r, "Failed to kill control group %s, ignoring: %m", u->cgroup_path); - /* FIXME: For now, we will not wait for the - * cgroup members to die, simply because - * cgroup notification is unreliable. It - * doesn't work at all in containers, and - * outside of containers it can be confused - * easily by leaving directories in the - * cgroup. */ + } else if (r > 0) { - /* wait_for_exit = true; */ + /* FIXME: For now, on the legacy hierarchy, we + * will not wait for the cgroup members to die + * if we are running in a container or if this + * is a delegation unit, simply because cgroup + * notification is unreliable in these + * cases. It doesn't work at all in + * containers, and outside of containers it + * can be confused easily by left-over + * directories in the cgroup -- which however + * should not exist in non-delegated units. On + * the unified hierarchy that's different, + * there we get proper events. Hence rely on + * them.*/ + + if (cg_unified() > 0 || + (detect_container(NULL) == 0 && !unit_cgroup_delegate(u))) + wait_for_exit = true; if (c->send_sighup && k != KILL_KILL) { set_free(pid_set); diff --git a/src/core/unit.h b/src/core/unit.h index 9df5a7e6f..3c7684411 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -184,9 +184,10 @@ struct Unit { /* Counterparts in the cgroup filesystem */ char *cgroup_path; - CGroupControllerMask cgroup_realized_mask; - CGroupControllerMask cgroup_subtree_mask; - CGroupControllerMask cgroup_members_mask; + CGroupMask cgroup_realized_mask; + CGroupMask cgroup_subtree_mask; + CGroupMask cgroup_members_mask; + int cgroup_inotify_wd; /* How to start OnFailure units */ JobMode on_failure_job_mode; @@ -404,9 +405,6 @@ struct UnitVTable { * of this type will immediately fail. */ bool (*supported)(void); - /* The interface name */ - const char *bus_interface; - /* The bus vtable */ const sd_bus_vtable *bus_vtable; @@ -442,6 +440,10 @@ extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX]; /* For casting the various unit types into a unit */ #define UNIT(u) (&(u)->meta) +#define UNIT_HAS_EXEC_CONTEXT(u) (UNIT_VTABLE(u)->exec_context_offset > 0) +#define UNIT_HAS_CGROUP_CONTEXT(u) (UNIT_VTABLE(u)->cgroup_context_offset > 0) +#define UNIT_HAS_KILL_CONTEXT(u) (UNIT_VTABLE(u)->kill_context_offset > 0) + #define UNIT_TRIGGER(u) ((Unit*) set_first((u)->dependencies[UNIT_TRIGGERS])) DEFINE_CAST(SERVICE, Service); @@ -493,7 +495,8 @@ int unit_load_fragment_and_dropin(Unit *u); int unit_load_fragment_and_dropin_optional(Unit *u); int unit_load(Unit *unit); -int unit_add_default_slice(Unit *u, CGroupContext *c); +int unit_set_slice(Unit *u, Unit *slice); +int unit_set_default_slice(Unit *u); const char *unit_description(Unit *u) _pure_; @@ -520,7 +523,6 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su int unit_watch_pid(Unit *u, pid_t pid); void unit_unwatch_pid(Unit *u, pid_t pid); -int unit_watch_all_pids(Unit *u); void unit_unwatch_all_pids(Unit *u); void unit_tidy_watch_pids(Unit *u, pid_t except1, pid_t except2); @@ -565,8 +567,6 @@ bool unit_active_or_pending(Unit *u); int unit_add_default_target_dependency(Unit *u, Unit *target); -char *unit_default_cgroup_path(Unit *u); - void unit_start_on_failure(Unit *u); void unit_trigger_notify(Unit *u); diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index 0a34f86be..cf25d9847 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -666,6 +666,7 @@ static int enumerate_partitions(dev_t devnum) { first = udev_enumerate_get_list_entry(e); udev_list_entry_foreach(item, first) { _cleanup_udev_device_unref_ struct udev_device *q; + unsigned long long flags; const char *stype, *subnode; sd_id128_t type_id; blkid_partition pp; @@ -705,10 +706,10 @@ static int enumerate_partitions(dev_t devnum) { if (sd_id128_from_string(stype, &type_id) < 0) continue; + flags = blkid_partition_get_flags(pp); + if (sd_id128_equal(type_id, GPT_SWAP)) { - unsigned long long flags; - flags = blkid_partition_get_flags(pp); if (flags & GPT_FLAG_NO_AUTO) continue; @@ -727,6 +728,10 @@ static int enumerate_partitions(dev_t devnum) { if (boot && nr >= boot_nr) continue; + /* Note that we do not honour the "no-auto" + * flag for the ESP, as it is often unset, to + * hide it from Windows. */ + boot_nr = nr; r = free_and_strdup(&boot, subnode); @@ -734,9 +739,7 @@ static int enumerate_partitions(dev_t devnum) { return log_oom(); } else if (sd_id128_equal(type_id, GPT_HOME)) { - unsigned long long flags; - flags = blkid_partition_get_flags(pp); if (flags & GPT_FLAG_NO_AUTO) continue; @@ -752,9 +755,7 @@ static int enumerate_partitions(dev_t devnum) { return log_oom(); } else if (sd_id128_equal(type_id, GPT_SRV)) { - unsigned long long flags; - flags = blkid_partition_get_flags(pp); if (flags & GPT_FLAG_NO_AUTO) continue; @@ -799,6 +800,10 @@ static int get_block_device(const char *path, dev_t *dev) { assert(path); assert(dev); + /* Get's the block device directly backing a file system. If + * the block device is encrypted, returns the device mapper + * block device. */ + if (lstat(path, &st)) return -errno; @@ -816,6 +821,77 @@ static int get_block_device(const char *path, dev_t *dev) { return 0; } +static int get_block_device_harder(const char *path, dev_t *dev) { + _cleanup_closedir_ DIR *d = NULL; + _cleanup_free_ char *p = NULL, *t = NULL; + struct dirent *de, *found = NULL; + const char *q; + unsigned maj, min; + dev_t dt; + int r; + + assert(path); + assert(dev); + + /* Gets the backing block device for a file system, and + * handles LUKS encrypted file systems, looking for its + * immediate parent, if there is one. */ + + r = get_block_device(path, &dt); + if (r <= 0) + return r; + + if (asprintf(&p, "/sys/dev/block/%u:%u/slaves", major(dt), minor(dt)) < 0) + return -ENOMEM; + + d = opendir(p); + if (!d) { + if (errno == ENOENT) + goto fallback; + + return -errno; + } + + FOREACH_DIRENT_ALL(de, d, return -errno) { + + if (STR_IN_SET(de->d_name, ".", "..")) + continue; + + if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN)) + continue; + + if (found) /* Don't try to support multiple backing block devices */ + goto fallback; + + found = de; + break; + } + + if (!found) + goto fallback; + + q = strjoina(p, "/", found->d_name, "/dev"); + + r = read_one_line_file(q, &t); + if (r == -ENOENT) + goto fallback; + if (r < 0) + return r; + + if (sscanf(t, "%u:%u", &maj, &min) != 2) + return -EINVAL; + + if (maj == 0) + goto fallback; + + *dev = makedev(maj, min); + return 1; + +fallback: + *dev = dt; + return 1; +} + static int parse_proc_cmdline_item(const char *key, const char *value) { int r; @@ -883,11 +959,11 @@ static int add_mounts(void) { dev_t devno; int r; - r = get_block_device("/", &devno); + r = get_block_device_harder("/", &devno); if (r < 0) return log_error_errno(r, "Failed to determine block device of root file system: %m"); else if (r == 0) { - r = get_block_device("/usr", &devno); + r = get_block_device_harder("/usr", &devno); if (r < 0) return log_error_errno(r, "Failed to determine block device of /usr file system: %m"); else if (r == 0) { diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index a78516c8b..c423be376 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -434,6 +434,7 @@ static int method_set_hostname(sd_bus_message *m, void *userdata, sd_bus_error * m, CAP_SYS_ADMIN, "org.freedesktop.hostname1.set-hostname", + NULL, interactive, UID_INVALID, &c->polkit_registry, @@ -486,6 +487,7 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_ m, CAP_SYS_ADMIN, "org.freedesktop.hostname1.set-static-hostname", + NULL, interactive, UID_INVALID, &c->polkit_registry, @@ -557,6 +559,7 @@ static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_mess m, CAP_SYS_ADMIN, prop == PROP_PRETTY_HOSTNAME ? "org.freedesktop.hostname1.set-static-hostname" : "org.freedesktop.hostname1.set-machine-info", + NULL, interactive, UID_INVALID, &c->polkit_registry, diff --git a/src/import/importd.c b/src/import/importd.c index 8b508eaee..ffff94ee7 100644 --- a/src/import/importd.c +++ b/src/import/importd.c @@ -735,6 +735,7 @@ static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_ msg, CAP_SYS_ADMIN, "org.freedesktop.import1.import", + NULL, false, UID_INVALID, &m->polkit_registry, @@ -799,6 +800,7 @@ static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_ msg, CAP_SYS_ADMIN, "org.freedesktop.import1.export", + NULL, false, UID_INVALID, &m->polkit_registry, @@ -864,6 +866,7 @@ static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_er msg, CAP_SYS_ADMIN, "org.freedesktop.import1.pull", + NULL, false, UID_INVALID, &m->polkit_registry, @@ -945,6 +948,7 @@ static int method_pull_dkr(sd_bus_message *msg, void *userdata, sd_bus_error *er msg, CAP_SYS_ADMIN, "org.freedesktop.import1.pull", + NULL, false, UID_INVALID, &m->polkit_registry, @@ -1079,6 +1083,7 @@ static int method_cancel(sd_bus_message *msg, void *userdata, sd_bus_error *erro msg, CAP_SYS_ADMIN, "org.freedesktop.import1.pull", + NULL, false, UID_INVALID, &t->manager->polkit_registry, @@ -1108,6 +1113,7 @@ static int method_cancel_transfer(sd_bus_message *msg, void *userdata, sd_bus_er msg, CAP_SYS_ADMIN, "org.freedesktop.import1.pull", + NULL, false, UID_INVALID, &m->polkit_registry, diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c index e5be7f776..51fe3aa50 100644 --- a/src/journal/journald-kmsg.c +++ b/src/journal/journald-kmsg.c @@ -190,7 +190,7 @@ static void dev_kmsg_record(Server *s, const char *p, size_t l) { for (j = 0; l > 0 && j < N_IOVEC_KERNEL_FIELDS; j++) { char *m; - /* Meta data fields attached */ + /* Metadata fields attached */ if (*k != ' ') break; diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h index 7c60ef123..df6f882af 100644 --- a/src/libsystemd-network/dhcp-internal.h +++ b/src/libsystemd-network/dhcp-internal.h @@ -45,10 +45,10 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_ uint8_t code, size_t optlen, const void *optval); typedef int (*dhcp_option_cb_t)(uint8_t code, uint8_t len, - const uint8_t *option, void *user_data); + const void *option, void *userdata); int dhcp_option_parse(DHCPMessage *message, size_t len, - dhcp_option_cb_t cb, void *user_data); + dhcp_option_cb_t cb, void *userdata); int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, uint8_t type, uint16_t arp_type, size_t optlen, diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h index 5a3fcddb1..c6b97ca8f 100644 --- a/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/libsystemd-network/dhcp-lease-internal.h @@ -25,7 +25,6 @@ #include #include -#include "refcnt.h" #include "util.h" #include "list.h" @@ -48,54 +47,62 @@ struct sd_dhcp_raw_option { }; struct sd_dhcp_lease { - RefCount n_ref; + unsigned n_ref; - int32_t time_offset; + /* each 0 if unset */ uint32_t t1; uint32_t t2; uint32_t lifetime; - uint32_t mtu_aging_timeout; + + /* each 0 if unset */ be32_t address; be32_t server_address; - be32_t subnet_mask; be32_t router; be32_t next_server; + + bool have_subnet_mask; + be32_t subnet_mask; + + bool have_broadcast; be32_t broadcast; + struct in_addr *dns; size_t dns_size; + struct in_addr *ntp; size_t ntp_size; - struct in_addr *policy_filter; - size_t policy_filter_size; + struct sd_dhcp_route *static_route; - size_t static_route_size; - size_t static_route_allocated; - uint16_t boot_file_size; - uint16_t mdr; - uint16_t mtu; - uint8_t ttl; - bool ip_forward; - bool ip_forward_non_local; + size_t static_route_size, static_route_allocated; + + uint16_t mtu; /* 0 if unset */ + char *domainname; char *hostname; char *root_path; - uint8_t *client_id; + + void *client_id; size_t client_id_len; - uint8_t *vendor_specific; + + void *vendor_specific; size_t vendor_specific_len; + + char *timezone; + LIST_HEAD(struct sd_dhcp_raw_option, private_options); }; int dhcp_lease_new(sd_dhcp_lease **ret); -int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option, - void *user_data); -int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, - const uint8_t *data, uint8_t len); + +int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata); +int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len); int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease); -int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const uint8_t *client_id, - size_t client_id_len); +int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len); + +int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file); +int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file); DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_lease*, sd_dhcp_lease_unref); #define _cleanup_dhcp_lease_unref_ _cleanup_(sd_dhcp_lease_unrefp) diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c index b6110c5f1..36be7d54e 100644 --- a/src/libsystemd-network/dhcp-option.c +++ b/src/libsystemd-network/dhcp-option.c @@ -140,7 +140,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload, uint8_t *message_type, dhcp_option_cb_t cb, - void *user_data) { + void *userdata) { uint8_t code, len; size_t offset = 0; @@ -199,7 +199,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo return -EINVAL; if (cb) - cb(code, len, &options[offset], user_data); + cb(code, len, &options[offset], userdata); offset += len; @@ -214,7 +214,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo } int dhcp_option_parse(DHCPMessage *message, size_t len, - dhcp_option_cb_t cb, void *user_data) { + dhcp_option_cb_t cb, void *userdata) { uint8_t overload = 0; uint8_t message_type = 0; int r; @@ -228,20 +228,20 @@ int dhcp_option_parse(DHCPMessage *message, size_t len, len -= sizeof(DHCPMessage); r = parse_options(message->options, len, &overload, &message_type, - cb, user_data); + cb, userdata); if (r < 0) return r; if (overload & DHCP_OVERLOAD_FILE) { r = parse_options(message->file, sizeof(message->file), - NULL, &message_type, cb, user_data); + NULL, &message_type, cb, userdata); if (r < 0) return r; } if (overload & DHCP_OVERLOAD_SNAME) { r = parse_options(message->sname, sizeof(message->sname), - NULL, &message_type, cb, user_data); + NULL, &message_type, cb, userdata); if (r < 0) return r; } diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h index 4308723f9..88a81d286 100644 --- a/src/libsystemd-network/dhcp-protocol.h +++ b/src/libsystemd-network/dhcp-protocol.h @@ -137,6 +137,8 @@ enum { DHCP_OPTION_REBINDING_T2_TIME = 59, DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60, DHCP_OPTION_CLIENT_IDENTIFIER = 61, + DHCP_OPTION_NEW_POSIX_TIMEZONE = 100, + DHCP_OPTION_NEW_TZDB_TIMEZONE = 101, DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121, DHCP_OPTION_PRIVATE_BASE = 224, DHCP_OPTION_PRIVATE_LAST = 254, diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h index 58750c441..5dc3c7aa2 100644 --- a/src/libsystemd-network/dhcp-server-internal.h +++ b/src/libsystemd-network/dhcp-server-internal.h @@ -26,7 +26,6 @@ #include "sd-dhcp-server.h" #include "hashmap.h" -#include "refcnt.h" #include "util.h" #include "log.h" @@ -34,7 +33,7 @@ typedef struct DHCPClientId { size_t length; - uint8_t *data; + void *data; } DHCPClientId; typedef struct DHCPLease { @@ -47,7 +46,7 @@ typedef struct DHCPLease { } DHCPLease; struct sd_dhcp_server { - RefCount n_ref; + unsigned n_ref; sd_event *event; int event_priority; @@ -55,15 +54,23 @@ struct sd_dhcp_server { int fd; int fd_raw; - int index; + int ifindex; be32_t address; be32_t netmask; - be32_t pool_start; - size_t pool_size; - size_t next_offer; + be32_t subnet; + uint32_t pool_offset; + uint32_t pool_size; + + char *timezone; + + struct in_addr *ntp, *dns; + unsigned n_ntp, n_dns; Hashmap *leases_by_client_id; DHCPLease **bound_leases; + DHCPLease invalid_lease; + + uint32_t max_lease_time, default_lease_time; }; typedef struct DHCPRequest { @@ -75,7 +82,7 @@ typedef struct DHCPRequest { size_t max_optlen; be32_t server_id; be32_t requested_ip; - int lifetime; + uint32_t lifetime; } DHCPRequest; DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_server*, sd_dhcp_server_unref); diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 037f580eb..4edecf771 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -24,13 +24,11 @@ #include -#include "refcnt.h" - #include "sd-dhcp6-lease.h" #include "dhcp6-internal.h" struct sd_dhcp6_lease { - RefCount n_ref; + unsigned n_ref; uint8_t *serverid; size_t serverid_len; diff --git a/src/libsystemd-network/dhcp6-network.c b/src/libsystemd-network/dhcp6-network.c index fe56c1027..187975364 100644 --- a/src/libsystemd-network/dhcp6-network.c +++ b/src/libsystemd-network/dhcp6-network.c @@ -41,8 +41,7 @@ { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } } -int dhcp_network_icmp6_bind_router_solicitation(int index) -{ +int dhcp_network_icmp6_bind_router_solicitation(int index) { struct icmp6_filter filter = { }; struct ipv6_mreq mreq = { .ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT, @@ -92,8 +91,7 @@ int dhcp_network_icmp6_bind_router_solicitation(int index) return r; } -int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) -{ +int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) { struct sockaddr_in6 dst = { .sin6_family = AF_INET6, .sin6_addr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT, diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 2fa4d5fac..f41bebced 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -338,9 +338,7 @@ int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, return count; } -int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, - char ***str_arr) -{ +int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char ***str_arr) { size_t pos = 0, idx = 0; _cleanup_free_ char **names = NULL; int r; diff --git a/src/libsystemd-network/lldp-internal.c b/src/libsystemd-network/lldp-internal.c index 0f354461f..3c04898e9 100644 --- a/src/libsystemd-network/lldp-internal.c +++ b/src/libsystemd-network/lldp-internal.c @@ -374,9 +374,8 @@ int lldp_mib_add_objects(Prioq *by_expiry, } /* Admission Control: Can this port attached to the existing chassis ? */ - if (REFCNT_GET(c->n_ref) >= LLDP_MIB_MAX_PORT_PER_CHASSIS) { - log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...", - REFCNT_GET(c->n_ref)); + if (c->n_ref >= LLDP_MIB_MAX_PORT_PER_CHASSIS) { + log_lldp("Port limit reached. Chassis has: %d ports. Dropping ...", c->n_ref); c = NULL; goto drop; @@ -394,7 +393,7 @@ int lldp_mib_add_objects(Prioq *by_expiry, /* Attach new port to chassis */ LIST_PREPEND(port, c->ports, p); - REFCNT_INC(c->n_ref); + c->n_ref ++; p = NULL; c = NULL; @@ -424,7 +423,8 @@ void lldp_neighbour_port_remove_and_free(lldp_neighbour_port *p) { lldp_neighbour_port_free(p); /* Drop the Chassis if no port is attached */ - if (REFCNT_DEC(c->n_ref) <= 1) { + c->n_ref --; + if (c->n_ref <= 1) { hashmap_remove(c->neighbour_mib, &c->chassis_id); lldp_chassis_free(c); } @@ -486,7 +486,7 @@ void lldp_chassis_free(lldp_chassis *c) { if (!c) return; - if (REFCNT_GET(c->n_ref) > 1) + if (c->n_ref > 1) return; free(c->chassis_id.data); @@ -513,7 +513,7 @@ int lldp_chassis_new(tlv_packet *tlv, if (!c) return -ENOMEM; - c->n_ref = REFCNT_INIT; + c->n_ref = 1; c->chassis_id.type = type; c->chassis_id.length = length; diff --git a/src/libsystemd-network/lldp-internal.h b/src/libsystemd-network/lldp-internal.h index 8e09ee8f3..f4eadbb87 100644 --- a/src/libsystemd-network/lldp-internal.h +++ b/src/libsystemd-network/lldp-internal.h @@ -24,7 +24,6 @@ #include "log.h" #include "list.h" -#include "refcnt.h" #include "lldp-tlv.h" #include "prioq.h" @@ -63,7 +62,7 @@ struct lldp_chassis_id { }; struct lldp_chassis { - RefCount n_ref; + unsigned n_ref; lldp_chassis_id chassis_id; diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index d8357c687..26bd4088d 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -525,7 +525,7 @@ int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t return 0; } -int serialize_dhcp_option(FILE *f, const char *key, const uint8_t *data, size_t size) { +int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size) { _cleanup_free_ char *hex_buf = NULL; assert(f); @@ -541,7 +541,7 @@ int serialize_dhcp_option(FILE *f, const char *key, const uint8_t *data, size_t return 0; } -int deserialize_dhcp_option(uint8_t **data, size_t *data_len, const char *string) { +int deserialize_dhcp_option(void **data, size_t *data_len, const char *string) { assert(data); assert(data_len); assert(string); diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h index dca82646c..d5d4ef42f 100644 --- a/src/libsystemd-network/network-internal.h +++ b/src/libsystemd-network/network-internal.h @@ -77,5 +77,5 @@ struct sd_dhcp_route; void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *routes, size_t size); int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t *ret_allocated, const char *string); -int serialize_dhcp_option(FILE *f, const char *key, const uint8_t *data, size_t size); -int deserialize_dhcp_option(uint8_t **data, size_t *data_len, const char *string); +int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size); +int deserialize_dhcp_option(void **data, size_t *data_len, const char *string); diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 46104afde..c12768cf0 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -27,7 +27,6 @@ #include #include "util.h" -#include "refcnt.h" #include "random-util.h" #include "async.h" @@ -41,7 +40,7 @@ #define MAX_MAC_ADDR_LEN CONST_MAX(INFINIBAND_ALEN, ETH_ALEN) struct sd_dhcp_client { - RefCount n_ref; + unsigned n_ref; DHCPState state; sd_event *event; @@ -106,7 +105,6 @@ static const uint8_t default_req_opts[] = { DHCP_OPTION_HOST_NAME, DHCP_OPTION_DOMAIN_NAME, DHCP_OPTION_DOMAIN_NAME_SERVER, - DHCP_OPTION_NTP_SERVER, }; static int client_receive_message_raw(sd_event_source *s, int fd, @@ -377,8 +375,7 @@ static int client_initialize(sd_dhcp_client *client) { client->state = DHCP_STATE_INIT; client->xid = 0; - if (client->lease) - client->lease = sd_dhcp_lease_unref(client->lease); + client->lease = sd_dhcp_lease_unref(client->lease); return 0; } @@ -1055,18 +1052,16 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, } lease->next_server = offer->siaddr; - lease->address = offer->yiaddr; - if (lease->address == INADDR_ANY || - lease->server_address == INADDR_ANY || + if (lease->address == 0 || + lease->server_address == 0 || lease->lifetime == 0) { - log_dhcp_client(client, "received lease lacks address, server " - "address or lease lifetime, ignoring"); + log_dhcp_client(client, "received lease lacks address, server address or lease lifetime, ignoring"); return -ENOMSG; } - if (lease->subnet_mask == INADDR_ANY) { + if (!lease->have_subnet_mask) { r = dhcp_lease_set_default_subnet_mask(lease); if (r < 0) { log_dhcp_client(client, "received lease lacks subnet " @@ -1168,13 +1163,17 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, return r; } -static uint64_t client_compute_timeout(sd_dhcp_client *client, - uint32_t lifetime, double factor) { +static uint64_t client_compute_timeout(sd_dhcp_client *client, uint32_t lifetime, double factor) { assert(client); assert(client->request_sent); - assert(lifetime); + assert(lifetime > 0); - return client->request_sent + ((lifetime - 3) * USEC_PER_SEC * factor) + + if (lifetime > 3) + lifetime -= 3; + else + lifetime = 0; + + return client->request_sent + (lifetime * USEC_PER_SEC * factor) + + (random_u32() & 0x1fffff); } @@ -1206,7 +1205,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { /* convert the various timeouts from relative (secs) to absolute (usecs) */ lifetime_timeout = client_compute_timeout(client, client->lease->lifetime, 1); - if (client->lease->t1 && client->lease->t2) { + if (client->lease->t1 > 0 && client->lease->t2 > 0) { /* both T1 and T2 are given */ if (client->lease->t1 < client->lease->t2 && client->lease->t2 < client->lease->lifetime) { @@ -1220,7 +1219,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5); client->lease->t1 = client->lease->lifetime / 2; } - } else if (client->lease->t2 && client->lease->t2 < client->lease->lifetime) { + } else if (client->lease->t2 > 0 && client->lease->t2 < client->lease->lifetime) { /* only T2 is given, and it is valid */ t2_timeout = client_compute_timeout(client, client->lease->t2, 1); t1_timeout = client_compute_timeout(client, client->lease->lifetime, 0.5); @@ -1230,7 +1229,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0); client->lease->t2 = (client->lease->lifetime * 7) / 8; } - } else if (client->lease->t1 && client->lease->t1 < client->lease->lifetime) { + } else if (client->lease->t1 > 0 && client->lease->t1 < client->lease->lifetime) { /* only T1 is given, and it is valid */ t1_timeout = client_compute_timeout(client, client->lease->t1, 1); t2_timeout = client_compute_timeout(client, client->lease->lifetime, 7.0 / 8.0); @@ -1676,30 +1675,41 @@ sd_event *sd_dhcp_client_get_event(sd_dhcp_client *client) { } sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client) { - if (client) - assert_se(REFCNT_INC(client->n_ref) >= 2); + + if (!client) + return NULL; + + assert(client->n_ref >= 1); + client->n_ref++; return client; } sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) { - if (client && REFCNT_DEC(client->n_ref) == 0) { - log_dhcp_client(client, "FREE"); - client_initialize(client); + if (!client) + return NULL; - client->receive_message = - sd_event_source_unref(client->receive_message); + assert(client->n_ref >= 1); + client->n_ref--; - sd_dhcp_client_detach_event(client); + if (client->n_ref > 0) + return NULL; - sd_dhcp_lease_unref(client->lease); + log_dhcp_client(client, "FREE"); - free(client->req_opts); - free(client->hostname); - free(client->vendor_class_identifier); - free(client); - } + client_initialize(client); + + client->receive_message = sd_event_source_unref(client->receive_message); + + sd_dhcp_client_detach_event(client); + + sd_dhcp_lease_unref(client->lease); + + free(client->req_opts); + free(client->hostname); + free(client->vendor_class_identifier); + free(client); return NULL; } @@ -1713,7 +1723,7 @@ int sd_dhcp_client_new(sd_dhcp_client **ret) { if (!client) return -ENOMEM; - client->n_ref = REFCNT_INIT; + client->n_ref = 1; client->state = DHCP_STATE_INIT; client->index = -1; client->fd = -1; diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index f5b9e2258..aa0784669 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -28,18 +28,31 @@ #include "unaligned.h" #include "in-addr-util.h" #include "hostname-util.h" +#include "dns-domain.h" +#include "network-internal.h" #include "dhcp-protocol.h" #include "dhcp-lease-internal.h" #include "sd-dhcp-lease.h" -#include "network-internal.h" -#include "dns-domain.h" int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) { assert_return(lease, -EINVAL); assert_return(addr, -EINVAL); + if (lease->address == 0) + return -ENODATA; + addr->s_addr = lease->address; + return 0; +} +int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr) { + assert_return(lease, -EINVAL); + assert_return(addr, -EINVAL); + + if (!lease->have_broadcast) + return -ENODATA; + + addr->s_addr = lease->broadcast; return 0; } @@ -47,8 +60,32 @@ int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) { assert_return(lease, -EINVAL); assert_return(lifetime, -EINVAL); + if (lease->lifetime <= 0) + return -ENODATA; + *lifetime = lease->lifetime; + return 0; +} + +int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1) { + assert_return(lease, -EINVAL); + assert_return(t1, -EINVAL); + + if (lease->t1 <= 0) + return -ENODATA; + *t1 = lease->t1; + return 0; +} + +int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2) { + assert_return(lease, -EINVAL); + assert_return(t2, -EINVAL); + + if (lease->t2 <= 0) + return -ENODATA; + + *t2 = lease->t2; return 0; } @@ -56,11 +93,10 @@ int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) { assert_return(lease, -EINVAL); assert_return(mtu, -EINVAL); - if (lease->mtu) - *mtu = lease->mtu; - else - return -ENOENT; + if (lease->mtu <= 0) + return -ENODATA; + *mtu = lease->mtu; return 0; } @@ -68,37 +104,32 @@ int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) { assert_return(lease, -EINVAL); assert_return(addr, -EINVAL); - if (lease->dns_size) { - *addr = lease->dns; - return lease->dns_size; - } else - return -ENOENT; + if (lease->dns_size <= 0) + return -ENODATA; - return 0; + *addr = lease->dns; + return (int) lease->dns_size; } int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) { assert_return(lease, -EINVAL); assert_return(addr, -EINVAL); - if (lease->ntp_size) { - *addr = lease->ntp; - return lease->ntp_size; - } else - return -ENOENT; + if (lease->ntp_size <= 0) + return -ENODATA; - return 0; + *addr = lease->ntp; + return (int) lease->ntp_size; } int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) { assert_return(lease, -EINVAL); assert_return(domainname, -EINVAL); - if (lease->domainname) - *domainname = lease->domainname; - else - return -ENOENT; + if (!lease->domainname) + return -ENODATA; + *domainname = lease->domainname; return 0; } @@ -106,11 +137,10 @@ int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname) { assert_return(lease, -EINVAL); assert_return(hostname, -EINVAL); - if (lease->hostname) - *hostname = lease->hostname; - else - return -ENOENT; + if (!lease->hostname) + return -ENODATA; + *hostname = lease->hostname; return 0; } @@ -118,11 +148,10 @@ int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) { assert_return(lease, -EINVAL); assert_return(root_path, -EINVAL); - if (lease->root_path) - *root_path = lease->root_path; - else - return -ENOENT; + if (!lease->root_path) + return -ENODATA; + *root_path = lease->root_path; return 0; } @@ -130,11 +159,10 @@ int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) { assert_return(lease, -EINVAL); assert_return(addr, -EINVAL); - if (lease->router != INADDR_ANY) - addr->s_addr = lease->router; - else - return -ENOENT; + if (lease->router == 0) + return -ENODATA; + addr->s_addr = lease->router; return 0; } @@ -142,8 +170,10 @@ int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) { assert_return(lease, -EINVAL); assert_return(addr, -EINVAL); - addr->s_addr = lease->subnet_mask; + if (!lease->have_subnet_mask) + return -ENODATA; + addr->s_addr = lease->subnet_mask; return 0; } @@ -151,8 +181,10 @@ int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *ad assert_return(lease, -EINVAL); assert_return(addr, -EINVAL); - addr->s_addr = lease->server_address; + if (lease->server_address == 0) + return -ENODATA; + addr->s_addr = lease->server_address; return 0; } @@ -160,136 +192,134 @@ int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) { assert_return(lease, -EINVAL); assert_return(addr, -EINVAL); - addr->s_addr = lease->next_server; + if (lease->next_server == 0) + return -ENODATA; + addr->s_addr = lease->next_server; return 0; } int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes) { - assert_return(lease, -EINVAL); assert_return(routes, -EINVAL); - if (lease->static_route_size) { - *routes = lease->static_route; - return lease->static_route_size; - } else - return -ENOENT; + if (lease->static_route_size <= 0) + return -ENODATA; - return 0; + *routes = lease->static_route; + return (int) lease->static_route_size; } -int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const uint8_t **data, - size_t *data_len) { +int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) { assert_return(lease, -EINVAL); assert_return(data, -EINVAL); assert_return(data_len, -EINVAL); - if (!lease->vendor_specific) - return -ENOENT; + if (lease->vendor_specific_len <= 0) + return -ENODATA; *data = lease->vendor_specific; *data_len = lease->vendor_specific_len; - return 0; } sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease) { - if (lease) - assert_se(REFCNT_INC(lease->n_ref) >= 2); + + if (!lease) + return NULL; + + assert(lease->n_ref >= 1); + lease->n_ref++; return lease; } sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) { - if (lease && REFCNT_DEC(lease->n_ref) == 0) { - while (lease->private_options) { - struct sd_dhcp_raw_option *option = lease->private_options; - LIST_REMOVE(options, lease->private_options, option); + if (!lease) + return NULL; - free(option->data); - free(option); - } - free(lease->hostname); - free(lease->domainname); - free(lease->dns); - free(lease->ntp); - free(lease->static_route); - free(lease->client_id); - free(lease->vendor_specific); - free(lease); - } + assert(lease->n_ref >= 1); + lease->n_ref--; - return NULL; -} + if (lease->n_ref > 0) + return NULL; -static void lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) { - assert(option); - assert(ret); + while (lease->private_options) { + struct sd_dhcp_raw_option *option = lease->private_options; - if (len == 4) { - *ret = unaligned_read_be32((be32_t*) option); + LIST_REMOVE(options, lease->private_options, option); - if (*ret < min) - *ret = min; + free(option->data); + free(option); } -} -static void lease_parse_s32(const uint8_t *option, size_t len, int32_t *ret) { - lease_parse_u32(option, len, (uint32_t *)ret, 0); + free(lease->hostname); + free(lease->domainname); + free(lease->dns); + free(lease->ntp); + free(lease->static_route); + free(lease->client_id); + free(lease->vendor_specific); + free(lease); + + return NULL; } -static void lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) { +static int lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) { assert(option); assert(ret); - if (len == 2) { - *ret = unaligned_read_be16((be16_t*) option); + if (len != 4) + return -EINVAL; - if (*ret < min) - *ret = min; - } + *ret = unaligned_read_be32((be32_t*) option); + if (*ret < min) + *ret = min; + + return 0; } -static void lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) { +static int lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) { assert(option); assert(ret); - if (len == 4) - memcpy(ret, option, 4); -} + if (len != 2) + return -EINVAL; -static void lease_parse_bool(const uint8_t *option, size_t len, bool *ret) { - assert(option); - assert(ret); + *ret = unaligned_read_be16((be16_t*) option); + if (*ret < min) + *ret = min; - if (len == 1) - *ret = !!(*option); + return 0; } -static void lease_parse_u8(const uint8_t *option, size_t len, uint8_t *ret, uint8_t min) { +static int lease_parse_be32(const uint8_t *option, size_t len, be32_t *ret) { assert(option); assert(ret); - if (len == 1) { - *ret = *option; + if (len != 4) + return -EINVAL; - if (*ret < min) - *ret = min; - } + memcpy(ret, option, 4); + return 0; } static int lease_parse_string(const uint8_t *option, size_t len, char **ret) { assert(option); assert(ret); - if (len >= 1) { + if (len <= 0) + *ret = mfree(*ret); + else { char *string; + if (memchr(option, 0, len)) + return -EINVAL; + string = strndup((const char *)option, len); if (!string) - return -errno; + return -ENOMEM; free(*ret); *ret = string; @@ -298,48 +328,47 @@ static int lease_parse_string(const uint8_t *option, size_t len, char **ret) { return 0; } -static int lease_parse_in_addrs_aux(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size, size_t mult) { +static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) { assert(option); assert(ret); - assert(ret_size); + assert(n_ret); - if (len && !(len % (4 * mult))) { - size_t size; + if (len <= 0) { + *ret = mfree(*ret); + *n_ret = 0; + } else { + size_t n_addresses; struct in_addr *addresses; - size = len / 4; + if (len % 4 != 0) + return -EINVAL; + + n_addresses = len / 4; - addresses = newdup(struct in_addr, option, size); + addresses = newdup(struct in_addr, option, n_addresses); if (!addresses) return -ENOMEM; free(*ret); *ret = addresses; - *ret_size = size; + *n_ret = n_addresses; } return 0; } -static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) { - return lease_parse_in_addrs_aux(option, len, ret, ret_size, 1); -} - -static int lease_parse_in_addrs_pairs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *ret_size) { - return lease_parse_in_addrs_aux(option, len, ret, ret_size, 2); -} - -static int lease_parse_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes, - size_t *routes_size, size_t *routes_allocated) { +static int lease_parse_routes( + const uint8_t *option, size_t len, + struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) { struct in_addr addr; - assert(option); + assert(option || len <= 0); assert(routes); assert(routes_size); assert(routes_allocated); - if (!len) + if (len <= 0) return 0; if (len % 8 != 0) @@ -354,15 +383,15 @@ static int lease_parse_routes(const uint8_t *option, size_t len, struct sd_dhcp_ r = in_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen); if (r < 0) { - log_error("Failed to determine destination prefix length from class based IP, ignoring"); + log_debug("Failed to determine destination prefix length from class based IP, ignoring"); continue; } - lease_parse_be32(option, 4, &addr.s_addr); + assert_se(lease_parse_be32(option, 4, &addr.s_addr) >= 0); route->dst_addr = inet_makeaddr(inet_netof(addr), 0); option += 4; - lease_parse_be32(option, 4, &route->gw_addr.s_addr); + assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0); option += 4; len -= 8; @@ -373,14 +402,18 @@ static int lease_parse_routes(const uint8_t *option, size_t len, struct sd_dhcp_ } /* parses RFC3442 Classless Static Route Option */ -static int lease_parse_classless_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes, - size_t *routes_size, size_t *routes_allocated) { +static int lease_parse_classless_routes( + const uint8_t *option, size_t len, + struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) { - assert(option); + assert(option || len <= 0); assert(routes); assert(routes_size); assert(routes_allocated); + if (len <= 0) + return 0; + /* option format: (subnet-mask-width significant-subnet-octets gateway-ip)* */ while (len > 0) { @@ -388,7 +421,7 @@ static int lease_parse_classless_routes(const uint8_t *option, size_t len, struc struct sd_dhcp_route *route; if (!GREEDY_REALLOC(*routes, *routes_allocated, *routes_size + 1)) - return -ENOMEM; + return -ENOMEM; route = *routes + *routes_size; @@ -419,223 +452,222 @@ static int lease_parse_classless_routes(const uint8_t *option, size_t len, struc return 0; } -int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option, - void *user_data) { - sd_dhcp_lease *lease = user_data; +int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata) { + sd_dhcp_lease *lease = userdata; int r; assert(lease); switch(code) { - case DHCP_OPTION_TIME_OFFSET: - lease_parse_s32(option, len, &lease->time_offset); - - break; - - case DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT: - lease_parse_u32(option, len, &lease->mtu_aging_timeout, 0); - - break; - case DHCP_OPTION_IP_ADDRESS_LEASE_TIME: - lease_parse_u32(option, len, &lease->lifetime, 1); + r = lease_parse_u32(option, len, &lease->lifetime, 1); + if (r < 0) + log_debug_errno(r, "Failed to parse lease time, ignoring: %m"); break; case DHCP_OPTION_SERVER_IDENTIFIER: - lease_parse_be32(option, len, &lease->server_address); + r = lease_parse_be32(option, len, &lease->server_address); + if (r < 0) + log_debug_errno(r, "Failed to parse server identifier, ignoring: %m"); break; case DHCP_OPTION_SUBNET_MASK: - lease_parse_be32(option, len, &lease->subnet_mask); - + r = lease_parse_be32(option, len, &lease->subnet_mask); + if (r < 0) + log_debug_errno(r, "Failed to parse subnet mask, ignoring: %m"); + else + lease->have_subnet_mask = true; break; case DHCP_OPTION_BROADCAST: - lease_parse_be32(option, len, &lease->broadcast); - + r = lease_parse_be32(option, len, &lease->broadcast); + if (r < 0) + log_debug_errno(r, "Failed to parse broadcast address, ignoring: %m"); + else + lease->have_broadcast = true; break; case DHCP_OPTION_ROUTER: - if(len >= 4) - lease_parse_be32(option, 4, &lease->router); - + if (len >= 4) { + r = lease_parse_be32(option, 4, &lease->router); + if (r < 0) + log_debug_errno(r, "Failed to parse router address, ignoring: %m"); + } break; case DHCP_OPTION_DOMAIN_NAME_SERVER: r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size); if (r < 0) - return r; - + log_debug_errno(r, "Failed to parse DNS server, ignoring: %m"); break; case DHCP_OPTION_NTP_SERVER: r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size); if (r < 0) - return r; - - break; - - case DHCP_OPTION_POLICY_FILTER: - r = lease_parse_in_addrs_pairs(option, len, &lease->policy_filter, &lease->policy_filter_size); - if (r < 0) - return r; - + log_debug_errno(r, "Failed to parse NTP server, ignoring: %m"); break; case DHCP_OPTION_STATIC_ROUTE: - r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, - &lease->static_route_allocated); + r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, &lease->static_route_allocated); if (r < 0) - return r; - + log_debug_errno(r, "Failed to parse static routes, ignoring: %m"); break; case DHCP_OPTION_INTERFACE_MTU: - lease_parse_u16(option, len, &lease->mtu, 68); - - break; - - case DHCP_OPTION_INTERFACE_MDR: - lease_parse_u16(option, len, &lease->mdr, 576); - - break; - - case DHCP_OPTION_INTERFACE_TTL: - lease_parse_u8(option, len, &lease->ttl, 1); - - break; - - case DHCP_OPTION_BOOT_FILE_SIZE: - lease_parse_u16(option, len, &lease->boot_file_size, 0); - + r = lease_parse_u16(option, len, &lease->mtu, 68); + if (r < 0) + log_debug_errno(r, "Failed to parse MTU, ignoring: %m"); break; - case DHCP_OPTION_DOMAIN_NAME: - { - _cleanup_free_ char *domainname = NULL; - char *e; + case DHCP_OPTION_DOMAIN_NAME: { + _cleanup_free_ char *domainname = NULL, *normalized = NULL; r = lease_parse_string(option, len, &domainname); - if (r < 0) - return r; - - /* Chop off trailing dot of domain name that some DHCP - * servers send us back. Internally we want to store - * host names without trailing dots and - * host_name_is_valid() doesn't accept them. */ - e = endswith(domainname, "."); - if (e) - *e = 0; + if (r < 0) { + log_debug_errno(r, "Failed to parse domain name, ignoring: %m"); + return 0; + } - if (is_localhost(domainname)) - break; + r = dns_name_normalize(domainname, &normalized); + if (r < 0) { + log_debug_errno(r, "Failed to normalize domain name '%s': %m", domainname); + return 0; + } - r = dns_name_is_valid(domainname); - if (r <= 0) { - if (r < 0) - log_error_errno(r, "Failed to validate domain name: %s: %m", domainname); - if (r == 0) - log_warning("Domain name is not valid, ignoring: %s", domainname); + if (is_localhost(normalized)) { + log_debug_errno(r, "Detected 'localhost' as suggested domain name, ignoring."); break; } free(lease->domainname); - lease->domainname = domainname; - domainname = NULL; + lease->domainname = normalized; + normalized = NULL; break; } - case DHCP_OPTION_HOST_NAME: - { - _cleanup_free_ char *hostname = NULL; - char *e; + + case DHCP_OPTION_HOST_NAME: { + _cleanup_free_ char *hostname = NULL, *normalized = NULL; r = lease_parse_string(option, len, &hostname); - if (r < 0) - return r; + if (r < 0) { + log_debug_errno(r, "Failed to parse host name, ignoring: %m"); + return 0; + } - e = endswith(hostname, "."); - if (e) - *e = 0; + r = dns_name_normalize(hostname, &normalized); + if (r < 0) { + log_debug_errno(r, "Failed to normalize host name '%s', ignoring: %m", hostname); + return 0; + } - if (!hostname_is_valid(hostname, false) || is_localhost(hostname)) - break; + if (is_localhost(normalized)) { + log_debug_errno(r, "Detected 'localhost' as suggested host name, ignoring."); + return 0; + } - free_and_replace(&lease->hostname, hostname); - hostname = NULL; + free(lease->hostname); + lease->hostname = normalized; + normalized = NULL; break; } + case DHCP_OPTION_ROOT_PATH: r = lease_parse_string(option, len, &lease->root_path); if (r < 0) - return r; - + log_debug_errno(r, "Failed to parse root path, ignoring: %m"); break; case DHCP_OPTION_RENEWAL_T1_TIME: - lease_parse_u32(option, len, &lease->t1, 1); - + r = lease_parse_u32(option, len, &lease->t1, 1); + if (r < 0) + log_debug_errno(r, "Failed to parse T1 time, ignoring: %m"); break; case DHCP_OPTION_REBINDING_T2_TIME: - lease_parse_u32(option, len, &lease->t2, 1); - + r = lease_parse_u32(option, len, &lease->t2, 1); + if (r < 0) + log_debug_errno(r, "Failed to parse T2 time, ignoring: %m"); break; - case DHCP_OPTION_ENABLE_IP_FORWARDING: - lease_parse_bool(option, len, &lease->ip_forward); - + case DHCP_OPTION_CLASSLESS_STATIC_ROUTE: + r = lease_parse_classless_routes( + option, len, + &lease->static_route, + &lease->static_route_size, + &lease->static_route_allocated); + if (r < 0) + log_debug_errno(r, "Failed to parse classless routes, ignoring: %m"); break; - case DHCP_OPTION_ENABLE_IP_FORWARDING_NL: - lease_parse_bool(option, len, &lease->ip_forward_non_local); + case DHCP_OPTION_NEW_TZDB_TIMEZONE: { + _cleanup_free_ char *tz = NULL; - break; + r = lease_parse_string(option, len, &tz); + if (r < 0) { + log_debug_errno(r, "Failed to parse timezone option, ignoring: %m"); + return 0; + } - case DHCP_OPTION_CLASSLESS_STATIC_ROUTE: - r = lease_parse_classless_routes(option, len, &lease->static_route, &lease->static_route_size, - &lease->static_route_allocated); - if (r < 0) - return r; + if (!timezone_is_valid(tz)) { + log_debug_errno(r, "Timezone is not valid, ignoring: %m"); + return 0; + } + + free(lease->timezone); + lease->timezone = tz; + tz = NULL; break; + } case DHCP_OPTION_VENDOR_SPECIFIC: - if (len >= 1) { - free(lease->vendor_specific); - lease->vendor_specific = memdup(option, len); - if (!lease->vendor_specific) + + if (len <= 0) + lease->vendor_specific = mfree(lease->vendor_specific); + else { + void *p; + + p = memdup(option, len); + if (!p) return -ENOMEM; - lease->vendor_specific_len = len; - } - break; + free(lease->vendor_specific); + lease->vendor_specific = p; + } - default: - if (code < DHCP_OPTION_PRIVATE_BASE || code > DHCP_OPTION_PRIVATE_LAST) - break; + lease->vendor_specific_len = len; + break; + case DHCP_OPTION_PRIVATE_BASE ... DHCP_OPTION_PRIVATE_LAST: r = dhcp_lease_insert_private_option(lease, code, option, len); if (r < 0) return r; + + break; + + default: + log_debug("Ignoring option DHCP option %i while parsing.", code); + break; } return 0; } -int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, - const uint8_t *data, uint8_t len) { +int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len) { struct sd_dhcp_raw_option *cur, *option; + assert(lease); + LIST_FOREACH(options, cur, lease->private_options) { if (tag < cur->tag) break; - else if (tag == cur->tag) { - log_error("Ignoring duplicate option, tagged %d.", tag); + if (tag == cur->tag) { + log_debug("Ignoring duplicate option, tagged %i.", tag); return 0; } } @@ -653,7 +685,6 @@ int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, } LIST_INSERT_BEFORE(options, lease->private_options, cur, option); - return 0; } @@ -665,24 +696,24 @@ int dhcp_lease_new(sd_dhcp_lease **ret) { return -ENOMEM; lease->router = INADDR_ANY; - lease->n_ref = REFCNT_INIT; - LIST_HEAD_INIT(lease->private_options); + lease->n_ref = 1; *ret = lease; return 0; } -int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { +int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { _cleanup_free_ char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; struct sd_dhcp_raw_option *option; struct in_addr address; const struct in_addr *addresses; - const uint8_t *client_id, *data; + const void *client_id, *data; size_t client_id_len, data_len; const char *string; uint16_t mtu; struct sd_dhcp_route *routes; + uint32_t t1, t2, lifetime; int r; assert(lease); @@ -694,19 +725,16 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { fchmod(fileno(f), 0644); - r = sd_dhcp_lease_get_address(lease, &address); - if (r < 0) - goto fail; - fprintf(f, - "# This is private data. Do not parse.\n" - "ADDRESS=%s\n", inet_ntoa(address)); + "# This is private data. Do not parse.\n"); - r = sd_dhcp_lease_get_netmask(lease, &address); - if (r < 0) - goto fail; + r = sd_dhcp_lease_get_address(lease, &address); + if (r >= 0) + fprintf(f, "ADDRESS=%s\n", inet_ntoa(address)); - fprintf(f, "NETMASK=%s\n", inet_ntoa(address)); + r = sd_dhcp_lease_get_netmask(lease, &address); + if (r >= 0) + fprintf(f, "NETMASK=%s\n", inet_ntoa(address)); r = sd_dhcp_lease_get_router(lease, &address); if (r >= 0) @@ -714,28 +742,45 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { r = sd_dhcp_lease_get_server_identifier(lease, &address); if (r >= 0) - fprintf(f, "SERVER_ADDRESS=%s\n", - inet_ntoa(address)); + fprintf(f, "SERVER_ADDRESS=%s\n", inet_ntoa(address)); r = sd_dhcp_lease_get_next_server(lease, &address); if (r >= 0) fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address)); + r = sd_dhcp_lease_get_broadcast(lease, &address); + if (r >= 0) + fprintf(f, "BROADCAST=%s\n", inet_ntoa(address)); + r = sd_dhcp_lease_get_mtu(lease, &mtu); if (r >= 0) fprintf(f, "MTU=%" PRIu16 "\n", mtu); - fputs("DNS=", f); - r = sd_dhcp_lease_get_dns(lease, &addresses); + r = sd_dhcp_lease_get_t1(lease, &t1); + if (r >= 0) + fprintf(f, "T1=%" PRIu32 "\n", t1); + + r = sd_dhcp_lease_get_t2(lease, &t2); + if (r >= 0) + fprintf(f, "T2=%" PRIu32 "\n", t2); + + r = sd_dhcp_lease_get_lifetime(lease, &lifetime); if (r >= 0) + fprintf(f, "LIFETIME=%" PRIu32 "\n", lifetime); + + r = sd_dhcp_lease_get_dns(lease, &addresses); + if (r > 0) { + fputs("DNS=", f); serialize_in_addrs(f, addresses, r); - fputs("\n", f); + fputs("\n", f); + } - fputs("NTP=", f); r = sd_dhcp_lease_get_ntp(lease, &addresses); - if (r >= 0) + if (r > 0) { + fputs("NTP=", f); serialize_in_addrs(f, addresses, r); - fputs("\n", f); + fputs("\n", f); + } r = sd_dhcp_lease_get_domainname(lease, &string); if (r >= 0) @@ -750,9 +795,13 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { fprintf(f, "ROOT_PATH=%s\n", string); r = sd_dhcp_lease_get_routes(lease, &routes); - if (r >= 0) + if (r > 0) serialize_dhcp_routes(f, "ROUTES", routes, r); + r = sd_dhcp_lease_get_timezone(lease, &string); + if (r >= 0) + fprintf(f, "TIMEZONE=%s\n", string); + r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len); if (r >= 0) { _cleanup_free_ char *client_id_hex; @@ -779,6 +828,7 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { LIST_FOREACH(options, option, lease->private_options) { char key[strlen("OPTION_000")+1]; + snprintf(key, sizeof(key), "OPTION_%"PRIu8, option->tag); r = serialize_dhcp_option(f, key, option->data, option->length); if (r < 0) @@ -803,16 +853,27 @@ fail: return log_error_errno(r, "Failed to save lease data %s: %m", lease_file); } -int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { +int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { + _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL; - _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL, - *server_address = NULL, *next_server = NULL, - *dns = NULL, *ntp = NULL, *mtu = NULL, - *routes = NULL, *client_id_hex = NULL, - *vendor_specific_hex = NULL, - *options[DHCP_OPTION_PRIVATE_LAST - - DHCP_OPTION_PRIVATE_BASE + 1] = { NULL }; - struct in_addr addr; + _cleanup_free_ char + *address = NULL, + *router = NULL, + *netmask = NULL, + *server_address = NULL, + *next_server = NULL, + *broadcast = NULL, + *dns = NULL, + *ntp = NULL, + *mtu = NULL, + *routes = NULL, + *client_id_hex = NULL, + *vendor_specific_hex = NULL, + *lifetime = NULL, + *t1 = NULL, + *t2 = NULL, + *options[DHCP_OPTION_PRIVATE_LAST - DHCP_OPTION_PRIVATE_BASE + 1] = {}; + int r, i; assert(lease_file); @@ -828,6 +889,7 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { "NETMASK", &netmask, "SERVER_IDENTIFIER", &server_address, "NEXT_SERVER", &next_server, + "BROADCAST", &broadcast, "DNS", &dns, "NTP", &ntp, "MTU", &mtu, @@ -836,7 +898,11 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { "ROOT_PATH", &lease->root_path, "ROUTES", &routes, "CLIENTID", &client_id_hex, + "TIMEZONE", &lease->timezone, "VENDOR_SPECIFIC", &vendor_specific_hex, + "LIFETIME", &lifetime, + "T1", &t1, + "T2", &t2, "OPTION_224", &options[0], "OPTION_225", &options[1], "OPTION_226", &options[2], @@ -869,100 +935,123 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { "OPTION_253", &options[29], "OPTION_254", &options[30], NULL); - if (r < 0) { - if (r == -ENOENT) - return 0; - - return log_error_errno(r, "Failed to read %s: %m", lease_file); - } - - r = inet_pton(AF_INET, address, &addr); if (r < 0) return r; - lease->address = addr.s_addr; + if (address) { + r = inet_pton(AF_INET, address, &lease->address); + if (r <= 0) + log_debug_errno(errno, "Failed to parse address %s, ignoring: %m", address); + } if (router) { - r = inet_pton(AF_INET, router, &addr); - if (r < 0) - return r; - - lease->router = addr.s_addr; + r = inet_pton(AF_INET, router, &lease->router); + if (r <= 0) + log_debug_errno(errno, "Failed to parse router %s, ignoring: %m", router); } - r = inet_pton(AF_INET, netmask, &addr); - if (r < 0) - return r; - - lease->subnet_mask = addr.s_addr; + if (netmask) { + r = inet_pton(AF_INET, netmask, &lease->subnet_mask); + if (r <= 0) + log_debug_errno(errno, "Failed to parse netmask %s, ignoring: %m", netmask); + else + lease->have_subnet_mask = true; + } if (server_address) { - r = inet_pton(AF_INET, server_address, &addr); - if (r < 0) - return r; - - lease->server_address = addr.s_addr; + r = inet_pton(AF_INET, server_address, &lease->server_address); + if (r <= 0) + log_debug_errno(errno, "Failed to parse netmask %s, ignoring: %m", server_address); } if (next_server) { - r = inet_pton(AF_INET, next_server, &addr); - if (r < 0) - return r; + r = inet_pton(AF_INET, next_server, &lease->next_server); + if (r <= 0) + log_debug_errno(errno, "Failed to parse next server %s, ignoring: %m", next_server); + } - lease->next_server = addr.s_addr; + if (broadcast) { + r = inet_pton(AF_INET, broadcast, &lease->broadcast); + if (r <= 0) + log_debug_errno(errno, "Failed to parse broadcast address %s, ignoring: %m", broadcast); + else + lease->have_broadcast = true; } if (dns) { r = deserialize_in_addrs(&lease->dns, dns); if (r < 0) - return r; - - lease->dns_size = r; + log_debug_errno(r, "Failed to deserialize DNS servers %s, ignoring: %m", dns); + else + lease->dns_size = r; } if (ntp) { r = deserialize_in_addrs(&lease->ntp, ntp); if (r < 0) - return r; - - lease->ntp_size = r; + log_debug_errno(r, "Failed to deserialize NTP servers %s, ignoring: %m", ntp); + else + lease->ntp_size = r; } if (mtu) { - uint16_t u; - if (sscanf(mtu, "%" SCNu16, &u) > 0) - lease->mtu = u; + r = safe_atou16(mtu, &lease->mtu); + if (r < 0) + log_debug_errno(r, "Failed to parse MTU %s, ignoring: %m", mtu); } if (routes) { - r = deserialize_dhcp_routes(&lease->static_route, &lease->static_route_size, - &lease->static_route_allocated, routes); + r = deserialize_dhcp_routes( + &lease->static_route, + &lease->static_route_size, + &lease->static_route_allocated, + routes); + if (r < 0) + log_debug_errno(r, "Failed to parse DHCP routes %s, ignoring: %m", routes); + } + + if (lifetime) { + r = safe_atou32(lifetime, &lease->lifetime); + if (r < 0) + log_debug_errno(r, "Failed to parse lifetime %s, ignoring: %m", lifetime); + } + + if (t1) { + r = safe_atou32(t1, &lease->t1); + if (r < 0) + log_debug_errno(r, "Failed to parse T1 %s, ignoring: %m", t1); + } + + if (t2) { + r = safe_atou32(t2, &lease->t2); if (r < 0) - return r; + log_debug_errno(r, "Failed to parse T2 %s, ignoring: %m", t2); } if (client_id_hex) { r = deserialize_dhcp_option(&lease->client_id, &lease->client_id_len, client_id_hex); if (r < 0) - return r; + log_debug_errno(r, "Failed to parse client ID %s, ignoring: %m", client_id_hex); } if (vendor_specific_hex) { r = deserialize_dhcp_option(&lease->vendor_specific, &lease->vendor_specific_len, vendor_specific_hex); if (r < 0) - return r; + log_debug_errno(r, "Failed to parse vendor specific data %s, ignoring: %m", vendor_specific_hex); } for (i = 0; i <= DHCP_OPTION_PRIVATE_LAST - DHCP_OPTION_PRIVATE_BASE; i++) { - _cleanup_free_ uint8_t *data = NULL; + _cleanup_free_ void *data = NULL; size_t len; if (!options[i]) continue; r = deserialize_dhcp_option(&data, &len, options[i]); - if (r < 0) - return r; + if (r < 0) { + log_debug_errno(r, "Failed to parse private DHCP option %s, ignoring: %m", options[i]); + continue; + } r = dhcp_lease_insert_private_option(lease, DHCP_OPTION_PRIVATE_BASE + i, data, len); if (r < 0) @@ -976,12 +1065,14 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { } int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) { - struct in_addr address; - struct in_addr mask; + struct in_addr address, mask; int r; assert(lease); + if (lease->address == 0) + return -ENODATA; + address.s_addr = lease->address; /* fall back to the default subnet masks based on address class */ @@ -990,35 +1081,53 @@ int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) { return r; lease->subnet_mask = mask.s_addr; + lease->have_subnet_mask = true; return 0; } -int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const uint8_t **client_id, - size_t *client_id_len) { +int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len) { assert_return(lease, -EINVAL); assert_return(client_id, -EINVAL); assert_return(client_id_len, -EINVAL); + if (!lease->client_id) + return -ENODATA; + *client_id = lease->client_id; *client_id_len = lease->client_id_len; + return 0; } -int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const uint8_t *client_id, - size_t client_id_len) { +int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const void *client_id, size_t client_id_len) { assert_return(lease, -EINVAL); - assert_return((!client_id && !client_id_len) || - (client_id && client_id_len), -EINVAL); + assert_return(client_id || client_id_len <= 0, -EINVAL); + + if (client_id_len <= 0) + lease->client_id = mfree(lease->client_id); + else { + void *p; - free (lease->client_id); - lease->client_id = NULL; - lease->client_id_len = 0; + p = memdup(client_id, client_id_len); + if (!p) + return -ENOMEM; - if (client_id) { - lease->client_id = memdup (client_id, client_id_len); + free(lease->client_id); + lease->client_id = p; lease->client_id_len = client_id_len; } return 0; } + +int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **tz) { + assert_return(lease, -EINVAL); + assert_return(tz, -EINVAL); + + if (!lease->timezone) + return -ENODATA; + + *tz = lease->timezone; + return 0; +} diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index faeab0fd3..1f167485e 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -22,46 +22,73 @@ #include +#include "in-addr-util.h" #include "siphash24.h" #include "sd-dhcp-server.h" #include "dhcp-server-internal.h" #include "dhcp-internal.h" -#define DHCP_DEFAULT_LEASE_TIME 3600 /* one hour */ +#define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR +#define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12) + +/* configures the server's address and subnet, and optionally the pool's size and offset into the subnet + * the whole pool must fit into the subnet, and may not contain the first (any) nor last (broadcast) address + * moreover, the server's own address may be in the pool, and is in that case reserved in order not to + * accidentally hand it out */ +int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size) { + struct in_addr netmask_addr; + be32_t netmask; + uint32_t server_off, broadcast_off, size_max; -int sd_dhcp_server_set_lease_pool(sd_dhcp_server *server, - struct in_addr *address, - size_t size) { assert_return(server, -EINVAL); assert_return(address, -EINVAL); - assert_return(address->s_addr, -EINVAL); - assert_return(size, -EINVAL); - assert_return(server->pool_start == htobe32(INADDR_ANY), -EBUSY); - assert_return(!server->pool_size, -EBUSY); - assert_return(!server->bound_leases, -EBUSY); + assert_return(address->s_addr != INADDR_ANY, -EINVAL); + assert_return(prefixlen <= 32, -ERANGE); + assert_return(server->address == INADDR_ANY, -EBUSY); + + assert_se(in_addr_prefixlen_to_netmask(&netmask_addr, prefixlen)); + netmask = netmask_addr.s_addr; + + server_off = be32toh(address->s_addr & ~netmask); + broadcast_off = be32toh(~netmask); + + /* the server address cannot be the subnet address */ + assert_return(server_off != 0, -ERANGE); + + /* nor the broadcast address */ + assert_return(server_off != broadcast_off, -ERANGE); + + /* 0 offset means we should set a default, we skip the first (subnet) address + and take the next one */ + if (offset == 0) + offset = 1; + + size_max = (broadcast_off + 1) /* the number of addresses in the subnet */ + - offset /* exclude the addresses before the offset */ + - 1; /* exclude the last (broadcast) address */ + + /* The pool must contain at least one address */ + assert_return(size_max >= 1, -ERANGE); + + if (size != 0) + assert_return(size <= size_max, -ERANGE); + else + size = size_max; server->bound_leases = new0(DHCPLease*, size); if (!server->bound_leases) return -ENOMEM; - server->pool_start = address->s_addr; + server->pool_offset = offset; server->pool_size = size; - return 0; -} - -int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address, - unsigned char prefixlen) { - assert_return(server, -EINVAL); - assert_return(address, -EINVAL); - assert_return(address->s_addr, -EINVAL); - assert_return(prefixlen <= 32, -ERANGE); - assert_return(server->address == htobe32(INADDR_ANY), -EBUSY); - assert_return(server->netmask == htobe32(INADDR_ANY), -EBUSY); - server->address = address->s_addr; - server->netmask = htobe32(0xfffffffflu << (32 - prefixlen)); + server->netmask = netmask; + server->subnet = address->s_addr & netmask; + + if (server_off >= offset && server_off - offset < size) + server->bound_leases[server_off - offset] = &server->invalid_lease; return 0; } @@ -73,8 +100,12 @@ bool sd_dhcp_server_is_running(sd_dhcp_server *server) { } sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server) { - if (server) - assert_se(REFCNT_INC(server->n_ref) >= 2); + + if (!server) + return NULL; + + assert(server->n_ref >= 1); + server->n_ref++; return server; } @@ -127,7 +158,10 @@ sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) { if (!server) return NULL; - if (REFCNT_DEC(server->n_ref) > 0) + assert(server->n_ref >= 1); + server->n_ref--; + + if (server->n_ref > 0) return NULL; log_dhcp_server(server, "UNREF"); @@ -136,6 +170,10 @@ sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) { sd_event_unref(server->event); + free(server->timezone); + free(server->dns); + free(server->ntp); + while ((lease = hashmap_steal_first(server->leases_by_client_id))) dhcp_lease_free(lease); hashmap_free(server->leases_by_client_id); @@ -156,13 +194,15 @@ int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex) { if (!server) return -ENOMEM; - server->n_ref = REFCNT_INIT; + server->n_ref = 1; server->fd_raw = -1; server->fd = -1; server->address = htobe32(INADDR_ANY); server->netmask = htobe32(INADDR_ANY); - server->index = ifindex; + server->ifindex = ifindex; server->leases_by_client_id = hashmap_new(&client_id_hash_ops); + server->default_lease_time = DIV_ROUND_UP(DHCP_DEFAULT_LEASE_TIME_USEC, USEC_PER_SEC); + server->max_lease_time = DIV_ROUND_UP(DHCP_MAX_LEASE_TIME_USEC, USEC_PER_SEC); *ret = server; server = NULL; @@ -223,13 +263,12 @@ static int dhcp_server_send_unicast_raw(sd_dhcp_server *server, union sockaddr_union link = { .ll.sll_family = AF_PACKET, .ll.sll_protocol = htons(ETH_P_IP), - .ll.sll_ifindex = server->index, + .ll.sll_ifindex = server->ifindex, .ll.sll_halen = ETH_ALEN, }; - int r; assert(server); - assert(server->index > 0); + assert(server->ifindex > 0); assert(server->address); assert(packet); assert(len > sizeof(DHCPPacket)); @@ -240,11 +279,7 @@ static int dhcp_server_send_unicast_raw(sd_dhcp_server *server, packet->dhcp.yiaddr, DHCP_PORT_CLIENT, len); - r = dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len); - if (r < 0) - return r; - - return 0; + return dhcp_network_send_raw_socket(server->fd_raw, &link, packet, len); } static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination, @@ -290,7 +325,7 @@ static int dhcp_server_send_udp(sd_dhcp_server *server, be32_t destination, pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg); assert(pktinfo); - pktinfo->ipi_ifindex = server->index; + pktinfo->ipi_ifindex = server->ifindex; pktinfo->ipi_spec_dst.s_addr = server->address; r = sendmsg(server->fd, &msg, 0); @@ -474,6 +509,33 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, if (r < 0) return r; + if (server->n_dns > 0) { + r = dhcp_option_append( + &packet->dhcp, req->max_optlen, &offset, 0, + DHCP_OPTION_DOMAIN_NAME_SERVER, + sizeof(struct in_addr) * server->n_dns, server->dns); + if (r < 0) + return r; + } + + if (server->n_ntp > 0) { + r = dhcp_option_append( + &packet->dhcp, req->max_optlen, &offset, 0, + DHCP_OPTION_NTP_SERVER, + sizeof(struct in_addr) * server->n_ntp, server->ntp); + if (r < 0) + return r; + } + + if (server->timezone) { + r = dhcp_option_append( + &packet->dhcp, req->max_optlen, &offset, 0, + DHCP_OPTION_NEW_TZDB_TIMEZONE, + strlen(server->timezone), server->timezone); + if (r < 0) + return r; + } + r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset); if (r < 0) return r; @@ -490,11 +552,7 @@ static int server_send_nak(sd_dhcp_server *server, DHCPRequest *req) { if (r < 0) return r; - r = dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset); - if (r < 0) - return r; - - return 0; + return dhcp_server_send_packet(server, req, packet, DHCP_NAK, offset); } static int server_send_forcerenew(sd_dhcp_server *server, be32_t address, @@ -532,9 +590,8 @@ static int server_send_forcerenew(sd_dhcp_server *server, be32_t address, return 0; } -static int parse_request(uint8_t code, uint8_t len, const uint8_t *option, - void *user_data) { - DHCPRequest *req = user_data; +static int parse_request(uint8_t code, uint8_t len, const void *option, void *userdata) { + DHCPRequest *req = userdata; assert(req); @@ -590,7 +647,7 @@ static void dhcp_request_free(DHCPRequest *req) { DEFINE_TRIVIAL_CLEANUP_FUNC(DHCPRequest*, dhcp_request_free); #define _cleanup_dhcp_request_free_ _cleanup_(dhcp_request_freep) -static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) { +static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMessage *message) { assert(req); assert(message); @@ -599,23 +656,27 @@ static int ensure_sane_request(DHCPRequest *req, DHCPMessage *message) { /* set client id based on MAC address if client did not send an explicit one */ if (!req->client_id.data) { - uint8_t *data; + void *data; - data = new0(uint8_t, ETH_ALEN + 1); + data = malloc0(ETH_ALEN + 1); if (!data) return -ENOMEM; + ((uint8_t*) data)[0] = 0x01; + memcpy((uint8_t*) data + 1, &message->chaddr, ETH_ALEN); + req->client_id.length = ETH_ALEN + 1; req->client_id.data = data; - req->client_id.data[0] = 0x01; - memcpy(&req->client_id.data[1], &message->chaddr, ETH_ALEN); } if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE) req->max_optlen = DHCP_MIN_OPTIONS_SIZE; - if (!req->lifetime) - req->lifetime = DHCP_DEFAULT_LEASE_TIME; + if (req->lifetime <= 0) + req->lifetime = MAX(1ULL, server->default_lease_time); + + if (server->max_lease_time > 0 && req->lifetime > server->max_lease_time) + req->lifetime = server->max_lease_time; return 0; } @@ -626,14 +687,15 @@ static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) { if (!server->pool_size) return -EINVAL; - if (be32toh(requested_ip) < be32toh(server->pool_start) || - be32toh(requested_ip) >= be32toh(server->pool_start) + - + server->pool_size) - return -EINVAL; + if (be32toh(requested_ip) < (be32toh(server->subnet) | server->pool_offset) || + be32toh(requested_ip) >= (be32toh(server->subnet) | (server->pool_offset + server->pool_size))) + return -ERANGE; - return be32toh(requested_ip) - be32toh(server->pool_start); + return be32toh(requested_ip & ~server->netmask) - server->pool_offset; } +#define HASH_KEY SD_ID128_MAKE(0d,1d,fe,bd,f1,24,bd,b3,47,f1,dd,6e,73,21,93,30) + int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, size_t length) { _cleanup_dhcp_request_free_ DHCPRequest *req = NULL; @@ -656,7 +718,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, if (type < 0) return 0; - r = ensure_sane_request(req, message); + r = ensure_sane_request(server, req, message); if (r < 0) /* this only fails on critical errors */ return r; @@ -665,8 +727,8 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, &req->client_id); switch(type) { - case DHCP_DISCOVER: - { + + case DHCP_DISCOVER: { be32_t address = INADDR_ANY; unsigned i; @@ -681,12 +743,20 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, if (existing_lease) address = existing_lease->address; else { + uint32_t next_offer; + + /* even with no persistence of leases, we try to offer the same client + the same IP address. we do this by using the hash of the client id + as the offset into the pool of leases when finding the next free one */ + + next_offer = client_id_hash_func(&req->client_id, HASH_KEY.bytes) % server->pool_size; + for (i = 0; i < server->pool_size; i++) { - if (!server->bound_leases[server->next_offer]) { - address = htobe32(be32toh(server->pool_start) + server->next_offer); + if (!server->bound_leases[next_offer]) { + address = server->subnet | htobe32(server->pool_offset + next_offer); break; } else - server->next_offer = (server->next_offer + 1) % server->pool_size; + next_offer = (next_offer + 1) % server->pool_size; } } @@ -716,9 +786,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, return 1; - break; - case DHCP_REQUEST: - { + case DHCP_REQUEST: { be32_t address; bool init_reboot = false; int pool_offset; @@ -840,6 +908,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, break; } + case DHCP_RELEASE: { int pool_offset; @@ -883,13 +952,12 @@ static int server_receive_message(sd_event_source *s, int fd, .msg_controllen = sizeof(cmsgbuf), }; struct cmsghdr *cmsg; - int buflen = 0, len, r; + int buflen = 0, len; assert(server); - r = ioctl(fd, FIONREAD, &buflen); - if (r < 0) - return r; + if (ioctl(fd, FIONREAD, &buflen) < 0) + return -errno; if (buflen < 0) return -EIO; @@ -914,7 +982,7 @@ static int server_receive_message(sd_event_source *s, int fd, /* TODO figure out if this can be done as a filter on * the socket, like for IPv6 */ - if (server->index != info->ipi_ifindex) + if (server->ifindex != info->ipi_ifindex) return 0; break; @@ -979,7 +1047,7 @@ int sd_dhcp_server_forcerenew(sd_dhcp_server *server) { for (i = 0; i < server->pool_size; i++) { DHCPLease *lease = server->bound_leases[i]; - if (!lease) + if (!lease || lease == &server->invalid_lease) continue; r = server_send_forcerenew(server, lease->address, @@ -993,3 +1061,91 @@ int sd_dhcp_server_forcerenew(sd_dhcp_server *server) { return r; } + +int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *tz) { + int r; + + assert_return(server, -EINVAL); + assert_return(timezone_is_valid(tz), -EINVAL); + + if (streq_ptr(tz, server->timezone)) + return 0; + + r = free_and_strdup(&server->timezone, tz); + if (r < 0) + return r; + + return 1; +} + +int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t) { + assert_return(server, -EINVAL); + + if (t == server->max_lease_time) + return 0; + + server->max_lease_time = t; + return 1; +} + +int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t) { + assert_return(server, -EINVAL); + + if (t == server->default_lease_time) + return 0; + + server->default_lease_time = t; + return 1; +} + +int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr dns[], unsigned n) { + assert_return(server, -EINVAL); + assert_return(dns || n <= 0, -EINVAL); + + if (server->n_dns == n && + memcmp(server->dns, dns, sizeof(struct in_addr) * n) == 0) + return 0; + + if (n <= 0) { + server->dns = mfree(server->dns); + server->n_dns = 0; + } else { + struct in_addr *c; + + c = newdup(struct in_addr, dns, n); + if (!c) + return -ENOMEM; + + free(server->dns); + server->dns = c; + server->n_dns = n; + } + + return 1; +} + +int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n) { + assert_return(server, -EINVAL); + assert_return(ntp || n <= 0, -EINVAL); + + if (server->n_ntp == n && + memcmp(server->ntp, ntp, sizeof(struct in_addr) * n) == 0) + return 0; + + if (n <= 0) { + server->ntp = mfree(server->ntp); + server->n_ntp = 0; + } else { + struct in_addr *c; + + c = newdup(struct in_addr, ntp, n); + if (!c) + return -ENOMEM; + + free(server->ntp); + server->ntp = c; + server->n_ntp = n; + } + + return 1; +} diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 10c365402..5489c7786 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -27,7 +27,6 @@ #include "udev.h" #include "udev-util.h" #include "util.h" -#include "refcnt.h" #include "random-util.h" #include "network-internal.h" @@ -40,7 +39,7 @@ #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN struct sd_dhcp6_client { - RefCount n_ref; + unsigned n_ref; enum DHCP6State state; sd_event *event; @@ -113,9 +112,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp6_client*, sd_dhcp6_client_unref); static int client_start(sd_dhcp6_client *client, enum DHCP6State state); -int sd_dhcp6_client_set_callback(sd_dhcp6_client *client, - sd_dhcp6_client_cb_t cb, void *userdata) -{ +int sd_dhcp6_client_set_callback(sd_dhcp6_client *client, sd_dhcp6_client_cb_t cb, void *userdata) { assert_return(client, -EINVAL); client->cb = cb; @@ -124,8 +121,7 @@ int sd_dhcp6_client_set_callback(sd_dhcp6_client *client, return 0; } -int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index) -{ +int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index) { assert_return(client, -EINVAL); assert_return(interface_index >= -1, -EINVAL); @@ -134,9 +130,11 @@ int sd_dhcp6_client_set_index(sd_dhcp6_client *client, int interface_index) return 0; } -int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr, - size_t addr_len, uint16_t arp_type) -{ +int sd_dhcp6_client_set_mac( + sd_dhcp6_client *client, + const uint8_t *addr, size_t addr_len, + uint16_t arp_type) { + assert_return(client, -EINVAL); assert_return(addr, -EINVAL); assert_return(addr_len > 0 && addr_len <= MAX_MAC_ADDR_LEN, -EINVAL); @@ -160,16 +158,17 @@ int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr, return 0; } -static int client_ensure_duid(sd_dhcp6_client *client) -{ +static int client_ensure_duid(sd_dhcp6_client *client) { if (client->duid_len != 0) return 0; + return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); } -int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid, - size_t duid_len) -{ +int sd_dhcp6_client_set_duid( + sd_dhcp6_client *client, + uint16_t type, + uint8_t *duid, size_t duid_len) { assert_return(client, -EINVAL); assert_return(duid, -EINVAL); assert_return(duid_len > 0 && duid_len <= MAX_DUID_LEN, -EINVAL); @@ -203,8 +202,7 @@ int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *du return 0; } -int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, - bool enabled) { +int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, bool enabled) { assert_return(client, -EINVAL); client->information_request = enabled; @@ -212,8 +210,7 @@ int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, return 0; } -int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, - bool *enabled) { +int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, bool *enabled) { assert_return(client, -EINVAL); assert_return(enabled, -EINVAL); @@ -222,8 +219,7 @@ int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, return 0; } -int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, - uint16_t option) { +int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) { size_t t; assert_return(client, -EINVAL); @@ -805,9 +801,7 @@ static int client_parse_message(sd_dhcp6_client *client, return r; } -static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, - size_t len) -{ +static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) { int r; _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL; bool rapid_commit; @@ -843,8 +837,7 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, return DHCP6_STATE_BOUND; } -static int client_receive_advertise(sd_dhcp6_client *client, - DHCP6Message *advertise, size_t len) { +static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) { int r; _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL; uint8_t pref_advertise = 0, pref_lease = 0; @@ -879,8 +872,7 @@ static int client_receive_advertise(sd_dhcp6_client *client, return r; } -static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, - void *userdata) { +static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, void *userdata) { sd_dhcp6_client *client = userdata; DHCP6_CLIENT_DONT_DESTROY(client); _cleanup_free_ DHCP6Message *message; @@ -991,8 +983,7 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, return 0; } -static int client_start(sd_dhcp6_client *client, enum DHCP6State state) -{ +static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { int r; usec_t timeout, time_now; char time_string[FORMAT_TIMESPAN_MAX]; @@ -1121,15 +1112,13 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) return 0; } -int sd_dhcp6_client_stop(sd_dhcp6_client *client) -{ +int sd_dhcp6_client_stop(sd_dhcp6_client *client) { client_stop(client, DHCP6_EVENT_STOP); return 0; } -int sd_dhcp6_client_start(sd_dhcp6_client *client) -{ +int sd_dhcp6_client_start(sd_dhcp6_client *client) { int r = 0; enum DHCP6State state = DHCP6_STATE_SOLICITATION; @@ -1185,9 +1174,7 @@ error: return r; } -int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, - int priority) -{ +int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int priority) { int r; assert_return(client, -EINVAL); @@ -1222,30 +1209,39 @@ sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) { } sd_dhcp6_client *sd_dhcp6_client_ref(sd_dhcp6_client *client) { - if (client) - assert_se(REFCNT_INC(client->n_ref) >= 2); + + if (!client) + return NULL; + + assert(client->n_ref >= 1); + client->n_ref++; return client; } sd_dhcp6_client *sd_dhcp6_client_unref(sd_dhcp6_client *client) { - if (client && REFCNT_DEC(client->n_ref) == 0) { - client_reset(client); - sd_dhcp6_client_detach_event(client); - sd_dhcp6_lease_unref(client->lease); + if (!client) + return NULL; - free(client->req_opts); - free(client); + assert(client->n_ref >= 1); + client->n_ref--; + if (client->n_ref > 0) return NULL; - } - return client; + client_reset(client); + + sd_dhcp6_client_detach_event(client); + sd_dhcp6_lease_unref(client->lease); + + free(client->req_opts); + free(client); + + return NULL; } -int sd_dhcp6_client_new(sd_dhcp6_client **ret) -{ +int sd_dhcp6_client_new(sd_dhcp6_client **ret) { _cleanup_dhcp6_client_unref_ sd_dhcp6_client *client = NULL; size_t t; @@ -1255,7 +1251,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) if (!client) return -ENOMEM; - client->n_ref = REFCNT_INIT; + client->n_ref = 1; client->ia_na.type = DHCP6_OPTION_IA_NA; diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index f0494b3c9..f34af6eab 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -247,8 +247,7 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains) { return -ENOENT; } -int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) -{ +int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { int r; uint16_t subopt; size_t sublen; @@ -361,26 +360,38 @@ int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) { } sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease) { - if (lease) - assert_se(REFCNT_INC(lease->n_ref) >= 2); + + if (!lease) + return NULL; + + assert(lease->n_ref >= 1); + lease->n_ref++; return lease; } sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) { - if (lease && REFCNT_DEC(lease->n_ref) == 0) { - free(lease->serverid); - dhcp6_lease_free_ia(&lease->ia); - free(lease->dns); + if (!lease) + return NULL; - lease->domains = strv_free(lease->domains); + assert(lease->n_ref >= 1); + lease->n_ref--; - free(lease->ntp); + if (lease->n_ref > 0) + return NULL; - lease->ntp_fqdn = strv_free(lease->ntp_fqdn); - free(lease); - } + free(lease->serverid); + dhcp6_lease_free_ia(&lease->ia); + + free(lease->dns); + + lease->domains = strv_free(lease->domains); + + free(lease->ntp); + + lease->ntp_fqdn = strv_free(lease->ntp_fqdn); + free(lease); return NULL; } @@ -392,7 +403,7 @@ int dhcp6_lease_new(sd_dhcp6_lease **ret) { if (!lease) return -ENOMEM; - lease->n_ref = REFCNT_INIT; + lease->n_ref = 1; LIST_HEAD_INIT(lease->ia.addresses); diff --git a/src/libsystemd-network/sd-icmp6-nd.c b/src/libsystemd-network/sd-icmp6-nd.c index 2f867e856..e80232a7e 100644 --- a/src/libsystemd-network/sd-icmp6-nd.c +++ b/src/libsystemd-network/sd-icmp6-nd.c @@ -25,7 +25,6 @@ #include #include "socket-util.h" -#include "refcnt.h" #include "async.h" #include "dhcp6-internal.h" @@ -47,7 +46,7 @@ enum icmp6_nd_state { typedef struct ICMP6Prefix ICMP6Prefix; struct ICMP6Prefix { - RefCount n_ref; + unsigned n_ref; LIST_FIELDS(ICMP6Prefix, prefixes); @@ -57,7 +56,7 @@ struct ICMP6Prefix { }; struct sd_icmp6_nd { - RefCount n_ref; + unsigned n_ref; enum icmp6_nd_state state; sd_event *event; @@ -78,13 +77,18 @@ struct sd_icmp6_nd { #define log_icmp6_nd(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "ICMPv6 CLIENT: " fmt, ##__VA_ARGS__) static ICMP6Prefix *icmp6_prefix_unref(ICMP6Prefix *prefix) { - if (prefix && REFCNT_DEC(prefix->n_ref) <= 0) { - prefix->timeout_valid = - sd_event_source_unref(prefix->timeout_valid); - free(prefix); - } + if (!prefix) + return NULL; + + assert(prefix->n_ref > 0); + prefix->n_ref--; + if (prefix->n_ref > 0) + return NULL; + + prefix->timeout_valid = sd_event_source_unref(prefix->timeout_valid); + free(prefix); return NULL; } @@ -97,7 +101,7 @@ static int icmp6_prefix_new(ICMP6Prefix **ret) { if (!prefix) return -ENOMEM; - prefix->n_ref = REFCNT_INIT; + prefix->n_ref = 1; LIST_INIT(prefixes, prefix); *ret = prefix; @@ -106,8 +110,7 @@ static int icmp6_prefix_new(ICMP6Prefix **ret) { return 0; } -static void icmp6_nd_notify(sd_icmp6_nd *nd, int event) -{ +static void icmp6_nd_notify(sd_icmp6_nd *nd, int event) { if (nd->callback) nd->callback(nd, event, nd->userdata); } @@ -177,9 +180,12 @@ sd_event *sd_icmp6_nd_get_event(sd_icmp6_nd *nd) { } sd_icmp6_nd *sd_icmp6_nd_ref(sd_icmp6_nd *nd) { - assert (nd); - assert_se(REFCNT_INC(nd->n_ref) >= 2); + if (!nd) + return NULL; + + assert(nd->n_ref > 0); + nd->n_ref++; return nd; } @@ -195,21 +201,28 @@ static int icmp6_nd_init(sd_icmp6_nd *nd) { } sd_icmp6_nd *sd_icmp6_nd_unref(sd_icmp6_nd *nd) { - if (nd && REFCNT_DEC(nd->n_ref) == 0) { - ICMP6Prefix *prefix, *p; + ICMP6Prefix *prefix, *p; - icmp6_nd_init(nd); - sd_icmp6_nd_detach_event(nd); + if (!nd) + return NULL; - LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) { - LIST_REMOVE(prefixes, nd->prefixes, prefix); + assert(nd->n_ref > 0); + nd->n_ref--; - prefix = icmp6_prefix_unref(prefix); - } + if (nd->n_ref > 0) + return NULL; + + icmp6_nd_init(nd); + sd_icmp6_nd_detach_event(nd); - free(nd); + LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) { + LIST_REMOVE(prefixes, nd->prefixes, prefix); + + prefix = icmp6_prefix_unref(prefix); } + free(nd); + return NULL; } @@ -225,7 +238,7 @@ int sd_icmp6_nd_new(sd_icmp6_nd **ret) { if (!nd) return -ENOMEM; - nd->n_ref = REFCNT_INIT; + nd->n_ref = 1; nd->index = -1; nd->fd = -1; @@ -376,9 +389,7 @@ int sd_icmp6_ra_get_prefixlen(sd_icmp6_nd *nd, const struct in6_addr *addr, return 0; } -int sd_icmp6_ra_get_expired_prefix(sd_icmp6_nd *nd, struct in6_addr **addr, - uint8_t *prefixlen) -{ +int sd_icmp6_ra_get_expired_prefix(sd_icmp6_nd *nd, struct in6_addr **addr, uint8_t *prefixlen) { assert_return(nd, -EINVAL); assert_return(addr, -EINVAL); assert_return(prefixlen, -EINVAL); @@ -525,9 +536,7 @@ static int icmp6_ra_parse(sd_icmp6_nd *nd, struct nd_router_advert *ra, return 0; } -static int icmp6_router_advertisment_recv(sd_event_source *s, int fd, - uint32_t revents, void *userdata) -{ +static int icmp6_router_advertisment_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) { sd_icmp6_nd *nd = userdata; int r, buflen = 0; ssize_t len; @@ -586,9 +595,7 @@ static int icmp6_router_advertisment_recv(sd_event_source *s, int fd, return 0; } -static int icmp6_router_solicitation_timeout(sd_event_source *s, uint64_t usec, - void *userdata) -{ +static int icmp6_router_solicitation_timeout(sd_event_source *s, uint64_t usec, void *userdata) { sd_icmp6_nd *nd = userdata; uint64_t time_now, next_timeout; struct ether_addr unset = { }; diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c index f080c5c0a..0fc05e148 100644 --- a/src/libsystemd-network/sd-ipv4ll.c +++ b/src/libsystemd-network/sd-ipv4ll.c @@ -26,7 +26,6 @@ #include "util.h" #include "siphash24.h" #include "list.h" -#include "refcnt.h" #include "random-util.h" #include "ipv4ll-internal.h" @@ -68,7 +67,7 @@ typedef enum IPv4LLState { } IPv4LLState; struct sd_ipv4ll { - RefCount n_ref; + unsigned n_ref; IPv4LLState state; int index; @@ -598,30 +597,39 @@ int sd_ipv4ll_stop(sd_ipv4ll *ll) { } sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll) { - if (ll) - assert_se(REFCNT_INC(ll->n_ref) >= 2); + + if (!ll) + return NULL; + + assert(ll->n_ref >= 1); + ll->n_ref++; return ll; } sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll) { - if (ll && REFCNT_DEC(ll->n_ref) == 0) { - ll->receive_message = - sd_event_source_unref(ll->receive_message); - ll->fd = safe_close(ll->fd); - ll->timer = sd_event_source_unref(ll->timer); + if (!ll) + return NULL; - sd_ipv4ll_detach_event(ll); + assert(ll->n_ref >= 1); + ll->n_ref--; - free(ll->random_data); - free(ll->random_data_state); - free(ll); + if (ll->n_ref > 0) + return ll; - return NULL; - } + ll->receive_message = sd_event_source_unref(ll->receive_message); + ll->fd = safe_close(ll->fd); - return ll; + ll->timer = sd_event_source_unref(ll->timer); + + sd_ipv4ll_detach_event(ll); + + free(ll->random_data); + free(ll->random_data_state); + free(ll); + + return NULL; } DEFINE_TRIVIAL_CLEANUP_FUNC(sd_ipv4ll*, sd_ipv4ll_unref); @@ -636,7 +644,7 @@ int sd_ipv4ll_new(sd_ipv4ll **ret) { if (!ll) return -ENOMEM; - ll->n_ref = REFCNT_INIT; + ll->n_ref = 1; ll->state = IPV4LL_STATE_INIT; ll->index = -1; ll->fd = -1; diff --git a/src/libsystemd-network/sd-pppoe.c b/src/libsystemd-network/sd-pppoe.c index ff064f563..c6c9da812 100644 --- a/src/libsystemd-network/sd-pppoe.c +++ b/src/libsystemd-network/sd-pppoe.c @@ -36,7 +36,6 @@ #include "random-util.h" #include "socket-util.h" #include "async.h" -#include "refcnt.h" #include "utf8.h" #define PPPOE_MAX_PACKET_SIZE 1484 @@ -68,7 +67,7 @@ typedef struct PPPoETags { } PPPoETags; struct sd_pppoe { - RefCount n_ref; + unsigned n_ref; PPPoEState state; uint64_t host_uniq; @@ -202,23 +201,34 @@ int sd_pppoe_detach_event(sd_pppoe *ppp) { } sd_pppoe *sd_pppoe_ref(sd_pppoe *ppp) { - if (ppp) - assert_se(REFCNT_INC(ppp->n_ref) >= 2); + + if (!ppp) + return NULL; + + assert(ppp->n_ref > 0); + ppp->n_ref++; return ppp; } sd_pppoe *sd_pppoe_unref(sd_pppoe *ppp) { - if (ppp && REFCNT_DEC(ppp->n_ref) <= 0) { - pppoe_tags_clear(&ppp->tags); - free(ppp->ifname); - free(ppp->service_name); - sd_pppoe_stop(ppp); - sd_pppoe_detach_event(ppp); - - free(ppp); - } + if (!ppp) + return NULL; + + assert(ppp->n_ref > 0); + ppp->n_ref--; + + if (ppp->n_ref > 0) + return NULL; + + pppoe_tags_clear(&ppp->tags); + free(ppp->ifname); + free(ppp->service_name); + sd_pppoe_stop(ppp); + sd_pppoe_detach_event(ppp); + + free(ppp); return NULL; } @@ -231,7 +241,7 @@ int sd_pppoe_new (sd_pppoe **ret) { if (!ppp) return -ENOMEM; - ppp->n_ref = REFCNT_INIT; + ppp->n_ref = 1; ppp->state = _PPPOE_STATE_INVALID; ppp->ifindex = -1; ppp->fd = -1; diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c index 200499d61..29c20b77e 100644 --- a/src/libsystemd-network/test-dhcp-client.c +++ b/src/libsystemd-network/test-dhcp-client.c @@ -43,16 +43,13 @@ static test_callback_recv_t callback_recv; static be32_t xid; static sd_event_source *test_hangcheck; -static int test_dhcp_hangcheck(sd_event_source *s, uint64_t usec, - void *userdata) -{ +static int test_dhcp_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) { assert_not_reached("Test case should have completed in 2 seconds"); return 0; } -static void test_request_basic(sd_event *e) -{ +static void test_request_basic(sd_event *e) { int r; sd_dhcp_client *client; @@ -87,10 +84,7 @@ static void test_request_basic(sd_event *e) assert_se(sd_dhcp_client_set_request_option(client, DHCP_OPTION_DOMAIN_NAME) == -EEXIST); assert_se(sd_dhcp_client_set_request_option(client, - DHCP_OPTION_DOMAIN_NAME_SERVER) - == -EEXIST); - assert_se(sd_dhcp_client_set_request_option(client, - DHCP_OPTION_NTP_SERVER) == -EEXIST); + DHCP_OPTION_DOMAIN_NAME_SERVER) == -EEXIST); assert_se(sd_dhcp_client_set_request_option(client, DHCP_OPTION_PAD) == -EINVAL); @@ -112,8 +106,7 @@ static void test_request_basic(sd_event *e) sd_dhcp_client_unref(client); } -static void test_checksum(void) -{ +static void test_checksum(void) { uint8_t buf[20] = { 0x45, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -126,9 +119,7 @@ static void test_checksum(void) assert_se(dhcp_packet_checksum((uint8_t*)&buf, 20) == be16toh(0x78ae)); } -static int check_options(uint8_t code, uint8_t len, const uint8_t *option, - void *user_data) -{ +static int check_options(uint8_t code, uint8_t len, const void *option, void *userdata) { switch(code) { case DHCP_OPTION_CLIENT_IDENTIFIER: { @@ -141,10 +132,10 @@ static int check_options(uint8_t code, uint8_t len, const uint8_t *option, assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid_len); assert_se(len == 19); - assert_se(option[0] == 0xff); + assert_se(((uint8_t*) option)[0] == 0xff); - assert_se(memcmp(&option[1], &iaid, sizeof(iaid)) == 0); - assert_se(memcmp(&option[5], &duid, duid_len) == 0); + assert_se(memcmp((uint8_t*) option + 1, &iaid, sizeof(iaid)) == 0); + assert_se(memcmp((uint8_t*) option + 5, &duid, duid_len) == 0); break; } @@ -155,9 +146,7 @@ static int check_options(uint8_t code, uint8_t len, const uint8_t *option, return 0; } -int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, - const void *packet, size_t len) -{ +int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, const void *packet, size_t len) { size_t size; _cleanup_free_ DHCPPacket *discover; uint16_t ip_check, udp_check; @@ -202,18 +191,20 @@ int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, return 575; } -int dhcp_network_bind_raw_socket(int index, union sockaddr_union *link, - uint32_t id, const uint8_t *addr, - size_t addr_len, uint16_t arp_type) -{ +int dhcp_network_bind_raw_socket( + int index, + union sockaddr_union *link, + uint32_t id, + const uint8_t *addr, size_t addr_len, + uint16_t arp_type) { + if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_fd) < 0) return -errno; return test_fd[0]; } -int dhcp_network_bind_udp_socket(be32_t address, uint16_t port) -{ +int dhcp_network_bind_udp_socket(be32_t address, uint16_t port) { int fd; fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0); @@ -223,14 +214,11 @@ int dhcp_network_bind_udp_socket(be32_t address, uint16_t port) return fd; } -int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, - const void *packet, size_t len) -{ +int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, const void *packet, size_t len) { return 0; } -static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp) -{ +static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp) { int res; res = dhcp_option_parse(dhcp, size, check_options, NULL); @@ -242,8 +230,7 @@ static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp) return 0; } -static void test_discover_message(sd_event *e) -{ +static void test_discover_message(sd_event *e) { sd_dhcp_client *client; int res, r; diff --git a/src/libsystemd-network/test-dhcp-option.c b/src/libsystemd-network/test-dhcp-option.c index a63a4ea73..b1ef17484 100644 --- a/src/libsystemd-network/test-dhcp-option.c +++ b/src/libsystemd-network/test-dhcp-option.c @@ -145,8 +145,8 @@ static void test_ignore_opts(uint8_t *descoption, int *descpos, int *desclen) { } } -static int test_options_cb(uint8_t code, uint8_t len, const uint8_t *option, void *user_data) { - struct option_desc *desc = user_data; +static int test_options_cb(uint8_t code, uint8_t len, const void *option, void *userdata) { + struct option_desc *desc = userdata; uint8_t *descoption = NULL; int *desclen = NULL, *descpos = NULL; uint8_t optcode = 0; @@ -207,10 +207,10 @@ static int test_options_cb(uint8_t code, uint8_t len, const uint8_t *option, voi for (i = 0; i < len; i++) { if (verbose) - printf("0x%02x(0x%02x) ", option[i], + printf("0x%02x(0x%02x) ", ((uint8_t*) option)[i], descoption[*descpos + 2 + i]); - assert_se(option[i] == descoption[*descpos + 2 + i]); + assert_se(((uint8_t*) option)[i] == descoption[*descpos + 2 + i]); } if (verbose) diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c index 9f60ab761..7d8a1f6bd 100644 --- a/src/libsystemd-network/test-dhcp-server.c +++ b/src/libsystemd-network/test-dhcp-server.c @@ -28,6 +28,14 @@ #include "sd-dhcp-server.h" #include "dhcp-server-internal.h" +static void test_pool(struct in_addr *address, unsigned size, int ret) { + _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL; + + assert_se(sd_dhcp_server_new(&server, 1) >= 0); + + assert_se(sd_dhcp_server_configure_pool(server, address, 8, 0, size) == ret); +} + static int test_basic(sd_event *event) { _cleanup_dhcp_server_unref_ sd_dhcp_server *server = NULL; struct in_addr address_lo = { @@ -54,15 +62,14 @@ static int test_basic(sd_event *event) { assert_se(!sd_dhcp_server_unref(server)); assert_se(sd_dhcp_server_start(server) == -EUNATCH); - assert_se(sd_dhcp_server_set_address(server, &address_any, 28) == -EINVAL); - assert_se(sd_dhcp_server_set_address(server, &address_lo, 38) == -ERANGE); - assert_se(sd_dhcp_server_set_address(server, &address_lo, 8) >= 0); - assert_se(sd_dhcp_server_set_address(server, &address_lo, 8) == -EBUSY); - assert_se(sd_dhcp_server_set_lease_pool(server, &address_any, 1) == -EINVAL); - assert_se(sd_dhcp_server_set_lease_pool(server, &address_lo, 0) == -EINVAL); - assert_se(sd_dhcp_server_set_lease_pool(server, &address_lo, 1) >= 0); - assert_se(sd_dhcp_server_set_lease_pool(server, &address_lo, 1) == -EBUSY); + assert_se(sd_dhcp_server_configure_pool(server, &address_any, 28, 0, 0) == -EINVAL); + assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 38, 0, 0) == -ERANGE); + assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0); + assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) == -EBUSY); + + test_pool(&address_any, 1, -EINVAL); + test_pool(&address_lo, 1, 0); r = sd_dhcp_server_start(server); @@ -119,12 +126,10 @@ static void test_message_handler(void) { }; assert_se(sd_dhcp_server_new(&server, 1) >= 0); - assert_se(sd_dhcp_server_set_address(server, &address_lo, 8) >= 0); + assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0); assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0); assert_se(sd_dhcp_server_start(server) >= 0); - assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); - assert_se(sd_dhcp_server_set_lease_pool(server, &address_lo, 10) >= 0); assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); test.end = 0; diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 6e6226244..12daac321 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -501,8 +501,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option, return 0; } -static int test_client_send_advertise(DHCP6Message *solicit) -{ +static int test_client_send_advertise(DHCP6Message *solicit) { DHCP6Message advertise; advertise.transaction_id = solicit->transaction_id; diff --git a/src/libsystemd-network/test-ipv4ll.c b/src/libsystemd-network/test-ipv4ll.c index 5677bfb2d..d60ee98b2 100644 --- a/src/libsystemd-network/test-ipv4ll.c +++ b/src/libsystemd-network/test-ipv4ll.c @@ -39,9 +39,9 @@ static int test_fd[2]; static int basic_request_handler_bind = 0; static int basic_request_handler_stop = 0; -static void* basic_request_handler_user_data = (void*)0xCABCAB; +static void* basic_request_handler_userdata = (void*)0xCABCAB; static void basic_request_handler(sd_ipv4ll *ll, int event, void *userdata) { - assert_se(userdata == basic_request_handler_user_data); + assert_se(userdata == basic_request_handler_userdata); switch(event) { case IPV4LL_EVENT_STOP: @@ -175,7 +175,7 @@ static void test_basic_request(sd_event *e) { assert_se(sd_ipv4ll_start(ll) == -EINVAL); assert_se(sd_ipv4ll_set_callback(ll, basic_request_handler, - basic_request_handler_user_data) == 0); + basic_request_handler_userdata) == 0); assert_se(sd_ipv4ll_start(ll) == -EINVAL); assert_se(sd_ipv4ll_set_index(ll, 1) == 0); diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index 7bf1d66dd..d5ad127bc 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -467,3 +467,9 @@ global: sd_bus_emit_object_removed; sd_bus_flush_close_unref; } LIBSYSTEMD_221; + +LIBSYSTEMD_226 { +global: + sd_pid_get_cgroup; + sd_peer_get_cgroup; +} LIBSYSTEMD_222; diff --git a/src/libsystemd/sd-bus/bus-container.c b/src/libsystemd/sd-bus/bus-container.c index 101e4af18..5c607f49b 100644 --- a/src/libsystemd/sd-bus/bus-container.c +++ b/src/libsystemd/sd-bus/bus-container.c @@ -29,10 +29,12 @@ #include "bus-container.h" int bus_container_connect_socket(sd_bus *b) { + _cleanup_close_pair_ int pair[2] = { -1, -1 }; _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1; pid_t child; siginfo_t si; - int r; + int r, error_buf = 0; + ssize_t n; assert(b); assert(b->input_fd < 0); @@ -57,6 +59,9 @@ int bus_container_connect_socket(sd_bus *b) { bus_socket_setup(b); + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) + return -errno; + child = fork(); if (child < 0) return -errno; @@ -64,9 +69,11 @@ int bus_container_connect_socket(sd_bus *b) { if (child == 0) { pid_t grandchild; + pair[0] = safe_close(pair[0]); + r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd); if (r < 0) - _exit(255); + _exit(EXIT_FAILURE); /* We just changed PID namespace, however it will only * take effect on the children we now fork. Hence, @@ -77,16 +84,16 @@ int bus_container_connect_socket(sd_bus *b) { grandchild = fork(); if (grandchild < 0) - _exit(255); + _exit(EXIT_FAILURE); if (grandchild == 0) { r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size); if (r < 0) { - if (errno == EINPROGRESS) - _exit(1); - - _exit(255); + /* Try to send error up */ + error_buf = errno; + (void) write(pair[1], &error_buf, sizeof(error_buf)); + _exit(EXIT_FAILURE); } _exit(EXIT_SUCCESS); @@ -94,24 +101,41 @@ int bus_container_connect_socket(sd_bus *b) { r = wait_for_terminate(grandchild, &si); if (r < 0) - _exit(255); + _exit(EXIT_FAILURE); if (si.si_code != CLD_EXITED) - _exit(255); + _exit(EXIT_FAILURE); _exit(si.si_status); } + pair[1] = safe_close(pair[1]); + r = wait_for_terminate(child, &si); if (r < 0) return r; + n = read(pair[0], &error_buf, sizeof(error_buf)); + if (n < 0) + return -errno; + + if (n > 0) { + if (n != sizeof(error_buf)) + return -EIO; + + if (error_buf < 0) + return -EIO; + + if (error_buf == EINPROGRESS) + return 1; + + if (error_buf > 0) + return -error_buf; + } + if (si.si_code != CLD_EXITED) return -EIO; - if (si.si_status == 1) - return 1; - if (si.si_status != EXIT_SUCCESS) return -EIO; @@ -125,15 +149,22 @@ int bus_container_connect_kernel(sd_bus *b) { struct cmsghdr cmsghdr; uint8_t buf[CMSG_SPACE(sizeof(int))]; } control = {}; + int error_buf = 0; + struct iovec iov = { + .iov_base = &error_buf, + .iov_len = sizeof(error_buf), + }; struct msghdr mh = { .msg_control = &control, .msg_controllen = sizeof(control), + .msg_iov = &iov, + .msg_iovlen = 1, }; struct cmsghdr *cmsg; pid_t child; siginfo_t si; - int r; - _cleanup_close_ int fd = -1; + int r, fd = -1; + ssize_t n; assert(b); assert(b->input_fd < 0); @@ -150,7 +181,7 @@ int bus_container_connect_kernel(sd_bus *b) { if (r < 0) return r; - if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0) + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) return -errno; child = fork(); @@ -178,10 +209,13 @@ int bus_container_connect_kernel(sd_bus *b) { _exit(EXIT_FAILURE); if (grandchild == 0) { - fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) + if (fd < 0) { + /* Try to send error up */ + error_buf = errno; + (void) write(pair[1], &error_buf, sizeof(error_buf)); _exit(EXIT_FAILURE); + } cmsg = CMSG_FIRSTHDR(&mh); cmsg->cmsg_level = SOL_SOCKET; @@ -213,20 +247,17 @@ int bus_container_connect_kernel(sd_bus *b) { if (r < 0) return r; - if (si.si_code != CLD_EXITED) - return -EIO; - - if (si.si_status != EXIT_SUCCESS) - return -EIO; - - if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0) + n = recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC); + if (n < 0) return -errno; - CMSG_FOREACH(cmsg, &mh) + CMSG_FOREACH(cmsg, &mh) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { int *fds; unsigned n_fds; + assert(fd < 0); + fds = (int*) CMSG_DATA(cmsg); n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); @@ -237,9 +268,18 @@ int bus_container_connect_kernel(sd_bus *b) { fd = fds[0]; } + } + + /* If there's an fd passed, we are good. */ + if (fd >= 0) { + b->input_fd = b->output_fd = fd; + return bus_kernel_take_fd(b); + } - b->input_fd = b->output_fd = fd; - fd = -1; + /* If there's an error passed, use it */ + if (n == sizeof(error_buf) && error_buf > 0) + return -error_buf; - return bus_kernel_take_fd(b); + /* Otherwise, we have no clue */ + return -EIO; } diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c index 1c365b7fc..c3cc2b721 100644 --- a/src/libsystemd/sd-bus/bus-creds.c +++ b/src/libsystemd/sd-bus/bus-creds.c @@ -1062,8 +1062,8 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { if (missing & SD_BUS_CREDS_AUDIT_SESSION_ID) { r = audit_session_from_pid(pid, &c->audit_session_id); - if (r == -ENXIO) { - /* ENXIO means: no audit session id assigned */ + if (r == -ENODATA) { + /* ENODATA means: no audit session id assigned */ c->audit_session_id = AUDIT_SESSION_INVALID; c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID; } else if (r < 0) { @@ -1075,8 +1075,8 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { if (missing & SD_BUS_CREDS_AUDIT_LOGIN_UID) { r = audit_loginuid_from_pid(pid, &c->audit_login_uid); - if (r == -ENXIO) { - /* ENXIO means: no audit login uid assigned */ + if (r == -ENODATA) { + /* ENODATA means: no audit login uid assigned */ c->audit_login_uid = UID_INVALID; c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; } else if (r < 0) { diff --git a/src/libsystemd/sd-bus/bus-match.c b/src/libsystemd/sd-bus/bus-match.c index 2b83f439a..7234e7926 100644 --- a/src/libsystemd/sd-bus/bus-match.c +++ b/src/libsystemd/sd-bus/bus-match.c @@ -932,7 +932,7 @@ fail: } char *bus_match_to_string(struct bus_match_component *components, unsigned n_components) { - _cleanup_free_ FILE *f = NULL; + _cleanup_fclose_ FILE *f = NULL; char *buffer = NULL; size_t size = 0; unsigned i; diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c index a21ed8cb6..366a02642 100644 --- a/src/libsystemd/sd-bus/bus-message.c +++ b/src/libsystemd/sd-bus/bus-message.c @@ -1024,7 +1024,9 @@ _public_ const char *sd_bus_message_get_sender(sd_bus_message *m) { _public_ const sd_bus_error *sd_bus_message_get_error(sd_bus_message *m) { assert_return(m, NULL); - assert_return(sd_bus_error_is_set(&m->error), NULL); + + if (!sd_bus_error_is_set(&m->error)) + return NULL; return &m->error; } diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c index 0593aa658..1d061cb9c 100644 --- a/src/libsystemd/sd-bus/bus-objects.c +++ b/src/libsystemd/sd-bus/bus-objects.c @@ -169,11 +169,18 @@ static int add_enumerated_to_set( return 0; } +enum { + /* if set, add_subtree() works recursively */ + CHILDREN_RECURSIVE = (1U << 1), + /* if set, add_subtree() scans object-manager hierarchies recursively */ + CHILDREN_SUBHIERARCHIES = (1U << 0), +}; + static int add_subtree_to_set( sd_bus *bus, const char *prefix, struct node *n, - bool skip_subhierarchies, + unsigned int flags, Set *s, sd_bus_error *error) { @@ -205,8 +212,9 @@ static int add_subtree_to_set( if (r < 0 && r != -EEXIST) return r; - if (!skip_subhierarchies || !i->object_managers) { - r = add_subtree_to_set(bus, prefix, i, skip_subhierarchies, s, error); + if ((flags & CHILDREN_RECURSIVE) && + ((flags & CHILDREN_SUBHIERARCHIES) || !i->object_managers)) { + r = add_subtree_to_set(bus, prefix, i, flags, s, error); if (r < 0) return r; if (bus->nodes_modified) @@ -221,7 +229,7 @@ static int get_child_nodes( sd_bus *bus, const char *prefix, struct node *n, - bool skip_subhierarchies, + unsigned int flags, Set **_s, sd_bus_error *error) { @@ -237,7 +245,7 @@ static int get_child_nodes( if (!s) return -ENOMEM; - r = add_subtree_to_set(bus, prefix, n, skip_subhierarchies, s, error); + r = add_subtree_to_set(bus, prefix, n, flags, s, error); if (r < 0) { set_free_free(s); return r; @@ -907,7 +915,7 @@ static int process_introspect( assert(n); assert(found_object); - r = get_child_nodes(bus, m->path, n, false, &s, &error); + r = get_child_nodes(bus, m->path, n, 0, &s, &error); if (r < 0) return bus_maybe_reply_error(m, r, &error); if (bus->nodes_modified) @@ -1173,7 +1181,7 @@ static int process_get_managed_objects( if (require_fallback || !n->object_managers) return 0; - r = get_child_nodes(bus, m->path, n, true, &s, &error); + r = get_child_nodes(bus, m->path, n, CHILDREN_RECURSIVE, &s, &error); if (r < 0) return r; if (bus->nodes_modified) diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index 5285278d9..4a9181613 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -1025,19 +1025,30 @@ static int bus_start_address(sd_bus *b) { if (b->exec_path) r = bus_socket_exec(b); + else if ((b->nspid > 0 || b->machine) && b->kernel) { r = bus_container_connect_kernel(b); if (r < 0 && !IN_SET(r, -ENOENT, -ESOCKTNOSUPPORT)) container_kdbus_available = true; - } else if (!container_kdbus_available && (b->nspid > 0 || b->machine) && b->sockaddr.sa.sa_family != AF_UNSPEC) - r = bus_container_connect_socket(b); - else if (b->kernel) { + + } else if ((b->nspid > 0 || b->machine) && b->sockaddr.sa.sa_family != AF_UNSPEC) { + if (!container_kdbus_available) + r = bus_container_connect_socket(b); + else + skipped = true; + + } else if (b->kernel) { r = bus_kernel_connect(b); if (r < 0 && !IN_SET(r, -ENOENT, -ESOCKTNOSUPPORT)) kdbus_available = true; - } else if (!kdbus_available && b->sockaddr.sa.sa_family != AF_UNSPEC) - r = bus_socket_connect(b); - else + + } else if (b->sockaddr.sa.sa_family != AF_UNSPEC) { + if (!kdbus_available) + r = bus_socket_connect(b); + else + skipped = true; + + } else skipped = true; if (!skipped) { @@ -1230,6 +1241,8 @@ fail: int bus_set_address_user(sd_bus *b) { const char *e; + uid_t uid; + int r; assert(b); @@ -1237,6 +1250,10 @@ int bus_set_address_user(sd_bus *b) { if (e) return sd_bus_set_address(b, e); + r = cg_pid_get_owner_uid(0, &uid); + if (r < 0) + uid = getuid(); + e = secure_getenv("XDG_RUNTIME_DIR"); if (e) { _cleanup_free_ char *ee = NULL; @@ -1245,9 +1262,9 @@ int bus_set_address_user(sd_bus *b) { if (!ee) return -ENOMEM; - (void) asprintf(&b->address, KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT, getuid(), ee); + (void) asprintf(&b->address, KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT, uid, ee); } else - (void) asprintf(&b->address, KERNEL_USER_BUS_ADDRESS_FMT, getuid()); + (void) asprintf(&b->address, KERNEL_USER_BUS_ADDRESS_FMT, uid); if (!b->address) return -ENOMEM; diff --git a/src/libsystemd/sd-bus/test-bus-creds.c b/src/libsystemd/sd-bus/test-bus-creds.c index edd5033db..580117165 100644 --- a/src/libsystemd/sd-bus/test-bus-creds.c +++ b/src/libsystemd/sd-bus/test-bus-creds.c @@ -22,11 +22,17 @@ #include "sd-bus.h" #include "bus-dump.h" #include "bus-util.h" +#include "cgroup-util.h" int main(int argc, char *argv[]) { _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; int r; + if (cg_unified() == -ENOEXEC) { + puts("Skipping test: /sys/fs/cgroup/ not available"); + return EXIT_TEST_SKIP; + } + r = sd_bus_creds_new_from_pid(&creds, 0, _SD_BUS_CREDS_ALL); assert_se(r >= 0); diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index 0e33ced34..fd39a5622 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -56,9 +56,22 @@ typedef enum EventSourceType { _SOURCE_EVENT_SOURCE_TYPE_INVALID = -1 } EventSourceType; +/* All objects we use in epoll events start with this value, so that + * we know how to dispatch it */ +typedef enum WakeupType { + WAKEUP_NONE, + WAKEUP_EVENT_SOURCE, + WAKEUP_CLOCK_DATA, + WAKEUP_SIGNAL_DATA, + _WAKEUP_TYPE_MAX, + _WAKEUP_TYPE_INVALID = -1, +} WakeupType; + #define EVENT_SOURCE_IS_TIME(t) IN_SET((t), SOURCE_TIME_REALTIME, SOURCE_TIME_BOOTTIME, SOURCE_TIME_MONOTONIC, SOURCE_TIME_REALTIME_ALARM, SOURCE_TIME_BOOTTIME_ALARM) struct sd_event_source { + WakeupType wakeup; + unsigned n_ref; sd_event *event; @@ -120,6 +133,7 @@ struct sd_event_source { }; struct clock_data { + WakeupType wakeup; int fd; /* For all clocks we maintain two priority queues each, one @@ -136,11 +150,23 @@ struct clock_data { bool needs_rearm:1; }; +struct signal_data { + WakeupType wakeup; + + /* For each priority we maintain one signal fd, so that we + * only have to dequeue a single event per priority at a + * time. */ + + int fd; + int64_t priority; + sigset_t sigset; + sd_event_source *current; +}; + struct sd_event { unsigned n_ref; int epoll_fd; - int signal_fd; int watchdog_fd; Prioq *pending; @@ -157,8 +183,8 @@ struct sd_event { usec_t perturb; - sigset_t sigset; - sd_event_source **signal_sources; + sd_event_source **signal_sources; /* indexed by signal number */ + Hashmap *signal_data; /* indexed by priority */ Hashmap *child_sources; unsigned n_enabled_child_sources; @@ -355,6 +381,7 @@ static int exit_prioq_compare(const void *a, const void *b) { static void free_clock_data(struct clock_data *d) { assert(d); + assert(d->wakeup == WAKEUP_CLOCK_DATA); safe_close(d->fd); prioq_free(d->earliest); @@ -378,7 +405,6 @@ static void event_free(sd_event *e) { *(e->default_event_ptr) = NULL; safe_close(e->epoll_fd); - safe_close(e->signal_fd); safe_close(e->watchdog_fd); free_clock_data(&e->realtime); @@ -392,6 +418,7 @@ static void event_free(sd_event *e) { prioq_free(e->exit); free(e->signal_sources); + hashmap_free(e->signal_data); hashmap_free(e->child_sources); set_free(e->post_sources); @@ -409,13 +436,12 @@ _public_ int sd_event_new(sd_event** ret) { return -ENOMEM; e->n_ref = 1; - e->signal_fd = e->watchdog_fd = e->epoll_fd = e->realtime.fd = e->boottime.fd = e->monotonic.fd = e->realtime_alarm.fd = e->boottime_alarm.fd = -1; + e->watchdog_fd = e->epoll_fd = e->realtime.fd = e->boottime.fd = e->monotonic.fd = e->realtime_alarm.fd = e->boottime_alarm.fd = -1; e->realtime.next = e->boottime.next = e->monotonic.next = e->realtime_alarm.next = e->boottime_alarm.next = USEC_INFINITY; + e->realtime.wakeup = e->boottime.wakeup = e->monotonic.wakeup = e->realtime_alarm.wakeup = e->boottime_alarm.wakeup = WAKEUP_CLOCK_DATA; e->original_pid = getpid(); e->perturb = USEC_INFINITY; - assert_se(sigemptyset(&e->sigset) == 0); - e->pending = prioq_new(pending_prioq_compare); if (!e->pending) { r = -ENOMEM; @@ -481,7 +507,8 @@ static void source_io_unregister(sd_event_source *s) { return; r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, s->io.fd, NULL); - assert_log(r >= 0); + if (r < 0) + log_debug_errno(errno, "Failed to remove source %s from epoll: %m", strna(s->description)); s->io.registered = false; } @@ -508,7 +535,6 @@ static int source_io_register( r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_MOD, s->io.fd, &ev); else r = epoll_ctl(s->event->epoll_fd, EPOLL_CTL_ADD, s->io.fd, &ev); - if (r < 0) return -errno; @@ -590,45 +616,171 @@ static struct clock_data* event_get_clock_data(sd_event *e, EventSourceType t) { } } -static bool need_signal(sd_event *e, int signal) { - return (e->signal_sources && e->signal_sources[signal] && - e->signal_sources[signal]->enabled != SD_EVENT_OFF) - || - (signal == SIGCHLD && - e->n_enabled_child_sources > 0); -} +static int event_make_signal_data( + sd_event *e, + int sig, + struct signal_data **ret) { -static int event_update_signal_fd(sd_event *e) { struct epoll_event ev = {}; - bool add_to_epoll; + struct signal_data *d; + bool added = false; + sigset_t ss_copy; + int64_t priority; int r; assert(e); if (event_pid_changed(e)) - return 0; + return -ECHILD; - add_to_epoll = e->signal_fd < 0; + if (e->signal_sources && e->signal_sources[sig]) + priority = e->signal_sources[sig]->priority; + else + priority = 0; - r = signalfd(e->signal_fd, &e->sigset, SFD_NONBLOCK|SFD_CLOEXEC); - if (r < 0) - return -errno; + d = hashmap_get(e->signal_data, &priority); + if (d) { + if (sigismember(&d->sigset, sig) > 0) { + if (ret) + *ret = d; + return 0; + } + } else { + r = hashmap_ensure_allocated(&e->signal_data, &uint64_hash_ops); + if (r < 0) + return r; + + d = new0(struct signal_data, 1); + if (!d) + return -ENOMEM; + + d->wakeup = WAKEUP_SIGNAL_DATA; + d->fd = -1; + d->priority = priority; + + r = hashmap_put(e->signal_data, &d->priority, d); + if (r < 0) + return r; + + added = true; + } + + ss_copy = d->sigset; + assert_se(sigaddset(&ss_copy, sig) >= 0); + + r = signalfd(d->fd, &ss_copy, SFD_NONBLOCK|SFD_CLOEXEC); + if (r < 0) { + r = -errno; + goto fail; + } - e->signal_fd = r; + d->sigset = ss_copy; - if (!add_to_epoll) + if (d->fd >= 0) { + if (ret) + *ret = d; return 0; + } + + d->fd = r; ev.events = EPOLLIN; - ev.data.ptr = INT_TO_PTR(SOURCE_SIGNAL); + ev.data.ptr = d; - r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, e->signal_fd, &ev); - if (r < 0) { - e->signal_fd = safe_close(e->signal_fd); - return -errno; + r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, d->fd, &ev); + if (r < 0) { + r = -errno; + goto fail; } + if (ret) + *ret = d; + return 0; + +fail: + if (added) { + d->fd = safe_close(d->fd); + hashmap_remove(e->signal_data, &d->priority); + free(d); + } + + return r; +} + +static void event_unmask_signal_data(sd_event *e, struct signal_data *d, int sig) { + assert(e); + assert(d); + + /* Turns off the specified signal in the signal data + * object. If the signal mask of the object becomes empty that + * way removes it. */ + + if (sigismember(&d->sigset, sig) == 0) + return; + + assert_se(sigdelset(&d->sigset, sig) >= 0); + + if (sigisemptyset(&d->sigset)) { + + /* If all the mask is all-zero we can get rid of the structure */ + hashmap_remove(e->signal_data, &d->priority); + assert(!d->current); + safe_close(d->fd); + free(d); + return; + } + + assert(d->fd >= 0); + + if (signalfd(d->fd, &d->sigset, SFD_NONBLOCK|SFD_CLOEXEC) < 0) + log_debug_errno(errno, "Failed to unset signal bit, ignoring: %m"); +} + +static void event_gc_signal_data(sd_event *e, const int64_t *priority, int sig) { + struct signal_data *d; + static const int64_t zero_priority = 0; + + assert(e); + + /* Rechecks if the specified signal is still something we are + * interested in. If not, we'll unmask it, and possibly drop + * the signalfd for it. */ + + if (sig == SIGCHLD && + e->n_enabled_child_sources > 0) + return; + + if (e->signal_sources && + e->signal_sources[sig] && + e->signal_sources[sig]->enabled != SD_EVENT_OFF) + return; + + /* + * The specified signal might be enabled in three different queues: + * + * 1) the one that belongs to the priority passed (if it is non-NULL) + * 2) the one that belongs to the priority of the event source of the signal (if there is one) + * 3) the 0 priority (to cover the SIGCHLD case) + * + * Hence, let's remove it from all three here. + */ + + if (priority) { + d = hashmap_get(e->signal_data, priority); + if (d) + event_unmask_signal_data(e, d, sig); + } + + if (e->signal_sources && e->signal_sources[sig]) { + d = hashmap_get(e->signal_data, &e->signal_sources[sig]->priority); + if (d) + event_unmask_signal_data(e, d, sig); + } + + d = hashmap_get(e->signal_data, &zero_priority); + if (d) + event_unmask_signal_data(e, d, sig); } static void source_disconnect(sd_event_source *s) { @@ -667,17 +819,11 @@ static void source_disconnect(sd_event_source *s) { case SOURCE_SIGNAL: if (s->signal.sig > 0) { + if (s->event->signal_sources) s->event->signal_sources[s->signal.sig] = NULL; - /* If the signal was on and now it is off... */ - if (s->enabled != SD_EVENT_OFF && !need_signal(s->event, s->signal.sig)) { - assert_se(sigdelset(&s->event->sigset, s->signal.sig) == 0); - - (void) event_update_signal_fd(s->event); - /* If disabling failed, we might get a spurious event, - * but otherwise nothing bad should happen. */ - } + event_gc_signal_data(s->event, &s->priority, s->signal.sig); } break; @@ -687,18 +833,10 @@ static void source_disconnect(sd_event_source *s) { if (s->enabled != SD_EVENT_OFF) { assert(s->event->n_enabled_child_sources > 0); s->event->n_enabled_child_sources--; - - /* We know the signal was on, if it is off now... */ - if (!need_signal(s->event, SIGCHLD)) { - assert_se(sigdelset(&s->event->sigset, SIGCHLD) == 0); - - (void) event_update_signal_fd(s->event); - /* If disabling failed, we might get a spurious event, - * but otherwise nothing bad should happen. */ - } } - hashmap_remove(s->event->child_sources, INT_TO_PTR(s->child.pid)); + (void) hashmap_remove(s->event->child_sources, INT_TO_PTR(s->child.pid)); + event_gc_signal_data(s->event, &s->priority, SIGCHLD); } break; @@ -777,6 +915,14 @@ static int source_set_pending(sd_event_source *s, bool b) { d->needs_rearm = true; } + if (s->type == SOURCE_SIGNAL && !b) { + struct signal_data *d; + + d = hashmap_get(s->event->signal_data, &s->priority); + if (d && d->current == s) + d->current = NULL; + } + return 0; } @@ -826,6 +972,7 @@ _public_ int sd_event_add_io( if (!s) return -ENOMEM; + s->wakeup = WAKEUP_EVENT_SOURCE; s->io.fd = fd; s->io.events = events; s->io.callback = callback; @@ -882,7 +1029,7 @@ static int event_setup_timer_fd( return -errno; ev.events = EPOLLIN; - ev.data.ptr = INT_TO_PTR(clock_to_event_source_type(clock)); + ev.data.ptr = d; r = epoll_ctl(e->epoll_fd, EPOLL_CTL_ADD, fd, &ev); if (r < 0) { @@ -992,9 +1139,9 @@ _public_ int sd_event_add_signal( void *userdata) { sd_event_source *s; + struct signal_data *d; sigset_t ss; int r; - bool previous; assert_return(e, -EINVAL); assert_return(sig > 0, -EINVAL); @@ -1019,8 +1166,6 @@ _public_ int sd_event_add_signal( } else if (e->signal_sources[sig]) return -EBUSY; - previous = need_signal(e, sig); - s = source_new(e, !ret, SOURCE_SIGNAL); if (!s) return -ENOMEM; @@ -1032,14 +1177,10 @@ _public_ int sd_event_add_signal( e->signal_sources[sig] = s; - if (!previous) { - assert_se(sigaddset(&e->sigset, sig) == 0); - - r = event_update_signal_fd(e); - if (r < 0) { - source_free(s); - return r; - } + r = event_make_signal_data(e, sig, &d); + if (r < 0) { + source_free(s); + return r; } /* Use the signal name as description for the event source by default */ @@ -1061,7 +1202,6 @@ _public_ int sd_event_add_child( sd_event_source *s; int r; - bool previous; assert_return(e, -EINVAL); assert_return(pid > 1, -EINVAL); @@ -1078,8 +1218,6 @@ _public_ int sd_event_add_child( if (hashmap_contains(e->child_sources, INT_TO_PTR(pid))) return -EBUSY; - previous = need_signal(e, SIGCHLD); - s = source_new(e, !ret, SOURCE_CHILD); if (!s) return -ENOMEM; @@ -1098,14 +1236,11 @@ _public_ int sd_event_add_child( e->n_enabled_child_sources ++; - if (!previous) { - assert_se(sigaddset(&e->sigset, SIGCHLD) == 0); - - r = event_update_signal_fd(e); - if (r < 0) { - source_free(s); - return r; - } + r = event_make_signal_data(e, SIGCHLD, NULL); + if (r < 0) { + e->n_enabled_child_sources--; + source_free(s); + return r; } e->need_process_child = true; @@ -1405,6 +1540,8 @@ _public_ int sd_event_source_get_priority(sd_event_source *s, int64_t *priority) } _public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority) { + int r; + assert_return(s, -EINVAL); assert_return(s->event->state != SD_EVENT_FINISHED, -ESTALE); assert_return(!event_pid_changed(s->event), -ECHILD); @@ -1412,7 +1549,25 @@ _public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority) if (s->priority == priority) return 0; - s->priority = priority; + if (s->type == SOURCE_SIGNAL && s->enabled != SD_EVENT_OFF) { + struct signal_data *old, *d; + + /* Move us from the signalfd belonging to the old + * priority to the signalfd of the new priority */ + + assert_se(old = hashmap_get(s->event->signal_data, &s->priority)); + + s->priority = priority; + + r = event_make_signal_data(s->event, s->signal.sig, &d); + if (r < 0) { + s->priority = old->priority; + return r; + } + + event_unmask_signal_data(s->event, old, s->signal.sig); + } else + s->priority = priority; if (s->pending) prioq_reshuffle(s->event->pending, s, &s->pending_index); @@ -1477,34 +1632,18 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { } case SOURCE_SIGNAL: - assert(need_signal(s->event, s->signal.sig)); - s->enabled = m; - if (!need_signal(s->event, s->signal.sig)) { - assert_se(sigdelset(&s->event->sigset, s->signal.sig) == 0); - - (void) event_update_signal_fd(s->event); - /* If disabling failed, we might get a spurious event, - * but otherwise nothing bad should happen. */ - } - + event_gc_signal_data(s->event, &s->priority, s->signal.sig); break; case SOURCE_CHILD: - assert(need_signal(s->event, SIGCHLD)); - s->enabled = m; assert(s->event->n_enabled_child_sources > 0); s->event->n_enabled_child_sources--; - if (!need_signal(s->event, SIGCHLD)) { - assert_se(sigdelset(&s->event->sigset, SIGCHLD) == 0); - - (void) event_update_signal_fd(s->event); - } - + event_gc_signal_data(s->event, &s->priority, SIGCHLD); break; case SOURCE_EXIT: @@ -1550,37 +1689,33 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) { } case SOURCE_SIGNAL: - /* Check status before enabling. */ - if (!need_signal(s->event, s->signal.sig)) { - assert_se(sigaddset(&s->event->sigset, s->signal.sig) == 0); - - r = event_update_signal_fd(s->event); - if (r < 0) { - s->enabled = SD_EVENT_OFF; - return r; - } - } s->enabled = m; + + r = event_make_signal_data(s->event, s->signal.sig, NULL); + if (r < 0) { + s->enabled = SD_EVENT_OFF; + event_gc_signal_data(s->event, &s->priority, s->signal.sig); + return r; + } + break; case SOURCE_CHILD: - /* Check status before enabling. */ - if (s->enabled == SD_EVENT_OFF) { - if (!need_signal(s->event, SIGCHLD)) { - assert_se(sigaddset(&s->event->sigset, s->signal.sig) == 0); - - r = event_update_signal_fd(s->event); - if (r < 0) { - s->enabled = SD_EVENT_OFF; - return r; - } - } + if (s->enabled == SD_EVENT_OFF) s->event->n_enabled_child_sources++; - } s->enabled = m; + + r = event_make_signal_data(s->event, SIGCHLD, NULL); + if (r < 0) { + s->enabled = SD_EVENT_OFF; + s->event->n_enabled_child_sources--; + event_gc_signal_data(s->event, &s->priority, SIGCHLD); + return r; + } + break; case SOURCE_EXIT: @@ -2024,20 +2159,35 @@ static int process_child(sd_event *e) { return 0; } -static int process_signal(sd_event *e, uint32_t events) { +static int process_signal(sd_event *e, struct signal_data *d, uint32_t events) { bool read_one = false; int r; assert(e); - assert_return(events == EPOLLIN, -EIO); + /* If there's a signal queued on this priority and SIGCHLD is + on this priority too, then make sure to recheck the + children we watch. This is because we only ever dequeue + the first signal per priority, and if we dequeue one, and + SIGCHLD might be enqueued later we wouldn't know, but we + might have higher priority children we care about hence we + need to check that explicitly. */ + + if (sigismember(&d->sigset, SIGCHLD)) + e->need_process_child = true; + + /* If there's already an event source pending for this + * priority we don't read another */ + if (d->current) + return 0; + for (;;) { struct signalfd_siginfo si; ssize_t n; sd_event_source *s = NULL; - n = read(e->signal_fd, &si, sizeof(si)); + n = read(d->fd, &si, sizeof(si)); if (n < 0) { if (errno == EAGAIN || errno == EINTR) return read_one; @@ -2052,24 +2202,21 @@ static int process_signal(sd_event *e, uint32_t events) { read_one = true; - if (si.ssi_signo == SIGCHLD) { - r = process_child(e); - if (r < 0) - return r; - if (r > 0) - continue; - } - if (e->signal_sources) s = e->signal_sources[si.ssi_signo]; - if (!s) continue; + if (s->pending) + continue; s->signal.siginfo = si; + d->current = s; + r = source_set_pending(s, true); if (r < 0) return r; + + return 1; } } @@ -2387,23 +2534,31 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { for (i = 0; i < m; i++) { - if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_TIME_REALTIME)) - r = flush_timer(e, e->realtime.fd, ev_queue[i].events, &e->realtime.next); - else if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_TIME_BOOTTIME)) - r = flush_timer(e, e->boottime.fd, ev_queue[i].events, &e->boottime.next); - else if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_TIME_MONOTONIC)) - r = flush_timer(e, e->monotonic.fd, ev_queue[i].events, &e->monotonic.next); - else if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_TIME_REALTIME_ALARM)) - r = flush_timer(e, e->realtime_alarm.fd, ev_queue[i].events, &e->realtime_alarm.next); - else if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_TIME_BOOTTIME_ALARM)) - r = flush_timer(e, e->boottime_alarm.fd, ev_queue[i].events, &e->boottime_alarm.next); - else if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_SIGNAL)) - r = process_signal(e, ev_queue[i].events); - else if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_WATCHDOG)) + if (ev_queue[i].data.ptr == INT_TO_PTR(SOURCE_WATCHDOG)) r = flush_timer(e, e->watchdog_fd, ev_queue[i].events, NULL); - else - r = process_io(e, ev_queue[i].data.ptr, ev_queue[i].events); + else { + WakeupType *t = ev_queue[i].data.ptr; + + switch (*t) { + + case WAKEUP_EVENT_SOURCE: + r = process_io(e, ev_queue[i].data.ptr, ev_queue[i].events); + break; + case WAKEUP_CLOCK_DATA: { + struct clock_data *d = ev_queue[i].data.ptr; + r = flush_timer(e, d->fd, ev_queue[i].events, &d->next); + break; + } + + case WAKEUP_SIGNAL_DATA: + r = process_signal(e, ev_queue[i].data.ptr, ev_queue[i].events); + break; + + default: + assert_not_reached("Invalid wake-up pointer"); + } + } if (r < 0) goto finish; } diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c index 408e1679a..c092e56b7 100644 --- a/src/libsystemd/sd-event/test-event.c +++ b/src/libsystemd/sd-event/test-event.c @@ -156,7 +156,7 @@ static int exit_handler(sd_event_source *s, void *userdata) { return 3; } -int main(int argc, char *argv[]) { +static void test_basic(void) { sd_event *e = NULL; sd_event_source *w = NULL, *x = NULL, *y = NULL, *z = NULL, *q = NULL, *t = NULL; static const char ch = 'x'; @@ -244,6 +244,70 @@ int main(int argc, char *argv[]) { safe_close_pair(b); safe_close_pair(d); safe_close_pair(k); +} + +static int last_rtqueue_sigval = 0; +static int n_rtqueue = 0; + +static int rtqueue_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + last_rtqueue_sigval = si->ssi_int; + n_rtqueue ++; + return 0; +} + +static void test_rtqueue(void) { + sd_event_source *u = NULL, *v = NULL, *s = NULL; + sd_event *e = NULL; + + assert_se(sd_event_default(&e) >= 0); + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN+2, SIGRTMIN+3, SIGUSR2, -1) >= 0); + assert_se(sd_event_add_signal(e, &u, SIGRTMIN+2, rtqueue_handler, NULL) >= 0); + assert_se(sd_event_add_signal(e, &v, SIGRTMIN+3, rtqueue_handler, NULL) >= 0); + assert_se(sd_event_add_signal(e, &s, SIGUSR2, rtqueue_handler, NULL) >= 0); + + assert_se(sd_event_source_set_priority(v, -10) >= 0); + + assert(sigqueue(getpid(), SIGRTMIN+2, (union sigval) { .sival_int = 1 }) >= 0); + assert(sigqueue(getpid(), SIGRTMIN+3, (union sigval) { .sival_int = 2 }) >= 0); + assert(sigqueue(getpid(), SIGUSR2, (union sigval) { .sival_int = 3 }) >= 0); + assert(sigqueue(getpid(), SIGRTMIN+3, (union sigval) { .sival_int = 4 }) >= 0); + assert(sigqueue(getpid(), SIGUSR2, (union sigval) { .sival_int = 5 }) >= 0); + + assert_se(n_rtqueue == 0); + assert_se(last_rtqueue_sigval == 0); + + assert_se(sd_event_run(e, (uint64_t) -1) >= 1); + assert_se(n_rtqueue == 1); + assert_se(last_rtqueue_sigval == 2); /* first SIGRTMIN+3 */ + + assert_se(sd_event_run(e, (uint64_t) -1) >= 1); + assert_se(n_rtqueue == 2); + assert_se(last_rtqueue_sigval == 4); /* second SIGRTMIN+3 */ + + assert_se(sd_event_run(e, (uint64_t) -1) >= 1); + assert_se(n_rtqueue == 3); + assert_se(last_rtqueue_sigval == 3); /* first SIGUSR2 */ + + assert_se(sd_event_run(e, (uint64_t) -1) >= 1); + assert_se(n_rtqueue == 4); + assert_se(last_rtqueue_sigval == 1); /* SIGRTMIN+2 */ + + assert_se(sd_event_run(e, 0) == 0); /* the other SIGUSR2 is dropped, because the first one was still queued */ + assert_se(n_rtqueue == 4); + assert_se(last_rtqueue_sigval == 1); + + sd_event_source_unref(u); + sd_event_source_unref(v); + sd_event_source_unref(s); + + sd_event_unref(e); +} + +int main(int argc, char *argv[]) { + + test_basic(); + test_rtqueue(); return 0; } diff --git a/src/libsystemd/sd-hwdb/sd-hwdb.c b/src/libsystemd/sd-hwdb/sd-hwdb.c index 40aa77ee5..06c98314e 100644 --- a/src/libsystemd/sd-hwdb/sd-hwdb.c +++ b/src/libsystemd/sd-hwdb/sd-hwdb.c @@ -79,8 +79,7 @@ static bool linebuf_add(struct linebuf *buf, const char *s, size_t len) { return true; } -static bool linebuf_add_char(struct linebuf *buf, char c) -{ +static bool linebuf_add_char(struct linebuf *buf, char c) { if (buf->len + 1 >= sizeof(buf->bytes)) return false; buf->bytes[buf->len++] = c; @@ -269,13 +268,13 @@ static int trie_search_f(sd_hwdb *hwdb, const char *search) { } static const char hwdb_bin_paths[] = - "/etc/systemd/hwdb/hwdb.bin\0" - "/etc/udev/hwdb.bin\0" - "/usr/lib/systemd/hwdb/hwdb.bin\0" + "/etc/systemd/hwdb/hwdb.bin\0" + "/etc/udev/hwdb.bin\0" + "/usr/lib/systemd/hwdb/hwdb.bin\0" #ifdef HAVE_SPLIT_USR - "/lib/systemd/hwdb/hwdb.bin\0" + "/lib/systemd/hwdb/hwdb.bin\0" #endif - UDEVLIBEXECDIR "/hwdb.bin\0"; + UDEVLIBEXECDIR "/hwdb.bin\0"; _public_ int sd_hwdb_new(sd_hwdb **ret) { _cleanup_hwdb_unref_ sd_hwdb *hwdb = NULL; diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c index 0eadc8c74..265c7c7db 100644 --- a/src/libsystemd/sd-login/sd-login.c +++ b/src/libsystemd/sd-login/sd-login.c @@ -35,6 +35,16 @@ #include "hostname-util.h" #include "sd-login.h" +/* Error codes: + * + * invalid input parameters → -EINVAL + * invalid fd → -EBADF + * process does not exist → -ESRCH + * cgroup does not exist → -ENOENT + * machine, session does not exist → -ENXIO + * requested metadata on object is missing → -ENODATA + */ + _public_ int sd_pid_get_session(pid_t pid, char **session) { assert_return(pid >= 0, -EINVAL); @@ -91,6 +101,32 @@ _public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid) { return cg_pid_get_owner_uid(pid, uid); } +_public_ int sd_pid_get_cgroup(pid_t pid, char **cgroup) { + char *c; + int r; + + assert_return(pid >= 0, -EINVAL); + assert_return(cgroup, -EINVAL); + + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &c); + if (r < 0) + return r; + + /* The internal APIs return the empty string for the root + * cgroup, let's return the "/" in the public APIs instead, as + * that's easier and less ambigious for people to grok. */ + if (isempty(c)) { + free(c); + c = strdup("/"); + if (!c) + return -ENOMEM; + + } + + *cgroup = c; + return 0; +} + _public_ int sd_peer_get_session(int fd, char **session) { struct ucred ucred = {}; int r; @@ -189,7 +225,23 @@ _public_ int sd_peer_get_user_slice(int fd, char **slice) { return cg_pid_get_user_slice(ucred.pid, slice); } +_public_ int sd_peer_get_cgroup(int fd, char **cgroup) { + struct ucred ucred; + int r; + + assert_return(fd >= 0, -EBADF); + assert_return(cgroup, -EINVAL); + + r = getpeercred(fd, &ucred); + if (r < 0) + return r; + + return sd_pid_get_cgroup(ucred.pid, cgroup); +} + static int file_of_uid(uid_t uid, char **p) { + + assert_return(uid_is_valid(uid), -EINVAL); assert(p); if (asprintf(p, "/run/systemd/users/" UID_FMT, uid) < 0) @@ -216,11 +268,15 @@ _public_ int sd_uid_get_state(uid_t uid, char**state) { if (!s) return -ENOMEM; - } else if (r < 0) { + } + if (r < 0) { free(s); return r; - } else if (!s) + } + if (isempty(s)) { + free(s); return -EIO; + } *state = s; return 0; @@ -237,11 +293,12 @@ _public_ int sd_uid_get_display(uid_t uid, char **session) { return r; r = parse_env_file(p, NEWLINE, "DISPLAY", &s, NULL); + if (r == -ENOENT) + return -ENODATA; if (r < 0) return r; - if (isempty(s)) - return -ENOENT; + return -ENODATA; *session = s; s = NULL; @@ -249,35 +306,63 @@ _public_ int sd_uid_get_display(uid_t uid, char **session) { return 0; } +static int file_of_seat(const char *seat, char **_p) { + char *p; + int r; + + assert(_p); + + if (seat) { + if (!filename_is_valid(seat)) + return -EINVAL; + + p = strappend("/run/systemd/seats/", seat); + } else { + _cleanup_free_ char *buf = NULL; + + r = sd_session_get_seat(NULL, &buf); + if (r < 0) + return r; + + p = strappend("/run/systemd/seats/", buf); + } + + if (!p) + return -ENOMEM; + + *_p = p; + p = NULL; + return 0; +} + _public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat) { _cleanup_free_ char *t = NULL, *s = NULL, *p = NULL; size_t l; int r; const char *word, *variable, *state; - assert_return(seat, -EINVAL); + assert_return(uid_is_valid(uid), -EINVAL); - variable = require_active ? "ACTIVE_UID" : "UIDS"; + r = file_of_seat(seat, &p); + if (r < 0) + return r; - p = strappend("/run/systemd/seats/", seat); - if (!p) - return -ENOMEM; + variable = require_active ? "ACTIVE_UID" : "UIDS"; r = parse_env_file(p, NEWLINE, variable, &s, NULL); - + if (r == -ENOENT) + return 0; if (r < 0) return r; - - if (!s) - return -EIO; + if (isempty(s)) + return 0; if (asprintf(&t, UID_FMT, uid) < 0) return -ENOMEM; - FOREACH_WORD(word, l, s, state) { + FOREACH_WORD(word, l, s, state) if (strneq(t, word, l)) return 1; - } return 0; } @@ -287,31 +372,22 @@ static int uid_get_array(uid_t uid, const char *variable, char ***array) { char **a; int r; + assert(variable); + r = file_of_uid(uid, &p); if (r < 0) return r; - r = parse_env_file(p, NEWLINE, - variable, &s, - NULL); - if (r < 0) { - if (r == -ENOENT) { - if (array) - *array = NULL; - return 0; - } - - return r; - } - - if (!s) { + r = parse_env_file(p, NEWLINE, variable, &s, NULL); + if (r == -ENOENT || (r >= 0 && isempty(s))) { if (array) *array = NULL; return 0; } + if (r < 0) + return r; a = strv_split(s, " "); - if (!a) return -ENOMEM; @@ -373,37 +449,39 @@ static int file_of_session(const char *session, char **_p) { } _public_ int sd_session_is_active(const char *session) { - int r; _cleanup_free_ char *p = NULL, *s = NULL; + int r; r = file_of_session(session, &p); if (r < 0) return r; r = parse_env_file(p, NEWLINE, "ACTIVE", &s, NULL); + if (r == -ENOENT) + return -ENXIO; if (r < 0) return r; - - if (!s) + if (isempty(s)) return -EIO; return parse_boolean(s); } _public_ int sd_session_is_remote(const char *session) { - int r; _cleanup_free_ char *p = NULL, *s = NULL; + int r; r = file_of_session(session, &p); if (r < 0) return r; r = parse_env_file(p, NEWLINE, "REMOTE", &s, NULL); + if (r == -ENOENT) + return -ENXIO; if (r < 0) return r; - - if (!s) - return -EIO; + if (isempty(s)) + return -ENODATA; return parse_boolean(s); } @@ -419,9 +497,11 @@ _public_ int sd_session_get_state(const char *session, char **state) { return r; r = parse_env_file(p, NEWLINE, "STATE", &s, NULL); + if (r == -ENOENT) + return -ENXIO; if (r < 0) return r; - else if (!s) + if (isempty(s)) return -EIO; *state = s; @@ -441,10 +521,11 @@ _public_ int sd_session_get_uid(const char *session, uid_t *uid) { return r; r = parse_env_file(p, NEWLINE, "UID", &s, NULL); + if (r == -ENOENT) + return -ENXIO; if (r < 0) return r; - - if (!s) + if (isempty(s)) return -EIO; return parse_uid(s, uid); @@ -455,17 +536,19 @@ static int session_get_string(const char *session, const char *field, char **val int r; assert_return(value, -EINVAL); + assert(field); r = file_of_session(session, &p); if (r < 0) return r; r = parse_env_file(p, NEWLINE, field, &s, NULL); + if (r == -ENOENT) + return -ENXIO; if (r < 0) return r; - if (isempty(s)) - return -ENOENT; + return -ENODATA; *value = s; s = NULL; @@ -485,6 +568,8 @@ _public_ int sd_session_get_vt(const char *session, unsigned *vtnr) { unsigned u; int r; + assert_return(vtnr, -EINVAL); + r = session_get_string(session, "VTNR", &vtnr_string); if (r < 0) return r; @@ -540,32 +625,6 @@ _public_ int sd_session_get_remote_host(const char *session, char **remote_host) return session_get_string(session, "REMOTE_HOST", remote_host); } -static int file_of_seat(const char *seat, char **_p) { - char *p; - int r; - - assert(_p); - - if (seat) - p = strappend("/run/systemd/seats/", seat); - else { - _cleanup_free_ char *buf = NULL; - - r = sd_session_get_seat(NULL, &buf); - if (r < 0) - return r; - - p = strappend("/run/systemd/seats/", buf); - } - - if (!p) - return -ENOMEM; - - *_p = p; - p = NULL; - return 0; -} - _public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) { _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL; int r; @@ -580,14 +639,16 @@ _public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) { "ACTIVE", &s, "ACTIVE_UID", &t, NULL); + if (r == -ENOENT) + return -ENXIO; if (r < 0) return r; if (session && !s) - return -ENOENT; + return -ENODATA; if (uid && !t) - return -ENOENT; + return -ENODATA; if (uid && t) { r = parse_uid(t, uid); @@ -618,7 +679,8 @@ _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **ui "SESSIONS", &s, "ACTIVE_SESSIONS", &t, NULL); - + if (r == -ENOENT) + return -ENXIO; if (r < 0) return r; @@ -650,7 +712,6 @@ _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **ui return -ENOMEM; r = parse_uid(k, b + i); - if (r < 0) continue; @@ -681,7 +742,7 @@ static int seat_get_can(const char *seat, const char *variable) { _cleanup_free_ char *p = NULL, *s = NULL; int r; - assert_return(variable, -EINVAL); + assert(variable); r = file_of_seat(seat, &p); if (r < 0) @@ -690,10 +751,12 @@ static int seat_get_can(const char *seat, const char *variable) { r = parse_env_file(p, NEWLINE, variable, &s, NULL); + if (r == -ENOENT) + return -ENXIO; if (r < 0) return r; - if (!s) - return 0; + if (isempty(s)) + return -ENODATA; return parse_boolean(s); } @@ -817,6 +880,8 @@ _public_ int sd_machine_get_class(const char *machine, char **class) { p = strjoina("/run/systemd/machines/", machine); r = parse_env_file(p, NEWLINE, "CLASS", &c, NULL); + if (r == -ENOENT) + return -ENXIO; if (r < 0) return r; if (!c) @@ -840,6 +905,8 @@ _public_ int sd_machine_get_ifindices(const char *machine, int **ifindices) { p = strjoina("/run/systemd/machines/", machine); r = parse_env_file(p, NEWLINE, "NETIF", &netif, NULL); + if (r == -ENOENT) + return -ENXIO; if (r < 0) return r; if (!netif) { diff --git a/src/libsystemd/sd-login/test-login.c b/src/libsystemd/sd-login/test-login.c index 05affa442..f734ce9ee 100644 --- a/src/libsystemd/sd-login/test-login.c +++ b/src/libsystemd/sd-login/test-login.c @@ -33,7 +33,7 @@ static void test_login(void) { _cleanup_free_ char *pp = NULL, *qq = NULL; int r, k; uid_t u, u2; - char *seat, *type, *class, *display, *remote_user, *remote_host; + char *seat, *type, *class, *display, *remote_user, *remote_host, *display_session, *cgroup; char *session; char *state; char *session2; @@ -50,6 +50,16 @@ static void test_login(void) { assert_se(sd_pid_get_owner_uid(0, &u2) == 0); printf("user = "UID_FMT"\n", u2); + assert_se(sd_pid_get_cgroup(0, &cgroup) == 0); + printf("cgroup = %s\n", cgroup); + free(cgroup); + + display_session = NULL; + r = sd_uid_get_display(u2, &display_session); + assert_se(r >= 0 || r == -ENODATA); + printf("user's display session = %s\n", strna(display_session)); + free(display_session); + assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == 0); sd_peer_get_session(pair[0], &pp); sd_peer_get_session(pair[1], &qq); @@ -100,16 +110,22 @@ static void test_login(void) { printf("class = %s\n", class); free(class); - assert_se(sd_session_get_display(session, &display) >= 0); - printf("display = %s\n", display); + display = NULL; + r = sd_session_get_display(session, &display); + assert_se(r >= 0 || r == -ENODATA); + printf("display = %s\n", strna(display)); free(display); - assert_se(sd_session_get_remote_user(session, &remote_user) >= 0); - printf("remote_user = %s\n", remote_user); + remote_user = NULL; + r = sd_session_get_remote_user(session, &remote_user); + assert_se(r >= 0 || r == -ENODATA); + printf("remote_user = %s\n", strna(remote_user)); free(remote_user); - assert_se(sd_session_get_remote_host(session, &remote_host) >= 0); - printf("remote_host = %s\n", remote_host); + remote_host = NULL; + r = sd_session_get_remote_host(session, &remote_host); + assert_se(r >= 0 || r == -ENODATA); + printf("remote_host = %s\n", strna(remote_host)); free(remote_host); assert_se(sd_session_get_seat(session, &seat) >= 0); diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c index b63fdf8fc..87d87359b 100644 --- a/src/libsystemd/sd-network/sd-network.c +++ b/src/libsystemd/sd-network/sd-network.c @@ -214,6 +214,28 @@ _public_ int sd_network_link_get_lldp(int ifindex, char **lldp) { return 0; } +int sd_network_link_get_timezone(int ifindex, char **ret) { + _cleanup_free_ char *s = NULL, *p = NULL; + int r; + + assert_return(ifindex > 0, -EINVAL); + assert_return(ret, -EINVAL); + + if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0) + return -ENOMEM; + + r = parse_env_file(p, NEWLINE, "TIMEZONE", &s, NULL); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + if (isempty(s)) + return -ENODATA; + + *ret = s; + s = NULL; + return 0; +} static int network_get_link_strv(const char *key, int ifindex, char ***ret) { _cleanup_free_ char *p = NULL, *s = NULL; diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c index b0dc82259..888b372c9 100644 --- a/src/libsystemd/sd-resolve/sd-resolve.c +++ b/src/libsystemd/sd-resolve/sd-resolve.c @@ -48,9 +48,6 @@ typedef enum { RESPONSE_ADDRINFO, REQUEST_NAMEINFO, RESPONSE_NAMEINFO, - REQUEST_RES_QUERY, - REQUEST_RES_SEARCH, - RESPONSE_RES, REQUEST_TERMINATE, RESPONSE_DIED } QueryType; @@ -104,12 +101,10 @@ struct sd_resolve_query { int _h_errno; struct addrinfo *addrinfo; char *serv, *host; - unsigned char *answer; union { sd_resolve_getaddrinfo_handler_t getaddrinfo_handler; sd_resolve_getnameinfo_handler_t getnameinfo_handler; - sd_resolve_res_handler_t res_handler; }; void *userdata; @@ -166,33 +161,16 @@ typedef struct NameInfoResponse { int _h_errno; } NameInfoResponse; -typedef struct ResRequest { - struct RHeader header; - int class; - int type; - size_t dname_len; -} ResRequest; - -typedef struct ResResponse { - struct RHeader header; - int ret; - int _errno; - int _h_errno; -} ResResponse; - typedef union Packet { RHeader rheader; AddrInfoRequest addrinfo_request; AddrInfoResponse addrinfo_response; NameInfoRequest nameinfo_request; NameInfoResponse nameinfo_response; - ResRequest res_request; - ResResponse res_response; } Packet; static int getaddrinfo_done(sd_resolve_query* q); static int getnameinfo_done(sd_resolve_query *q); -static int res_query_done(sd_resolve_query* q); static void resolve_query_disconnect(sd_resolve_query *q); @@ -343,38 +321,6 @@ static int send_nameinfo_reply( return 0; } -static int send_res_reply(int out_fd, unsigned id, const unsigned char *answer, int ret, int _errno, int _h_errno) { - - ResResponse resp = { - .header.type = RESPONSE_RES, - .header.id = id, - .ret = ret, - ._errno = _errno, - ._h_errno = _h_errno, - }; - - struct msghdr mh = {}; - struct iovec iov[2]; - size_t l; - - assert(out_fd >= 0); - - l = ret > 0 ? (size_t) ret : 0; - - resp.header.length = sizeof(ResResponse) + l; - - iov[0] = (struct iovec) { .iov_base = &resp, .iov_len = sizeof(ResResponse) }; - iov[1] = (struct iovec) { .iov_base = (void*) answer, .iov_len = l }; - - mh.msg_iov = iov; - mh.msg_iovlen = ELEMENTSOF(iov); - - if (sendmsg(out_fd, &mh, MSG_NOSIGNAL) < 0) - return -errno; - - return 0; -} - static int handle_request(int out_fd, const Packet *packet, size_t length) { const RHeader *req; @@ -437,29 +383,6 @@ static int handle_request(int out_fd, const Packet *packet, size_t length) { errno, h_errno); } - case REQUEST_RES_QUERY: - case REQUEST_RES_SEARCH: { - const ResRequest *res_req = &packet->res_request; - union { - HEADER header; - uint8_t space[BUFSIZE]; - } answer; - const char *dname; - int ret; - - assert(length >= sizeof(ResRequest)); - assert(length == sizeof(ResRequest) + res_req->dname_len); - - dname = (const char *) res_req + sizeof(ResRequest); - - if (req->type == REQUEST_RES_QUERY) - ret = res_query(dname, res_req->class, res_req->type, (unsigned char *) &answer, BUFSIZE); - else - ret = res_search(dname, res_req->class, res_req->type, (unsigned char *) &answer, BUFSIZE); - - return send_res_reply(out_fd, req->id, (unsigned char *) &answer, ret, errno, h_errno); - } - case REQUEST_TERMINATE: /* Quit */ return -ECONNRESET; @@ -752,11 +675,6 @@ static int complete_query(sd_resolve *resolve, sd_resolve_query *q) { r = getnameinfo_done(q); break; - case REQUEST_RES_QUERY: - case REQUEST_RES_SEARCH: - r = res_query_done(q); - break; - default: assert_not_reached("Cannot complete unknown query type"); } @@ -923,28 +841,6 @@ static int handle_response(sd_resolve *resolve, const Packet *packet, size_t len return complete_query(resolve, q); } - case RESPONSE_RES: { - const ResResponse *res_resp = &packet->res_response; - - assert(length >= sizeof(ResResponse)); - assert(q->type == REQUEST_RES_QUERY || q->type == REQUEST_RES_SEARCH); - - q->ret = res_resp->ret; - q->_errno = res_resp->_errno; - q->_h_errno = res_resp->_h_errno; - - if (res_resp->ret >= 0) { - q->answer = memdup((const char *)resp + sizeof(ResResponse), res_resp->ret); - if (!q->answer) { - q->ret = -1; - q->_errno = ENOMEM; - q->_h_errno = 0; - } - } - - return complete_query(resolve, q); - } - default: return 0; } @@ -1181,79 +1077,6 @@ static int getnameinfo_done(sd_resolve_query *q) { return q->getnameinfo_handler(q, q->ret, q->host, q->serv, q->userdata); } -static int resolve_res( - sd_resolve *resolve, - sd_resolve_query **_q, - QueryType qtype, - const char *dname, - int class, int type, - sd_resolve_res_handler_t callback, void *userdata) { - - struct msghdr mh = {}; - struct iovec iov[2]; - ResRequest req = {}; - sd_resolve_query *q; - int r; - - assert_return(resolve, -EINVAL); - assert_return(dname, -EINVAL); - assert_return(callback, -EINVAL); - assert_return(!resolve_pid_changed(resolve), -ECHILD); - - r = alloc_query(resolve, !_q, &q); - if (r < 0) - return r; - - q->type = qtype; - q->res_handler = callback; - q->userdata = userdata; - - req.dname_len = strlen(dname) + 1; - req.class = class; - req.type = type; - - req.header.id = q->id; - req.header.type = qtype; - req.header.length = sizeof(ResRequest) + req.dname_len; - - iov[0] = (struct iovec) { .iov_base = &req, .iov_len = sizeof(ResRequest) }; - iov[1] = (struct iovec) { .iov_base = (void*) dname, .iov_len = req.dname_len }; - - mh.msg_iov = iov; - mh.msg_iovlen = 2; - - if (sendmsg(resolve->fds[REQUEST_SEND_FD], &mh, MSG_NOSIGNAL) < 0) { - sd_resolve_query_unref(q); - return -errno; - } - - resolve->n_outstanding++; - - if (_q) - *_q = q; - - return 0; -} - -_public_ int sd_resolve_res_query(sd_resolve *resolve, sd_resolve_query** q, const char *dname, int class, int type, sd_resolve_res_handler_t callback, void *userdata) { - return resolve_res(resolve, q, REQUEST_RES_QUERY, dname, class, type, callback, userdata); -} - -_public_ int sd_resolve_res_search(sd_resolve *resolve, sd_resolve_query** q, const char *dname, int class, int type, sd_resolve_res_handler_t callback, void *userdata) { - return resolve_res(resolve, q, REQUEST_RES_SEARCH, dname, class, type, callback, userdata); -} - -static int res_query_done(sd_resolve_query* q) { - assert(q); - assert(q->done); - assert(q->res_handler); - - errno = q->_errno; - h_errno = q->_h_errno; - - return q->res_handler(q, q->ret, q->answer, q->userdata); -} - _public_ sd_resolve_query* sd_resolve_query_ref(sd_resolve_query *q) { assert_return(q, NULL); @@ -1310,7 +1133,6 @@ static void resolve_query_free(sd_resolve_query *q) { resolve_freeaddrinfo(q->addrinfo); free(q->host); free(q->serv); - free(q->answer); free(q); } diff --git a/src/libsystemd/sd-resolve/test-resolve.c b/src/libsystemd/sd-resolve/test-resolve.c index 354a4071b..e8056529f 100644 --- a/src/libsystemd/sd-resolve/test-resolve.c +++ b/src/libsystemd/sd-resolve/test-resolve.c @@ -67,59 +67,8 @@ static int getnameinfo_handler(sd_resolve_query *q, int ret, const char *host, c return 0; } -static int res_handler(sd_resolve_query *q, int ret, unsigned char *answer, void *userdata) { - int qdcount, ancount, len; - const unsigned char *pos = answer + sizeof(HEADER); - unsigned char *end = answer + ret; - HEADER *head = (HEADER *) answer; - char name[256]; - assert_se(q); - - if (ret < 0) { - log_error("res_query() error: %s %i", strerror(errno), errno); - return 0; - } - - if (ret == 0) { - log_error("No reply for SRV lookup"); - return 0; - } - - qdcount = ntohs(head->qdcount); - ancount = ntohs(head->ancount); - - printf("%d answers for srv lookup:\n", ancount); - - /* Ignore the questions */ - while (qdcount-- > 0 && (len = dn_expand(answer, end, pos, name, 255)) >= 0) { - assert_se(len >= 0); - pos += len + QFIXEDSZ; - } - - /* Parse the answers */ - while (ancount-- > 0 && (len = dn_expand(answer, end, pos, name, 255)) >= 0) { - /* Ignore the initial string */ - uint16_t pref, weight, port; - assert_se(len >= 0); - pos += len; - /* Ignore type, ttl, class and dlen */ - pos += 10; - - GETSHORT(pref, pos); - GETSHORT(weight, pos); - GETSHORT(port, pos); - len = dn_expand(answer, end, pos, name, 255); - printf("\tpreference: %2d weight: %2d port: %d host: %s\n", - pref, weight, port, name); - - pos += len; - } - - return 0; -} - int main(int argc, char *argv[]) { - _cleanup_resolve_query_unref_ sd_resolve_query *q1 = NULL, *q2 = NULL, *q3 = NULL; + _cleanup_resolve_query_unref_ sd_resolve_query *q1 = NULL, *q2 = NULL; _cleanup_resolve_unref_ sd_resolve *resolve = NULL; int r = 0; @@ -150,15 +99,9 @@ int main(int argc, char *argv[]) { if (r < 0) log_error_errno(r, "sd_resolve_getnameinfo(): %m"); - /* Make a res_query() call */ - r = sd_resolve_res_query(resolve, &q3, "_xmpp-client._tcp.gmail.com", C_IN, T_SRV, res_handler, NULL); - if (r < 0) - log_error_errno(r, "sd_resolve_res_query(): %m"); - - /* Wait until the three queries are completed */ + /* Wait until the two queries are completed */ while (sd_resolve_query_is_done(q1) == 0 || - sd_resolve_query_is_done(q2) == 0 || - sd_resolve_query_is_done(q3) == 0) { + sd_resolve_query_is_done(q2) == 0) { r = sd_resolve_wait(resolve, (uint64_t) -1); if (r < 0) { diff --git a/src/locale/localed.c b/src/locale/localed.c index e9489f04c..e304588c5 100644 --- a/src/locale/localed.c +++ b/src/locale/localed.c @@ -960,6 +960,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-locale", + NULL, interactive, UID_INVALID, &c->polkit_registry, @@ -1049,6 +1050,7 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-keyboard", + NULL, interactive, UID_INVALID, &c->polkit_registry, @@ -1092,6 +1094,7 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro } #ifdef HAVE_XKBCOMMON +_printf_(3, 0) static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) { const char *fmt; @@ -1179,6 +1182,7 @@ static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_err m, CAP_SYS_ADMIN, "org.freedesktop.locale1.set-keyboard", + NULL, interactive, UID_INVALID, &c->polkit_registry, diff --git a/src/login/70-power-switch.rules b/src/login/70-power-switch.rules index 71f9fe6c7..36d2a3eb4 100644 --- a/src/login/70-power-switch.rules +++ b/src/login/70-power-switch.rules @@ -11,6 +11,7 @@ SUBSYSTEM=="input", KERNEL=="event*", SUBSYSTEMS=="acpi", TAG+="power-switch" SUBSYSTEM=="input", KERNEL=="event*", KERNELS=="thinkpad_acpi", TAG+="power-switch" SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="twl4030_pwrbutton", TAG+="power-switch" SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="tps65217_pwr_but", TAG+="power-switch" +SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="* WMI hotkeys", TAG+="power-switch" SUBSYSTEM=="input", KERNEL=="event*", \ SUBSYSTEMS=="platform", DRIVERS=="gpio-keys", ATTRS{keys}=="116", TAG+="power-switch" diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 5fa98e069..a7e64071c 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -263,7 +263,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit if (isempty(cgroup)) return 0; - if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0) + if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0) return 0; c = columns(); diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 5b2b36b9c..b6fa50aa5 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -942,6 +942,7 @@ static int method_lock_sessions(sd_bus_message *message, void *userdata, sd_bus_ message, CAP_SYS_ADMIN, "org.freedesktop.login1.lock-sessions", + NULL, false, UID_INVALID, &m->polkit_registry, @@ -1096,6 +1097,7 @@ static int method_set_user_linger(sd_bus_message *message, void *userdata, sd_bu message, CAP_SYS_ADMIN, "org.freedesktop.login1.set-user-linger", + NULL, interactive, UID_INVALID, &m->polkit_registry, @@ -1268,6 +1270,7 @@ static int method_attach_device(sd_bus_message *message, void *userdata, sd_bus_ message, CAP_SYS_ADMIN, "org.freedesktop.login1.attach-device", + NULL, interactive, UID_INVALID, &m->polkit_registry, @@ -1299,6 +1302,7 @@ static int method_flush_devices(sd_bus_message *message, void *userdata, sd_bus_ message, CAP_SYS_ADMIN, "org.freedesktop.login1.flush-devices", + NULL, interactive, UID_INVALID, &m->polkit_registry, @@ -1339,8 +1343,7 @@ static int bus_manager_log_shutdown( InhibitWhat w, const char *unit_name) { - const char *p; - const char *q; + const char *p, *q; assert(m); assert(unit_name); @@ -1365,8 +1368,8 @@ static int bus_manager_log_shutdown( q = NULL; } - if (m->wall_message) - p = strjoina(p, " (", m->wall_message, ")", NULL); + if (!isempty(m->wall_message)) + p = strjoina(p, " (", m->wall_message, ")"); return log_struct(LOG_NOTICE, LOG_MESSAGE_ID(SD_MESSAGE_SHUTDOWN), @@ -1650,7 +1653,7 @@ static int verify_shutdown_creds( blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL); if (multiple_sessions && action_multiple_sessions) { - r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_multiple_sessions, interactive, UID_INVALID, &m->polkit_registry, error); + r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, interactive, UID_INVALID, &m->polkit_registry, error); if (r < 0) return r; if (r == 0) @@ -1658,7 +1661,7 @@ static int verify_shutdown_creds( } if (blocked && action_ignore_inhibit) { - r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, interactive, UID_INVALID, &m->polkit_registry, error); + r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, interactive, UID_INVALID, &m->polkit_registry, error); if (r < 0) return r; if (r == 0) @@ -1666,7 +1669,7 @@ static int verify_shutdown_creds( } if (!multiple_sessions && !blocked && action) { - r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action, interactive, UID_INVALID, &m->polkit_registry, error); + r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action, NULL, interactive, UID_INVALID, &m->polkit_registry, error); if (r < 0) return r; if (r == 0) @@ -1786,10 +1789,9 @@ static int nologin_timeout_handler( } static int update_schedule_file(Manager *m) { - - int r; + _cleanup_free_ char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *t = NULL, *temp_path = NULL; + int r; assert(m); @@ -1797,10 +1799,6 @@ static int update_schedule_file(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to create shutdown subdirectory: %m"); - t = cescape(m->wall_message); - if (!t) - return log_oom(); - r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path); if (r < 0) return log_error_errno(r, "Failed to save information about scheduled shutdowns: %m"); @@ -1815,8 +1813,17 @@ static int update_schedule_file(Manager *m) { m->enable_wall_messages, m->scheduled_shutdown_type); - if (!isempty(m->wall_message)) + if (!isempty(m->wall_message)) { + _cleanup_free_ char *t; + + t = cescape(m->wall_message); + if (!t) { + r = -ENOMEM; + goto fail; + } + fprintf(f, "WALL_MESSAGE=%s\n", t); + } r = fflush_and_check(f); if (r < 0) @@ -2084,7 +2091,7 @@ static int method_can_shutdown_or_sleep( blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL); if (multiple_sessions) { - r = bus_test_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, UID_INVALID, &challenge, error); + r = bus_test_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, UID_INVALID, &challenge, error); if (r < 0) return r; @@ -2097,7 +2104,7 @@ static int method_can_shutdown_or_sleep( } if (blocked) { - r = bus_test_polkit(message, CAP_SYS_BOOT, action_ignore_inhibit, UID_INVALID, &challenge, error); + r = bus_test_polkit(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, UID_INVALID, &challenge, error); if (r < 0) return r; @@ -2113,7 +2120,7 @@ static int method_can_shutdown_or_sleep( /* If neither inhibit nor multiple sessions * apply then just check the normal policy */ - r = bus_test_polkit(message, CAP_SYS_BOOT, action, UID_INVALID, &challenge, error); + r = bus_test_polkit(message, CAP_SYS_BOOT, action, NULL, UID_INVALID, &challenge, error); if (r < 0) return r; @@ -2232,6 +2239,7 @@ static int method_set_reboot_to_firmware_setup( r = bus_verify_polkit_async(message, CAP_SYS_ADMIN, "org.freedesktop.login1.set-reboot-to-firmware-setup", + NULL, false, UID_INVALID, &m->polkit_registry, @@ -2270,6 +2278,7 @@ static int method_can_reboot_to_firmware_setup( r = bus_test_polkit(message, CAP_SYS_ADMIN, "org.freedesktop.login1.set-reboot-to-firmware-setup", + NULL, UID_INVALID, &challenge, error); @@ -2294,7 +2303,7 @@ static int method_set_wall_message( int r; Manager *m = userdata; char *wall_message; - bool enable_wall_messages; + int enable_wall_messages; assert(message); assert(m); @@ -2306,19 +2315,24 @@ static int method_set_wall_message( r = bus_verify_polkit_async(message, CAP_SYS_ADMIN, "org.freedesktop.login1.set-wall-message", + NULL, false, UID_INVALID, &m->polkit_registry, error); - if (r < 0) return r; if (r == 0) return 1; /* Will call us back */ - r = free_and_strdup(&m->wall_message, wall_message); - if (r < 0) - return log_oom(); + if (isempty(wall_message)) + m->wall_message = mfree(m->wall_message); + else { + r = free_and_strdup(&m->wall_message, wall_message); + if (r < 0) + return log_oom(); + } + m->enable_wall_messages = enable_wall_messages; return sd_bus_reply_method_return(message, NULL); @@ -2373,6 +2387,7 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error w == INHIBIT_HANDLE_SUSPEND_KEY ? "org.freedesktop.login1.inhibit-handle-suspend-key" : w == INHIBIT_HANDLE_HIBERNATE_KEY ? "org.freedesktop.login1.inhibit-handle-hibernate-key" : "org.freedesktop.login1.inhibit-handle-lid-switch", + NULL, false, UID_INVALID, &m->polkit_registry, diff --git a/src/login/logind-seat-dbus.c b/src/login/logind-seat-dbus.c index ce67ffde3..346e1d2ce 100644 --- a/src/login/logind-seat-dbus.c +++ b/src/login/logind-seat-dbus.c @@ -204,6 +204,7 @@ int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_er message, CAP_KILL, "org.freedesktop.login1.manage", + NULL, false, UID_INVALID, &s->manager->polkit_registry, diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c index 563153e2d..e6b4ccd7c 100644 --- a/src/login/logind-session-dbus.c +++ b/src/login/logind-session-dbus.c @@ -191,6 +191,7 @@ int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus message, CAP_KILL, "org.freedesktop.login1.manage", + NULL, false, s->user->uid, &s->manager->polkit_registry, @@ -232,6 +233,7 @@ int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_erro message, CAP_SYS_ADMIN, "org.freedesktop.login1.lock-sessions", + NULL, false, s->user->uid, &s->manager->polkit_registry, @@ -306,6 +308,7 @@ int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro message, CAP_KILL, "org.freedesktop.login1.manage", + NULL, false, s->user->uid, &s->manager->polkit_registry, diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c index 36c0e8626..20ea2fbdc 100644 --- a/src/login/logind-user-dbus.c +++ b/src/login/logind-user-dbus.c @@ -179,6 +179,7 @@ int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_er message, CAP_KILL, "org.freedesktop.login1.manage", + NULL, false, u->uid, &u->manager->polkit_registry, @@ -207,6 +208,7 @@ int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error * message, CAP_KILL, "org.freedesktop.login1.manage", + NULL, false, u->uid, &u->manager->polkit_registry, diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index f83d18b03..f66f1ce84 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -179,24 +179,37 @@ static int export_legacy_dbus_address( const char *runtime) { _cleanup_free_ char *s = NULL; - int r; + int r = PAM_BUF_ERR; - /* skip export if kdbus is not active */ - if (!is_kdbus_available()) - return PAM_SUCCESS; + if (is_kdbus_available()) { + if (asprintf(&s, KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT, uid, runtime) < 0) + goto error; + } else { + /* FIXME: We *really* should move the access() check into the + * daemons that spawn dbus-daemon, instead of forcing + * DBUS_SESSION_BUS_ADDRESS= here. */ + + s = strjoin(runtime, "/bus", NULL); + if (!s) + goto error; + + if (access(s, F_OK) < 0) + return PAM_SUCCESS; - if (asprintf(&s, KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT, uid, runtime) < 0) { - pam_syslog(handle, LOG_ERR, "Failed to set bus variable."); - return PAM_BUF_ERR; + s = mfree(s); + if (asprintf(&s, UNIX_USER_BUS_ADDRESS_FMT, runtime) < 0) + goto error; } r = pam_misc_setenv(handle, "DBUS_SESSION_BUS_ADDRESS", s, 0); - if (r != PAM_SUCCESS) { - pam_syslog(handle, LOG_ERR, "Failed to set bus variable."); - return r; - } + if (r != PAM_SUCCESS) + goto error; return PAM_SUCCESS; + +error: + pam_syslog(handle, LOG_ERR, "Failed to set bus variable."); + return r; } _public_ PAM_EXTERN int pam_sm_open_session( diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c index 95d7bca4b..2453a9ff0 100644 --- a/src/machine/image-dbus.c +++ b/src/machine/image-dbus.c @@ -43,6 +43,7 @@ int bus_image_method_remove( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", + NULL, false, UID_INVALID, &m->polkit_registry, @@ -83,6 +84,7 @@ int bus_image_method_rename( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", + NULL, false, UID_INVALID, &m->polkit_registry, @@ -123,6 +125,7 @@ int bus_image_method_clone( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", + NULL, false, UID_INVALID, &m->polkit_registry, @@ -158,6 +161,7 @@ int bus_image_method_mark_read_only( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", + NULL, false, UID_INVALID, &m->polkit_registry, @@ -194,6 +198,7 @@ int bus_image_method_set_limit( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", + NULL, false, UID_INVALID, &m->polkit_registry, diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index af2b8eff0..cc3811670 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -45,6 +45,7 @@ #include "formats-util.h" #include "process-util.h" #include "env-util.h" +#include "terminal-util.h" static int property_get_id( sd_bus *bus, @@ -123,6 +124,7 @@ int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus message, CAP_KILL, "org.freedesktop.machine1.manage-machines", + NULL, false, UID_INVALID, &m->manager->polkit_registry, @@ -168,6 +170,7 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro message, CAP_KILL, "org.freedesktop.machine1.manage-machines", + NULL, false, UID_INVALID, &m->manager->polkit_registry, @@ -487,6 +490,7 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_ message, CAP_SYS_ADMIN, m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty", + NULL, false, UID_INVALID, &m->manager->polkit_registry, @@ -500,7 +504,7 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_ if (master < 0) return master; - r = ptsname_malloc(master, &pty_name); + r = ptsname_namespace(master, &pty_name); if (r < 0) return r; @@ -515,7 +519,7 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_ return sd_bus_send(NULL, reply, NULL); } -static int container_bus_new(Machine *m, sd_bus **ret) { +static int container_bus_new(Machine *m, sd_bus_error *error, sd_bus **ret) { int r; assert(m); @@ -544,6 +548,8 @@ static int container_bus_new(Machine *m, sd_bus **ret) { bus->is_system = true; r = sd_bus_start(bus); + if (r == -ENOENT) + return sd_bus_error_set_errnof(error, r, "There is no system bus in container %s.", m->name); if (r < 0) return r; @@ -576,6 +582,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu message, CAP_SYS_ADMIN, m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login", + NULL, false, UID_INVALID, &m->manager->polkit_registry, @@ -589,7 +596,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu if (master < 0) return master; - r = ptsname_malloc(master, &pty_name); + r = ptsname_namespace(master, &pty_name); if (r < 0) return r; @@ -597,10 +604,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu if (!p) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name); - if (unlockpt(master) < 0) - return -errno; - - r = container_bus_new(m, &allocated_bus); + r = container_bus_new(m, error, &allocated_bus); if (r < 0) return r; @@ -677,6 +681,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu message, CAP_SYS_ADMIN, m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell", + NULL, false, UID_INVALID, &m->manager->polkit_registry, @@ -690,7 +695,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu if (master < 0) return master; - r = ptsname_malloc(master, &pty_name); + r = ptsname_namespace(master, &pty_name); if (r < 0) return r; @@ -701,10 +706,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu utmp_id = path_startswith(pty_name, "/dev/"); assert(utmp_id); - if (unlockpt(master) < 0) - return -errno; - - r = container_bus_new(m, &allocated_bus); + r = container_bus_new(m, error, &allocated_bus); if (r < 0) return r; @@ -888,6 +890,7 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-machines", + NULL, false, UID_INVALID, &m->manager->polkit_registry, @@ -1150,6 +1153,7 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-machines", + NULL, false, UID_INVALID, &m->manager->polkit_registry, diff --git a/src/machine/machine.c b/src/machine/machine.c index 0d1b119dc..a056ec3b0 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -547,8 +547,18 @@ int machine_openpt(Machine *m, int flags) { switch (m->class) { - case MACHINE_HOST: - return posix_openpt(flags); + case MACHINE_HOST: { + int fd; + + fd = posix_openpt(flags); + if (fd < 0) + return -errno; + + if (unlockpt(fd) < 0) + return -errno; + + return fd; + } case MACHINE_CONTAINER: if (m->leader <= 0) diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 926035d18..bb8c5ac64 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -361,8 +361,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { bus, "org.freedesktop.systemd1", path, - endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : - endswith(unit, ".slice") ? "org.freedesktop.systemd1.Slice" : "org.freedesktop.systemd1.Service", + unit_dbus_interface_from_name(unit), "ControlGroup", &error, &reply, @@ -376,7 +375,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { if (r < 0) return bus_log_parse_error(r); - if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0) + if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0) return 0; c = columns(); diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 0d52c693e..eef9c5fa5 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -810,6 +810,7 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-machines", + NULL, false, UID_INVALID, &m->polkit_registry, diff --git a/src/network/networkctl.c b/src/network/networkctl.c index c9b53dc3f..786579def 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -497,7 +497,7 @@ static int link_status_one( sd_hwdb *hwdb, const char *name) { _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL; - _cleanup_free_ char *setup_state = NULL, *operational_state = NULL; + _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *tz = NULL; _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL; _cleanup_device_unref_ sd_device *d = NULL; char devid[2 + DECIMAL_STR_MAX(int)]; @@ -570,7 +570,6 @@ static int link_status_one( setup_state_to_color(setup_state, &on_color_setup, &off_color_setup); sd_network_link_get_dns(ifindex, &dns); - sd_network_link_get_ntp(ifindex, &ntp); sd_network_link_get_domains(ifindex, &domains); r = sd_network_link_get_wildcard_domain(ifindex); if (r > 0) { @@ -652,6 +651,8 @@ static int link_status_one( dump_list(" DNS: ", dns); if (!strv_isempty(domains)) dump_list(" Domain: ", domains); + + (void) sd_network_link_get_ntp(ifindex, &ntp); if (!strv_isempty(ntp)) dump_list(" NTP: ", ntp); @@ -661,6 +662,10 @@ static int link_status_one( if (!strv_isempty(carrier_bound_by)) dump_list("Carrier Bound By: ", carrier_bound_by); + (void) sd_network_link_get_timezone(ifindex, &tz); + if (tz) + printf(" Time Zone: %s", tz); + return 0; } diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c index 584a956a7..d609daafd 100644 --- a/src/network/networkd-address-pool.c +++ b/src/network/networkd-address-pool.c @@ -19,9 +19,8 @@ along with systemd; If not, see . ***/ - #include "networkd.h" -#include "networkd-link.h" +#include "networkd-address-pool.h" int address_pool_new( Manager *m, diff --git a/src/network/networkd-address-pool.h b/src/network/networkd-address-pool.h new file mode 100644 index 000000000..e6207ccce --- /dev/null +++ b/src/network/networkd-address-pool.h @@ -0,0 +1,43 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +typedef struct AddressPool AddressPool; + +#include "networkd.h" + +struct AddressPool { + Manager *manager; + + int family; + unsigned prefixlen; + + union in_addr_union in_addr; + + LIST_FIELDS(AddressPool, address_pools); +}; + +int address_pool_new(Manager *m, AddressPool **ret, int family, const union in_addr_union *u, unsigned prefixlen); +int address_pool_new_from_string(Manager *m, AddressPool **ret, int family, const char *p, unsigned prefixlen); +void address_pool_free(AddressPool *p); + +int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union *found); diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 172ca43a7..b0d296941 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -25,8 +25,10 @@ #include "util.h" #include "conf-parser.h" #include "firewall-util.h" +#include "netlink-util.h" + #include "networkd.h" -#include "networkd-link.h" +#include "networkd-address.h" static void address_init(Address *address) { assert(address); diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h new file mode 100644 index 000000000..39789a238 --- /dev/null +++ b/src/network/networkd-address.h @@ -0,0 +1,73 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "in-addr-util.h" + +typedef struct Address Address; + +#include "networkd.h" +#include "networkd-network.h" +#include "networkd-link.h" + +#define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU + +struct Address { + Network *network; + unsigned section; + + int family; + unsigned char prefixlen; + unsigned char scope; + uint32_t flags; + char *label; + + struct in_addr broadcast; + struct ifa_cacheinfo cinfo; + + union in_addr_union in_addr; + union in_addr_union in_addr_peer; + + bool ip_masquerade_done; + + LIST_FIELDS(Address, addresses); +}; + +int address_new_static(Network *network, unsigned section, Address **ret); +int address_new_dynamic(Address **ret); +void address_free(Address *address); +int address_configure(Address *address, Link *link, sd_netlink_message_handler_t callback); +int address_update(Address *address, Link *link, sd_netlink_message_handler_t callback); +int address_drop(Address *address, Link *link, sd_netlink_message_handler_t callback); +int address_establish(Address *address, Link *link); +int address_release(Address *address, Link *link); +bool address_equal(Address *a1, Address *a2); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free); +#define _cleanup_address_free_ _cleanup_(address_freep) + +int config_parse_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_broadcast(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_label(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 1b2ff7c76..4ffb01382 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -39,8 +39,7 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, r = sd_netlink_message_get_errno(m); if (r < 0 && r != -EEXIST) { - log_link_error(link, "could not set DHCPv4 route: %s", - strerror(-r)); + log_link_error_errno(link, r, "Could not set DHCPv4 route: %m"); link_enter_failed(link); } @@ -61,40 +60,25 @@ static int link_set_dhcp_routes(Link *link) { assert(link->dhcp_lease); r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway); - if (r < 0 && r != -ENOENT) { - log_link_warning(link, - "DHCP error: could not get gateway: %s", - strerror(-r)); - return r; - } + if (r < 0 && r != -ENODATA) + return log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m"); + if (r >= 0) { struct in_addr address; _cleanup_route_free_ Route *route = NULL; _cleanup_route_free_ Route *route_gw = NULL; r = sd_dhcp_lease_get_address(link->dhcp_lease, &address); - if (r < 0) { - log_link_warning(link, - "DHCP error: could not get address: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_link_warning_errno(link, r, "DHCP error: could not get address: %m"); r = route_new_dynamic(&route, RTPROT_DHCP); - if (r < 0) { - log_link_error(link, - "Could not allocate route: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate route: %m"); r = route_new_dynamic(&route_gw, RTPROT_DHCP); - if (r < 0) { - log_link_error(link, - "Could not allocate route: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate route: %m"); /* The dhcp netmask may mask out the gateway. Add an explicit * route for the gw host so that we can route no matter the @@ -107,12 +91,8 @@ static int link_set_dhcp_routes(Link *link) { route_gw->metrics = link->network->dhcp_route_metric; r = route_configure(route_gw, link, &dhcp4_route_handler); - if (r < 0) { - log_link_warning(link, - "could not set host route: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_link_warning_errno(link, r, "Could not set host route: %m"); link->dhcp4_messages ++; @@ -123,9 +103,7 @@ static int link_set_dhcp_routes(Link *link) { r = route_configure(route, link, &dhcp4_route_handler); if (r < 0) { - log_link_warning(link, - "could not set routes: %s", - strerror(-r)); + log_link_warning_errno(link, r, "Could not set routes: %m"); link_enter_failed(link); return r; } @@ -134,25 +112,17 @@ static int link_set_dhcp_routes(Link *link) { } n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes); - if (n == -ENOENT) + if (n == -ENODATA) return 0; - if (n < 0) { - log_link_warning(link, - "DHCP error: could not get routes: %s", - strerror(-n)); - - return n; - } + if (n < 0) + return log_link_warning_errno(link, n, "DHCP error: could not get routes: %m"); for (i = 0; i < n; i++) { _cleanup_route_free_ Route *route = NULL; r = route_new_dynamic(&route, RTPROT_DHCP); - if (r < 0) { - log_link_error(link, "Could not allocate route: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate route: %m"); route->family = AF_INET; route->in_addr.in = static_routes[i].gw_addr; @@ -161,12 +131,8 @@ static int link_set_dhcp_routes(Link *link) { route->metrics = link->network->dhcp_route_metric; r = route_configure(route, link, &dhcp4_route_handler); - if (r < 0) { - log_link_warning(link, - "could not set host route: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_link_warning_errno(link, r, "Could not set host route: %m"); link->dhcp4_messages ++; } @@ -270,18 +236,16 @@ static int dhcp_lease_lost(Link *link) { if (link->network->dhcp_hostname) { const char *hostname = NULL; - if (!link->network->hostname) - r = sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname); - else + if (link->network->hostname) hostname = link->network->hostname; + else + (void) sd_dhcp_lease_get_hostname(link->dhcp_lease, &hostname); - if (r >= 0 || hostname) { - r = link_set_hostname(link, hostname); + if (hostname) { + /* If a hostname was set due to the lease, then unset it now. */ + r = link_set_hostname(link, NULL); if (r < 0) - log_link_error_errno(link, r, - "Failed to set transient hostname to '%s': %m", - hostname); - + log_link_warning_errno(link, r, "Failed to reset transient hostname: %m"); } } @@ -300,8 +264,7 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, r = sd_netlink_message_get_errno(m); if (r < 0 && r != -EEXIST) { - log_link_error(link, "could not set DHCPv4 address: %s", - strerror(-r)); + log_link_error_errno(link, r, "Could not set DHCPv4 address: %m"); link_enter_failed(link); } else if (r >= 0) link_rtnl_process_address(rtnl, m, link->manager); @@ -357,45 +320,30 @@ static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) { assert(link->network); r = sd_dhcp_client_get_lease(client, &lease); - if (r < 0) { - log_link_warning(link, "DHCP error: no lease %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_link_warning_errno(link, r, "DHCP error: no lease: %m"); sd_dhcp_lease_unref(link->dhcp_lease); link->dhcp4_configured = false; link->dhcp_lease = sd_dhcp_lease_ref(lease); r = sd_dhcp_lease_get_address(lease, &address); - if (r < 0) { - log_link_warning(link, "DHCP error: no address: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_link_warning_errno(link, r, "DHCP error: no address: %m"); r = sd_dhcp_lease_get_netmask(lease, &netmask); - if (r < 0) { - log_link_warning(link, "DHCP error: no netmask: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_link_warning_errno(link, r, "DHCP error: no netmask: %m"); if (!link->network->dhcp_critical) { - r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, - &lifetime); - if (r < 0) { - log_link_warning(link, - "DHCP error: no lifetime: %s", - strerror(-r)); - return r; - } + r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime); + if (r < 0) + return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m"); } r = dhcp4_update_address(link, &address, &netmask, lifetime); if (r < 0) { - log_link_warning(link, "could not update IP address: %s", - strerror(-r)); + log_link_warning_errno(link, r, "Could not update IP address: %m"); link_enter_failed(link); return r; } @@ -417,21 +365,21 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { r = sd_dhcp_client_get_lease(client, &lease); if (r < 0) - return log_link_error_errno(link, r, "DHCP error: no lease: %m"); + return log_link_error_errno(link, r, "DHCP error: No lease: %m"); r = sd_dhcp_lease_get_address(lease, &address); if (r < 0) - return log_link_error_errno(link, r, "DHCP error: no address: %m"); + return log_link_error_errno(link, r, "DHCP error: No address: %m"); r = sd_dhcp_lease_get_netmask(lease, &netmask); if (r < 0) - return log_link_error_errno(link, r, "DHCP error: no netmask: %m"); + return log_link_error_errno(link, r, "DHCP error: No netmask: %m"); prefixlen = in_addr_netmask_to_prefixlen(&netmask); r = sd_dhcp_lease_get_router(lease, &gateway); - if (r < 0 && r != -ENOENT) - return log_link_error_errno(link, r, "DHCP error: could not get gateway: %m"); + if (r < 0 && r != -ENODATA) + return log_link_error_errno(link, r, "DHCP error: Could not get gateway: %m"); if (r >= 0) log_struct(LOG_INFO, @@ -470,18 +418,30 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { if (link->network->dhcp_hostname) { const char *hostname = NULL; - if (!link->network->hostname) - r = sd_dhcp_lease_get_hostname(lease, &hostname); - else + if (link->network->hostname) hostname = link->network->hostname; + else + (void) sd_dhcp_lease_get_hostname(lease, &hostname); - if (r >= 0 || hostname) { + if (hostname) { r = link_set_hostname(link, hostname); if (r < 0) log_link_error_errno(link, r, "Failed to set transient hostname to '%s': %m", hostname); } } + if (link->network->dhcp_timezone) { + const char *tz = NULL; + + (void) sd_dhcp_lease_get_timezone(link->dhcp_lease, &tz); + + if (tz) { + r = link_set_timezone(link, tz); + if (r < 0) + log_link_error_errno(link, r, "Failed to set timezone to '%s': %m", tz); + } + } + if (!link->network->dhcp_critical) { r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime); if (r < 0) { @@ -515,8 +475,7 @@ static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { case DHCP_EVENT_STOP: case DHCP_EVENT_IP_CHANGE: if (link->network->dhcp_critical) { - log_link_error(link, - "DHCPv4 connection considered system critical, ignoring request to reconfigure it."); + log_link_error(link, "DHCPv4 connection considered system critical, ignoring request to reconfigure it."); return; } @@ -553,13 +512,9 @@ static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { break; default: if (event < 0) - log_link_warning(link, - "DHCP error: client failed: %s", - strerror(-event)); + log_link_warning_errno(link, event, "DHCP error: Client failed: %m"); else - log_link_warning(link, - "DHCP unknown event: %d", - event); + log_link_warning(link, "DHCP unknown event: %i", event); break; } @@ -607,10 +562,10 @@ int dhcp4_configure(Link *link) { } if (link->network->dhcp_mtu) { - r = sd_dhcp_client_set_request_option(link->dhcp_client, - DHCP_OPTION_INTERFACE_MTU); - if (r < 0) - return r; + r = sd_dhcp_client_set_request_option(link->dhcp_client, + DHCP_OPTION_INTERFACE_MTU); + if (r < 0) + return r; } if (link->network->dhcp_routes) { @@ -624,6 +579,15 @@ int dhcp4_configure(Link *link) { return r; } + /* Always acquire the timezone and NTP*/ + r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_NTP_SERVER); + if (r < 0) + return r; + + r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_NEW_TZDB_TIMEZONE); + if (r < 0) + return r; + if (link->network->dhcp_sendhost) { _cleanup_free_ char *hostname = NULL; const char *hn = NULL; diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c index 0f2510e90..22efadb84 100644 --- a/src/network/networkd-fdb.c +++ b/src/network/networkd-fdb.c @@ -22,10 +22,12 @@ #include #include -#include "networkd.h" -#include "networkd-link.h" #include "conf-parser.h" #include "util.h" +#include "netlink-util.h" + +#include "networkd.h" +#include "networkd-fdb.h" /* create a new FDB entry or get an existing one. */ int fdb_entry_new_static(Network *const network, diff --git a/src/network/networkd-fdb.h b/src/network/networkd-fdb.h new file mode 100644 index 000000000..f0efb902d --- /dev/null +++ b/src/network/networkd-fdb.h @@ -0,0 +1,47 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright (C) 2014 Intel Corporation. All rights reserved. + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +typedef struct FdbEntry FdbEntry; + +#include "networkd.h" +#include "networkd-network.h" + +struct FdbEntry { + Network *network; + unsigned section; + + struct ether_addr *mac_addr; + uint16_t vlan_id; + + LIST_FIELDS(FdbEntry, static_fdb_entries); +}; + +int fdb_entry_new_static(Network *const network, const unsigned section, FdbEntry **ret); +void fdb_entry_free(FdbEntry *fdb_entry); +int fdb_entry_configure(Link *const link, FdbEntry *const fdb_entry); + +DEFINE_TRIVIAL_CLEANUP_FUNC(FdbEntry*, fdb_entry_free); +#define _cleanup_fdbentry_free_ _cleanup_(fdb_entry_freep) + +int config_parse_fdb_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_fdb_vlan_id(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-link.c b/src/network/networkd-link.c index cc9dc393c..1dc9db0fc 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -29,7 +29,10 @@ #include "socket-util.h" #include "bus-util.h" #include "udev-util.h" +#include "netlink-util.h" +#include "dhcp-lease-internal.h" #include "network-internal.h" + #include "networkd-link.h" #include "networkd-netdev.h" @@ -613,6 +616,96 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userda return 1; } +static int link_push_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) { + _cleanup_free_ struct in_addr *addresses = NULL; + size_t n_addresses = 0, n_allocated = 0; + char **a; + + log_debug("Copying DNS server information from %s", link->ifname); + + if (!link->network) + return 0; + + STRV_FOREACH(a, link->network->dns) { + struct in_addr ia; + + /* Only look for IPv4 addresses */ + if (inet_pton(AF_INET, *a, &ia) <= 0) + continue; + + if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1)) + return log_oom(); + + addresses[n_addresses++] = ia; + } + + if (link->network->dhcp_dns && + link->dhcp_lease) { + const struct in_addr *da = NULL; + int n; + + n = sd_dhcp_lease_get_dns(link->dhcp_lease, &da); + if (n > 0) { + + if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n)) + return log_oom(); + + memcpy(addresses + n_addresses, da, n * sizeof(struct in_addr)); + n_addresses += n; + } + } + + if (n_addresses <= 0) + return 0; + + return sd_dhcp_server_set_dns(s, addresses, n_addresses); +} + +static int link_push_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) { + _cleanup_free_ struct in_addr *addresses = NULL; + size_t n_addresses = 0, n_allocated = 0; + char **a; + + if (!link->network) + return 0; + + log_debug("Copying NTP server information from %s", link->ifname); + + STRV_FOREACH(a, link->network->ntp) { + struct in_addr ia; + + /* Only look for IPv4 addresses */ + if (inet_pton(AF_INET, *a, &ia) <= 0) + continue; + + if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1)) + return log_oom(); + + addresses[n_addresses++] = ia; + } + + if (link->network->dhcp_ntp && + link->dhcp_lease) { + const struct in_addr *da = NULL; + int n; + + n = sd_dhcp_lease_get_ntp(link->dhcp_lease, &da); + if (n > 0) { + + if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n)) + return log_oom(); + + memcpy(addresses + n_addresses, da, n * sizeof(struct in_addr)); + n_addresses += n; + } + } + + if (n_addresses <= 0) + return 0; + + return sd_dhcp_server_set_ntp(s, addresses, n_addresses); +} + static int link_enter_set_addresses(Link *link) { Address *ad; int r; @@ -637,8 +730,9 @@ static int link_enter_set_addresses(Link *link) { /* now that we can figure out a default address for the dhcp server, start it */ if (link_dhcp4_server_enabled(link)) { - struct in_addr pool_start; Address *address; + Link *uplink = NULL; + bool acquired_uplink = false; address = link_find_dhcp_server_address(link); if (!address) { @@ -647,16 +741,9 @@ static int link_enter_set_addresses(Link *link) { return 0; } - r = sd_dhcp_server_set_address(link->dhcp_server, - &address->in_addr.in, - address->prefixlen); - if (r < 0) - return r; - - /* offer 32 addresses starting from the address following the server address */ - pool_start.s_addr = htobe32(be32toh(address->in_addr.in.s_addr) + 1); - r = sd_dhcp_server_set_lease_pool(link->dhcp_server, - &pool_start, 32); + /* use the server address' subnet as the pool */ + r = sd_dhcp_server_configure_pool(link->dhcp_server, &address->in_addr.in, address->prefixlen, + link->network->dhcp_server_pool_offset, link->network->dhcp_server_pool_size); if (r < 0) return r; @@ -665,13 +752,83 @@ static int link_enter_set_addresses(Link *link) { &main_address->in_addr.in); if (r < 0) return r; - - r = sd_dhcp_server_set_prefixlen(link->dhcp_server, - main_address->prefixlen); - if (r < 0) - return r; */ + if (link->network->dhcp_server_max_lease_time_usec > 0) { + r = sd_dhcp_server_set_max_lease_time( + link->dhcp_server, + DIV_ROUND_UP(link->network->dhcp_server_max_lease_time_usec, USEC_PER_SEC)); + if (r < 0) + return r; + } + + if (link->network->dhcp_server_default_lease_time_usec > 0) { + r = sd_dhcp_server_set_default_lease_time( + link->dhcp_server, + DIV_ROUND_UP(link->network->dhcp_server_default_lease_time_usec, USEC_PER_SEC)); + if (r < 0) + return r; + } + + if (link->network->dhcp_server_emit_dns) { + + if (link->network->n_dhcp_server_dns > 0) + r = sd_dhcp_server_set_dns(link->dhcp_server, link->network->dhcp_server_dns, link->network->n_dhcp_server_dns); + else { + uplink = manager_find_uplink(link->manager, link); + acquired_uplink = true; + + if (!uplink) { + log_link_debug(link, "Not emitting DNS server information on link, couldn't find suitable uplink."); + r = 0; + } else + r = link_push_dns_to_dhcp_server(uplink, link->dhcp_server); + } + if (r < 0) + log_link_warning_errno(link, r, "Failed to set DNS server for DHCP server, ignoring: %m"); + } + + + if (link->network->dhcp_server_emit_ntp) { + + if (link->network->n_dhcp_server_ntp > 0) + r = sd_dhcp_server_set_ntp(link->dhcp_server, link->network->dhcp_server_ntp, link->network->n_dhcp_server_ntp); + else { + if (!acquired_uplink) + uplink = manager_find_uplink(link->manager, link); + + if (!uplink) { + log_link_debug(link, "Not emitting NTP server information on link, couldn't find suitable uplink."); + r = 0; + } else + r = link_push_ntp_to_dhcp_server(uplink, link->dhcp_server); + + } + if (r < 0) + log_link_warning_errno(link, r, "Failed to set NTP server for DHCP server, ignoring: %m"); + } + + if (link->network->dhcp_server_emit_timezone) { + _cleanup_free_ char *buffer = NULL; + const char *tz = NULL; + + if (link->network->dhcp_server_timezone) + tz = link->network->dhcp_server_timezone; + else { + r = get_timezone(&buffer); + if (r < 0) + log_warning_errno(r, "Failed to determine timezone: %m"); + else + tz = buffer; + } + + if (tz) { + r = sd_dhcp_server_set_timezone(link->dhcp_server, tz); + if (r < 0) + return r; + } + } + r = sd_dhcp_server_start(link->dhcp_server); if (r < 0) { log_link_warning_errno(link, r, "Could not start DHCPv4 server instance: %m"); @@ -743,7 +900,7 @@ static int link_set_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userd static int set_hostname_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { _cleanup_link_unref_ Link *link = userdata; - int r; + const sd_bus_error *e; assert(m); assert(link); @@ -751,21 +908,20 @@ static int set_hostname_handler(sd_bus_message *m, void *userdata, sd_bus_error if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return 1; - r = sd_bus_message_get_errno(m); - if (r > 0) - log_link_warning_errno(link, r, "Could not set hostname: %m"); + e = sd_bus_message_get_error(m); + if (e) + log_link_warning_errno(link, sd_bus_error_get_errno(e), "Could not set hostname: %s", e->message); return 1; } int link_set_hostname(Link *link, const char *hostname) { - int r = 0; + int r; assert(link); assert(link->manager); - assert(hostname); - log_link_debug(link, "Setting transient hostname: '%s'", hostname); + log_link_debug(link, "Setting transient hostname: '%s'", strna(hostname)); if (!link->manager->bus) { /* TODO: replace by assert when we can rely on kdbus */ @@ -794,6 +950,57 @@ int link_set_hostname(Link *link, const char *hostname) { return 0; } +static int set_timezone_handler(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { + _cleanup_link_unref_ Link *link = userdata; + const sd_bus_error *e; + + assert(m); + assert(link); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + e = sd_bus_message_get_error(m); + if (e) + log_link_warning_errno(link, sd_bus_error_get_errno(e), "Could not set timezone: %s", e->message); + + return 1; +} + +int link_set_timezone(Link *link, const char *tz) { + int r; + + assert(link); + assert(link->manager); + assert(tz); + + log_link_debug(link, "Setting system timezone: '%s'", tz); + + if (!link->manager->bus) { + log_link_info(link, "Not connected to system bus, ignoring timezone."); + return 0; + } + + r = sd_bus_call_method_async( + link->manager->bus, + NULL, + "org.freedesktop.timedate1", + "/org/freedesktop/timedate1", + "org.freedesktop.timedate1", + "SetTimezone", + set_timezone_handler, + link, + "sb", + tz, + false); + if (r < 0) + return log_link_error_errno(link, r, "Could not set timezone: %m"); + + link_ref(link); + + return 0; +} + static int set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { _cleanup_link_unref_ Link *link = userdata; int r; @@ -2413,10 +2620,18 @@ int link_save(Link *link) { fputs("\n", f); } + if (link->dhcp_lease) { + const char *tz = NULL; + + r = sd_dhcp_lease_get_timezone(link->dhcp_lease, &tz); + if (r >= 0) + fprintf(f, "TIMEZONE=%s\n", tz); + } + if (link->dhcp_lease) { assert(link->network); - r = sd_dhcp_lease_save(link->dhcp_lease, link->lease_file); + r = dhcp_lease_save(link->dhcp_lease, link->lease_file); if (r < 0) goto fail; diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 2dcbbda60..f588faf20 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -1,5 +1,7 @@ /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ +#pragma once + /*** This file is part of systemd. @@ -19,11 +21,16 @@ along with systemd; If not, see . ***/ -#pragma once - #include -#include "networkd.h" +#include "sd-dhcp-client.h" +#include "sd-dhcp-server.h" +#include "sd-ipv4ll.h" +#include "sd-icmp6-nd.h" +#include "sd-dhcp6-client.h" +#include "sd-lldp.h" + +typedef struct Link Link; typedef enum LinkState { LINK_STATE_PENDING, @@ -38,6 +45,21 @@ typedef enum LinkState { _LINK_STATE_INVALID = -1 } LinkState; +typedef enum LinkOperationalState { + LINK_OPERSTATE_OFF, + LINK_OPERSTATE_NO_CARRIER, + LINK_OPERSTATE_DORMANT, + LINK_OPERSTATE_CARRIER, + LINK_OPERSTATE_DEGRADED, + LINK_OPERSTATE_ROUTABLE, + _LINK_OPERSTATE_MAX, + _LINK_OPERSTATE_INVALID = -1 +} LinkOperationalState; + +#include "networkd.h" +#include "networkd-network.h" +#include "networkd-address.h" + struct Link { Manager *manager; @@ -115,6 +137,7 @@ bool link_has_carrier(Link *link); int link_set_mtu(Link *link, uint32_t mtu); int link_set_hostname(Link *link, const char *hostname); +int link_set_timezone(Link *link, const char *timezone); int ipv4ll_configure(Link *link); int dhcp4_configure(Link *link); @@ -130,6 +153,9 @@ bool link_dhcp6_enabled(Link *link); const char* link_state_to_string(LinkState s) _const_; LinkState link_state_from_string(const char *s) _pure_; +const char* link_operstate_to_string(LinkOperationalState s) _const_; +LinkOperationalState link_operstate_from_string(const char *s) _pure_; + extern const sd_bus_vtable link_vtable[]; int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 09a929e0d..92b607297 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -22,20 +22,21 @@ #include #include +#include "sd-netlink.h" +#include "sd-daemon.h" + #include "conf-parser.h" #include "path-util.h" -#include "networkd.h" -#include "networkd-netdev.h" -#include "networkd-link.h" #include "libudev-private.h" #include "udev-util.h" #include "netlink-util.h" #include "bus-util.h" #include "def.h" #include "virt.h" +#include "set.h" +#include "local-addresses.h" -#include "sd-netlink.h" -#include "sd-daemon.h" +#include "networkd.h" /* use 8 MB for receive socket kernel queue. */ #define RCVBUF_SIZE (8*1024*1024) @@ -99,7 +100,7 @@ static int manager_reset_all(Manager *m) { HASHMAP_FOREACH(link, m->links, i) { r = link_carrier_reset(link); if (r < 0) - log_link_warning_errno(link, r, "could not reset carrier: %m"); + log_link_warning_errno(link, r, "Could not reset carrier: %m"); } return 0; @@ -204,7 +205,7 @@ static int manager_udev_process_link(Manager *m, struct udev_device *device) { ifindex = udev_device_get_ifindex(device); if (ifindex <= 0) { - log_debug("ignoring udev ADD event for device with invalid ifindex"); + log_debug("Ignoring udev ADD event for device with invalid ifindex"); return 0; } @@ -291,23 +292,23 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa if (sd_netlink_message_is_error(message)) { r = sd_netlink_message_get_errno(message); if (r < 0) - log_warning_errno(r, "rtnl: could not receive link: %m"); + log_warning_errno(r, "rtnl: Could not receive link: %m"); return 0; } r = sd_netlink_message_get_type(message, &type); if (r < 0) { - log_warning_errno(r, "rtnl: could not get message type: %m"); + log_warning_errno(r, "rtnl: Could not get message type: %m"); return 0; } else if (type != RTM_NEWLINK && type != RTM_DELLINK) { - log_warning("rtnl: received unexpected message type when processing link"); + log_warning("rtnl: Received unexpected message type when processing link"); return 0; } r = sd_rtnl_message_link_get_ifindex(message, &ifindex); if (r < 0) { - log_warning_errno(r, "rtnl: could not get ifindex from link: %m"); + log_warning_errno(r, "rtnl: Could not get ifindex from link: %m"); return 0; } else if (ifindex <= 0) { log_warning("rtnl: received link message with invalid ifindex: %d", ifindex); @@ -317,7 +318,7 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa r = sd_netlink_message_read_string(message, IFLA_IFNAME, &name); if (r < 0) { - log_warning_errno(r, "rtnl: received link message without ifname: %m"); + log_warning_errno(r, "rtnl: Received link message without ifname: %m"); return 0; } else netdev_get(m, name, &netdev); @@ -328,7 +329,7 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa /* link is new, so add it */ r = link_add(m, message, &link); if (r < 0) { - log_warning_errno(r, "could not add new link: %m"); + log_warning_errno(r, "Could not add new link: %m"); return 0; } } @@ -337,7 +338,7 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa /* netdev exists, so make sure the ifindex matches */ r = netdev_set_ifindex(netdev, message); if (r < 0) { - log_warning_errno(r, "could not set ifindex on netdev: %m"); + log_warning_errno(r, "Could not set ifindex on netdev: %m"); return 0; } } @@ -476,13 +477,13 @@ void manager_free(Manager *m) { free(m->state_file); + sd_event_source_unref(m->udev_event_source); udev_monitor_unref(m->udev_monitor); udev_unref(m->udev); + sd_bus_unref(m->bus); sd_bus_slot_unref(m->prepare_for_sleep_slot); - sd_event_source_unref(m->udev_event_source); sd_event_source_unref(m->bus_retry_event_source); - sd_event_unref(m->event); while ((link = hashmap_first(m->links))) link_unref(link); @@ -501,6 +502,7 @@ void manager_free(Manager *m) { address_pool_free(pool); sd_netlink_unref(m->rtnl); + sd_event_unref(m->event); free(m); } @@ -755,7 +757,7 @@ int manager_save(Manager *m) { r = set_put_in_addrv(dns, addresses, r); if (r < 0) return r; - } else if (r < 0 && r != -ENOENT) + } else if (r < 0 && r != -ENODATA) return r; } @@ -767,7 +769,7 @@ int manager_save(Manager *m) { r = set_put_in_addrv(ntp, addresses, r); if (r < 0) return r; - } else if (r < 0 && r != -ENOENT) + } else if (r < 0 && r != -ENODATA) return r; } @@ -779,7 +781,7 @@ int manager_save(Manager *m) { r = set_put_strdup(domains, domainname); if (r < 0) return r; - } else if (r != -ENOENT) + } else if (r != -ENODATA) return r; } } @@ -846,36 +848,39 @@ int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, uni return 0; } -const char *address_family_boolean_to_string(AddressFamilyBoolean b) { - if (b == ADDRESS_FAMILY_YES || - b == ADDRESS_FAMILY_NO) - return yes_no(b == ADDRESS_FAMILY_YES); +Link* manager_find_uplink(Manager *m, Link *exclude) { + _cleanup_free_ struct local_address *gateways = NULL; + int n, i; - if (b == ADDRESS_FAMILY_IPV4) - return "ipv4"; - if (b == ADDRESS_FAMILY_IPV6) - return "ipv6"; + assert(m); - return NULL; -} + /* Looks for a suitable "uplink", via black magic: an + * interface that is up and where the default route with the + * highest priority points to. */ -AddressFamilyBoolean address_family_boolean_from_string(const char *s) { - int r; + n = local_gateways(m->rtnl, 0, AF_UNSPEC, &gateways); + if (n < 0) { + log_warning_errno(n, "Failed to determine list of default gateways: %m"); + return NULL; + } + + for (i = 0; i < n; i++) { + Link *link; + + link = hashmap_get(m->links, INT_TO_PTR(gateways[i].ifindex)); + if (!link) { + log_debug("Weird, found a gateway for a link we don't know. Ignoring."); + continue; + } - /* Make this a true superset of a boolean */ + if (link == exclude) + continue; - r = parse_boolean(s); - if (r > 0) - return ADDRESS_FAMILY_YES; - if (r == 0) - return ADDRESS_FAMILY_NO; + if (link->operstate < LINK_OPERSTATE_ROUTABLE) + continue; - if (streq(s, "ipv4")) - return ADDRESS_FAMILY_IPV4; - if (streq(s, "ipv6")) - return ADDRESS_FAMILY_IPV6; + return link; + } - return _ADDRESS_FAMILY_BOOLEAN_INVALID; + return NULL; } - -DEFINE_CONFIG_PARSE_ENUM(config_parse_address_family_boolean, address_family_boolean, AddressFamilyBoolean, "Failed to parse option"); diff --git a/src/network/networkd-netdev-bond.h b/src/network/networkd-netdev-bond.h index 9991fa731..0cdce1605 100644 --- a/src/network/networkd-netdev-bond.h +++ b/src/network/networkd-netdev-bond.h @@ -1,5 +1,7 @@ /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ +#pragma once + /*** This file is part of systemd. @@ -19,7 +21,7 @@ along with systemd; If not, see . ***/ -#pragma once +#include "in-addr-util.h" typedef struct Bond Bond; diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf index 9469160eb..e0bd0e024 100644 --- a/src/network/networkd-netdev-gperf.gperf +++ b/src/network/networkd-netdev-gperf.gperf @@ -39,6 +39,7 @@ Tunnel.DiscoverPathMTU, config_parse_bool, 0, Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode) Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, offsetof(Tunnel, ipv6_flowlabel) Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp) +Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit) Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer) Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer) VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id) diff --git a/src/network/networkd-netdev-tunnel.c b/src/network/networkd-netdev-tunnel.c index 265e67b7e..a906e473b 100644 --- a/src/network/networkd-netdev-tunnel.c +++ b/src/network/networkd-netdev-tunnel.c @@ -284,6 +284,12 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netl if (t->copy_dscp) t->flags |= IP6_TNL_F_RCV_DSCP_COPY; + if (t->encap_limit != IPV6_DEFAULT_TNL_ENCAP_LIMIT) { + r = sd_netlink_message_append_u8(m, IFLA_IPTUN_ENCAP_LIMIT, t->encap_limit); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_ENCAP_LIMIT attribute: %m"); + } + r = sd_netlink_message_append_u32(m, IFLA_IPTUN_FLAGS, t->flags); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_FLAGS attribute: %m"); @@ -442,6 +448,45 @@ int config_parse_ipv6_flowlabel(const char* unit, return 0; } +int config_parse_encap_limit(const char* unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + Tunnel *t = userdata; + int k = 0; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (streq(rvalue, "none")) + t->flags |= IP6_TNL_F_IGN_ENCAP_LIMIT; + else { + r = safe_atoi(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse Tunnel Encapsulation Limit option, ignoring: %s", rvalue); + return 0; + } + + if (k > 255 || k < 0) + log_syntax(unit, LOG_ERR, filename, line, k, "Invalid Tunnel Encapsulation value, ignoring: %d", k); + else { + t->encap_limit = k; + t->flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT; + } + } + + return 0; +} + static void ipip_init(NetDev *n) { Tunnel *t = IPIP(n); diff --git a/src/network/networkd-netdev-tunnel.h b/src/network/networkd-netdev-tunnel.h index e4fa74aef..fa7decce1 100644 --- a/src/network/networkd-netdev-tunnel.h +++ b/src/network/networkd-netdev-tunnel.h @@ -95,3 +95,9 @@ int config_parse_ipv6_flowlabel(const char *unit, const char *filename, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); + +int config_parse_encap_limit(const char *unit, const char *filename, + unsigned line, const char *section, + unsigned section_line, const char *lvalue, + int ltype, const char *rvalue, void *data, + void *userdata); diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c index cd31387b4..ff1edf2c3 100644 --- a/src/network/networkd-netdev.c +++ b/src/network/networkd-netdev.c @@ -21,15 +21,18 @@ #include -#include "networkd-netdev.h" -#include "networkd-link.h" -#include "network-internal.h" #include "conf-files.h" #include "conf-parser.h" #include "list.h" #include "siphash24.h" +#include "netlink-util.h" +#include "network-internal.h" + +#include "networkd.h" +#include "networkd-netdev.h" const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = { + [NETDEV_KIND_BRIDGE] = &bridge_vtable, [NETDEV_KIND_BOND] = &bond_vtable, [NETDEV_KIND_VLAN] = &vlan_vtable, diff --git a/src/network/networkd-netdev.h b/src/network/networkd-netdev.h index 19fb5bb18..1f8510c4f 100644 --- a/src/network/networkd-netdev.h +++ b/src/network/networkd-netdev.h @@ -21,11 +21,14 @@ #pragma once -#include "networkd.h" #include "list.h" +typedef struct NetDev NetDev; typedef struct NetDevVTable NetDevVTable; +#include "networkd.h" +#include "networkd-link.h" + typedef struct netdev_join_callback netdev_join_callback; struct netdev_join_callback { diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 7ac7ef1ea..10ca9dae3 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -73,6 +73,17 @@ DHCP.RequestBroadcast, config_parse_bool, 0 DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical) DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier) DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric) +DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_timezone) +DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec) +DHCPServer.DefaultLeaseTimeSec,config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec) +DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns) +DHCPServer.DNS, config_parse_dhcp_server_dns, 0, 0 +DHCPServer.EmitNTP, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_ntp) +DHCPServer.NTP, config_parse_dhcp_server_ntp, 0, 0 +DHCPServer.EmitTimezone, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_timezone) +DHCPServer.Timezone, config_parse_timezone, 0, offsetof(Network, dhcp_server_timezone) +DHCPServer.PoolOffset, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_offset) +DHCPServer.PoolSize, config_parse_uint32, 0, offsetof(Network, dhcp_server_pool_size) Bridge.Cost, config_parse_unsigned, 0, offsetof(Network, cost) Bridge.UseBPDU, config_parse_bool, 0, offsetof(Network, use_bpdu) Bridge.HairPin, config_parse_bool, 0, offsetof(Network, hairpin) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 6587ea994..ee1440198 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -26,11 +26,11 @@ #include "conf-parser.h" #include "util.h" #include "hostname-util.h" -#include "networkd.h" -#include "networkd-netdev.h" -#include "networkd-link.h" -#include "network-internal.h" #include "dns-domain.h" +#include "network-internal.h" + +#include "networkd.h" +#include "networkd-network.h" static int network_load_one(Manager *manager, const char *filename) { _cleanup_network_free_ Network *network = NULL; @@ -107,6 +107,10 @@ static int network_load_one(Manager *manager, const char *filename) { network->dhcp_route_metric = DHCP_ROUTE_METRIC; network->dhcp_client_identifier = DHCP_CLIENT_ID_DUID; + network->dhcp_server_emit_dns = true; + network->dhcp_server_emit_ntp = true; + network->dhcp_server_emit_timezone = true; + network->use_bpdu = true; network->allow_port_to_be_root = true; network->unicast_flood = true; @@ -124,7 +128,8 @@ static int network_load_one(Manager *manager, const char *filename) { "Address\0" "Route\0" "DHCP\0" - "DHCPv4\0" + "DHCPv4\0" /* compat */ + "DHCPServer\0" "Bridge\0" "BridgeFDB\0", config_item_perf_lookup, network_network_gperf_lookup, @@ -258,6 +263,10 @@ void network_free(Network *network) { condition_free_list(network->match_kernel); condition_free_list(network->match_arch); + free(network->dhcp_server_timezone); + free(network->dhcp_server_dns); + free(network->dhcp_server_ntp); + free(network); } @@ -632,57 +641,6 @@ static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = { DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DCHPClientIdentifier); DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DCHPClientIdentifier, "Failed to parse client identifier type"); -static const char* const resolve_support_table[_RESOLVE_SUPPORT_MAX] = { - [RESOLVE_SUPPORT_NO] = "no", - [RESOLVE_SUPPORT_YES] = "yes", - [RESOLVE_SUPPORT_RESOLVE] = "resolve", -}; - -DEFINE_STRING_TABLE_LOOKUP(resolve_support, ResolveSupport); - -int config_parse_resolve( - const char* unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - ResolveSupport *resolve = data; - int k; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(resolve); - - /* Our enum shall be a superset of booleans, hence first try - * to parse as boolean, and then as enum */ - - k = parse_boolean(rvalue); - if (k > 0) - *resolve = RESOLVE_SUPPORT_YES; - else if (k == 0) - *resolve = RESOLVE_SUPPORT_NO; - else { - ResolveSupport s; - - s = resolve_support_from_string(rvalue); - if (s < 0){ - log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse %s option, ignoring: %s", lvalue, rvalue); - return 0; - } - - *resolve = s; - } - - return 0; -} - int config_parse_ipv6token( const char* unit, const char *filename, @@ -726,40 +684,6 @@ int config_parse_ipv6token( return 0; } -int config_parse_address_family_boolean_with_kernel( - 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) { - - AddressFamilyBoolean *fwd = data, s; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - s = address_family_boolean_from_string(rvalue); - if (s < 0) { - if (streq(rvalue, "kernel")) - s = _ADDRESS_FAMILY_BOOLEAN_INVALID; - else { - log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse IPForwarding option, ignoring: %s", rvalue); - return 0; - } - } - - *fwd = s; - - return 0; -} - static const char* const ipv6_privacy_extensions_table[_IPV6_PRIVACY_EXTENSIONS_MAX] = { [IPV6_PRIVACY_EXTENSIONS_NO] = "no", [IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC] = "prefer-public", @@ -816,37 +740,165 @@ int config_parse_ipv6_privacy_extensions( return 0; } -int config_parse_hostname(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 **hostname = data; - char *hn = NULL; +int config_parse_hostname( + 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 **hostname = data, *hn = NULL; int r; assert(filename); assert(lvalue); assert(rvalue); - r = config_parse_string(unit, filename, line, section, section_line, - lvalue, ltype, rvalue, &hn, userdata); + r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &hn, userdata); if (r < 0) return r; - if (!hostname_is_valid(hn, true)) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, "hostname is not valid, ignoring assignment: %s", rvalue); - + if (!hostname_is_valid(hn, false)) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Hostname is not valid, ignoring assignment: %s", rvalue); free(hn); return 0; } + free(*hostname); *hostname = hostname_cleanup(hn); + return 0; +} + +int config_parse_timezone( + 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 **datap = data, *tz = NULL; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &tz, userdata); + if (r < 0) + return r; + + if (!timezone_is_valid(tz)) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Timezone is not valid, ignoring assignment: %s", rvalue); + free(tz); + return 0; + } + + free(*datap); + *datap = tz; return 0; } + +int config_parse_dhcp_server_dns( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Network *n = data; + const char *p = rvalue; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + for (;;) { + _cleanup_free_ char *w = NULL; + struct in_addr a, *m; + + r = extract_first_word(&p, &w, NULL, 0); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue); + return 0; + } + + if (r == 0) + return 0; + + if (inet_pton(AF_INET, w, &a) <= 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DNS server address, ignoring: %s", w); + continue; + } + + m = realloc(n->dhcp_server_dns, (n->n_dhcp_server_dns + 1) * sizeof(struct in_addr)); + if (!m) + return log_oom(); + + m[n->n_dhcp_server_dns++] = a; + n->dhcp_server_dns = m; + } +} + +int config_parse_dhcp_server_ntp( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Network *n = data; + const char *p = rvalue; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + for (;;) { + _cleanup_free_ char *w = NULL; + struct in_addr a, *m; + + r = extract_first_word(&p, &w, NULL, 0); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, r, line, "Failed to extract word, ignoring: %s", rvalue); + return 0; + } + + if (r == 0) + return 0; + + if (inet_pton(AF_INET, w, &a) <= 0) { + log_syntax(unit, LOG_ERR, filename, r, line, "Failed to parse NTP server address, ignoring: %s", w); + continue; + } + + m = realloc(n->dhcp_server_ntp, (n->n_dhcp_server_ntp + 1) * sizeof(struct in_addr)); + if (!m) + return log_oom(); + + m[n->n_dhcp_server_ntp++] = a; + n->dhcp_server_ntp = m; + } +} diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h new file mode 100644 index 000000000..c3439a70b --- /dev/null +++ b/src/network/networkd-network.h @@ -0,0 +1,181 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "condition.h" + +typedef struct Network Network; + +#include "networkd.h" +#include "networkd-netdev.h" +#include "networkd-address.h" +#include "networkd-route.h" +#include "networkd-fdb.h" +#include "networkd-util.h" + +#define DHCP_ROUTE_METRIC 1024 +#define IPV4LL_ROUTE_METRIC 2048 + +typedef enum DCHPClientIdentifier { + DHCP_CLIENT_ID_MAC, + DHCP_CLIENT_ID_DUID, + _DHCP_CLIENT_ID_MAX, + _DHCP_CLIENT_ID_INVALID = -1, +} DCHPClientIdentifier; + +typedef enum IPv6PrivacyExtensions { + /* The values map to the kernel's /proc/sys/net/ipv6/conf/xxx/use_tempaddr values */ + IPV6_PRIVACY_EXTENSIONS_NO, + IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC, + IPV6_PRIVACY_EXTENSIONS_YES, /* aka prefer-temporary */ + _IPV6_PRIVACY_EXTENSIONS_MAX, + _IPV6_PRIVACY_EXTENSIONS_INVALID = -1, +} IPv6PrivacyExtensions; + +struct Network { + Manager *manager; + + char *filename; + char *name; + + struct ether_addr *match_mac; + char **match_path; + char **match_driver; + char **match_type; + char **match_name; + + Condition *match_host; + Condition *match_virt; + Condition *match_kernel; + Condition *match_arch; + + char *description; + + NetDev *bridge; + NetDev *bond; + Hashmap *stacked_netdevs; + + /* DHCP Client Support */ + AddressFamilyBoolean dhcp; + DCHPClientIdentifier dhcp_client_identifier; + char *dhcp_vendor_class_identifier; + char *hostname; + bool dhcp_dns; + bool dhcp_ntp; + bool dhcp_mtu; + bool dhcp_hostname; + bool dhcp_domains; + bool dhcp_sendhost; + bool dhcp_broadcast; + bool dhcp_critical; + bool dhcp_routes; + bool dhcp_timezone; + unsigned dhcp_route_metric; + + /* DHCP Server Support */ + bool dhcp_server; + bool dhcp_server_emit_dns; + struct in_addr *dhcp_server_dns; + unsigned n_dhcp_server_dns; + bool dhcp_server_emit_ntp; + struct in_addr *dhcp_server_ntp; + unsigned n_dhcp_server_ntp; + bool dhcp_server_emit_timezone; + char *dhcp_server_timezone; + usec_t dhcp_server_default_lease_time_usec, dhcp_server_max_lease_time_usec; + uint32_t dhcp_server_pool_offset; + uint32_t dhcp_server_pool_size; + + /* IPV4LL Support */ + AddressFamilyBoolean link_local; + bool ipv4ll_route; + + /* Bridge Support */ + bool use_bpdu; + bool hairpin; + bool fast_leave; + bool allow_port_to_be_root; + bool unicast_flood; + unsigned cost; + + AddressFamilyBoolean ip_forward; + bool ip_masquerade; + + union in_addr_union ipv6_token; + IPv6PrivacyExtensions ipv6_privacy_extensions; + + struct ether_addr *mac; + unsigned mtu; + + bool lldp; + + LIST_HEAD(Address, static_addresses); + LIST_HEAD(Route, static_routes); + LIST_HEAD(FdbEntry, static_fdb_entries); + + Hashmap *addresses_by_section; + Hashmap *routes_by_section; + Hashmap *fdb_entries_by_section; + + bool wildcard_domain; + char **domains, **dns, **ntp, **bind_carrier; + + ResolveSupport llmnr; + + LIST_FIELDS(Network, networks); +}; + +void network_free(Network *network); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Network*, network_free); +#define _cleanup_network_free_ _cleanup_(network_freep) + +int network_load(Manager *manager); + +int network_get_by_name(Manager *manager, const char *name, Network **ret); +int network_get(Manager *manager, struct udev_device *device, const char *ifname, const struct ether_addr *mac, Network **ret); +int network_apply(Manager *manager, Network *network, Link *link); + +int config_parse_netdev(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_domains(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_tunnel(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_dhcp(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_dhcp_client_identifier(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_ipv6token(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_ipv6_privacy_extensions(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_hostname(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_timezone(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_dhcp_server_dns(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_dhcp_server_ntp(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); + +/* Legacy IPv4LL support */ +int config_parse_ipv4ll(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); + +const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, unsigned length); + +extern const sd_bus_vtable network_vtable[]; + +int network_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); +int network_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); + +const char* ipv6_privacy_extensions_to_string(IPv6PrivacyExtensions i) _const_; +IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_; diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 31b10c458..fbaad4057 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -19,12 +19,12 @@ along with systemd; If not, see . ***/ - -#include "networkd.h" -#include "networkd-link.h" - #include "util.h" #include "conf-parser.h" +#include "netlink-util.h" + +#include "networkd.h" +#include "networkd-route.h" int route_new_static(Network *network, unsigned section, Route **ret) { _cleanup_route_free_ Route *route = NULL; diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h new file mode 100644 index 000000000..d090b9c91 --- /dev/null +++ b/src/network/networkd-route.h @@ -0,0 +1,60 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +typedef struct Route Route; + +#include "networkd.h" +#include "networkd-network.h" + +struct Route { + Network *network; + unsigned section; + + int family; + unsigned char dst_prefixlen; + unsigned char src_prefixlen; + unsigned char scope; + uint32_t metrics; + unsigned char protocol; /* RTPROT_* */ + + union in_addr_union in_addr; + union in_addr_union dst_addr; + union in_addr_union src_addr; + union in_addr_union prefsrc_addr; + + LIST_FIELDS(Route, routes); +}; + +int route_new_static(Network *network, unsigned section, Route **ret); +int route_new_dynamic(Route **ret, unsigned char rtm_protocol); +void route_free(Route *route); +int route_configure(Route *route, Link *link, sd_netlink_message_handler_t callback); +int route_drop(Route *route, Link *link, sd_netlink_message_handler_t callback); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Route*, route_free); +#define _cleanup_route_free_ _cleanup_(route_freep) + +int config_parse_gateway(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_destination(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_route_priority(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_route_scope(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/network/networkd-util.c b/src/network/networkd-util.c new file mode 100644 index 000000000..a41cd8623 --- /dev/null +++ b/src/network/networkd-util.c @@ -0,0 +1,144 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "util.h" +#include "conf-parser.h" + +#include "networkd-util.h" + +const char *address_family_boolean_to_string(AddressFamilyBoolean b) { + if (b == ADDRESS_FAMILY_YES || + b == ADDRESS_FAMILY_NO) + return yes_no(b == ADDRESS_FAMILY_YES); + + if (b == ADDRESS_FAMILY_IPV4) + return "ipv4"; + if (b == ADDRESS_FAMILY_IPV6) + return "ipv6"; + + return NULL; +} + +AddressFamilyBoolean address_family_boolean_from_string(const char *s) { + int r; + + /* Make this a true superset of a boolean */ + + r = parse_boolean(s); + if (r > 0) + return ADDRESS_FAMILY_YES; + if (r == 0) + return ADDRESS_FAMILY_NO; + + if (streq(s, "ipv4")) + return ADDRESS_FAMILY_IPV4; + if (streq(s, "ipv6")) + return ADDRESS_FAMILY_IPV6; + + return _ADDRESS_FAMILY_BOOLEAN_INVALID; +} + +DEFINE_CONFIG_PARSE_ENUM(config_parse_address_family_boolean, address_family_boolean, AddressFamilyBoolean, "Failed to parse option"); + +int config_parse_address_family_boolean_with_kernel( + 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) { + + AddressFamilyBoolean *fwd = data, s; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + s = address_family_boolean_from_string(rvalue); + if (s < 0) { + if (streq(rvalue, "kernel")) + s = _ADDRESS_FAMILY_BOOLEAN_INVALID; + else { + log_syntax(unit, LOG_ERR, filename, line, s, "Failed to parse IPForwarding= option, ignoring: %s", rvalue); + return 0; + } + } + + *fwd = s; + + return 0; +} + +static const char* const resolve_support_table[_RESOLVE_SUPPORT_MAX] = { + [RESOLVE_SUPPORT_NO] = "no", + [RESOLVE_SUPPORT_YES] = "yes", + [RESOLVE_SUPPORT_RESOLVE] = "resolve", +}; + +DEFINE_STRING_TABLE_LOOKUP(resolve_support, ResolveSupport); + +int config_parse_resolve( + const char* unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + ResolveSupport *resolve = data; + int k; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(resolve); + + /* Our enum shall be a superset of booleans, hence first try + * to parse as boolean, and then as enum */ + + k = parse_boolean(rvalue); + if (k > 0) + *resolve = RESOLVE_SUPPORT_YES; + else if (k == 0) + *resolve = RESOLVE_SUPPORT_NO; + else { + ResolveSupport s; + + s = resolve_support_from_string(rvalue); + if (s < 0){ + log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse %s= option, ignoring: %s", lvalue, rvalue); + return 0; + } + + *resolve = s; + } + + return 0; +} diff --git a/src/network/networkd-util.h b/src/network/networkd-util.h new file mode 100644 index 000000000..cc41aae85 --- /dev/null +++ b/src/network/networkd-util.h @@ -0,0 +1,52 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Tom Gundersen + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "macro.h" + +typedef enum AddressFamilyBoolean { + /* This is a bitmask, though it usually doesn't feel that way! */ + ADDRESS_FAMILY_NO = 0, + ADDRESS_FAMILY_IPV4 = 1, + ADDRESS_FAMILY_IPV6 = 2, + ADDRESS_FAMILY_YES = 3, + _ADDRESS_FAMILY_BOOLEAN_MAX, + _ADDRESS_FAMILY_BOOLEAN_INVALID = -1, +} AddressFamilyBoolean; + +typedef enum ResolveSupport { + RESOLVE_SUPPORT_NO, + RESOLVE_SUPPORT_YES, + RESOLVE_SUPPORT_RESOLVE, + _RESOLVE_SUPPORT_MAX, + _RESOLVE_SUPPORT_INVALID = -1, +} ResolveSupport; + +int config_parse_resolve(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_address_family_boolean(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_address_family_boolean_with_kernel(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); + +const char* resolve_support_to_string(ResolveSupport i) _const_; +ResolveSupport resolve_support_from_string(const char *s) _pure_; + +const char *address_family_boolean_to_string(AddressFamilyBoolean b) _const_; +AddressFamilyBoolean address_family_boolean_from_string(const char *s) _const_; diff --git a/src/network/networkd.h b/src/network/networkd.h index 5340922bf..eea57ac15 100644 --- a/src/network/networkd.h +++ b/src/network/networkd.h @@ -26,213 +26,17 @@ #include "sd-event.h" #include "sd-netlink.h" #include "sd-bus.h" -#include "sd-dhcp-client.h" -#include "sd-dhcp-server.h" -#include "sd-ipv4ll.h" -#include "sd-icmp6-nd.h" -#include "sd-dhcp6-client.h" #include "udev.h" -#include "sd-lldp.h" -#include "netlink-util.h" #include "hashmap.h" #include "list.h" -#include "set.h" -#include "condition.h" -#include "in-addr-util.h" -#define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU -#define DHCP_ROUTE_METRIC 1024 -#define IPV4LL_ROUTE_METRIC 2048 - -typedef struct NetDev NetDev; -typedef struct Network Network; -typedef struct Link Link; -typedef struct Address Address; -typedef struct Route Route; typedef struct Manager Manager; -typedef struct AddressPool AddressPool; -typedef struct FdbEntry FdbEntry; - -typedef enum AddressFamilyBoolean { - /* This is a bitmask, though it usually doesn't feel that way! */ - ADDRESS_FAMILY_NO = 0, - ADDRESS_FAMILY_IPV4 = 1, - ADDRESS_FAMILY_IPV6 = 2, - ADDRESS_FAMILY_YES = 3, - _ADDRESS_FAMILY_BOOLEAN_MAX, - _ADDRESS_FAMILY_BOOLEAN_INVALID = -1, -} AddressFamilyBoolean; - -typedef enum ResolveSupport { - RESOLVE_SUPPORT_NO, - RESOLVE_SUPPORT_YES, - RESOLVE_SUPPORT_RESOLVE, - _RESOLVE_SUPPORT_MAX, - _RESOLVE_SUPPORT_INVALID = -1, -} ResolveSupport; - -typedef enum LinkOperationalState { - LINK_OPERSTATE_OFF, - LINK_OPERSTATE_NO_CARRIER, - LINK_OPERSTATE_DORMANT, - LINK_OPERSTATE_CARRIER, - LINK_OPERSTATE_DEGRADED, - LINK_OPERSTATE_ROUTABLE, - _LINK_OPERSTATE_MAX, - _LINK_OPERSTATE_INVALID = -1 -} LinkOperationalState; - -typedef enum DCHPClientIdentifier { - DHCP_CLIENT_ID_MAC, - DHCP_CLIENT_ID_DUID, - _DHCP_CLIENT_ID_MAX, - _DHCP_CLIENT_ID_INVALID = -1, -} DCHPClientIdentifier; - -typedef enum IPv6PrivacyExtensions { - /* The values map to the kernel's /proc/sys/net/ipv6/conf/xxx/use_tempaddr values */ - IPV6_PRIVACY_EXTENSIONS_NO, - IPV6_PRIVACY_EXTENSIONS_PREFER_PUBLIC, - IPV6_PRIVACY_EXTENSIONS_YES, /* aka prefer-temporary */ - _IPV6_PRIVACY_EXTENSIONS_MAX, - _IPV6_PRIVACY_EXTENSIONS_INVALID = -1, -} IPv6PrivacyExtensions; - -struct FdbEntry { - Network *network; - unsigned section; - - struct ether_addr *mac_addr; - uint16_t vlan_id; - - LIST_FIELDS(FdbEntry, static_fdb_entries); -}; - -struct Network { - Manager *manager; - - char *filename; - char *name; - - struct ether_addr *match_mac; - char **match_path; - char **match_driver; - char **match_type; - char **match_name; - - Condition *match_host; - Condition *match_virt; - Condition *match_kernel; - Condition *match_arch; - - char *description; - NetDev *bridge; - NetDev *bond; - Hashmap *stacked_netdevs; - AddressFamilyBoolean dhcp; - DCHPClientIdentifier dhcp_client_identifier; - char *dhcp_vendor_class_identifier; - char *hostname; - bool dhcp_dns; - bool dhcp_ntp; - bool dhcp_mtu; - bool dhcp_hostname; - bool dhcp_domains; - bool dhcp_sendhost; - bool dhcp_broadcast; - bool dhcp_critical; - bool dhcp_routes; - unsigned dhcp_route_metric; - AddressFamilyBoolean link_local; - bool ipv4ll_route; - union in_addr_union ipv6_token; - - bool dhcp_server; - - bool use_bpdu; - bool hairpin; - bool fast_leave; - bool allow_port_to_be_root; - bool unicast_flood; - unsigned cost; - - AddressFamilyBoolean ip_forward; - bool ip_masquerade; - - IPv6PrivacyExtensions ipv6_privacy_extensions; - - struct ether_addr *mac; - unsigned mtu; - - bool lldp; - - LIST_HEAD(Address, static_addresses); - LIST_HEAD(Route, static_routes); - LIST_HEAD(FdbEntry, static_fdb_entries); - - Hashmap *addresses_by_section; - Hashmap *routes_by_section; - Hashmap *fdb_entries_by_section; - bool wildcard_domain; - char **domains, **dns, **ntp, **bind_carrier; - - ResolveSupport llmnr; - - LIST_FIELDS(Network, networks); -}; - -struct Address { - Network *network; - unsigned section; - - int family; - unsigned char prefixlen; - unsigned char scope; - uint32_t flags; - char *label; - - struct in_addr broadcast; - struct ifa_cacheinfo cinfo; - - union in_addr_union in_addr; - union in_addr_union in_addr_peer; - - bool ip_masquerade_done; - - LIST_FIELDS(Address, addresses); -}; - -struct Route { - Network *network; - unsigned section; - - int family; - unsigned char dst_prefixlen; - unsigned char src_prefixlen; - unsigned char scope; - uint32_t metrics; - unsigned char protocol; /* RTPROT_* */ - - union in_addr_union in_addr; - union in_addr_union dst_addr; - union in_addr_union src_addr; - union in_addr_union prefsrc_addr; - - LIST_FIELDS(Route, routes); -}; - -struct AddressPool { - Manager *manager; - - int family; - unsigned prefixlen; - - union in_addr_union in_addr; - - LIST_FIELDS(AddressPool, address_pools); -}; +#include "networkd-network.h" +#include "networkd-address-pool.h" +#include "networkd-link.h" +#include "networkd-util.h" struct Manager { sd_netlink *rtnl; @@ -281,183 +85,7 @@ int manager_save(Manager *m); int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found); +Link* manager_find_uplink(Manager *m, Link *exclude); + DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); #define _cleanup_manager_free_ _cleanup_(manager_freep) - -/* Network */ - -int network_load(Manager *manager); - -void network_free(Network *network); - -DEFINE_TRIVIAL_CLEANUP_FUNC(Network*, network_free); -#define _cleanup_network_free_ _cleanup_(network_freep) - -int network_get_by_name(Manager *manager, const char *name, Network **ret); -int network_get(Manager *manager, struct udev_device *device, - const char *ifname, const struct ether_addr *mac, - Network **ret); -int network_apply(Manager *manager, Network *network, Link *link); - -int config_parse_netdev(const char *unit, const char *filename, unsigned line, - const char *section, unsigned section_line, const char *lvalue, - int ltype, const char *rvalue, void *data, void *userdata); - -int config_parse_domains(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata); - -int config_parse_tunnel(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata); - -extern const sd_bus_vtable network_vtable[]; - -int network_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); -int network_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error); - -/* gperf */ -const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, unsigned length); - -/* Route */ -int route_new_static(Network *network, unsigned section, Route **ret); -int route_new_dynamic(Route **ret, unsigned char rtm_protocol); -void route_free(Route *route); -int route_configure(Route *route, Link *link, sd_netlink_message_handler_t callback); -int route_drop(Route *route, Link *link, sd_netlink_message_handler_t callback); - - -DEFINE_TRIVIAL_CLEANUP_FUNC(Route*, route_free); -#define _cleanup_route_free_ _cleanup_(route_freep) - -int config_parse_gateway(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_destination(const char *unit, const char *filename, unsigned line, - const char *section, unsigned section_line, const char *lvalue, - int ltype, const char *rvalue, void *data, void *userdata); - -int config_parse_route_priority(const char *unit, const char *filename, unsigned line, - const char *section, unsigned section_line, const char *lvalue, - int ltype, const char *rvalue, void *data, void *userdata); - -int config_parse_route_scope(const char *unit, const char *filename, unsigned line, - const char *section, unsigned section_line, const char *lvalue, - int ltype, const char *rvalue, void *data, void *userdata); -/* Address */ -int address_new_static(Network *network, unsigned section, Address **ret); -int address_new_dynamic(Address **ret); -void address_free(Address *address); -int address_configure(Address *address, Link *link, sd_netlink_message_handler_t callback); -int address_update(Address *address, Link *link, sd_netlink_message_handler_t callback); -int address_drop(Address *address, Link *link, sd_netlink_message_handler_t callback); -int address_establish(Address *address, Link *link); -int address_release(Address *address, Link *link); -bool address_equal(Address *a1, Address *a2); - -DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free); -#define _cleanup_address_free_ _cleanup_(address_freep) - -int config_parse_address(const char *unit, const char *filename, unsigned line, - const char *section, unsigned section_line, const char *lvalue, - int ltype, const char *rvalue, void *data, void *userdata); - -int config_parse_broadcast(const char *unit, const char *filename, unsigned line, - const char *section, unsigned section_line, const char *lvalue, - int ltype, const char *rvalue, void *data, void *userdata); - -int config_parse_label(const char *unit, const char *filename, unsigned line, - const char *section, unsigned section_line, const char *lvalue, - int ltype, const char *rvalue, void *data, void *userdata); - -/* Forwarding database table. */ -int fdb_entry_configure(Link *const link, FdbEntry *const fdb_entry); -void fdb_entry_free(FdbEntry *fdb_entry); -int fdb_entry_new_static(Network *const network, const unsigned section, FdbEntry **ret); - -DEFINE_TRIVIAL_CLEANUP_FUNC(FdbEntry*, fdb_entry_free); -#define _cleanup_fdbentry_free_ _cleanup_(fdb_entry_freep) - -int config_parse_fdb_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_fdb_vlan_id(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); - -/* DHCP support */ - -int config_parse_dhcp(const char *unit, const char *filename, unsigned line, - const char *section, unsigned section_line, const char *lvalue, - int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_dhcp_client_identifier(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); - -/* IPv4LL support (legacy) */ - -int config_parse_ipv4ll(const char *unit, const char *filename, unsigned line, - const char *section, unsigned section_line, const char *lvalue, - int ltype, const char *rvalue, void *data, void *userdata); - -/* IPv6 support */ -int config_parse_ipv6token(const char *unit, const char *filename, unsigned line, - const char *section, unsigned section_line, const char *lvalue, - int ltype, const char *rvalue, void *data, void *userdata); - -/* Resolve protocols support */ - -const char* resolve_support_to_string(ResolveSupport i) _const_; -ResolveSupport resolve_support_from_string(const char *s) _pure_; - -int config_parse_resolve(const char *unit, const char *filename, unsigned line, - const char *section, unsigned section_line, const char *lvalue, - int ltype, const char *rvalue, void *data, void *userdata); - -/* Address Pool */ - -int address_pool_new(Manager *m, AddressPool **ret, int family, const union in_addr_union *u, unsigned prefixlen); -int address_pool_new_from_string(Manager *m, AddressPool **ret, int family, const char *p, unsigned prefixlen); -void address_pool_free(AddressPool *p); - -int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union *found); - -const char *address_family_boolean_to_string(AddressFamilyBoolean b) _const_; -AddressFamilyBoolean address_family_boolean_from_string(const char *s) _const_; - -int config_parse_address_family_boolean(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); - -/* IPForwarding parser */ -int config_parse_address_family_boolean_with_kernel(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); - -/* Operational State */ - -const char* link_operstate_to_string(LinkOperationalState s) _const_; -LinkOperationalState link_operstate_from_string(const char *s) _pure_; - -/* IPv6 privacy extensions support */ - -const char* ipv6_privacy_extensions_to_string(IPv6PrivacyExtensions i) _const_; -IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_; - -int config_parse_ipv6_privacy_extensions(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); - - -/* Hostname */ -int config_parse_hostname(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/.gitignore b/src/nspawn/.gitignore new file mode 100644 index 000000000..85c81fff2 --- /dev/null +++ b/src/nspawn/.gitignore @@ -0,0 +1 @@ +/nspawn-gperf.c diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c new file mode 100644 index 000000000..c0e9ccd7a --- /dev/null +++ b/src/nspawn/nspawn-cgroup.c @@ -0,0 +1,162 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "util.h" +#include "strv.h" +#include "mkdir.h" +#include "fileio.h" +#include "cgroup-util.h" + +#include "nspawn-cgroup.h" + +int chown_cgroup(pid_t pid, uid_t uid_shift) { + _cleanup_free_ char *path = NULL, *fs = NULL; + _cleanup_close_ int fd = -1; + const char *fn; + int r; + + r = cg_pid_get_path(NULL, pid, &path); + if (r < 0) + return log_error_errno(r, "Failed to get container cgroup path: %m"); + + r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &fs); + if (r < 0) + return log_error_errno(r, "Failed to get file system path for container cgroup: %m"); + + fd = open(fs, O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (fd < 0) + return log_error_errno(errno, "Failed to open %s: %m", fs); + + FOREACH_STRING(fn, + ".", + "tasks", + "notify_on_release", + "cgroup.procs", + "cgroup.clone_children", + "cgroup.controllers", + "cgroup.subtree_control", + "cgroup.populated") + if (fchownat(fd, fn, uid_shift, uid_shift, 0) < 0) + log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno, + "Failed to chown() cgroup file %s, ignoring: %m", fn); + + return 0; +} + +int sync_cgroup(pid_t pid, bool unified_requested) { + _cleanup_free_ char *cgroup = NULL; + char tree[] = "/tmp/unifiedXXXXXX", pid_string[DECIMAL_STR_MAX(pid) + 1]; + bool undo_mount = false; + const char *fn; + int unified, r; + + unified = cg_unified(); + if (unified < 0) + return log_error_errno(unified, "Failed to determine whether the unified hierachy is used: %m"); + + if ((unified > 0) == unified_requested) + return 0; + + /* When the host uses the legacy cgroup setup, but the + * container shall use the unified hierarchy, let's make sure + * we copy the path from the name=systemd hierarchy into the + * unified hierarchy. Similar for the reverse situation. */ + + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &cgroup); + if (r < 0) + return log_error_errno(r, "Failed to get control group of " PID_FMT ": %m", pid); + + /* In order to access the unified hierarchy we need to mount it */ + if (!mkdtemp(tree)) + return log_error_errno(errno, "Failed to generate temporary mount point for unified hierarchy: %m"); + + if (unified) + r = mount("cgroup", tree, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, "none,name=systemd,xattr"); + else + r = mount("cgroup", tree, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, "__DEVEL__sane_behavior"); + if (r < 0) { + r = log_error_errno(errno, "Failed to mount unified hierarchy: %m"); + goto finish; + } + + undo_mount = true; + + fn = strjoina(tree, cgroup, "/cgroup.procs"); + (void) mkdir_parents(fn, 0755); + + sprintf(pid_string, PID_FMT, pid); + r = write_string_file(fn, pid_string, 0); + if (r < 0) + log_error_errno(r, "Failed to move process: %m"); + +finish: + if (undo_mount) + (void) umount(tree); + + (void) rmdir(tree); + return r; +} + +int create_subcgroup(pid_t pid, bool unified_requested) { + _cleanup_free_ char *cgroup = NULL; + const char *child; + int unified, r; + CGroupMask supported; + + /* In the unified hierarchy inner nodes may only only contain + * subgroups, but not processes. Hence, if we running in the + * unified hierarchy and the container does the same, and we + * did not create a scope unit for the container move us and + * the container into two separate subcgroups. */ + + if (!unified_requested) + return 0; + + unified = cg_unified(); + if (unified < 0) + return log_error_errno(unified, "Failed to determine whether the unified hierachy is used: %m"); + if (unified == 0) + return 0; + + r = cg_mask_supported(&supported); + if (r < 0) + return log_error_errno(r, "Failed to determine supported controllers: %m"); + + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup); + if (r < 0) + return log_error_errno(r, "Failed to get our control group: %m"); + + child = strjoina(cgroup, "/payload"); + r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, child, pid); + if (r < 0) + return log_error_errno(r, "Failed to create %s subcgroup: %m", child); + + child = strjoina(cgroup, "/supervisor"); + r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, child, 0); + if (r < 0) + return log_error_errno(r, "Failed to create %s subcgroup: %m", child); + + /* Try to enable as many controllers as possible for the new payload. */ + (void) cg_enable_everywhere(supported, supported, cgroup); + return 0; +} diff --git a/src/nspawn/nspawn-cgroup.h b/src/nspawn/nspawn-cgroup.h new file mode 100644 index 000000000..985fdfaad --- /dev/null +++ b/src/nspawn/nspawn-cgroup.h @@ -0,0 +1,29 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +int chown_cgroup(pid_t pid, uid_t uid_shift); +int sync_cgroup(pid_t pid, bool unified_requested); +int create_subcgroup(pid_t pid, bool unified_requested); diff --git a/src/nspawn/nspawn-expose-ports.c b/src/nspawn/nspawn-expose-ports.c new file mode 100644 index 000000000..38250b6e0 --- /dev/null +++ b/src/nspawn/nspawn-expose-ports.c @@ -0,0 +1,277 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "sd-netlink.h" + +#include "util.h" +#include "in-addr-util.h" +#include "firewall-util.h" +#include "local-addresses.h" +#include "netlink-util.h" + +#include "nspawn-expose-ports.h" + +int expose_port_parse(ExposePort **l, const char *s) { + + const char *split, *e; + uint16_t container_port, host_port; + int protocol; + ExposePort *p; + int r; + + assert(l); + assert(s); + + if ((e = startswith(s, "tcp:"))) + protocol = IPPROTO_TCP; + else if ((e = startswith(s, "udp:"))) + protocol = IPPROTO_UDP; + else { + e = s; + protocol = IPPROTO_TCP; + } + + split = strchr(e, ':'); + if (split) { + char v[split - e + 1]; + + memcpy(v, e, split - e); + v[split - e] = 0; + + r = safe_atou16(v, &host_port); + if (r < 0 || host_port <= 0) + return -EINVAL; + + r = safe_atou16(split + 1, &container_port); + } else { + r = safe_atou16(e, &container_port); + host_port = container_port; + } + + if (r < 0 || container_port <= 0) + return -EINVAL; + + LIST_FOREACH(ports, p, *l) + if (p->protocol == protocol && p->host_port == host_port) + return -EEXIST; + + p = new(ExposePort, 1); + if (!p) + return -ENOMEM; + + p->protocol = protocol; + p->host_port = host_port; + p->container_port = container_port; + + LIST_PREPEND(ports, *l, p); + + return 0; +} + +void expose_port_free_all(ExposePort *p) { + + while (p) { + ExposePort *q = p; + LIST_REMOVE(ports, p, q); + free(q); + } +} + +int expose_port_flush(ExposePort* l, union in_addr_union *exposed) { + ExposePort *p; + int r, af = AF_INET; + + assert(exposed); + + if (!l) + return 0; + + if (in_addr_is_null(af, exposed)) + return 0; + + log_debug("Lost IP address."); + + LIST_FOREACH(ports, p, l) { + r = fw_add_local_dnat(false, + af, + p->protocol, + NULL, + NULL, 0, + NULL, 0, + p->host_port, + exposed, + p->container_port, + NULL); + if (r < 0) + log_warning_errno(r, "Failed to modify firewall: %m"); + } + + *exposed = IN_ADDR_NULL; + return 0; +} + +int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *exposed) { + _cleanup_free_ struct local_address *addresses = NULL; + _cleanup_free_ char *pretty = NULL; + union in_addr_union new_exposed; + ExposePort *p; + bool add; + int af = AF_INET, r; + + assert(exposed); + + /* Invoked each time an address is added or removed inside the + * container */ + + if (!l) + return 0; + + r = local_addresses(rtnl, 0, af, &addresses); + if (r < 0) + return log_error_errno(r, "Failed to enumerate local addresses: %m"); + + add = r > 0 && + addresses[0].family == af && + addresses[0].scope < RT_SCOPE_LINK; + + if (!add) + return expose_port_flush(l, exposed); + + new_exposed = addresses[0].address; + if (in_addr_equal(af, exposed, &new_exposed)) + return 0; + + in_addr_to_string(af, &new_exposed, &pretty); + log_debug("New container IP is %s.", strna(pretty)); + + LIST_FOREACH(ports, p, l) { + + r = fw_add_local_dnat(true, + af, + p->protocol, + NULL, + NULL, 0, + NULL, 0, + p->host_port, + &new_exposed, + p->container_port, + in_addr_is_null(af, exposed) ? NULL : exposed); + if (r < 0) + log_warning_errno(r, "Failed to modify firewall: %m"); + } + + *exposed = new_exposed; + return 0; +} + +int expose_port_send_rtnl(int send_fd) { + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(int))]; + } control = {}; + struct msghdr mh = { + .msg_control = &control, + .msg_controllen = sizeof(control), + }; + struct cmsghdr *cmsg; + _cleanup_close_ int fd = -1; + ssize_t k; + + assert(send_fd >= 0); + + fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE); + if (fd < 0) + return log_error_errno(errno, "Failed to allocate container netlink: %m"); + + cmsg = CMSG_FIRSTHDR(&mh); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); + + mh.msg_controllen = cmsg->cmsg_len; + + /* Store away the fd in the socket, so that it stays open as + * long as we run the child */ + k = sendmsg(send_fd, &mh, MSG_NOSIGNAL); + if (k < 0) + return log_error_errno(errno, "Failed to send netlink fd: %m"); + + return 0; +} + +int expose_port_watch_rtnl( + sd_event *event, + int recv_fd, + sd_netlink_message_handler_t handler, + union in_addr_union *exposed, + sd_netlink **ret) { + + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(int))]; + } control = {}; + struct msghdr mh = { + .msg_control = &control, + .msg_controllen = sizeof(control), + }; + struct cmsghdr *cmsg; + _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; + int fd, r; + ssize_t k; + + assert(event); + assert(recv_fd >= 0); + assert(ret); + + k = recvmsg(recv_fd, &mh, MSG_NOSIGNAL); + if (k < 0) + return log_error_errno(errno, "Failed to recv netlink fd: %m"); + + cmsg = CMSG_FIRSTHDR(&mh); + assert(cmsg->cmsg_level == SOL_SOCKET); + assert(cmsg->cmsg_type == SCM_RIGHTS); + assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int))); + memcpy(&fd, CMSG_DATA(cmsg), sizeof(int)); + + r = sd_netlink_open_fd(&rtnl, fd); + if (r < 0) { + safe_close(fd); + return log_error_errno(r, "Failed to create rtnl object: %m"); + } + + r = sd_netlink_add_match(rtnl, RTM_NEWADDR, handler, exposed); + if (r < 0) + return log_error_errno(r, "Failed to subscribe to RTM_NEWADDR messages: %m"); + + r = sd_netlink_add_match(rtnl, RTM_DELADDR, handler, exposed); + if (r < 0) + return log_error_errno(r, "Failed to subscribe to RTM_DELADDR messages: %m"); + + r = sd_netlink_attach_event(rtnl, event, 0); + if (r < 0) + return log_error_errno(r, "Failed to add to even loop: %m"); + + *ret = rtnl; + rtnl = NULL; + + return 0; +} diff --git a/src/nspawn/nspawn-expose-ports.h b/src/nspawn/nspawn-expose-ports.h new file mode 100644 index 000000000..39cec2869 --- /dev/null +++ b/src/nspawn/nspawn-expose-ports.h @@ -0,0 +1,45 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "sd-event.h" +#include "sd-netlink.h" +#include "list.h" +#include "in-addr-util.h" + +typedef struct ExposePort { + int protocol; + uint16_t host_port; + uint16_t container_port; + LIST_FIELDS(struct ExposePort, ports); +} ExposePort; + +void expose_port_free_all(ExposePort *p); +int expose_port_parse(ExposePort **l, const char *s); + +int expose_port_watch_rtnl(sd_event *event, int recv_fd, sd_netlink_message_handler_t handler, union in_addr_union *exposed, sd_netlink **ret); +int expose_port_send_rtnl(int send_fd); + +int expose_port_execute(sd_netlink *rtnl, ExposePort *l, union in_addr_union *exposed); +int expose_port_flush(ExposePort* l, union in_addr_union *exposed); diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf new file mode 100644 index 000000000..074b38030 --- /dev/null +++ b/src/nspawn/nspawn-gperf.gperf @@ -0,0 +1,38 @@ +%{ +#include +#include "conf-parser.h" +#include "nspawn-settings.h" +#include "nspawn-expose-ports.h" +%} +struct ConfigPerfItem; +%null_strings +%language=ANSI-C +%define slot-name section_and_lvalue +%define hash-function-name nspawn_gperf_hash +%define lookup-function-name nspawn_gperf_lookup +%readonly-tables +%omit-struct-type +%struct-type +%includes +%% +Exec.Boot, config_parse_tristate, 0, offsetof(Settings, boot) +Exec.Parameters, config_parse_strv, 0, offsetof(Settings, parameters) +Exec.Environment, config_parse_strv, 0, offsetof(Settings, environment) +Exec.User, config_parse_string, 0, offsetof(Settings, user) +Exec.Capability, config_parse_capability, 0, offsetof(Settings, capability) +Exec.DropCapability, config_parse_capability, 0, offsetof(Settings, drop_capability) +Exec.KillSignal, config_parse_signal, 0, offsetof(Settings, kill_signal) +Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality) +Exec.MachineID, config_parse_id128, 0, offsetof(Settings, machine_id) +Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only) +Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode) +Files.Bind, config_parse_bind, 0, 0 +Files.BindReadOnly, config_parse_bind, 1, 0 +Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0 +Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network) +Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces) +Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, network_macvlan) +Network.IPVLAN, config_parse_strv, 0, offsetof(Settings, network_ipvlan) +Network.VirtualEthernet, config_parse_tristate, 0, offsetof(Settings, network_veth) +Network.Bridge config_parse_string, 0, offsetof(Settings, network_bridge) +Network.Port, config_parse_expose_port, 0, 0 diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c new file mode 100644 index 000000000..2bca39f45 --- /dev/null +++ b/src/nspawn/nspawn-mount.c @@ -0,0 +1,869 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "util.h" +#include "rm-rf.h" +#include "strv.h" +#include "path-util.h" +#include "mkdir.h" +#include "label.h" +#include "set.h" +#include "cgroup-util.h" + +#include "nspawn-mount.h" + +CustomMount* custom_mount_add(CustomMount **l, unsigned *n, CustomMountType t) { + CustomMount *c, *ret; + + assert(l); + assert(n); + assert(t >= 0); + assert(t < _CUSTOM_MOUNT_TYPE_MAX); + + c = realloc(*l, (*n + 1) * sizeof(CustomMount)); + if (!c) + return NULL; + + *l = c; + ret = *l + *n; + (*n)++; + + *ret = (CustomMount) { .type = t }; + + return ret; +} + +void custom_mount_free_all(CustomMount *l, unsigned n) { + unsigned i; + + for (i = 0; i < n; i++) { + CustomMount *m = l + i; + + free(m->source); + free(m->destination); + free(m->options); + + if (m->work_dir) { + (void) rm_rf(m->work_dir, REMOVE_ROOT|REMOVE_PHYSICAL); + free(m->work_dir); + } + + strv_free(m->lower); + } + + free(l); +} + +int custom_mount_compare(const void *a, const void *b) { + const CustomMount *x = a, *y = b; + int r; + + r = path_compare(x->destination, y->destination); + if (r != 0) + return r; + + if (x->type < y->type) + return -1; + if (x->type > y->type) + return 1; + + return 0; +} + +int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only) { + _cleanup_free_ char *source = NULL, *destination = NULL, *opts = NULL; + const char *p = s; + CustomMount *m; + int r; + + assert(l); + assert(n); + + r = extract_many_words(&p, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination, NULL); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + + if (r == 1) { + destination = strdup(source); + if (!destination) + return -ENOMEM; + } + + if (r == 2 && !isempty(p)) { + opts = strdup(p); + if (!opts) + return -ENOMEM; + } + + if (!path_is_absolute(source)) + return -EINVAL; + + if (!path_is_absolute(destination)) + return -EINVAL; + + m = custom_mount_add(l, n, CUSTOM_MOUNT_BIND); + if (!m) + return log_oom(); + + m->source = source; + m->destination = destination; + m->read_only = read_only; + m->options = opts; + + source = destination = opts = NULL; + return 0; +} + +int tmpfs_mount_parse(CustomMount **l, unsigned *n, const char *s) { + _cleanup_free_ char *path = NULL, *opts = NULL; + const char *p = s; + CustomMount *m; + int r; + + assert(l); + assert(n); + assert(s); + + r = extract_first_word(&p, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + + if (isempty(p)) + opts = strdup("mode=0755"); + else + opts = strdup(p); + if (!opts) + return -ENOMEM; + + if (!path_is_absolute(path)) + return -EINVAL; + + m = custom_mount_add(l, n, CUSTOM_MOUNT_TMPFS); + if (!m) + return -ENOMEM; + + m->destination = path; + m->options = opts; + + path = opts = NULL; + return 0; +} + +static int tmpfs_patch_options( + const char *options, + bool userns, uid_t uid_shift, uid_t uid_range, + const char *selinux_apifs_context, + char **ret) { + + char *buf = NULL; + + if (userns && uid_shift != 0) { + assert(uid_shift != UID_INVALID); + + if (options) + (void) asprintf(&buf, "%s,uid=" UID_FMT ",gid=" UID_FMT, options, uid_shift, uid_shift); + else + (void) asprintf(&buf, "uid=" UID_FMT ",gid=" UID_FMT, uid_shift, uid_shift); + if (!buf) + return -ENOMEM; + + options = buf; + } + +#ifdef HAVE_SELINUX + if (selinux_apifs_context) { + char *t; + + if (options) + t = strjoin(options, ",context=\"", selinux_apifs_context, "\"", NULL); + else + t = strjoin("context=\"", selinux_apifs_context, "\"", NULL); + if (!t) { + free(buf); + return -ENOMEM; + } + + free(buf); + buf = t; + } +#endif + + *ret = buf; + return !!buf; +} + +int mount_all(const char *dest, + bool userns, uid_t uid_shift, uid_t uid_range, + const char *selinux_apifs_context) { + + typedef struct MountPoint { + const char *what; + const char *where; + const char *type; + const char *options; + unsigned long flags; + bool fatal; + bool userns; + } MountPoint; + + static const MountPoint mount_table[] = { + { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true, true }, + { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, true, true }, /* Bind mount first */ + { NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, true, true }, /* Then, make it r/o */ + { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, true, false }, + { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true, false }, + { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false }, + { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false }, + { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME, true, false }, +#ifdef HAVE_SELINUX + { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, false, false }, /* Bind mount first */ + { NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, false, false }, /* Then, make it r/o */ +#endif + }; + + unsigned k; + int r; + + for (k = 0; k < ELEMENTSOF(mount_table); k++) { + _cleanup_free_ char *where = NULL, *options = NULL; + const char *o; + + if (userns != mount_table[k].userns) + continue; + + where = prefix_root(dest, mount_table[k].where); + if (!where) + return log_oom(); + + r = path_is_mount_point(where, AT_SYMLINK_FOLLOW); + if (r < 0 && r != -ENOENT) + return log_error_errno(r, "Failed to detect whether %s is a mount point: %m", where); + + /* Skip this entry if it is not a remount. */ + if (mount_table[k].what && r > 0) + continue; + + r = mkdir_p(where, 0755); + if (r < 0) { + if (mount_table[k].fatal) + return log_error_errno(r, "Failed to create directory %s: %m", where); + + log_warning_errno(r, "Failed to create directory %s: %m", where); + continue; + } + + o = mount_table[k].options; + if (streq_ptr(mount_table[k].type, "tmpfs")) { + r = tmpfs_patch_options(o, userns, uid_shift, uid_range, selinux_apifs_context, &options); + if (r < 0) + return log_oom(); + if (r > 0) + o = options; + } + + if (mount(mount_table[k].what, + where, + mount_table[k].type, + mount_table[k].flags, + o) < 0) { + + if (mount_table[k].fatal) + return log_error_errno(errno, "mount(%s) failed: %m", where); + + log_warning_errno(errno, "mount(%s) failed, ignoring: %m", where); + } + } + + return 0; +} + +static int parse_mount_bind_options(const char *options, unsigned long *mount_flags, char **mount_opts) { + const char *p = options; + unsigned long flags = *mount_flags; + char *opts = NULL; + + assert(options); + + for (;;) { + _cleanup_free_ char *word = NULL; + int r = extract_first_word(&p, &word, ",", 0); + if (r < 0) + return log_error_errno(r, "Failed to extract mount option: %m"); + if (r == 0) + break; + + if (streq(word, "rbind")) + flags |= MS_REC; + else if (streq(word, "norbind")) + flags &= ~MS_REC; + else { + log_error("Invalid bind mount option: %s", word); + return -EINVAL; + } + } + + *mount_flags = flags; + /* in the future mount_opts will hold string options for mount(2) */ + *mount_opts = opts; + + return 0; +} + +static int mount_bind(const char *dest, CustomMount *m) { + struct stat source_st, dest_st; + const char *where; + unsigned long mount_flags = MS_BIND | MS_REC; + _cleanup_free_ char *mount_opts = NULL; + int r; + + assert(m); + + if (m->options) { + r = parse_mount_bind_options(m->options, &mount_flags, &mount_opts); + if (r < 0) + return r; + } + + if (stat(m->source, &source_st) < 0) + return log_error_errno(errno, "Failed to stat %s: %m", m->source); + + where = prefix_roota(dest, m->destination); + + if (stat(where, &dest_st) >= 0) { + if (S_ISDIR(source_st.st_mode) && !S_ISDIR(dest_st.st_mode)) { + log_error("Cannot bind mount directory %s on file %s.", m->source, where); + return -EINVAL; + } + + if (!S_ISDIR(source_st.st_mode) && S_ISDIR(dest_st.st_mode)) { + log_error("Cannot bind mount file %s on directory %s.", m->source, where); + return -EINVAL; + } + + } else if (errno == ENOENT) { + r = mkdir_parents_label(where, 0755); + if (r < 0) + return log_error_errno(r, "Failed to make parents of %s: %m", where); + } else { + log_error_errno(errno, "Failed to stat %s: %m", where); + return -errno; + } + + /* Create the mount point. Any non-directory file can be + * mounted on any non-directory file (regular, fifo, socket, + * char, block). + */ + if (S_ISDIR(source_st.st_mode)) + r = mkdir_label(where, 0755); + else + r = touch(where); + if (r < 0 && r != -EEXIST) + return log_error_errno(r, "Failed to create mount point %s: %m", where); + + if (mount(m->source, where, NULL, mount_flags, mount_opts) < 0) + return log_error_errno(errno, "mount(%s) failed: %m", where); + + if (m->read_only) { + r = bind_remount_recursive(where, true); + if (r < 0) + return log_error_errno(r, "Read-only bind mount failed: %m"); + } + + return 0; +} + +static int mount_tmpfs( + const char *dest, + CustomMount *m, + bool userns, uid_t uid_shift, uid_t uid_range, + const char *selinux_apifs_context) { + + const char *where, *options; + _cleanup_free_ char *buf = NULL; + int r; + + assert(dest); + assert(m); + + where = prefix_roota(dest, m->destination); + + r = mkdir_p_label(where, 0755); + if (r < 0 && r != -EEXIST) + return log_error_errno(r, "Creating mount point for tmpfs %s failed: %m", where); + + r = tmpfs_patch_options(m->options, userns, uid_shift, uid_range, selinux_apifs_context, &buf); + if (r < 0) + return log_oom(); + options = r > 0 ? buf : m->options; + + if (mount("tmpfs", where, "tmpfs", MS_NODEV|MS_STRICTATIME, options) < 0) + return log_error_errno(errno, "tmpfs mount to %s failed: %m", where); + + return 0; +} + +static char *joined_and_escaped_lower_dirs(char * const *lower) { + _cleanup_strv_free_ char **sv = NULL; + + sv = strv_copy(lower); + if (!sv) + return NULL; + + strv_reverse(sv); + + if (!strv_shell_escape(sv, ",:")) + return NULL; + + return strv_join(sv, ":"); +} + +static int mount_overlay(const char *dest, CustomMount *m) { + _cleanup_free_ char *lower = NULL; + const char *where, *options; + int r; + + assert(dest); + assert(m); + + where = prefix_roota(dest, m->destination); + + r = mkdir_label(where, 0755); + if (r < 0 && r != -EEXIST) + return log_error_errno(r, "Creating mount point for overlay %s failed: %m", where); + + (void) mkdir_p_label(m->source, 0755); + + lower = joined_and_escaped_lower_dirs(m->lower); + if (!lower) + return log_oom(); + + if (m->read_only) { + _cleanup_free_ char *escaped_source = NULL; + + escaped_source = shell_escape(m->source, ",:"); + if (!escaped_source) + return log_oom(); + + options = strjoina("lowerdir=", escaped_source, ":", lower); + } else { + _cleanup_free_ char *escaped_source = NULL, *escaped_work_dir = NULL; + + assert(m->work_dir); + (void) mkdir_label(m->work_dir, 0700); + + escaped_source = shell_escape(m->source, ",:"); + if (!escaped_source) + return log_oom(); + escaped_work_dir = shell_escape(m->work_dir, ",:"); + if (!escaped_work_dir) + return log_oom(); + + options = strjoina("lowerdir=", lower, ",upperdir=", escaped_source, ",workdir=", escaped_work_dir); + } + + if (mount("overlay", where, "overlay", m->read_only ? MS_RDONLY : 0, options) < 0) + return log_error_errno(errno, "overlay mount to %s failed: %m", where); + + return 0; +} + +int mount_custom( + const char *dest, + CustomMount *mounts, unsigned n, + bool userns, uid_t uid_shift, uid_t uid_range, + const char *selinux_apifs_context) { + + unsigned i; + int r; + + assert(dest); + + for (i = 0; i < n; i++) { + CustomMount *m = mounts + i; + + switch (m->type) { + + case CUSTOM_MOUNT_BIND: + r = mount_bind(dest, m); + break; + + case CUSTOM_MOUNT_TMPFS: + r = mount_tmpfs(dest, m, userns, uid_shift, uid_range, selinux_apifs_context); + break; + + case CUSTOM_MOUNT_OVERLAY: + r = mount_overlay(dest, m); + break; + + default: + assert_not_reached("Unknown custom mount type"); + } + + if (r < 0) + return r; + } + + return 0; +} + +static int mount_legacy_cgroup_hierarchy(const char *dest, const char *controller, const char *hierarchy, bool read_only) { + char *to; + int r; + + to = strjoina(dest, "/sys/fs/cgroup/", hierarchy); + + r = path_is_mount_point(to, 0); + if (r < 0 && r != -ENOENT) + return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to); + if (r > 0) + return 0; + + mkdir_p(to, 0755); + + /* The superblock mount options of the mount point need to be + * identical to the hosts', and hence writable... */ + if (mount("cgroup", to, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, controller) < 0) + return log_error_errno(errno, "Failed to mount to %s: %m", to); + + /* ... hence let's only make the bind mount read-only, not the + * superblock. */ + if (read_only) { + if (mount(NULL, to, NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL) < 0) + return log_error_errno(errno, "Failed to remount %s read-only: %m", to); + } + return 1; +} + +static int mount_legacy_cgroups( + const char *dest, + bool userns, uid_t uid_shift, uid_t uid_range, + const char *selinux_apifs_context) { + + _cleanup_set_free_free_ Set *controllers = NULL; + const char *cgroup_root; + int r; + + cgroup_root = prefix_roota(dest, "/sys/fs/cgroup"); + + /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */ + r = path_is_mount_point(cgroup_root, AT_SYMLINK_FOLLOW); + if (r < 0) + return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m"); + if (r == 0) { + _cleanup_free_ char *options = NULL; + + r = tmpfs_patch_options("mode=755", userns, uid_shift, uid_range, selinux_apifs_context, &options); + if (r < 0) + return log_oom(); + + if (mount("tmpfs", cgroup_root, "tmpfs", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options) < 0) + return log_error_errno(errno, "Failed to mount /sys/fs/cgroup: %m"); + } + + if (cg_unified() > 0) + goto skip_controllers; + + controllers = set_new(&string_hash_ops); + if (!controllers) + return log_oom(); + + r = cg_kernel_controllers(controllers); + if (r < 0) + return log_error_errno(r, "Failed to determine cgroup controllers: %m"); + + for (;;) { + _cleanup_free_ char *controller = NULL, *origin = NULL, *combined = NULL; + + controller = set_steal_first(controllers); + if (!controller) + break; + + origin = prefix_root("/sys/fs/cgroup/", controller); + if (!origin) + return log_oom(); + + r = readlink_malloc(origin, &combined); + if (r == -EINVAL) { + /* Not a symbolic link, but directly a single cgroup hierarchy */ + + r = mount_legacy_cgroup_hierarchy(dest, controller, controller, true); + if (r < 0) + return r; + + } else if (r < 0) + return log_error_errno(r, "Failed to read link %s: %m", origin); + else { + _cleanup_free_ char *target = NULL; + + target = prefix_root(dest, origin); + if (!target) + return log_oom(); + + /* A symbolic link, a combination of controllers in one hierarchy */ + + if (!filename_is_valid(combined)) { + log_warning("Ignoring invalid combined hierarchy %s.", combined); + continue; + } + + r = mount_legacy_cgroup_hierarchy(dest, combined, combined, true); + if (r < 0) + return r; + + r = symlink_idempotent(combined, target); + if (r == -EINVAL) { + log_error("Invalid existing symlink for combined hierarchy"); + return r; + } + if (r < 0) + return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m"); + } + } + +skip_controllers: + r = mount_legacy_cgroup_hierarchy(dest, "none,name=systemd,xattr", "systemd", false); + if (r < 0) + return r; + + if (mount(NULL, cgroup_root, NULL, MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755") < 0) + return log_error_errno(errno, "Failed to remount %s read-only: %m", cgroup_root); + + return 0; +} + +static int mount_unified_cgroups(const char *dest) { + const char *p; + int r; + + assert(dest); + + p = strjoina(dest, "/sys/fs/cgroup"); + + r = path_is_mount_point(p, AT_SYMLINK_FOLLOW); + if (r < 0) + return log_error_errno(r, "Failed to determine if %s is mounted already: %m", p); + if (r > 0) { + p = strjoina(dest, "/sys/fs/cgroup/cgroup.procs"); + if (access(p, F_OK) >= 0) + return 0; + if (errno != ENOENT) + return log_error_errno(errno, "Failed to determine if mount point %s contains the unified cgroup hierarchy: %m", p); + + log_error("%s is already mounted but not a unified cgroup hierarchy. Refusing.", p); + return -EINVAL; + } + + if (mount("cgroup", p, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, "__DEVEL__sane_behavior") < 0) + return log_error_errno(errno, "Failed to mount unified cgroup hierarchy to %s: %m", p); + + return 0; +} + +int mount_cgroups( + const char *dest, + bool unified_requested, + bool userns, uid_t uid_shift, uid_t uid_range, + const char *selinux_apifs_context) { + + if (unified_requested) + return mount_unified_cgroups(dest); + else + return mount_legacy_cgroups(dest, userns, uid_shift, uid_range, selinux_apifs_context); +} + +int mount_systemd_cgroup_writable( + const char *dest, + bool unified_requested) { + + _cleanup_free_ char *own_cgroup_path = NULL; + const char *systemd_root, *systemd_own; + int r; + + assert(dest); + + r = cg_pid_get_path(NULL, 0, &own_cgroup_path); + if (r < 0) + return log_error_errno(r, "Failed to determine our own cgroup path: %m"); + + /* If we are living in the top-level, then there's nothing to do... */ + if (path_equal(own_cgroup_path, "/")) + return 0; + + if (unified_requested) { + systemd_own = strjoina(dest, "/sys/fs/cgroup", own_cgroup_path); + systemd_root = prefix_roota(dest, "/sys/fs/cgroup"); + } else { + systemd_own = strjoina(dest, "/sys/fs/cgroup/systemd", own_cgroup_path); + systemd_root = prefix_roota(dest, "/sys/fs/cgroup/systemd"); + } + + /* Make our own cgroup a (writable) bind mount */ + if (mount(systemd_own, systemd_own, NULL, MS_BIND, NULL) < 0) + return log_error_errno(errno, "Failed to turn %s into a bind mount: %m", own_cgroup_path); + + /* And then remount the systemd cgroup root read-only */ + if (mount(NULL, systemd_root, NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL) < 0) + return log_error_errno(errno, "Failed to mount cgroup root read-only: %m"); + + return 0; +} + +int setup_volatile_state( + const char *directory, + VolatileMode mode, + bool userns, uid_t uid_shift, uid_t uid_range, + const char *selinux_apifs_context) { + + _cleanup_free_ char *buf = NULL; + const char *p, *options; + int r; + + assert(directory); + + if (mode != VOLATILE_STATE) + return 0; + + /* --volatile=state means we simply overmount /var + with a tmpfs, and the rest read-only. */ + + r = bind_remount_recursive(directory, true); + if (r < 0) + return log_error_errno(r, "Failed to remount %s read-only: %m", directory); + + p = prefix_roota(directory, "/var"); + r = mkdir(p, 0755); + if (r < 0 && errno != EEXIST) + return log_error_errno(errno, "Failed to create %s: %m", directory); + + options = "mode=755"; + r = tmpfs_patch_options(options, userns, uid_shift, uid_range, selinux_apifs_context, &buf); + if (r < 0) + return log_oom(); + if (r > 0) + options = buf; + + if (mount("tmpfs", p, "tmpfs", MS_STRICTATIME, options) < 0) + return log_error_errno(errno, "Failed to mount tmpfs to /var: %m"); + + return 0; +} + +int setup_volatile( + const char *directory, + VolatileMode mode, + bool userns, uid_t uid_shift, uid_t uid_range, + const char *selinux_apifs_context) { + + bool tmpfs_mounted = false, bind_mounted = false; + char template[] = "/tmp/nspawn-volatile-XXXXXX"; + _cleanup_free_ char *buf = NULL; + const char *f, *t, *options; + int r; + + assert(directory); + + if (mode != VOLATILE_YES) + return 0; + + /* --volatile=yes means we mount a tmpfs to the root dir, and + the original /usr to use inside it, and that read-only. */ + + if (!mkdtemp(template)) + return log_error_errno(errno, "Failed to create temporary directory: %m"); + + options = "mode=755"; + r = tmpfs_patch_options(options, userns, uid_shift, uid_range, selinux_apifs_context, &buf); + if (r < 0) + return log_oom(); + if (r > 0) + options = buf; + + if (mount("tmpfs", template, "tmpfs", MS_STRICTATIME, options) < 0) { + r = log_error_errno(errno, "Failed to mount tmpfs for root directory: %m"); + goto fail; + } + + tmpfs_mounted = true; + + f = prefix_roota(directory, "/usr"); + t = prefix_roota(template, "/usr"); + + r = mkdir(t, 0755); + if (r < 0 && errno != EEXIST) { + r = log_error_errno(errno, "Failed to create %s: %m", t); + goto fail; + } + + if (mount(f, t, NULL, MS_BIND|MS_REC, NULL) < 0) { + r = log_error_errno(errno, "Failed to create /usr bind mount: %m"); + goto fail; + } + + bind_mounted = true; + + r = bind_remount_recursive(t, true); + if (r < 0) { + log_error_errno(r, "Failed to remount %s read-only: %m", t); + goto fail; + } + + if (mount(template, directory, NULL, MS_MOVE, NULL) < 0) { + r = log_error_errno(errno, "Failed to move root mount: %m"); + goto fail; + } + + (void) rmdir(template); + + return 0; + +fail: + if (bind_mounted) + (void) umount(t); + + if (tmpfs_mounted) + (void) umount(template); + (void) rmdir(template); + return r; +} + +VolatileMode volatile_mode_from_string(const char *s) { + int b; + + if (isempty(s)) + return _VOLATILE_MODE_INVALID; + + b = parse_boolean(s); + if (b > 0) + return VOLATILE_YES; + if (b == 0) + return VOLATILE_NO; + + if (streq(s, "state")) + return VOLATILE_STATE; + + return _VOLATILE_MODE_INVALID; +} diff --git a/src/nspawn/nspawn-mount.h b/src/nspawn/nspawn-mount.h new file mode 100644 index 000000000..5abd44cc4 --- /dev/null +++ b/src/nspawn/nspawn-mount.h @@ -0,0 +1,70 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +typedef enum VolatileMode { + VOLATILE_NO, + VOLATILE_YES, + VOLATILE_STATE, + _VOLATILE_MODE_MAX, + _VOLATILE_MODE_INVALID = -1 +} VolatileMode; + +typedef enum CustomMountType { + CUSTOM_MOUNT_BIND, + CUSTOM_MOUNT_TMPFS, + CUSTOM_MOUNT_OVERLAY, + _CUSTOM_MOUNT_TYPE_MAX, + _CUSTOM_MOUNT_TYPE_INVALID = -1 +} CustomMountType; + +typedef struct CustomMount { + CustomMountType type; + bool read_only; + char *source; /* for overlayfs this is the upper directory */ + char *destination; + char *options; + char *work_dir; + char **lower; +} CustomMount; + +CustomMount* custom_mount_add(CustomMount **l, unsigned *n, CustomMountType t); + +void custom_mount_free_all(CustomMount *l, unsigned n); +int bind_mount_parse(CustomMount **l, unsigned *n, const char *s, bool read_only); +int tmpfs_mount_parse(CustomMount **l, unsigned *n, const char *s); + +int custom_mount_compare(const void *a, const void *b); + +int mount_all(const char *dest, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); + +int mount_cgroups(const char *dest, bool unified_requested, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); +int mount_systemd_cgroup_writable(const char *dest, bool unified_requested); + +int mount_custom(const char *dest, CustomMount *mounts, unsigned n, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); + +int setup_volatile(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); +int setup_volatile_state(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); + +VolatileMode volatile_mode_from_string(const char *s); diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c new file mode 100644 index 000000000..74abe5379 --- /dev/null +++ b/src/nspawn/nspawn-network.c @@ -0,0 +1,440 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "sd-id128.h" +#include "sd-netlink.h" +#include "libudev.h" + +#include "util.h" +#include "ether-addr-util.h" +#include "siphash24.h" +#include "netlink-util.h" +#include "udev-util.h" + +#include "nspawn-network.h" + +#define HOST_HASH_KEY SD_ID128_MAKE(1a,37,6f,c7,46,ec,45,0b,ad,a3,d5,31,06,60,5d,b1) +#define CONTAINER_HASH_KEY SD_ID128_MAKE(c3,c4,f9,19,b5,57,b2,1c,e6,cf,14,27,03,9c,ee,a2) +#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 generate_mac( + const char *machine_name, + struct ether_addr *mac, + sd_id128_t hash_key, + uint64_t idx) { + + uint8_t result[8]; + size_t l, sz; + uint8_t *v, *i; + int r; + + l = strlen(machine_name); + sz = sizeof(sd_id128_t) + l; + if (idx > 0) + sz += sizeof(idx); + + v = alloca(sz); + + /* fetch some persistent data unique to the host */ + r = sd_id128_get_machine((sd_id128_t*) v); + if (r < 0) + return r; + + /* combine with some data unique (on this host) to this + * container instance */ + i = mempcpy(v + sizeof(sd_id128_t), machine_name, l); + if (idx > 0) { + idx = htole64(idx); + memcpy(i, &idx, sizeof(idx)); + } + + /* Let's hash the host machine ID plus the container name. We + * use a fixed, but originally randomly created hash key here. */ + siphash24(result, v, sz, hash_key.bytes); + + assert_cc(ETH_ALEN <= sizeof(result)); + memcpy(mac->ether_addr_octet, result, ETH_ALEN); + + /* see eth_random_addr in the kernel */ + mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */ + mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */ + + return 0; +} + +int setup_veth(const char *machine_name, + pid_t pid, + char iface_name[IFNAMSIZ], + bool bridge) { + + _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL; + _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; + struct ether_addr mac_host, mac_container; + int r, i; + + /* Use two different interface name prefixes depending whether + * we are in bridge mode or not. */ + snprintf(iface_name, IFNAMSIZ - 1, "%s-%s", + bridge ? "vb" : "ve", machine_name); + + r = generate_mac(machine_name, &mac_container, CONTAINER_HASH_KEY, 0); + if (r < 0) + return log_error_errno(r, "Failed to generate predictable MAC address for container side: %m"); + + r = generate_mac(machine_name, &mac_host, HOST_HASH_KEY, 0); + if (r < 0) + return log_error_errno(r, "Failed to generate predictable MAC address for host side: %m"); + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0); + if (r < 0) + return log_error_errno(r, "Failed to allocate netlink message: %m"); + + r = sd_netlink_message_append_string(m, IFLA_IFNAME, iface_name); + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface name: %m"); + + r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac_host); + if (r < 0) + return log_error_errno(r, "Failed to add netlink MAC address: %m"); + + r = sd_netlink_message_open_container(m, IFLA_LINKINFO); + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); + + r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "veth"); + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); + + r = sd_netlink_message_open_container(m, VETH_INFO_PEER); + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); + + r = sd_netlink_message_append_string(m, IFLA_IFNAME, "host0"); + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface name: %m"); + + r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac_container); + if (r < 0) + return log_error_errno(r, "Failed to add netlink MAC address: %m"); + + r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid); + if (r < 0) + return log_error_errno(r, "Failed to add netlink namespace field: %m"); + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); + + r = sd_netlink_call(rtnl, m, 0, NULL); + if (r < 0) + return log_error_errno(r, "Failed to add new veth interfaces (host0, %s): %m", iface_name); + + i = (int) if_nametoindex(iface_name); + if (i <= 0) + return log_error_errno(errno, "Failed to resolve interface %s: %m", iface_name); + + return i; +} + +int setup_bridge(const char *veth_name, const char *bridge_name) { + _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL; + _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; + int r, bridge_ifi; + + 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"); + + 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"); + + 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"); + + 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"); + + 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"); + + 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 bridge_ifi; +} + +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)]; + int ifi; + + ifi = (int) if_nametoindex(name); + if (ifi <= 0) + return log_error_errno(errno, "Failed to resolve interface %s: %m", name); + + sprintf(ifi_str, "n%i", ifi); + d = udev_device_new_from_device_id(udev, ifi_str); + if (!d) + return log_error_errno(errno, "Failed to get udev device for interface %s: %m", name); + + if (udev_device_get_is_initialized(d) <= 0) { + log_error("Network interface %s is not initialized yet.", name); + return -EBUSY; + } + + return ifi; +} + +int move_network_interfaces(pid_t pid, char **ifaces) { + _cleanup_udev_unref_ struct udev *udev = NULL; + _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; + char **i; + int r; + + if (strv_isempty(ifaces)) + return 0; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + udev = udev_new(); + if (!udev) { + log_error("Failed to connect to udev."); + return -ENOMEM; + } + + STRV_FOREACH(i, ifaces) { + _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL; + int ifi; + + ifi = parse_interface(udev, *i); + if (ifi < 0) + return ifi; + + r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, ifi); + if (r < 0) + return log_error_errno(r, "Failed to allocate netlink message: %m"); + + r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid); + if (r < 0) + return log_error_errno(r, "Failed to append namespace PID to netlink message: %m"); + + r = sd_netlink_call(rtnl, m, 0, NULL); + if (r < 0) + return log_error_errno(r, "Failed to move interface %s to namespace: %m", *i); + } + + return 0; +} + +int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) { + _cleanup_udev_unref_ struct udev *udev = NULL; + _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; + unsigned idx = 0; + char **i; + int r; + + if (strv_isempty(ifaces)) + return 0; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + udev = udev_new(); + if (!udev) { + log_error("Failed to connect to udev."); + return -ENOMEM; + } + + STRV_FOREACH(i, ifaces) { + _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL; + _cleanup_free_ char *n = NULL; + struct ether_addr mac; + int ifi; + + ifi = parse_interface(udev, *i); + if (ifi < 0) + return ifi; + + r = generate_mac(machine_name, &mac, MACVLAN_HASH_KEY, idx++); + if (r < 0) + return log_error_errno(r, "Failed to create MACVLAN MAC address: %m"); + + r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0); + if (r < 0) + return log_error_errno(r, "Failed to allocate netlink message: %m"); + + r = sd_netlink_message_append_u32(m, IFLA_LINK, ifi); + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface index: %m"); + + n = strappend("mv-", *i); + if (!n) + return log_oom(); + + strshorten(n, IFNAMSIZ-1); + + r = sd_netlink_message_append_string(m, IFLA_IFNAME, n); + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface name: %m"); + + r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac); + if (r < 0) + return log_error_errno(r, "Failed to add netlink MAC address: %m"); + + r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid); + if (r < 0) + return log_error_errno(r, "Failed to add netlink namespace field: %m"); + + r = sd_netlink_message_open_container(m, IFLA_LINKINFO); + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); + + r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "macvlan"); + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); + + r = sd_netlink_message_append_u32(m, IFLA_MACVLAN_MODE, MACVLAN_MODE_BRIDGE); + if (r < 0) + return log_error_errno(r, "Failed to append macvlan mode: %m"); + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); + + r = sd_netlink_call(rtnl, m, 0, NULL); + if (r < 0) + return log_error_errno(r, "Failed to add new macvlan interfaces: %m"); + } + + return 0; +} + +int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) { + _cleanup_udev_unref_ struct udev *udev = NULL; + _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; + char **i; + int r; + + if (strv_isempty(ifaces)) + return 0; + + r = sd_netlink_open(&rtnl); + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); + + udev = udev_new(); + if (!udev) { + log_error("Failed to connect to udev."); + return -ENOMEM; + } + + STRV_FOREACH(i, ifaces) { + _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL; + _cleanup_free_ char *n = NULL; + int ifi; + + ifi = parse_interface(udev, *i); + if (ifi < 0) + return ifi; + + r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0); + if (r < 0) + return log_error_errno(r, "Failed to allocate netlink message: %m"); + + r = sd_netlink_message_append_u32(m, IFLA_LINK, ifi); + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface index: %m"); + + n = strappend("iv-", *i); + if (!n) + return log_oom(); + + strshorten(n, IFNAMSIZ-1); + + r = sd_netlink_message_append_string(m, IFLA_IFNAME, n); + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface name: %m"); + + r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid); + if (r < 0) + return log_error_errno(r, "Failed to add netlink namespace field: %m"); + + r = sd_netlink_message_open_container(m, IFLA_LINKINFO); + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); + + r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "ipvlan"); + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); + + r = sd_netlink_message_append_u16(m, IFLA_IPVLAN_MODE, IPVLAN_MODE_L2); + if (r < 0) + return log_error_errno(r, "Failed to add ipvlan mode: %m"); + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); + + r = sd_netlink_call(rtnl, m, 0, NULL); + if (r < 0) + return log_error_errno(r, "Failed to add new ipvlan interfaces: %m"); + } + + return 0; +} diff --git a/src/nspawn/nspawn-network.h b/src/nspawn/nspawn-network.h new file mode 100644 index 000000000..311e6d06c --- /dev/null +++ b/src/nspawn/nspawn-network.h @@ -0,0 +1,36 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include +#include + +int setup_veth(const char *machine_name, pid_t pid, char iface_name[IFNAMSIZ], bool bridge); + +int setup_bridge(const char *veth_name, 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); + +int move_network_interfaces(pid_t pid, char **ifaces); diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c new file mode 100644 index 000000000..b2776a61c --- /dev/null +++ b/src/nspawn/nspawn-register.c @@ -0,0 +1,244 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "sd-bus.h" + +#include "util.h" +#include "strv.h" +#include "bus-util.h" +#include "bus-error.h" + +#include "nspawn-register.h" + +int register_machine( + const char *machine_name, + pid_t pid, + const char *directory, + sd_id128_t uuid, + int local_ifindex, + const char *slice, + CustomMount *mounts, + unsigned n_mounts, + int kill_signal, + char **properties, + bool keep_unit) { + + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL; + int r; + + r = sd_bus_default_system(&bus); + if (r < 0) + return log_error_errno(r, "Failed to open system bus: %m"); + + if (keep_unit) { + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "RegisterMachineWithNetwork", + &error, + NULL, + "sayssusai", + machine_name, + SD_BUS_MESSAGE_APPEND_ID128(uuid), + "nspawn", + "container", + (uint32_t) pid, + strempty(directory), + local_ifindex > 0 ? 1 : 0, local_ifindex); + } else { + _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + char **i; + unsigned j; + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "CreateMachineWithNetwork"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append( + m, + "sayssusai", + machine_name, + SD_BUS_MESSAGE_APPEND_ID128(uuid), + "nspawn", + "container", + (uint32_t) pid, + strempty(directory), + local_ifindex > 0 ? 1 : 0, local_ifindex); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return bus_log_create_error(r); + + if (!isempty(slice)) { + r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_append(m, "(sv)", "DevicePolicy", "s", "strict"); + if (r < 0) + return bus_log_create_error(r); + + /* If you make changes here, also make sure to update + * systemd-nspawn@.service, to keep the device + * policies in sync regardless if we are run with or + * without the --keep-unit switch. */ + r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 9, + /* Allow the container to + * access and create the API + * device nodes, so that + * PrivateDevices= in the + * container can work + * fine */ + "/dev/null", "rwm", + "/dev/zero", "rwm", + "/dev/full", "rwm", + "/dev/random", "rwm", + "/dev/urandom", "rwm", + "/dev/tty", "rwm", + "/dev/net/tun", "rwm", + /* Allow the container + * access to ptys. However, + * do not permit the + * container to ever create + * these device nodes. */ + "/dev/pts/ptmx", "rw", + "char-pts", "rw"); + if (r < 0) + return bus_log_create_error(r); + + for (j = 0; j < n_mounts; j++) { + CustomMount *cm = mounts + j; + + if (cm->type != CUSTOM_MOUNT_BIND) + continue; + + r = is_device_node(cm->source); + if (r < 0) + return log_error_errno(r, "Failed to stat %s: %m", cm->source); + + if (r) { + r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 1, + cm->source, cm->read_only ? "r" : "rw"); + if (r < 0) + return log_error_errno(r, "Failed to append message arguments: %m"); + } + } + + if (kill_signal != 0) { + r = sd_bus_message_append(m, "(sv)", "KillSignal", "i", kill_signal); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "(sv)", "KillMode", "s", "mixed"); + if (r < 0) + return bus_log_create_error(r); + } + + STRV_FOREACH(i, properties) { + r = sd_bus_message_open_container(m, 'r', "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = bus_append_unit_property_assignment(m, *i); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, NULL); + } + + if (r < 0) { + log_error("Failed to register machine: %s", bus_error_message(&error, r)); + return r; + } + + return 0; +} + +int terminate_machine(pid_t pid) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL; + const char *path; + int r; + + r = sd_bus_default_system(&bus); + if (r < 0) + return log_error_errno(r, "Failed to open system bus: %m"); + + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "GetMachineByPID", + &error, + &reply, + "u", + (uint32_t) pid); + if (r < 0) { + /* Note that the machine might already have been + * cleaned up automatically, hence don't consider it a + * failure if we cannot get the machine object. */ + log_debug("Failed to get machine: %s", bus_error_message(&error, r)); + return 0; + } + + r = sd_bus_message_read(reply, "o", &path); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_call_method( + bus, + "org.freedesktop.machine1", + path, + "org.freedesktop.machine1.Machine", + "Terminate", + &error, + NULL, + NULL); + if (r < 0) { + log_debug("Failed to terminate machine: %s", bus_error_message(&error, r)); + return 0; + } + + return 0; +} diff --git a/src/nspawn/nspawn-register.h b/src/nspawn/nspawn-register.h new file mode 100644 index 000000000..b27841ff5 --- /dev/null +++ b/src/nspawn/nspawn-register.h @@ -0,0 +1,31 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "sd-id128.h" + +#include "nspawn-mount.h" + +int register_machine(const char *machine_name, pid_t pid, const char *directory, sd_id128_t uuid, int local_ifindex, const char *slice, CustomMount *mounts, unsigned n_mounts, int kill_signal, char **properties, bool keep_unit); +int terminate_machine(pid_t pid); diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c new file mode 100644 index 000000000..419f5d1c4 --- /dev/null +++ b/src/nspawn/nspawn-settings.c @@ -0,0 +1,262 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "util.h" +#include "conf-parser.h" +#include "strv.h" +#include "cap-list.h" + +#include "nspawn-settings.h" + +int settings_load(FILE *f, const char *path, Settings **ret) { + _cleanup_(settings_freep) Settings *s = NULL; + int r; + + assert(path); + assert(ret); + + s = new0(Settings, 1); + if (!s) + return -ENOMEM; + + s->boot = -1; + s->personality = PERSONALITY_INVALID; + + s->read_only = -1; + s->volatile_mode = _VOLATILE_MODE_INVALID; + + s->private_network = -1; + s->network_veth = -1; + + r = config_parse(NULL, path, f, + "Exec\0" + "Network\0" + "Files\0", + config_item_perf_lookup, nspawn_gperf_lookup, + false, + false, + true, + s); + if (r < 0) + return r; + + *ret = s; + s = NULL; + + return 0; +} + +Settings* settings_free(Settings *s) { + + if (!s) + return NULL; + + strv_free(s->parameters); + strv_free(s->environment); + free(s->user); + + strv_free(s->network_interfaces); + strv_free(s->network_macvlan); + strv_free(s->network_ipvlan); + free(s->network_bridge); + expose_port_free_all(s->expose_ports); + + custom_mount_free_all(s->custom_mounts, s->n_custom_mounts); + free(s); + + return NULL; +} + +DEFINE_CONFIG_PARSE_ENUM(config_parse_volatile_mode, volatile_mode, VolatileMode, "Failed to parse volatile mode"); + +int config_parse_expose_port( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Settings *s = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = expose_port_parse(&s->expose_ports, rvalue); + if (r == -EEXIST) { + log_syntax(unit, LOG_ERR, filename, line, r, "Duplicate port specification, ignoring: %s", rvalue); + return 0; + } + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse host port %s: %m", rvalue); + return 0; + } + + return 0; +} + +int config_parse_capability( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + uint64_t u = 0, *result = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + for (;;) { + _cleanup_free_ char *word = NULL; + int cap; + + r = extract_first_word(&rvalue, &word, NULL, 0); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract capability string, ignoring: %s", rvalue); + return 0; + } + if (r == 0) + break; + + cap = capability_from_name(word); + if (cap < 0) { + log_syntax(unit, LOG_ERR, filename, line, cap, "Failed to parse capability, ignoring: %s", word); + continue; + } + + u |= 1 << ((uint64_t) cap); + } + + if (u == 0) + return 0; + + *result |= u; + return 0; +} + +int config_parse_id128( + 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) { + + sd_id128_t t, *result = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = sd_id128_from_string(rvalue, &t); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue); + return 0; + } + + *result = t; + return 0; +} + +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) { + + Settings *settings = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = bind_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue, ltype); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid bind mount specification %s: %m", rvalue); + return 0; + } + + return 0; +} + +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) { + + Settings *settings = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = tmpfs_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid temporary file system specification %s: %m", rvalue); + return 0; + } + + if (settings->network_bridge) + settings->network_veth = true; + + if (settings->network_interfaces || + settings->network_macvlan || + settings->network_ipvlan || + settings->network_bridge || + settings->network_veth) + settings->private_network = true; + + return 0; +} diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h new file mode 100644 index 000000000..4cec40c1b --- /dev/null +++ b/src/nspawn/nspawn-settings.h @@ -0,0 +1,87 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "macro.h" + +#include "nspawn-mount.h" +#include "nspawn-expose-ports.h" + +typedef enum SettingsMask { + SETTING_BOOT = 1 << 0, + SETTING_ENVIRONMENT = 1 << 1, + SETTING_USER = 1 << 2, + SETTING_CAPABILITY = 1 << 3, + SETTING_KILL_SIGNAL = 1 << 4, + SETTING_PERSONALITY = 1 << 5, + SETTING_MACHINE_ID = 1 << 6, + SETTING_NETWORK = 1 << 7, + SETTING_EXPOSE_PORTS = 1 << 8, + SETTING_READ_ONLY = 1 << 9, + SETTING_VOLATILE_MODE = 1 << 10, + SETTING_CUSTOM_MOUNTS = 1 << 11, + _SETTINGS_MASK_ALL = (1 << 12) -1 +} SettingsMask; + +typedef struct Settings { + /* [Run] */ + int boot; + char **parameters; + char **environment; + char *user; + uint64_t capability; + uint64_t drop_capability; + int kill_signal; + unsigned long personality; + sd_id128_t machine_id; + + /* [Image] */ + int read_only; + VolatileMode volatile_mode; + CustomMount *custom_mounts; + unsigned n_custom_mounts; + + /* [Network] */ + int private_network; + int network_veth; + char *network_bridge; + char **network_interfaces; + char **network_macvlan; + char **network_ipvlan; + ExposePort *expose_ports; +} Settings; + +int settings_load(FILE *f, const char *path, Settings **ret); +Settings* settings_free(Settings *s); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Settings*, settings_free); + +const struct ConfigPerfItem* nspawn_gperf_lookup(const char *key, unsigned length); + +int config_parse_capability(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_id128(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_expose_port(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_volatile_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_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); diff --git a/src/nspawn/nspawn-setuid.c b/src/nspawn/nspawn-setuid.c new file mode 100644 index 000000000..eda7f6290 --- /dev/null +++ b/src/nspawn/nspawn-setuid.c @@ -0,0 +1,272 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include + +#include "util.h" +#include "signal-util.h" +#include "mkdir.h" +#include "process-util.h" + +#include "nspawn-setuid.h" + +static int spawn_getent(const char *database, const char *key, pid_t *rpid) { + int pipe_fds[2]; + pid_t pid; + + assert(database); + assert(key); + assert(rpid); + + if (pipe2(pipe_fds, O_CLOEXEC) < 0) + return log_error_errno(errno, "Failed to allocate pipe: %m"); + + pid = fork(); + if (pid < 0) + return log_error_errno(errno, "Failed to fork getent child: %m"); + else if (pid == 0) { + int nullfd; + char *empty_env = NULL; + + if (dup3(pipe_fds[1], STDOUT_FILENO, 0) < 0) + _exit(EXIT_FAILURE); + + if (pipe_fds[0] > 2) + safe_close(pipe_fds[0]); + if (pipe_fds[1] > 2) + safe_close(pipe_fds[1]); + + nullfd = open("/dev/null", O_RDWR); + if (nullfd < 0) + _exit(EXIT_FAILURE); + + if (dup3(nullfd, STDIN_FILENO, 0) < 0) + _exit(EXIT_FAILURE); + + if (dup3(nullfd, STDERR_FILENO, 0) < 0) + _exit(EXIT_FAILURE); + + if (nullfd > 2) + safe_close(nullfd); + + (void) reset_all_signal_handlers(); + (void) reset_signal_mask(); + close_all_fds(NULL, 0); + + execle("/usr/bin/getent", "getent", database, key, NULL, &empty_env); + execle("/bin/getent", "getent", database, key, NULL, &empty_env); + _exit(EXIT_FAILURE); + } + + pipe_fds[1] = safe_close(pipe_fds[1]); + + *rpid = pid; + + return pipe_fds[0]; +} + +int change_uid_gid(const char *user, char **_home) { + char line[LINE_MAX], *x, *u, *g, *h; + const char *word, *state; + _cleanup_free_ uid_t *uids = NULL; + _cleanup_free_ char *home = NULL; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_close_ int fd = -1; + unsigned n_uids = 0; + size_t sz = 0, l; + uid_t uid; + gid_t gid; + pid_t pid; + int r; + + assert(_home); + + if (!user || streq(user, "root") || streq(user, "0")) { + /* Reset everything fully to 0, just in case */ + + r = reset_uid_gid(); + if (r < 0) + return log_error_errno(r, "Failed to become root: %m"); + + *_home = NULL; + return 0; + } + + /* First, get user credentials */ + fd = spawn_getent("passwd", user, &pid); + if (fd < 0) + return fd; + + f = fdopen(fd, "r"); + if (!f) + return log_oom(); + fd = -1; + + if (!fgets(line, sizeof(line), f)) { + + if (!ferror(f)) { + log_error("Failed to resolve user %s.", user); + return -ESRCH; + } + + log_error_errno(errno, "Failed to read from getent: %m"); + return -errno; + } + + truncate_nl(line); + + wait_for_terminate_and_warn("getent passwd", pid, true); + + x = strchr(line, ':'); + if (!x) { + log_error("/etc/passwd entry has invalid user field."); + return -EIO; + } + + u = strchr(x+1, ':'); + if (!u) { + log_error("/etc/passwd entry has invalid password field."); + return -EIO; + } + + u++; + g = strchr(u, ':'); + if (!g) { + log_error("/etc/passwd entry has invalid UID field."); + return -EIO; + } + + *g = 0; + g++; + x = strchr(g, ':'); + if (!x) { + log_error("/etc/passwd entry has invalid GID field."); + return -EIO; + } + + *x = 0; + h = strchr(x+1, ':'); + if (!h) { + log_error("/etc/passwd entry has invalid GECOS field."); + return -EIO; + } + + h++; + x = strchr(h, ':'); + if (!x) { + log_error("/etc/passwd entry has invalid home directory field."); + return -EIO; + } + + *x = 0; + + r = parse_uid(u, &uid); + if (r < 0) { + log_error("Failed to parse UID of user."); + return -EIO; + } + + r = parse_gid(g, &gid); + if (r < 0) { + log_error("Failed to parse GID of user."); + return -EIO; + } + + home = strdup(h); + if (!home) + return log_oom(); + + /* Second, get group memberships */ + fd = spawn_getent("initgroups", user, &pid); + if (fd < 0) + return fd; + + fclose(f); + f = fdopen(fd, "r"); + if (!f) + return log_oom(); + fd = -1; + + if (!fgets(line, sizeof(line), f)) { + if (!ferror(f)) { + log_error("Failed to resolve user %s.", user); + return -ESRCH; + } + + log_error_errno(errno, "Failed to read from getent: %m"); + return -errno; + } + + truncate_nl(line); + + wait_for_terminate_and_warn("getent initgroups", pid, true); + + /* Skip over the username and subsequent separator whitespace */ + x = line; + x += strcspn(x, WHITESPACE); + x += strspn(x, WHITESPACE); + + FOREACH_WORD(word, l, x, state) { + char c[l+1]; + + memcpy(c, word, l); + c[l] = 0; + + if (!GREEDY_REALLOC(uids, sz, n_uids+1)) + return log_oom(); + + r = parse_uid(c, &uids[n_uids++]); + if (r < 0) { + log_error("Failed to parse group data from getent."); + return -EIO; + } + } + + r = mkdir_parents(home, 0775); + if (r < 0) + return log_error_errno(r, "Failed to make home root directory: %m"); + + r = mkdir_safe(home, 0755, uid, gid); + if (r < 0 && r != -EEXIST) + return log_error_errno(r, "Failed to make home directory: %m"); + + (void) fchown(STDIN_FILENO, uid, gid); + (void) fchown(STDOUT_FILENO, uid, gid); + (void) fchown(STDERR_FILENO, uid, gid); + + if (setgroups(n_uids, uids) < 0) + return log_error_errno(errno, "Failed to set auxiliary groups: %m"); + + if (setresgid(gid, gid, gid) < 0) + return log_error_errno(errno, "setregid() failed: %m"); + + if (setresuid(uid, uid, uid) < 0) + return log_error_errno(errno, "setreuid() failed: %m"); + + if (_home) { + *_home = home; + home = NULL; + } + + return 0; +} diff --git a/src/nspawn/nspawn-setuid.h b/src/nspawn/nspawn-setuid.h new file mode 100644 index 000000000..33be44a94 --- /dev/null +++ b/src/nspawn/nspawn-setuid.h @@ -0,0 +1,24 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +int change_uid_gid(const char *user, char **ret); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 837947ee2..33943a4b2 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -19,95 +19,78 @@ along with systemd; If not, see . ***/ -#include -#include -#include -#include -#include -#include -#include -#include +#ifdef HAVE_BLKID +#include +#endif #include -#include #include -#include -#include -#include -#include -#include -#include -#include #include -#include - -#ifdef HAVE_SELINUX -#include -#endif - +#include #ifdef HAVE_SECCOMP #include #endif - -#ifdef HAVE_BLKID -#include +#ifdef HAVE_SELINUX +#include #endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "sd-daemon.h" -#include "sd-bus.h" #include "sd-id128.h" -#include "sd-netlink.h" -#include "random-util.h" -#include "log.h" -#include "util.h" -#include "mkdir.h" -#include "rm-rf.h" -#include "macro.h" -#include "missing.h" + +#include "barrier.h" +#include "base-filesystem.h" +#include "blkid-util.h" +#include "btrfs-util.h" +#include "build.h" +#include "cap-list.h" +#include "capability.h" #include "cgroup-util.h" -#include "strv.h" -#include "path-util.h" -#include "loopback-setup.h" +#include "copy.h" #include "dev-setup.h" +#include "env-util.h" +#include "event-util.h" #include "fdset.h" -#include "build.h" #include "fileio.h" -#include "bus-util.h" -#include "bus-error.h" -#include "ptyfwd.h" -#include "env-util.h" -#include "netlink-util.h" -#include "udev-util.h" -#include "blkid-util.h" +#include "formats-util.h" #include "gpt.h" -#include "siphash24.h" -#include "copy.h" -#include "base-filesystem.h" -#include "barrier.h" -#include "event-util.h" -#include "capability.h" -#include "cap-list.h" -#include "btrfs-util.h" +#include "hostname-util.h" +#include "log.h" +#include "loopback-setup.h" #include "machine-image.h" -#include "list.h" -#include "in-addr-util.h" -#include "firewall-util.h" -#include "local-addresses.h" -#include "formats-util.h" +#include "macro.h" +#include "missing.h" +#include "mkdir.h" +#include "netlink-util.h" +#include "path-util.h" #include "process-util.h" -#include "terminal-util.h" -#include "hostname-util.h" -#include "signal-util.h" - +#include "ptyfwd.h" +#include "random-util.h" +#include "rm-rf.h" #ifdef HAVE_SECCOMP #include "seccomp-util.h" #endif +#include "signal-util.h" +#include "strv.h" +#include "terminal-util.h" +#include "udev-util.h" +#include "util.h" -typedef struct ExposePort { - int protocol; - uint16_t host_port; - uint16_t container_port; - LIST_FIELDS(struct ExposePort, ports); -} ExposePort; +#include "nspawn-settings.h" +#include "nspawn-mount.h" +#include "nspawn-network.h" +#include "nspawn-expose-ports.h" +#include "nspawn-cgroup.h" +#include "nspawn-register.h" +#include "nspawn-setuid.h" typedef enum ContainerStatus { CONTAINER_TERMINATED, @@ -121,28 +104,6 @@ typedef enum LinkJournal { LINK_GUEST } LinkJournal; -typedef enum Volatile { - VOLATILE_NO, - VOLATILE_YES, - VOLATILE_STATE, -} Volatile; - -typedef enum CustomMountType { - CUSTOM_MOUNT_BIND, - CUSTOM_MOUNT_TMPFS, - CUSTOM_MOUNT_OVERLAY, -} CustomMountType; - -typedef struct CustomMount { - CustomMountType type; - bool read_only; - char *source; /* for overlayfs this is the upper directory */ - char *destination; - char *options; - char *work_dir; - char **lower; -} CustomMount; - static char *arg_directory = NULL; static char *arg_template = NULL; static char *arg_user = NULL; @@ -195,15 +156,19 @@ static char **arg_network_interfaces = NULL; static char **arg_network_macvlan = NULL; static char **arg_network_ipvlan = NULL; static bool arg_network_veth = false; -static const char *arg_network_bridge = NULL; +static char *arg_network_bridge = NULL; static unsigned long arg_personality = PERSONALITY_INVALID; static char *arg_image = NULL; -static Volatile arg_volatile = VOLATILE_NO; +static VolatileMode arg_volatile_mode = VOLATILE_NO; static ExposePort *arg_expose_ports = NULL; static char **arg_property = NULL; static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U; static bool arg_userns = false; static int arg_kill_signal = 0; +static bool arg_unified_cgroup_hierarchy = false; +static SettingsMask arg_settings_mask = 0; +static int arg_settings_trusted = -1; +static char **arg_parameters = NULL; static void help(void) { printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n" @@ -257,9 +222,11 @@ static void help(void) { " try-guest, try-host\n" " -j Equivalent to --link-journal=try-guest\n" " --read-only Mount the root directory read-only\n" - " --bind=PATH[:PATH] Bind mount a file or directory from the host into\n" + " --bind=PATH[:PATH[:OPTIONS]]\n" + " Bind mount a file or directory from the host into\n" " the container\n" - " --bind-ro=PATH[:PATH] Similar, but creates a read-only bind mount\n" + " --bind-ro=PATH[:PATH[:OPTIONS]\n" + " Similar, but creates a read-only bind mount\n" " --tmpfs=PATH:[OPTIONS] Mount an empty tmpfs to the specified directory\n" " --overlay=PATH[:PATH...]:PATH\n" " Create an overlay mount from the host to \n" @@ -272,62 +239,10 @@ static void help(void) { " --keep-unit Do not register a scope for the machine, reuse\n" " the service unit nspawn is running in\n" " --volatile[=MODE] Run the system in volatile mode\n" + " --settings=BOOLEAN Load additional settings from .nspawn file\n" , program_invocation_short_name); } -static CustomMount* custom_mount_add(CustomMountType t) { - CustomMount *c, *ret; - - c = realloc(arg_custom_mounts, (arg_n_custom_mounts + 1) * sizeof(CustomMount)); - if (!c) - return NULL; - - arg_custom_mounts = c; - ret = arg_custom_mounts + arg_n_custom_mounts; - arg_n_custom_mounts++; - - *ret = (CustomMount) { .type = t }; - - return ret; -} - -static void custom_mount_free_all(void) { - unsigned i; - - for (i = 0; i < arg_n_custom_mounts; i++) { - CustomMount *m = &arg_custom_mounts[i]; - - free(m->source); - free(m->destination); - free(m->options); - - if (m->work_dir) { - (void) rm_rf(m->work_dir, REMOVE_ROOT|REMOVE_PHYSICAL); - free(m->work_dir); - } - - strv_free(m->lower); - } - - arg_custom_mounts = mfree(arg_custom_mounts); - arg_n_custom_mounts = 0; -} - -static int custom_mount_compare(const void *a, const void *b) { - const CustomMount *x = a, *y = b; - int r; - - r = path_compare(x->destination, y->destination); - if (r != 0) - return r; - - if (x->type < y->type) - return -1; - if (x->type > y->type) - return 1; - - return 0; -} static int custom_mounts_prepare(void) { unsigned i; @@ -383,6 +298,30 @@ static int set_sanitized_path(char **b, const char *path) { return 0; } +static int detect_unified_cgroup_hierarchy(void) { + const char *e; + int r; + + /* Allow the user to control whether the unified hierarchy is used */ + e = getenv("UNIFIED_CGROUP_HIERARCHY"); + if (e) { + r = parse_boolean(e); + if (r < 0) + return log_error_errno(r, "Failed to parse $UNIFIED_CGROUP_HIERARCHY."); + + arg_unified_cgroup_hierarchy = r; + return 0; + } + + /* Otherwise inherit the default from the host system */ + r = cg_unified(); + if (r < 0) + return log_error_errno(r, "Failed to determine whether the unified cgroups hierarchy is used: %m"); + + arg_unified_cgroup_hierarchy = r; + return 0; +} + static int parse_argv(int argc, char *argv[]) { enum { @@ -412,6 +351,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_PROPERTY, ARG_PRIVATE_USERS, ARG_KILL_SIGNAL, + ARG_SETTINGS, }; static const struct option options[] = { @@ -454,11 +394,13 @@ static int parse_argv(int argc, char *argv[]) { { "property", required_argument, NULL, ARG_PROPERTY }, { "private-users", optional_argument, NULL, ARG_PRIVATE_USERS }, { "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL }, + { "settings", required_argument, NULL, ARG_SETTINGS }, {} }; int c, r; uint64_t plus = 0, minus = 0; + bool mask_all_settings = false, mask_no_settings = false; assert(argc >= 0); assert(argv); @@ -506,16 +448,20 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return log_oom(); + arg_settings_mask |= SETTING_USER; break; case ARG_NETWORK_BRIDGE: - arg_network_bridge = optarg; + r = free_and_strdup(&arg_network_bridge, optarg); + if (r < 0) + return log_oom(); /* fall through */ case 'n': arg_network_veth = true; arg_private_network = true; + arg_settings_mask |= SETTING_NETWORK; break; case ARG_NETWORK_INTERFACE: @@ -523,6 +469,7 @@ static int parse_argv(int argc, char *argv[]) { return log_oom(); arg_private_network = true; + arg_settings_mask |= SETTING_NETWORK; break; case ARG_NETWORK_MACVLAN: @@ -530,6 +477,7 @@ static int parse_argv(int argc, char *argv[]) { return log_oom(); arg_private_network = true; + arg_settings_mask |= SETTING_NETWORK; break; case ARG_NETWORK_IPVLAN: @@ -540,10 +488,12 @@ static int parse_argv(int argc, char *argv[]) { case ARG_PRIVATE_NETWORK: arg_private_network = true; + arg_settings_mask |= SETTING_NETWORK; break; case 'b': arg_boot = true; + arg_settings_mask |= SETTING_BOOT; break; case ARG_UUID: @@ -552,6 +502,8 @@ static int parse_argv(int argc, char *argv[]) { log_error("Invalid UUID: %s", optarg); return r; } + + arg_settings_mask |= SETTING_MACHINE_ID; break; case 'S': @@ -584,6 +536,7 @@ static int parse_argv(int argc, char *argv[]) { case ARG_READ_ONLY: arg_read_only = true; + arg_settings_mask |= SETTING_READ_ONLY; break; case ARG_CAPABILITY: @@ -619,6 +572,7 @@ static int parse_argv(int argc, char *argv[]) { } } + arg_settings_mask |= SETTING_CAPABILITY; break; } @@ -654,81 +608,21 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_BIND: - case ARG_BIND_RO: { - const char *current = optarg; - _cleanup_free_ char *source = NULL, *destination = NULL; - CustomMount *m; - - r = extract_many_words(¤t, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &source, &destination, NULL); - switch (r) { - case 1: - destination = strdup(source); - case 2: - break; - case -ENOMEM: - return log_oom(); - default: - log_error("Invalid bind mount specification: %s", optarg); - return -EINVAL; - } - - if (!source || !destination) - return log_oom(); - - if (!path_is_absolute(source) || !path_is_absolute(destination)) { - log_error("Invalid bind mount specification: %s", optarg); - return -EINVAL; - } - - m = custom_mount_add(CUSTOM_MOUNT_BIND); - if (!m) - return log_oom(); - - m->source = source; - m->destination = destination; - m->read_only = c == ARG_BIND_RO; - - source = destination = NULL; + case ARG_BIND_RO: + r = bind_mount_parse(&arg_custom_mounts, &arg_n_custom_mounts, optarg, c == ARG_BIND_RO); + if (r < 0) + return log_error_errno(r, "Failed to parse --bind(-ro)= argument %s: %m", optarg); + arg_settings_mask |= SETTING_CUSTOM_MOUNTS; break; - } - - case ARG_TMPFS: { - const char *current = optarg; - _cleanup_free_ char *path = NULL, *opts = NULL; - CustomMount *m; - - r = extract_first_word(¤t, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r == -ENOMEM) - return log_oom(); - else if (r < 0) { - log_error("Invalid tmpfs specification: %s", optarg); - return r; - } - if (r) - opts = strdup(current); - else - opts = strdup("mode=0755"); - if (!path || !opts) - return log_oom(); - - if (!path_is_absolute(path)) { - log_error("Invalid tmpfs specification: %s", optarg); - return -EINVAL; - } - - m = custom_mount_add(CUSTOM_MOUNT_TMPFS); - if (!m) - return log_oom(); - - m->destination = path; - m->options = opts; - - path = opts = NULL; + case ARG_TMPFS: + r = tmpfs_mount_parse(&arg_custom_mounts, &arg_n_custom_mounts, optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --tmpfs= argument %s: %m", optarg); + arg_settings_mask |= SETTING_CUSTOM_MOUNTS; break; - } case ARG_OVERLAY: case ARG_OVERLAY_RO: { @@ -779,7 +673,7 @@ static int parse_argv(int argc, char *argv[]) { lower[n - 2] = NULL; } - m = custom_mount_add(CUSTOM_MOUNT_OVERLAY); + m = custom_mount_add(&arg_custom_mounts, &arg_n_custom_mounts, CUSTOM_MOUNT_OVERLAY); if (!m) return log_oom(); @@ -791,6 +685,7 @@ static int parse_argv(int argc, char *argv[]) { upper = destination = NULL; lower = NULL; + arg_settings_mask |= SETTING_CUSTOM_MOUNTS; break; } @@ -808,6 +703,8 @@ static int parse_argv(int argc, char *argv[]) { strv_free(arg_setenv); arg_setenv = n; + + arg_settings_mask |= SETTING_ENVIRONMENT; break; } @@ -841,85 +738,36 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } + arg_settings_mask |= SETTING_PERSONALITY; break; case ARG_VOLATILE: if (!optarg) - arg_volatile = VOLATILE_YES; - else { - r = parse_boolean(optarg); - if (r < 0) { - if (streq(optarg, "state")) - arg_volatile = VOLATILE_STATE; - else { - log_error("Failed to parse --volatile= argument: %s", optarg); - return r; - } - } else - arg_volatile = r ? VOLATILE_YES : VOLATILE_NO; - } - - break; - - case 'p': { - const char *split, *e; - uint16_t container_port, host_port; - int protocol; - ExposePort *p; - - if ((e = startswith(optarg, "tcp:"))) - protocol = IPPROTO_TCP; - else if ((e = startswith(optarg, "udp:"))) - protocol = IPPROTO_UDP; + arg_volatile_mode = VOLATILE_YES; else { - e = optarg; - protocol = IPPROTO_TCP; - } - - split = strchr(e, ':'); - if (split) { - char v[split - e + 1]; - - memcpy(v, e, split - e); - v[split - e] = 0; - - r = safe_atou16(v, &host_port); - if (r < 0 || host_port <= 0) { - log_error("Failed to parse host port: %s", optarg); - return -EINVAL; - } - - r = safe_atou16(split + 1, &container_port); - } else { - r = safe_atou16(e, &container_port); - host_port = container_port; - } - - if (r < 0 || container_port <= 0) { - log_error("Failed to parse host port: %s", optarg); - return -EINVAL; - } + VolatileMode m; - LIST_FOREACH(ports, p, arg_expose_ports) { - if (p->protocol == protocol && p->host_port == host_port) { - log_error("Duplicate port specification: %s", optarg); + m = volatile_mode_from_string(optarg); + if (m < 0) { + log_error("Failed to parse --volatile= argument: %s", optarg); return -EINVAL; - } + } else + arg_volatile_mode = m; } - p = new(ExposePort, 1); - if (!p) - return log_oom(); - - p->protocol = protocol; - p->host_port = host_port; - p->container_port = container_port; + arg_settings_mask |= SETTING_VOLATILE_MODE; + break; - LIST_PREPEND(ports, arg_expose_ports, p); + case 'p': + r = expose_port_parse(&arg_expose_ports, optarg); + if (r == -EEXIST) + return log_error_errno(r, "Duplicate port specification: %s", optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse host port %s: %m", optarg); + arg_settings_mask |= SETTING_EXPOSE_PORTS; break; - } case ARG_PROPERTY: if (strv_extend(&arg_property, optarg) < 0) @@ -963,6 +811,42 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } + arg_settings_mask |= SETTING_KILL_SIGNAL; + break; + + case ARG_SETTINGS: + + /* no → do not read files + * yes → read files, do not override cmdline, trust only subset + * override → read files, override cmdline, trust only subset + * trusted → read files, do not override cmdline, trust all + */ + + r = parse_boolean(optarg); + if (r < 0) { + if (streq(optarg, "trusted")) { + mask_all_settings = false; + mask_no_settings = false; + arg_settings_trusted = true; + + } else if (streq(optarg, "override")) { + mask_all_settings = false; + mask_no_settings = true; + arg_settings_trusted = -1; + } else + return log_error_errno(r, "Failed to parse --settings= argument: %s", optarg); + } else if (r > 0) { + /* yes */ + mask_all_settings = false; + mask_no_settings = false; + arg_settings_trusted = -1; + } else { + /* no */ + mask_all_settings = true; + mask_no_settings = false; + arg_settings_trusted = false; + } + break; case '?': @@ -1015,7 +899,37 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } - if (arg_volatile != VOLATILE_NO && arg_read_only) { + if (arg_userns && access("/proc/self/uid_map", F_OK) < 0) + return log_error_errno(EOPNOTSUPP, "--private-users= is not supported, kernel compiled without user namespace support."); + + if (argc > optind) { + arg_parameters = strv_copy(argv + optind); + if (!arg_parameters) + return log_oom(); + + arg_settings_mask |= SETTING_BOOT; + } + + /* Load all settings from .nspawn files */ + if (mask_no_settings) + arg_settings_mask = 0; + + /* Don't load any settings from .nspawn files */ + if (mask_all_settings) + arg_settings_mask = _SETTINGS_MASK_ALL; + + arg_retain = (arg_retain | plus | (arg_private_network ? 1ULL << CAP_NET_ADMIN : 0)) & ~minus; + + r = detect_unified_cgroup_hierarchy(); + if (r < 0) + return r; + + return 1; +} + +static int verify_arguments(void) { + + if (arg_volatile_mode != VOLATILE_NO && arg_read_only) { log_error("Cannot combine --read-only with --volatile. Note that --volatile already implies a read-only base hierarchy."); return -EINVAL; } @@ -1025,457 +939,26 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } - if (arg_userns && access("/proc/self/uid_map", F_OK) < 0) - return log_error_errno(EOPNOTSUPP, "--private-users= is not supported, kernel compiled without user namespace support."); - - arg_retain = (arg_retain | plus | (arg_private_network ? 1ULL << CAP_NET_ADMIN : 0)) & ~minus; - if (arg_boot && arg_kill_signal <= 0) arg_kill_signal = SIGRTMIN+3; - return 1; + return 0; } -static int tmpfs_patch_options(const char *options, char **ret) { - char *buf = NULL; - - if (arg_userns && arg_uid_shift != 0) { - assert(arg_uid_shift != UID_INVALID); - - if (options) - (void) asprintf(&buf, "%s,uid=" UID_FMT ",gid=" UID_FMT, options, arg_uid_shift, arg_uid_shift); - else - (void) asprintf(&buf, "uid=" UID_FMT ",gid=" UID_FMT, arg_uid_shift, arg_uid_shift); - if (!buf) - return -ENOMEM; +static int userns_lchown(const char *p, uid_t uid, gid_t gid) { + assert(p); - options = buf; - } + if (!arg_userns) + return 0; -#ifdef HAVE_SELINUX - if (arg_selinux_apifs_context) { - char *t; + if (uid == UID_INVALID && gid == GID_INVALID) + return 0; - if (options) - t = strjoin(options, ",context=\"", arg_selinux_apifs_context, "\"", NULL); - else - t = strjoin("context=\"", arg_selinux_apifs_context, "\"", NULL); - if (!t) { - free(buf); - return -ENOMEM; - } + if (uid != UID_INVALID) { + uid += arg_uid_shift; - free(buf); - buf = t; - } -#endif - - *ret = buf; - return !!buf; -} - -static int mount_all(const char *dest, bool userns) { - - typedef struct MountPoint { - const char *what; - const char *where; - const char *type; - const char *options; - unsigned long flags; - bool fatal; - bool userns; - } MountPoint; - - static const MountPoint mount_table[] = { - { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true, true }, - { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, true, true }, /* Bind mount first */ - { NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, true, true }, /* Then, make it r/o */ - { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, true, false }, - { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, true, false }, - { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true, false }, - { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false }, - { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false }, - { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME, true, false }, -#ifdef HAVE_SELINUX - { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, false, false }, /* Bind mount first */ - { NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, false, false }, /* Then, make it r/o */ -#endif - }; - - unsigned k; - int r; - - for (k = 0; k < ELEMENTSOF(mount_table); k++) { - _cleanup_free_ char *where = NULL, *options = NULL; - const char *o; - - if (userns != mount_table[k].userns) - continue; - - where = prefix_root(dest, mount_table[k].where); - if (!where) - return log_oom(); - - r = path_is_mount_point(where, AT_SYMLINK_FOLLOW); - if (r < 0 && r != -ENOENT) - return log_error_errno(r, "Failed to detect whether %s is a mount point: %m", where); - - /* Skip this entry if it is not a remount. */ - if (mount_table[k].what && r > 0) - continue; - - r = mkdir_p(where, 0755); - if (r < 0) { - if (mount_table[k].fatal) - return log_error_errno(r, "Failed to create directory %s: %m", where); - - log_warning_errno(r, "Failed to create directory %s: %m", where); - continue; - } - - o = mount_table[k].options; - if (streq_ptr(mount_table[k].type, "tmpfs")) { - r = tmpfs_patch_options(o, &options); - if (r < 0) - return log_oom(); - if (r > 0) - o = options; - } - - if (mount(mount_table[k].what, - where, - mount_table[k].type, - mount_table[k].flags, - o) < 0) { - - if (mount_table[k].fatal) - return log_error_errno(errno, "mount(%s) failed: %m", where); - - log_warning_errno(errno, "mount(%s) failed, ignoring: %m", where); - } - } - - return 0; -} - -static int mount_bind(const char *dest, CustomMount *m) { - struct stat source_st, dest_st; - const char *where; - int r; - - assert(m); - - if (stat(m->source, &source_st) < 0) - return log_error_errno(errno, "Failed to stat %s: %m", m->source); - - where = prefix_roota(dest, m->destination); - - if (stat(where, &dest_st) >= 0) { - if (S_ISDIR(source_st.st_mode) && !S_ISDIR(dest_st.st_mode)) { - log_error("Cannot bind mount directory %s on file %s.", m->source, where); - return -EINVAL; - } - - if (!S_ISDIR(source_st.st_mode) && S_ISDIR(dest_st.st_mode)) { - log_error("Cannot bind mount file %s on directory %s.", m->source, where); - return -EINVAL; - } - - } else if (errno == ENOENT) { - r = mkdir_parents_label(where, 0755); - if (r < 0) - return log_error_errno(r, "Failed to make parents of %s: %m", where); - } else { - log_error_errno(errno, "Failed to stat %s: %m", where); - return -errno; - } - - /* Create the mount point. Any non-directory file can be - * mounted on any non-directory file (regular, fifo, socket, - * char, block). - */ - if (S_ISDIR(source_st.st_mode)) - r = mkdir_label(where, 0755); - else - r = touch(where); - if (r < 0 && r != -EEXIST) - return log_error_errno(r, "Failed to create mount point %s: %m", where); - - if (mount(m->source, where, NULL, MS_BIND, NULL) < 0) - return log_error_errno(errno, "mount(%s) failed: %m", where); - - if (m->read_only) { - r = bind_remount_recursive(where, true); - if (r < 0) - return log_error_errno(r, "Read-only bind mount failed: %m"); - } - - return 0; -} - -static int mount_tmpfs(const char *dest, CustomMount *m) { - const char *where, *options; - _cleanup_free_ char *buf = NULL; - int r; - - assert(dest); - assert(m); - - where = prefix_roota(dest, m->destination); - - r = mkdir_p_label(where, 0755); - if (r < 0 && r != -EEXIST) - return log_error_errno(r, "Creating mount point for tmpfs %s failed: %m", where); - - r = tmpfs_patch_options(m->options, &buf); - if (r < 0) - return log_oom(); - options = r > 0 ? buf : m->options; - - if (mount("tmpfs", where, "tmpfs", MS_NODEV|MS_STRICTATIME, options) < 0) - return log_error_errno(errno, "tmpfs mount to %s failed: %m", where); - - return 0; -} - -static char *joined_and_escaped_lower_dirs(char * const *lower) { - _cleanup_strv_free_ char **sv = NULL; - - sv = strv_copy(lower); - if (!sv) - return NULL; - - strv_reverse(sv); - - if (!strv_shell_escape(sv, ",:")) - return NULL; - - return strv_join(sv, ":"); -} - -static int mount_overlay(const char *dest, CustomMount *m) { - _cleanup_free_ char *lower = NULL; - const char *where, *options; - int r; - - assert(dest); - assert(m); - - where = prefix_roota(dest, m->destination); - - r = mkdir_label(where, 0755); - if (r < 0 && r != -EEXIST) - return log_error_errno(r, "Creating mount point for overlay %s failed: %m", where); - - (void) mkdir_p_label(m->source, 0755); - - lower = joined_and_escaped_lower_dirs(m->lower); - if (!lower) - return log_oom(); - - if (m->read_only) { - _cleanup_free_ char *escaped_source = NULL; - - escaped_source = shell_escape(m->source, ",:"); - if (!escaped_source) - return log_oom(); - - options = strjoina("lowerdir=", escaped_source, ":", lower); - } else { - _cleanup_free_ char *escaped_source = NULL, *escaped_work_dir = NULL; - - assert(m->work_dir); - (void) mkdir_label(m->work_dir, 0700); - - escaped_source = shell_escape(m->source, ",:"); - if (!escaped_source) - return log_oom(); - escaped_work_dir = shell_escape(m->work_dir, ",:"); - if (!escaped_work_dir) - return log_oom(); - - options = strjoina("lowerdir=", lower, ",upperdir=", escaped_source, ",workdir=", escaped_work_dir); - } - - if (mount("overlay", where, "overlay", m->read_only ? MS_RDONLY : 0, options) < 0) - return log_error_errno(errno, "overlay mount to %s failed: %m", where); - - return 0; -} - -static int mount_custom(const char *dest) { - unsigned i; - int r; - - assert(dest); - - for (i = 0; i < arg_n_custom_mounts; i++) { - CustomMount *m = &arg_custom_mounts[i]; - - switch (m->type) { - - case CUSTOM_MOUNT_BIND: - r = mount_bind(dest, m); - break; - - case CUSTOM_MOUNT_TMPFS: - r = mount_tmpfs(dest, m); - break; - - case CUSTOM_MOUNT_OVERLAY: - r = mount_overlay(dest, m); - break; - - default: - assert_not_reached("Unknown custom mount type"); - } - - if (r < 0) - return r; - } - - return 0; -} - -static int mount_cgroup_hierarchy(const char *dest, const char *controller, const char *hierarchy, bool read_only) { - char *to; - int r; - - to = strjoina(dest, "/sys/fs/cgroup/", hierarchy); - - r = path_is_mount_point(to, 0); - if (r < 0 && r != -ENOENT) - return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to); - if (r > 0) - return 0; - - mkdir_p(to, 0755); - - /* The superblock mount options of the mount point need to be - * identical to the hosts', and hence writable... */ - if (mount("cgroup", to, "cgroup", MS_NOSUID|MS_NOEXEC|MS_NODEV, controller) < 0) - return log_error_errno(errno, "Failed to mount to %s: %m", to); - - /* ... hence let's only make the bind mount read-only, not the - * superblock. */ - if (read_only) { - if (mount(NULL, to, NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL) < 0) - return log_error_errno(errno, "Failed to remount %s read-only: %m", to); - } - return 1; -} - -static int mount_cgroup(const char *dest) { - _cleanup_set_free_free_ Set *controllers = NULL; - const char *cgroup_root; - int r; - - controllers = set_new(&string_hash_ops); - if (!controllers) - return log_oom(); - - r = cg_kernel_controllers(controllers); - if (r < 0) - return log_error_errno(r, "Failed to determine cgroup controllers: %m"); - - for (;;) { - _cleanup_free_ char *controller = NULL, *origin = NULL, *combined = NULL; - - controller = set_steal_first(controllers); - if (!controller) - break; - - origin = prefix_root("/sys/fs/cgroup/", controller); - if (!origin) - return log_oom(); - - r = readlink_malloc(origin, &combined); - if (r == -EINVAL) { - /* Not a symbolic link, but directly a single cgroup hierarchy */ - - r = mount_cgroup_hierarchy(dest, controller, controller, true); - if (r < 0) - return r; - - } else if (r < 0) - return log_error_errno(r, "Failed to read link %s: %m", origin); - else { - _cleanup_free_ char *target = NULL; - - target = prefix_root(dest, origin); - if (!target) - return log_oom(); - - /* A symbolic link, a combination of controllers in one hierarchy */ - - if (!filename_is_valid(combined)) { - log_warning("Ignoring invalid combined hierarchy %s.", combined); - continue; - } - - r = mount_cgroup_hierarchy(dest, combined, combined, true); - if (r < 0) - return r; - - r = symlink_idempotent(combined, target); - if (r == -EINVAL) { - log_error("Invalid existing symlink for combined hierarchy"); - return r; - } - if (r < 0) - return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m"); - } - } - - r = mount_cgroup_hierarchy(dest, "name=systemd,xattr", "systemd", false); - if (r < 0) - return r; - - cgroup_root = prefix_roota(dest, "/sys/fs/cgroup"); - if (mount(NULL, cgroup_root, NULL, MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755") < 0) - return log_error_errno(errno, "Failed to remount %s read-only: %m", cgroup_root); - - return 0; -} - -static int mount_systemd_cgroup_writable(const char *dest) { - _cleanup_free_ char *own_cgroup_path = NULL; - const char *systemd_root, *systemd_own; - int r; - - assert(dest); - - r = cg_pid_get_path(NULL, 0, &own_cgroup_path); - if (r < 0) - return log_error_errno(r, "Failed to determine our own cgroup path: %m"); - - /* Make our own cgroup a (writable) bind mount */ - systemd_own = strjoina(dest, "/sys/fs/cgroup/systemd", own_cgroup_path); - if (mount(systemd_own, systemd_own, NULL, MS_BIND, NULL) < 0) - return log_error_errno(errno, "Failed to turn %s into a bind mount: %m", own_cgroup_path); - - /* And then remount the systemd cgroup root read-only */ - systemd_root = prefix_roota(dest, "/sys/fs/cgroup/systemd"); - if (mount(NULL, systemd_root, NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL) < 0) - return log_error_errno(errno, "Failed to mount cgroup root read-only: %m"); - - return 0; -} - -static int userns_lchown(const char *p, uid_t uid, gid_t gid) { - assert(p); - - if (!arg_userns) - return 0; - - if (uid == UID_INVALID && gid == GID_INVALID) - return 0; - - if (uid != UID_INVALID) { - uid += arg_uid_shift; - - if (uid < arg_uid_shift || uid >= arg_uid_shift + arg_uid_range) - return -EOVERFLOW; + if (uid < arg_uid_shift || uid >= arg_uid_shift + arg_uid_range) + return -EOVERFLOW; } if (gid != GID_INVALID) { @@ -1599,159 +1082,51 @@ static int setup_resolv_conf(const char *dest) { return 0; } -static int setup_volatile_state(const char *directory) { - _cleanup_free_ char *buf = NULL; - const char *p, *options; - int r; - - assert(directory); - - if (arg_volatile != VOLATILE_STATE) - return 0; - - /* --volatile=state means we simply overmount /var - with a tmpfs, and the rest read-only. */ - - r = bind_remount_recursive(directory, true); - if (r < 0) - return log_error_errno(r, "Failed to remount %s read-only: %m", directory); - - p = prefix_roota(directory, "/var"); - r = mkdir(p, 0755); - if (r < 0 && errno != EEXIST) - return log_error_errno(errno, "Failed to create %s: %m", directory); - - options = "mode=755"; - r = tmpfs_patch_options(options, &buf); - if (r < 0) - return log_oom(); - if (r > 0) - options = buf; +static char* id128_format_as_uuid(sd_id128_t id, char s[37]) { + assert(s); - if (mount("tmpfs", p, "tmpfs", MS_STRICTATIME, options) < 0) - return log_error_errno(errno, "Failed to mount tmpfs to /var: %m"); + snprintf(s, 37, + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + SD_ID128_FORMAT_VAL(id)); - return 0; + return s; } -static int setup_volatile(const char *directory) { - bool tmpfs_mounted = false, bind_mounted = false; - char template[] = "/tmp/nspawn-volatile-XXXXXX"; - _cleanup_free_ char *buf = NULL; - const char *f, *t, *options; +static int setup_boot_id(const char *dest) { + const char *from, *to; + sd_id128_t rnd = {}; + char as_uuid[37]; int r; - assert(directory); - - if (arg_volatile != VOLATILE_YES) + if (arg_share_system) return 0; - /* --volatile=yes means we mount a tmpfs to the root dir, and - the original /usr to use inside it, and that read-only. */ + /* Generate a new randomized boot ID, so that each boot-up of + * the container gets a new one */ - if (!mkdtemp(template)) - return log_error_errno(errno, "Failed to create temporary directory: %m"); + from = prefix_roota(dest, "/run/proc-sys-kernel-random-boot-id"); + to = prefix_roota(dest, "/proc/sys/kernel/random/boot_id"); - options = "mode=755"; - r = tmpfs_patch_options(options, &buf); + r = sd_id128_randomize(&rnd); if (r < 0) - return log_oom(); - if (r > 0) - options = buf; + return log_error_errno(r, "Failed to generate random boot id: %m"); - if (mount("tmpfs", template, "tmpfs", MS_STRICTATIME, options) < 0) { - r = log_error_errno(errno, "Failed to mount tmpfs for root directory: %m"); - goto fail; - } + id128_format_as_uuid(rnd, as_uuid); - tmpfs_mounted = true; + r = write_string_file(from, as_uuid, WRITE_STRING_FILE_CREATE); + if (r < 0) + return log_error_errno(r, "Failed to write boot id: %m"); - f = prefix_roota(directory, "/usr"); - t = prefix_roota(template, "/usr"); + if (mount(from, to, NULL, MS_BIND, NULL) < 0) + r = log_error_errno(errno, "Failed to bind mount boot id: %m"); + else if (mount(NULL, to, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL) < 0) + log_warning_errno(errno, "Failed to make boot id read-only: %m"); - r = mkdir(t, 0755); - if (r < 0 && errno != EEXIST) { - r = log_error_errno(errno, "Failed to create %s: %m", t); - goto fail; - } + unlink(from); + return r; +} - if (mount(f, t, NULL, MS_BIND|MS_REC, NULL) < 0) { - r = log_error_errno(errno, "Failed to create /usr bind mount: %m"); - goto fail; - } - - bind_mounted = true; - - r = bind_remount_recursive(t, true); - if (r < 0) { - log_error_errno(r, "Failed to remount %s read-only: %m", t); - goto fail; - } - - if (mount(template, directory, NULL, MS_MOVE, NULL) < 0) { - r = log_error_errno(errno, "Failed to move root mount: %m"); - goto fail; - } - - (void) rmdir(template); - - return 0; - -fail: - if (bind_mounted) - (void) umount(t); - - if (tmpfs_mounted) - (void) umount(template); - (void) rmdir(template); - return r; -} - -static char* id128_format_as_uuid(sd_id128_t id, char s[37]) { - assert(s); - - snprintf(s, 37, - "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", - SD_ID128_FORMAT_VAL(id)); - - return s; -} - -static int setup_boot_id(const char *dest) { - const char *from, *to; - sd_id128_t rnd = {}; - char as_uuid[37]; - int r; - - if (arg_share_system) - return 0; - - /* Generate a new randomized boot ID, so that each boot-up of - * the container gets a new one */ - - from = prefix_roota(dest, "/run/proc-sys-kernel-random-boot-id"); - to = prefix_roota(dest, "/proc/sys/kernel/random/boot_id"); - - r = sd_id128_randomize(&rnd); - if (r < 0) - return log_error_errno(r, "Failed to generate random boot id: %m"); - - id128_format_as_uuid(rnd, as_uuid); - - r = write_string_file(from, as_uuid, WRITE_STRING_FILE_CREATE); - if (r < 0) - return log_error_errno(r, "Failed to write boot id: %m"); - - if (mount(from, to, NULL, MS_BIND, NULL) < 0) - r = log_error_errno(errno, "Failed to bind mount boot id: %m"); - else if (mount(NULL, to, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL) < 0) - log_warning_errno(errno, "Failed to make boot id read-only: %m"); - - unlink(from); - return r; -} - -static int copy_devnodes(const char *dest) { +static int copy_devnodes(const char *dest) { static const char devnodes[] = "null\0" @@ -1945,132 +1320,6 @@ static int setup_kmsg(const char *dest, int kmsg_socket) { return 0; } -static int send_rtnl(int send_fd) { - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; - struct msghdr mh = { - .msg_control = &control, - .msg_controllen = sizeof(control), - }; - struct cmsghdr *cmsg; - _cleanup_close_ int fd = -1; - ssize_t k; - - assert(send_fd >= 0); - - if (!arg_expose_ports) - return 0; - - fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE); - if (fd < 0) - return log_error_errno(errno, "Failed to allocate container netlink: %m"); - - cmsg = CMSG_FIRSTHDR(&mh); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(int)); - memcpy(CMSG_DATA(cmsg), &fd, sizeof(int)); - - mh.msg_controllen = cmsg->cmsg_len; - - /* Store away the fd in the socket, so that it stays open as - * long as we run the child */ - k = sendmsg(send_fd, &mh, MSG_NOSIGNAL); - if (k < 0) - return log_error_errno(errno, "Failed to send netlink fd: %m"); - - return 0; -} - -static int flush_ports(union in_addr_union *exposed) { - ExposePort *p; - int r, af = AF_INET; - - assert(exposed); - - if (!arg_expose_ports) - return 0; - - if (in_addr_is_null(af, exposed)) - return 0; - - log_debug("Lost IP address."); - - LIST_FOREACH(ports, p, arg_expose_ports) { - r = fw_add_local_dnat(false, - af, - p->protocol, - NULL, - NULL, 0, - NULL, 0, - p->host_port, - exposed, - p->container_port, - NULL); - if (r < 0) - log_warning_errno(r, "Failed to modify firewall: %m"); - } - - *exposed = IN_ADDR_NULL; - return 0; -} - -static int expose_ports(sd_netlink *rtnl, union in_addr_union *exposed) { - _cleanup_free_ struct local_address *addresses = NULL; - _cleanup_free_ char *pretty = NULL; - union in_addr_union new_exposed; - ExposePort *p; - bool add; - int af = AF_INET, r; - - assert(exposed); - - /* Invoked each time an address is added or removed inside the - * container */ - - if (!arg_expose_ports) - return 0; - - r = local_addresses(rtnl, 0, af, &addresses); - if (r < 0) - return log_error_errno(r, "Failed to enumerate local addresses: %m"); - - add = r > 0 && - addresses[0].family == af && - addresses[0].scope < RT_SCOPE_LINK; - - if (!add) - return flush_ports(exposed); - - new_exposed = addresses[0].address; - if (in_addr_equal(af, exposed, &new_exposed)) - return 0; - - in_addr_to_string(af, &new_exposed, &pretty); - log_debug("New container IP is %s.", strna(pretty)); - - LIST_FOREACH(ports, p, arg_expose_ports) { - - r = fw_add_local_dnat(true, - af, - p->protocol, - NULL, - NULL, 0, - NULL, 0, - p->host_port, - &new_exposed, - p->container_port, - in_addr_is_null(af, exposed) ? NULL : exposed); - if (r < 0) - log_warning_errno(r, "Failed to modify firewall: %m"); - } - - *exposed = new_exposed; - return 0; -} - static int on_address_change(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { union in_addr_union *exposed = userdata; @@ -2078,62 +1327,7 @@ static int on_address_change(sd_netlink *rtnl, sd_netlink_message *m, void *user assert(m); assert(exposed); - expose_ports(rtnl, exposed); - return 0; -} - -static int watch_rtnl(sd_event *event, int recv_fd, union in_addr_union *exposed, sd_netlink **ret) { - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(int))]; - } control = {}; - struct msghdr mh = { - .msg_control = &control, - .msg_controllen = sizeof(control), - }; - struct cmsghdr *cmsg; - _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; - int fd, r; - ssize_t k; - - assert(event); - assert(recv_fd >= 0); - assert(ret); - - if (!arg_expose_ports) - return 0; - - k = recvmsg(recv_fd, &mh, MSG_NOSIGNAL); - if (k < 0) - return log_error_errno(errno, "Failed to recv netlink fd: %m"); - - cmsg = CMSG_FIRSTHDR(&mh); - assert(cmsg->cmsg_level == SOL_SOCKET); - assert(cmsg->cmsg_type == SCM_RIGHTS); - assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int))); - memcpy(&fd, CMSG_DATA(cmsg), sizeof(int)); - - r = sd_netlink_open_fd(&rtnl, fd); - if (r < 0) { - safe_close(fd); - return log_error_errno(r, "Failed to create rtnl object: %m"); - } - - r = sd_netlink_add_match(rtnl, RTM_NEWADDR, on_address_change, exposed); - if (r < 0) - return log_error_errno(r, "Failed to subscribe to RTM_NEWADDR messages: %m"); - - r = sd_netlink_add_match(rtnl, RTM_DELADDR, on_address_change, exposed); - if (r < 0) - return log_error_errno(r, "Failed to subscribe to RTM_DELADDR messages: %m"); - - r = sd_netlink_attach_event(rtnl, event, 0); - if (r < 0) - return log_error_errno(r, "Failed to add to even loop: %m"); - - *ret = rtnl; - rtnl = NULL; - + expose_port_execute(rtnl, arg_expose_ports, exposed); return 0; } @@ -2310,220 +1504,6 @@ static int drop_capabilities(void) { return capability_bounding_set_drop(~arg_retain, false); } -static int register_machine(pid_t pid, int local_ifindex) { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL; - int r; - - if (!arg_register) - return 0; - - r = sd_bus_default_system(&bus); - if (r < 0) - return log_error_errno(r, "Failed to open system bus: %m"); - - if (arg_keep_unit) { - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "RegisterMachineWithNetwork", - &error, - NULL, - "sayssusai", - arg_machine, - SD_BUS_MESSAGE_APPEND_ID128(arg_uuid), - "nspawn", - "container", - (uint32_t) pid, - strempty(arg_directory), - local_ifindex > 0 ? 1 : 0, local_ifindex); - } else { - _cleanup_bus_message_unref_ sd_bus_message *m = NULL; - char **i; - unsigned j; - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "CreateMachineWithNetwork"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append( - m, - "sayssusai", - arg_machine, - SD_BUS_MESSAGE_APPEND_ID128(arg_uuid), - "nspawn", - "container", - (uint32_t) pid, - strempty(arg_directory), - local_ifindex > 0 ? 1 : 0, local_ifindex); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "(sv)"); - if (r < 0) - return bus_log_create_error(r); - - if (!isempty(arg_slice)) { - r = sd_bus_message_append(m, "(sv)", "Slice", "s", arg_slice); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_append(m, "(sv)", "DevicePolicy", "s", "strict"); - if (r < 0) - return bus_log_create_error(r); - - /* If you make changes here, also make sure to update - * systemd-nspawn@.service, to keep the device - * policies in sync regardless if we are run with or - * without the --keep-unit switch. */ - r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 9, - /* Allow the container to - * access and create the API - * device nodes, so that - * PrivateDevices= in the - * container can work - * fine */ - "/dev/null", "rwm", - "/dev/zero", "rwm", - "/dev/full", "rwm", - "/dev/random", "rwm", - "/dev/urandom", "rwm", - "/dev/tty", "rwm", - "/dev/net/tun", "rwm", - /* Allow the container - * access to ptys. However, - * do not permit the - * container to ever create - * these device nodes. */ - "/dev/pts/ptmx", "rw", - "char-pts", "rw"); - if (r < 0) - return bus_log_create_error(r); - - for (j = 0; j < arg_n_custom_mounts; j++) { - CustomMount *cm = &arg_custom_mounts[j]; - - if (cm->type != CUSTOM_MOUNT_BIND) - continue; - - r = is_device_node(cm->source); - if (r < 0) - return log_error_errno(r, "Failed to stat %s: %m", cm->source); - - if (r) { - r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 1, - cm->source, cm->read_only ? "r" : "rw"); - if (r < 0) - return log_error_errno(r, "Failed to append message arguments: %m"); - } - } - - if (arg_kill_signal != 0) { - r = sd_bus_message_append(m, "(sv)", "KillSignal", "i", arg_kill_signal); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(m, "(sv)", "KillMode", "s", "mixed"); - if (r < 0) - return bus_log_create_error(r); - } - - STRV_FOREACH(i, arg_property) { - r = sd_bus_message_open_container(m, 'r', "sv"); - if (r < 0) - return bus_log_create_error(r); - - r = bus_append_unit_property_assignment(m, *i); - if (r < 0) - return r; - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_call(bus, m, 0, &error, NULL); - } - - if (r < 0) { - log_error("Failed to register machine: %s", bus_error_message(&error, r)); - return r; - } - - return 0; -} - -static int terminate_machine(pid_t pid) { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_bus_flush_close_unref_ sd_bus *bus = NULL; - const char *path; - int r; - - if (!arg_register) - return 0; - - /* If we are reusing the unit, then just exit, systemd will do - * the right thing when we exit. */ - if (arg_keep_unit) - return 0; - - r = sd_bus_default_system(&bus); - if (r < 0) - return log_error_errno(r, "Failed to open system bus: %m"); - - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - "/org/freedesktop/machine1", - "org.freedesktop.machine1.Manager", - "GetMachineByPID", - &error, - &reply, - "u", - (uint32_t) pid); - if (r < 0) { - /* Note that the machine might already have been - * cleaned up automatically, hence don't consider it a - * failure if we cannot get the machine object. */ - log_debug("Failed to get machine: %s", bus_error_message(&error, r)); - return 0; - } - - r = sd_bus_message_read(reply, "o", &path); - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_call_method( - bus, - "org.freedesktop.machine1", - path, - "org.freedesktop.machine1.Machine", - "Terminate", - &error, - NULL, - NULL); - if (r < 0) { - log_debug("Failed to terminate machine: %s", bus_error_message(&error, r)); - return 0; - } - - return 0; -} - static int reset_audit_loginuid(void) { _cleanup_free_ char *p = NULL; int r; @@ -2543,435 +1523,14 @@ static int reset_audit_loginuid(void) { r = write_string_file("/proc/self/loginuid", "4294967295", 0); if (r < 0) { - log_error_errno(r, - "Failed to reset audit login UID. This probably means that your kernel is too\n" - "old and you have audit enabled. Note that the auditing subsystem is known to\n" - "be incompatible with containers on old kernels. Please make sure to upgrade\n" - "your kernel or to off auditing with 'audit=0' on the kernel command line before\n" - "using systemd-nspawn. Sleeping for 5s... (%m)"); - - sleep(5); - } - - return 0; -} - -#define HOST_HASH_KEY SD_ID128_MAKE(1a,37,6f,c7,46,ec,45,0b,ad,a3,d5,31,06,60,5d,b1) -#define CONTAINER_HASH_KEY SD_ID128_MAKE(c3,c4,f9,19,b5,57,b2,1c,e6,cf,14,27,03,9c,ee,a2) -#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 generate_mac(struct ether_addr *mac, sd_id128_t hash_key, uint64_t idx) { - uint8_t result[8]; - size_t l, sz; - uint8_t *v, *i; - int r; - - l = strlen(arg_machine); - sz = sizeof(sd_id128_t) + l; - if (idx > 0) - sz += sizeof(idx); - - v = alloca(sz); - - /* fetch some persistent data unique to the host */ - r = sd_id128_get_machine((sd_id128_t*) v); - if (r < 0) - return r; - - /* combine with some data unique (on this host) to this - * container instance */ - i = mempcpy(v + sizeof(sd_id128_t), arg_machine, l); - if (idx > 0) { - idx = htole64(idx); - memcpy(i, &idx, sizeof(idx)); - } - - /* Let's hash the host machine ID plus the container name. We - * use a fixed, but originally randomly created hash key here. */ - siphash24(result, v, sz, hash_key.bytes); - - assert_cc(ETH_ALEN <= sizeof(result)); - memcpy(mac->ether_addr_octet, result, ETH_ALEN); - - /* see eth_random_addr in the kernel */ - mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */ - mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */ - - return 0; -} - -static int setup_veth(pid_t pid, char iface_name[IFNAMSIZ], int *ifi) { - _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL; - _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; - struct ether_addr mac_host, mac_container; - int r, i; - - if (!arg_private_network) - return 0; - - if (!arg_network_veth) - return 0; - - /* Use two different interface name prefixes depending whether - * we are in bridge mode or not. */ - snprintf(iface_name, IFNAMSIZ - 1, "%s-%s", - arg_network_bridge ? "vb" : "ve", arg_machine); - - r = generate_mac(&mac_container, CONTAINER_HASH_KEY, 0); - if (r < 0) - return log_error_errno(r, "Failed to generate predictable MAC address for container side: %m"); - - r = generate_mac(&mac_host, HOST_HASH_KEY, 0); - if (r < 0) - return log_error_errno(r, "Failed to generate predictable MAC address for host side: %m"); - - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); - - r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0); - if (r < 0) - return log_error_errno(r, "Failed to allocate netlink message: %m"); - - r = sd_netlink_message_append_string(m, IFLA_IFNAME, iface_name); - if (r < 0) - return log_error_errno(r, "Failed to add netlink interface name: %m"); - - r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac_host); - if (r < 0) - return log_error_errno(r, "Failed to add netlink MAC address: %m"); - - r = sd_netlink_message_open_container(m, IFLA_LINKINFO); - if (r < 0) - return log_error_errno(r, "Failed to open netlink container: %m"); - - r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "veth"); - if (r < 0) - return log_error_errno(r, "Failed to open netlink container: %m"); - - r = sd_netlink_message_open_container(m, VETH_INFO_PEER); - if (r < 0) - return log_error_errno(r, "Failed to open netlink container: %m"); - - r = sd_netlink_message_append_string(m, IFLA_IFNAME, "host0"); - if (r < 0) - return log_error_errno(r, "Failed to add netlink interface name: %m"); - - r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac_container); - if (r < 0) - return log_error_errno(r, "Failed to add netlink MAC address: %m"); - - r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid); - if (r < 0) - return log_error_errno(r, "Failed to add netlink namespace field: %m"); - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_error_errno(r, "Failed to close netlink container: %m"); - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_error_errno(r, "Failed to close netlink container: %m"); - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_error_errno(r, "Failed to close netlink container: %m"); - - r = sd_netlink_call(rtnl, m, 0, NULL); - if (r < 0) - return log_error_errno(r, "Failed to add new veth interfaces (host0, %s): %m", iface_name); - - i = (int) if_nametoindex(iface_name); - if (i <= 0) - return log_error_errno(errno, "Failed to resolve interface %s: %m", iface_name); - - *ifi = i; - - return 0; -} - -static int setup_bridge(const char veth_name[], int *ifi) { - _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL; - _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; - int r, bridge; - - if (!arg_private_network) - return 0; - - if (!arg_network_veth) - return 0; - - if (!arg_network_bridge) - return 0; - - bridge = (int) if_nametoindex(arg_network_bridge); - if (bridge <= 0) - return log_error_errno(errno, "Failed to resolve interface %s: %m", arg_network_bridge); - - *ifi = bridge; - - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); - - r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, 0); - if (r < 0) - return log_error_errno(r, "Failed to allocate netlink message: %m"); - - 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"); - - 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"); - - r = sd_netlink_message_append_u32(m, IFLA_MASTER, bridge); - if (r < 0) - return log_error_errno(r, "Failed to add netlink master field: %m"); - - 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 0; -} - -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)]; - int ifi; - - ifi = (int) if_nametoindex(name); - if (ifi <= 0) - return log_error_errno(errno, "Failed to resolve interface %s: %m", name); - - sprintf(ifi_str, "n%i", ifi); - d = udev_device_new_from_device_id(udev, ifi_str); - if (!d) - return log_error_errno(errno, "Failed to get udev device for interface %s: %m", name); - - if (udev_device_get_is_initialized(d) <= 0) { - log_error("Network interface %s is not initialized yet.", name); - return -EBUSY; - } - - return ifi; -} - -static int move_network_interfaces(pid_t pid) { - _cleanup_udev_unref_ struct udev *udev = NULL; - _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; - char **i; - int r; - - if (!arg_private_network) - return 0; - - if (strv_isempty(arg_network_interfaces)) - return 0; - - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); - - udev = udev_new(); - if (!udev) { - log_error("Failed to connect to udev."); - return -ENOMEM; - } - - STRV_FOREACH(i, arg_network_interfaces) { - _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL; - int ifi; - - ifi = parse_interface(udev, *i); - if (ifi < 0) - return ifi; - - r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, ifi); - if (r < 0) - return log_error_errno(r, "Failed to allocate netlink message: %m"); - - r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid); - if (r < 0) - return log_error_errno(r, "Failed to append namespace PID to netlink message: %m"); - - r = sd_netlink_call(rtnl, m, 0, NULL); - if (r < 0) - return log_error_errno(r, "Failed to move interface %s to namespace: %m", *i); - } - - return 0; -} - -static int setup_macvlan(pid_t pid) { - _cleanup_udev_unref_ struct udev *udev = NULL; - _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; - unsigned idx = 0; - char **i; - int r; - - if (!arg_private_network) - return 0; - - if (strv_isempty(arg_network_macvlan)) - return 0; - - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); - - udev = udev_new(); - if (!udev) { - log_error("Failed to connect to udev."); - return -ENOMEM; - } - - STRV_FOREACH(i, arg_network_macvlan) { - _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL; - _cleanup_free_ char *n = NULL; - struct ether_addr mac; - int ifi; - - ifi = parse_interface(udev, *i); - if (ifi < 0) - return ifi; - - r = generate_mac(&mac, MACVLAN_HASH_KEY, idx++); - if (r < 0) - return log_error_errno(r, "Failed to create MACVLAN MAC address: %m"); - - r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0); - if (r < 0) - return log_error_errno(r, "Failed to allocate netlink message: %m"); - - r = sd_netlink_message_append_u32(m, IFLA_LINK, ifi); - if (r < 0) - return log_error_errno(r, "Failed to add netlink interface index: %m"); - - n = strappend("mv-", *i); - if (!n) - return log_oom(); - - strshorten(n, IFNAMSIZ-1); - - r = sd_netlink_message_append_string(m, IFLA_IFNAME, n); - if (r < 0) - return log_error_errno(r, "Failed to add netlink interface name: %m"); - - r = sd_netlink_message_append_ether_addr(m, IFLA_ADDRESS, &mac); - if (r < 0) - return log_error_errno(r, "Failed to add netlink MAC address: %m"); - - r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid); - if (r < 0) - return log_error_errno(r, "Failed to add netlink namespace field: %m"); - - r = sd_netlink_message_open_container(m, IFLA_LINKINFO); - if (r < 0) - return log_error_errno(r, "Failed to open netlink container: %m"); - - r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "macvlan"); - if (r < 0) - return log_error_errno(r, "Failed to open netlink container: %m"); - - r = sd_netlink_message_append_u32(m, IFLA_MACVLAN_MODE, MACVLAN_MODE_BRIDGE); - if (r < 0) - return log_error_errno(r, "Failed to append macvlan mode: %m"); - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_error_errno(r, "Failed to close netlink container: %m"); - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_error_errno(r, "Failed to close netlink container: %m"); - - r = sd_netlink_call(rtnl, m, 0, NULL); - if (r < 0) - return log_error_errno(r, "Failed to add new macvlan interfaces: %m"); - } - - return 0; -} - -static int setup_ipvlan(pid_t pid) { - _cleanup_udev_unref_ struct udev *udev = NULL; - _cleanup_netlink_unref_ sd_netlink *rtnl = NULL; - char **i; - int r; - - if (!arg_private_network) - return 0; - - if (strv_isempty(arg_network_ipvlan)) - return 0; - - r = sd_netlink_open(&rtnl); - if (r < 0) - return log_error_errno(r, "Failed to connect to netlink: %m"); - - udev = udev_new(); - if (!udev) { - log_error("Failed to connect to udev."); - return -ENOMEM; - } - - STRV_FOREACH(i, arg_network_ipvlan) { - _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL; - _cleanup_free_ char *n = NULL; - int ifi; - - ifi = parse_interface(udev, *i); - if (ifi < 0) - return ifi; - - r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0); - if (r < 0) - return log_error_errno(r, "Failed to allocate netlink message: %m"); - - r = sd_netlink_message_append_u32(m, IFLA_LINK, ifi); - if (r < 0) - return log_error_errno(r, "Failed to add netlink interface index: %m"); - - n = strappend("iv-", *i); - if (!n) - return log_oom(); - - strshorten(n, IFNAMSIZ-1); - - r = sd_netlink_message_append_string(m, IFLA_IFNAME, n); - if (r < 0) - return log_error_errno(r, "Failed to add netlink interface name: %m"); - - r = sd_netlink_message_append_u32(m, IFLA_NET_NS_PID, pid); - if (r < 0) - return log_error_errno(r, "Failed to add netlink namespace field: %m"); - - r = sd_netlink_message_open_container(m, IFLA_LINKINFO); - if (r < 0) - return log_error_errno(r, "Failed to open netlink container: %m"); - - r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "ipvlan"); - if (r < 0) - return log_error_errno(r, "Failed to open netlink container: %m"); - - r = sd_netlink_message_append_u16(m, IFLA_IPVLAN_MODE, IPVLAN_MODE_L2); - if (r < 0) - return log_error_errno(r, "Failed to add ipvlan mode: %m"); - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_error_errno(r, "Failed to close netlink container: %m"); - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_error_errno(r, "Failed to close netlink container: %m"); + log_error_errno(r, + "Failed to reset audit login UID. This probably means that your kernel is too\n" + "old and you have audit enabled. Note that the auditing subsystem is known to\n" + "be incompatible with containers on old kernels. Please make sure to upgrade\n" + "your kernel or to off auditing with 'audit=0' on the kernel command line before\n" + "using systemd-nspawn. Sleeping for 5s... (%m)"); - r = sd_netlink_call(rtnl, m, 0, NULL); - if (r < 0) - return log_error_errno(r, "Failed to add new ipvlan interfaces: %m"); + sleep(5); } return 0; @@ -3675,247 +2234,6 @@ static void loop_remove(int nr, int *image_fd) { log_debug_errno(errno, "Failed to remove loop %d: %m", nr); } -static int spawn_getent(const char *database, const char *key, pid_t *rpid) { - int pipe_fds[2]; - pid_t pid; - - assert(database); - assert(key); - assert(rpid); - - if (pipe2(pipe_fds, O_CLOEXEC) < 0) - return log_error_errno(errno, "Failed to allocate pipe: %m"); - - pid = fork(); - if (pid < 0) - return log_error_errno(errno, "Failed to fork getent child: %m"); - else if (pid == 0) { - int nullfd; - char *empty_env = NULL; - - if (dup3(pipe_fds[1], STDOUT_FILENO, 0) < 0) - _exit(EXIT_FAILURE); - - if (pipe_fds[0] > 2) - safe_close(pipe_fds[0]); - if (pipe_fds[1] > 2) - safe_close(pipe_fds[1]); - - nullfd = open("/dev/null", O_RDWR); - if (nullfd < 0) - _exit(EXIT_FAILURE); - - if (dup3(nullfd, STDIN_FILENO, 0) < 0) - _exit(EXIT_FAILURE); - - if (dup3(nullfd, STDERR_FILENO, 0) < 0) - _exit(EXIT_FAILURE); - - if (nullfd > 2) - safe_close(nullfd); - - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - close_all_fds(NULL, 0); - - execle("/usr/bin/getent", "getent", database, key, NULL, &empty_env); - execle("/bin/getent", "getent", database, key, NULL, &empty_env); - _exit(EXIT_FAILURE); - } - - pipe_fds[1] = safe_close(pipe_fds[1]); - - *rpid = pid; - - return pipe_fds[0]; -} - -static int change_uid_gid(char **_home) { - char line[LINE_MAX], *x, *u, *g, *h; - const char *word, *state; - _cleanup_free_ uid_t *uids = NULL; - _cleanup_free_ char *home = NULL; - _cleanup_fclose_ FILE *f = NULL; - _cleanup_close_ int fd = -1; - unsigned n_uids = 0; - size_t sz = 0, l; - uid_t uid; - gid_t gid; - pid_t pid; - int r; - - assert(_home); - - if (!arg_user || streq(arg_user, "root") || streq(arg_user, "0")) { - /* Reset everything fully to 0, just in case */ - - r = reset_uid_gid(); - if (r < 0) - return log_error_errno(r, "Failed to become root: %m"); - - *_home = NULL; - return 0; - } - - /* First, get user credentials */ - fd = spawn_getent("passwd", arg_user, &pid); - if (fd < 0) - return fd; - - f = fdopen(fd, "r"); - if (!f) - return log_oom(); - fd = -1; - - if (!fgets(line, sizeof(line), f)) { - - if (!ferror(f)) { - log_error("Failed to resolve user %s.", arg_user); - return -ESRCH; - } - - log_error_errno(errno, "Failed to read from getent: %m"); - return -errno; - } - - truncate_nl(line); - - wait_for_terminate_and_warn("getent passwd", pid, true); - - x = strchr(line, ':'); - if (!x) { - log_error("/etc/passwd entry has invalid user field."); - return -EIO; - } - - u = strchr(x+1, ':'); - if (!u) { - log_error("/etc/passwd entry has invalid password field."); - return -EIO; - } - - u++; - g = strchr(u, ':'); - if (!g) { - log_error("/etc/passwd entry has invalid UID field."); - return -EIO; - } - - *g = 0; - g++; - x = strchr(g, ':'); - if (!x) { - log_error("/etc/passwd entry has invalid GID field."); - return -EIO; - } - - *x = 0; - h = strchr(x+1, ':'); - if (!h) { - log_error("/etc/passwd entry has invalid GECOS field."); - return -EIO; - } - - h++; - x = strchr(h, ':'); - if (!x) { - log_error("/etc/passwd entry has invalid home directory field."); - return -EIO; - } - - *x = 0; - - r = parse_uid(u, &uid); - if (r < 0) { - log_error("Failed to parse UID of user."); - return -EIO; - } - - r = parse_gid(g, &gid); - if (r < 0) { - log_error("Failed to parse GID of user."); - return -EIO; - } - - home = strdup(h); - if (!home) - return log_oom(); - - /* Second, get group memberships */ - fd = spawn_getent("initgroups", arg_user, &pid); - if (fd < 0) - return fd; - - fclose(f); - f = fdopen(fd, "r"); - if (!f) - return log_oom(); - fd = -1; - - if (!fgets(line, sizeof(line), f)) { - if (!ferror(f)) { - log_error("Failed to resolve user %s.", arg_user); - return -ESRCH; - } - - log_error_errno(errno, "Failed to read from getent: %m"); - return -errno; - } - - truncate_nl(line); - - wait_for_terminate_and_warn("getent initgroups", pid, true); - - /* Skip over the username and subsequent separator whitespace */ - x = line; - x += strcspn(x, WHITESPACE); - x += strspn(x, WHITESPACE); - - FOREACH_WORD(word, l, x, state) { - char c[l+1]; - - memcpy(c, word, l); - c[l] = 0; - - if (!GREEDY_REALLOC(uids, sz, n_uids+1)) - return log_oom(); - - r = parse_uid(c, &uids[n_uids++]); - if (r < 0) { - log_error("Failed to parse group data from getent."); - return -EIO; - } - } - - r = mkdir_parents(home, 0775); - if (r < 0) - return log_error_errno(r, "Failed to make home root directory: %m"); - - r = mkdir_safe(home, 0755, uid, gid); - if (r < 0 && r != -EEXIST) - return log_error_errno(r, "Failed to make home directory: %m"); - - (void) fchown(STDIN_FILENO, uid, gid); - (void) fchown(STDOUT_FILENO, uid, gid); - (void) fchown(STDERR_FILENO, uid, gid); - - if (setgroups(n_uids, uids) < 0) - return log_error_errno(errno, "Failed to set auxiliary groups: %m"); - - if (setresgid(gid, gid, gid) < 0) - return log_error_errno(errno, "setregid() failed: %m"); - - if (setresuid(uid, uid, uid) < 0) - return log_error_errno(errno, "setreuid() failed: %m"); - - if (_home) { - *_home = home; - home = NULL; - } - - return 0; -} - /* * Return values: * < 0 : wait_for_terminate() failed to get the state of the @@ -4117,9 +2435,7 @@ static int inner_child( bool secondary, int kmsg_socket, int rtnl_socket, - FDSet *fds, - int argc, - char *argv[]) { + FDSet *fds) { _cleanup_free_ char *home = NULL; unsigned n_env = 2; @@ -4143,6 +2459,8 @@ static int inner_child( assert(directory); assert(kmsg_socket >= 0); + cg_unified_flush(); + if (arg_userns) { /* Tell the parent, that it now can write the UID map. */ (void) barrier_place(barrier); /* #1 */ @@ -4154,7 +2472,7 @@ static int inner_child( } } - r = mount_all(NULL, true); + r = mount_all(NULL, true, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context); if (r < 0) return r; @@ -4165,7 +2483,7 @@ static int inner_child( return -ESRCH; } - r = mount_systemd_cgroup_writable(""); + r = mount_systemd_cgroup_writable("", arg_unified_cgroup_hierarchy); if (r < 0) return r; @@ -4190,10 +2508,12 @@ static int inner_child( if (arg_private_network) loopback_setup(); - r = send_rtnl(rtnl_socket); - if (r < 0) - return r; - rtnl_socket = safe_close(rtnl_socket); + if (arg_expose_ports) { + r = expose_port_send_rtnl(rtnl_socket); + if (r < 0) + return r; + rtnl_socket = safe_close(rtnl_socket); + } if (drop_capabilities() < 0) return log_error_errno(errno, "drop_capabilities() failed: %m"); @@ -4214,7 +2534,7 @@ static int inner_child( return log_error_errno(errno, "setexeccon(\"%s\") failed: %m", arg_selinux_context); #endif - r = change_uid_gid(&home); + r = change_uid_gid(arg_user, &home); if (r < 0) return r; @@ -4273,9 +2593,12 @@ static int inner_child( /* Automatically search for the init system */ - m = 1 + argc - optind; + m = 1 + strv_length(arg_parameters); a = newa(char*, m + 1); - memcpy(a + 1, argv + optind, m * sizeof(char*)); + if (strv_isempty(arg_parameters)) + a[1] = NULL; + else + memcpy(a + 1, arg_parameters, m * sizeof(char*)); a[0] = (char*) "/usr/lib/systemd/systemd"; execve(a[0], a, env_use); @@ -4285,10 +2608,10 @@ static int inner_child( a[0] = (char*) "/sbin/init"; execve(a[0], a, env_use); - } else if (argc > optind) - execvpe(argv[optind], argv + optind, env_use); + } else if (!strv_isempty(arg_parameters)) + execvpe(arg_parameters[0], arg_parameters, env_use); else { - chdir(home ? home : "/root"); + chdir(home ?: "/root"); execle("/bin/bash", "-bash", NULL, env_use); execle("/bin/sh", "-sh", NULL, env_use); } @@ -4310,9 +2633,7 @@ static int outer_child( int kmsg_socket, int rtnl_socket, int uid_shift_socket, - FDSet *fds, - int argc, - char *argv[]) { + FDSet *fds) { pid_t pid; ssize_t l; @@ -4324,6 +2645,8 @@ static int outer_child( assert(pid_socket >= 0); assert(kmsg_socket >= 0); + cg_unified_flush(); + if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0) return log_error_errno(errno, "PR_SET_PDEATHSIG failed: %m"); @@ -4382,11 +2705,11 @@ static int outer_child( if (mount(directory, directory, NULL, MS_BIND|MS_REC, NULL) < 0) return log_error_errno(errno, "Failed to make bind mount: %m"); - r = setup_volatile(directory); + r = setup_volatile(directory, arg_volatile_mode, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_context); if (r < 0) return r; - r = setup_volatile_state(directory); + r = setup_volatile_state(directory, arg_volatile_mode, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_context); if (r < 0) return r; @@ -4400,16 +2723,18 @@ static int outer_child( return log_error_errno(r, "Failed to make tree read-only: %m"); } - r = mount_all(directory, false); + r = mount_all(directory, false, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context); if (r < 0) return r; - if (copy_devnodes(directory) < 0) + r = copy_devnodes(directory); + if (r < 0) return r; dev_setup(directory, arg_uid_shift, arg_uid_shift); - if (setup_pts(directory) < 0) + r = setup_pts(directory); + if (r < 0) return r; r = setup_propagate(directory); @@ -4436,11 +2761,11 @@ static int outer_child( if (r < 0) return r; - r = mount_custom(directory); + r = mount_custom(directory, arg_custom_mounts, arg_n_custom_mounts, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context); if (r < 0) return r; - r = mount_cgroup(directory); + r = mount_cgroups(directory, arg_unified_cgroup_hierarchy, arg_userns, arg_uid_shift, arg_uid_range, arg_selinux_apifs_context); if (r < 0) return r; @@ -4455,7 +2780,6 @@ static int outer_child( NULL); if (pid < 0) return log_error_errno(errno, "Failed to fork inner child: %m"); - if (pid == 0) { pid_socket = safe_close(pid_socket); uid_shift_socket = safe_close(uid_shift_socket); @@ -4464,7 +2788,7 @@ static int outer_child( * requested, so that we all are owned by the user if * user namespaces are turned on. */ - r = inner_child(barrier, directory, secondary, kmsg_socket, rtnl_socket, fds, argc, argv); + r = inner_child(barrier, directory, secondary, kmsg_socket, rtnl_socket, fds); if (r < 0) _exit(EXIT_FAILURE); @@ -4505,27 +2829,198 @@ static int setup_uid_map(pid_t pid) { return 0; } -static int chown_cgroup(pid_t pid) { - _cleanup_free_ char *path = NULL, *fs = NULL; - _cleanup_close_ int fd = -1; - const char *fn; +static int load_settings(void) { + _cleanup_(settings_freep) Settings *settings = NULL; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *p = NULL; + const char *fn, *i; int r; - r = cg_pid_get_path(NULL, pid, &path); - if (r < 0) - return log_error_errno(r, "Failed to get container cgroup path: %m"); + /* If all settings are masked, there's no point in looking for + * the settings file */ + if ((arg_settings_mask & _SETTINGS_MASK_ALL) == _SETTINGS_MASK_ALL) + return 0; + + fn = strjoina(arg_machine, ".nspawn"); + + /* We first look in the admin's directories in /etc and /run */ + FOREACH_STRING(i, "/etc/systemd/nspawn", "/run/systemd/nspawn") { + _cleanup_free_ char *j = NULL; + + j = strjoin(i, "/", fn, NULL); + if (!j) + return log_oom(); + + f = fopen(j, "re"); + if (f) { + p = j; + j = NULL; + + /* By default we trust configuration from /etc and /run */ + if (arg_settings_trusted < 0) + arg_settings_trusted = true; + + break; + } + + if (errno != ENOENT) + return log_error_errno(errno, "Failed to open %s: %m", j); + } + + if (!f) { + /* After that, let's look for a file next to the + * actual image we shall boot. */ + + if (arg_image) { + p = file_in_same_dir(arg_image, fn); + if (!p) + return log_oom(); + } else if (arg_directory) { + p = file_in_same_dir(arg_directory, fn); + if (!p) + return log_oom(); + } + + if (p) { + f = fopen(p, "re"); + if (!f && errno != ENOENT) + return log_error_errno(errno, "Failed to open %s: %m", p); + + /* By default we do not trust configuration from /var/lib/machines */ + if (arg_settings_trusted < 0) + arg_settings_trusted = false; + } + } - r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &fs); + if (!f) + return 0; + + log_debug("Settings are trusted: %s", yes_no(arg_settings_trusted)); + + r = settings_load(f, p, &settings); if (r < 0) - return log_error_errno(r, "Failed to get file system path for container cgroup: %m"); + return r; - fd = open(fs, O_RDONLY|O_CLOEXEC|O_DIRECTORY); - if (fd < 0) - return log_error_errno(errno, "Failed to open %s: %m", fs); + /* Copy over bits from the settings, unless they have been + * explicitly masked by command line switches. */ + + if ((arg_settings_mask & SETTING_BOOT) == 0 && + settings->boot >= 0) { + arg_boot = settings->boot; + + strv_free(arg_parameters); + arg_parameters = settings->parameters; + settings->parameters = NULL; + } + + if ((arg_settings_mask & SETTING_ENVIRONMENT) == 0 && + settings->environment) { + strv_free(arg_setenv); + arg_setenv = settings->environment; + settings->environment = NULL; + } + + if ((arg_settings_mask & SETTING_USER) == 0 && + settings->user) { + free(arg_user); + arg_user = settings->user; + settings->user = NULL; + } + + if ((arg_settings_mask & SETTING_CAPABILITY) == 0) { + + if (!arg_settings_trusted && settings->capability != 0) + log_warning("Ignoring Capability= setting, file %s is not trusted.", p); + else + arg_retain |= settings->capability; + + arg_retain &= ~settings->drop_capability; + } + + if ((arg_settings_mask & SETTING_KILL_SIGNAL) == 0 && + settings->kill_signal > 0) + arg_kill_signal = settings->kill_signal; + + if ((arg_settings_mask & SETTING_PERSONALITY) == 0 && + settings->personality != PERSONALITY_INVALID) + arg_personality = settings->personality; + + if ((arg_settings_mask & SETTING_MACHINE_ID) == 0 && + !sd_id128_is_null(settings->machine_id)) { + + if (!arg_settings_trusted) + log_warning("Ignoring MachineID= setting, file %s is not trusted.", p); + else + arg_uuid = settings->machine_id; + } + + if ((arg_settings_mask & SETTING_READ_ONLY) == 0 && + settings->read_only >= 0) + arg_read_only = settings->read_only; + + if ((arg_settings_mask & SETTING_VOLATILE_MODE) == 0 && + settings->volatile_mode != _VOLATILE_MODE_INVALID) + arg_volatile_mode = settings->volatile_mode; + + if ((arg_settings_mask & SETTING_CUSTOM_MOUNTS) == 0 && + settings->n_custom_mounts > 0) { + + if (!arg_settings_trusted) + log_warning("Ignoring TemporaryFileSystem=, Bind= and BindReadOnly= settings, file %s is not trusted.", p); + else { + custom_mount_free_all(arg_custom_mounts, arg_n_custom_mounts); + arg_custom_mounts = settings->custom_mounts; + arg_n_custom_mounts = settings->n_custom_mounts; + + settings->custom_mounts = NULL; + settings->n_custom_mounts = 0; + } + } - FOREACH_STRING(fn, ".", "tasks", "notify_on_release", "cgroup.procs", "cgroup.clone_children") - if (fchownat(fd, fn, arg_uid_shift, arg_uid_shift, 0) < 0) - log_warning_errno(errno, "Failed to chown() cgroup file %s, ignoring: %m", fn); + if ((arg_settings_mask & SETTING_NETWORK) == 0 && + (settings->private_network >= 0 || + settings->network_veth >= 0 || + settings->network_bridge || + settings->network_interfaces || + settings->network_macvlan || + settings->network_ipvlan)) { + + if (!arg_settings_trusted) + log_warning("Ignoring network settings, file %s is not trusted.", p); + else { + strv_free(arg_network_interfaces); + arg_network_interfaces = settings->network_interfaces; + settings->network_interfaces = NULL; + + strv_free(arg_network_macvlan); + arg_network_macvlan = settings->network_macvlan; + settings->network_macvlan = NULL; + + strv_free(arg_network_ipvlan); + arg_network_ipvlan = settings->network_ipvlan; + settings->network_ipvlan = NULL; + + free(arg_network_bridge); + arg_network_bridge = settings->network_bridge; + settings->network_bridge = NULL; + + arg_network_veth = settings->network_veth > 0 || settings->network_bridge; + + arg_private_network = true; /* all these settings imply private networking */ + } + } + + if ((arg_settings_mask & SETTING_EXPOSE_PORTS) == 0 && + settings->expose_ports) { + + if (!arg_settings_trusted) + log_warning("Ignoring Port= setting, file %s is not trusted.", p); + else { + expose_port_free_all(arg_expose_ports); + arg_expose_ports = settings->expose_ports; + settings->expose_ports = NULL; + } + } return 0; } @@ -4553,15 +3048,22 @@ int main(int argc, char *argv[]) { if (r <= 0) goto finish; - r = determine_names(); - if (r < 0) - goto finish; - if (geteuid() != 0) { log_error("Need to be root."); r = -EPERM; goto finish; } + r = determine_names(); + if (r < 0) + goto finish; + + r = load_settings(); + if (r < 0) + goto finish; + + r = verify_arguments(); + if (r < 0) + goto finish; n_fd_passed = sd_listen_fds(false); if (n_fd_passed > 0) { @@ -4770,23 +3272,23 @@ int main(int argc, char *argv[]) { goto finish; } - if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0) { + if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0) { r = log_error_errno(errno, "Failed to create kmsg socket pair: %m"); goto finish; } - if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, rtnl_socket_pair) < 0) { + if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, rtnl_socket_pair) < 0) { r = log_error_errno(errno, "Failed to create rtnl socket pair: %m"); goto finish; } - if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, pid_socket_pair) < 0) { + if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pid_socket_pair) < 0) { r = log_error_errno(errno, "Failed to create pid socket pair: %m"); goto finish; } if (arg_userns) - if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0) { + if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0) { r = log_error_errno(errno, "Failed to create uid shift socket pair: %m"); goto finish; } @@ -4842,8 +3344,7 @@ int main(int argc, char *argv[]) { kmsg_socket_pair[1], rtnl_socket_pair[1], uid_shift_socket_pair[1], - fds, - argc, argv); + fds); if (r < 0) _exit(EXIT_FAILURE); @@ -4858,6 +3359,7 @@ int main(int argc, char *argv[]) { kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]); rtnl_socket_pair[1] = safe_close(rtnl_socket_pair[1]); pid_socket_pair[1] = safe_close(pid_socket_pair[1]); + uid_shift_socket_pair[1] = safe_close(uid_shift_socket_pair[1]); /* Wait for the outer child. */ r = wait_for_terminate_and_warn("namespace helper", pid, NULL); @@ -4876,7 +3378,7 @@ int main(int argc, char *argv[]) { goto finish; } if (l != sizeof(pid)) { - log_error("Short read while reading inner child PID: %m"); + log_error("Short read while reading inner child PID."); r = EIO; goto finish; } @@ -4896,7 +3398,7 @@ int main(int argc, char *argv[]) { goto finish; } if (l != sizeof(arg_uid_shift)) { - log_error("Short read while reading UID shift: %m"); + log_error("Short read while reading UID shift."); r = EIO; goto finish; } @@ -4908,31 +3410,64 @@ int main(int argc, char *argv[]) { (void) barrier_place(&barrier); /* #2 */ } - r = move_network_interfaces(pid); - if (r < 0) - goto finish; + if (arg_private_network) { - r = setup_veth(pid, veth_name, &ifi); - if (r < 0) - goto finish; + r = move_network_interfaces(pid, arg_network_interfaces); + if (r < 0) + goto finish; - r = setup_bridge(veth_name, &ifi); - if (r < 0) - goto finish; + if (arg_network_veth) { + r = setup_veth(arg_machine, pid, veth_name, !!arg_network_bridge); + if (r < 0) + goto finish; + else if (r > 0) + ifi = r; - r = setup_macvlan(pid); - if (r < 0) - goto finish; + if (arg_network_bridge) { + r = setup_bridge(veth_name, arg_network_bridge); + if (r < 0) + goto finish; + if (r > 0) + ifi = r; + } + } - r = setup_ipvlan(pid); - if (r < 0) - goto finish; + r = setup_macvlan(arg_machine, pid, arg_network_macvlan); + if (r < 0) + goto finish; + + r = setup_ipvlan(arg_machine, pid, arg_network_ipvlan); + if (r < 0) + goto finish; + } + + if (arg_register) { + r = register_machine( + arg_machine, + pid, + arg_directory, + arg_uuid, + ifi, + arg_slice, + arg_custom_mounts, arg_n_custom_mounts, + arg_kill_signal, + arg_property, + arg_keep_unit); + if (r < 0) + goto finish; + } - r = register_machine(pid, ifi); + r = sync_cgroup(pid, arg_unified_cgroup_hierarchy); if (r < 0) goto finish; - r = chown_cgroup(pid); + if (arg_keep_unit) { + r = create_subcgroup(pid, arg_unified_cgroup_hierarchy); + if (r < 0) + goto finish; + } + + r = chown_cgroup(pid, arg_uid_shift); if (r < 0) goto finish; @@ -4985,11 +3520,11 @@ int main(int argc, char *argv[]) { sd_event_add_signal(event, NULL, SIGCHLD, NULL, NULL); if (arg_expose_ports) { - r = watch_rtnl(event, rtnl_socket_pair[0], &exposed, &rtnl); + r = expose_port_watch_rtnl(event, rtnl_socket_pair[0], on_address_change, &exposed, &rtnl); if (r < 0) goto finish; - (void) expose_ports(rtnl, &exposed); + (void) expose_port_execute(rtnl, arg_expose_ports, &exposed); } rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]); @@ -5014,7 +3549,8 @@ int main(int argc, char *argv[]) { putc('\n', stdout); /* Kill if it is not dead yet anyway */ - terminate_machine(pid); + if (arg_register && !arg_keep_unit) + terminate_machine(pid); /* Normally redundant, but better safe than sorry */ kill(pid, SIGKILL); @@ -5052,7 +3588,7 @@ int main(int argc, char *argv[]) { break; } - flush_ports(&exposed); + expose_port_flush(arg_expose_ports, &exposed); } finish: @@ -5084,24 +3620,21 @@ finish: (void) rm_rf(p, REMOVE_ROOT); } + expose_port_flush(arg_expose_ports, &exposed); + free(arg_directory); free(arg_template); free(arg_image); free(arg_machine); free(arg_user); strv_free(arg_setenv); + free(arg_network_bridge); strv_free(arg_network_interfaces); strv_free(arg_network_macvlan); strv_free(arg_network_ipvlan); - custom_mount_free_all(); - - flush_ports(&exposed); - - while (arg_expose_ports) { - ExposePort *p = arg_expose_ports; - LIST_REMOVE(ports, arg_expose_ports, p); - free(p); - } + strv_free(arg_parameters); + custom_mount_free_all(arg_custom_mounts, arg_n_custom_mounts); + expose_port_free_all(arg_expose_ports); return r < 0 ? EXIT_FAILURE : ret; } diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 5628e579a..fbbabaf23 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -78,7 +78,7 @@ struct DnsPacket { DnsQuestion *question; DnsAnswer *answer; - /* Packet reception meta data */ + /* Packet reception metadata */ int ifindex; int family, ipproto; union in_addr_union sender, destination; diff --git a/src/run/run.c b/src/run/run.c index 3dd97022d..657c6fcaf 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -36,7 +36,9 @@ #include "ptyfwd.h" #include "formats-util.h" #include "signal-util.h" +#include "spawn-polkit-agent.h" +static bool arg_ask_password = true; static bool arg_scope = false; static bool arg_remain_after_exit = false; static bool arg_no_block = false; @@ -64,6 +66,18 @@ static char *arg_on_calendar = NULL; static char **arg_timer_property = NULL; static bool arg_quiet = false; +static void polkit_agent_open_if_enabled(void) { + + /* Open the polkit agent as a child process if necessary */ + if (!arg_ask_password) + return; + + if (arg_transport != BUS_TRANSPORT_LOCAL) + return; + + polkit_agent_open(); +} + static void help(void) { printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n" "Run the specified command in a transient scope or service or timer\n" @@ -71,6 +85,7 @@ static void help(void) { "specified with --unit option then command can be omitted.\n\n" " -h --help Show this help\n" " --version Show package version\n" + " --no-ask-password Do not prompt for password\n" " --user Run as user unit\n" " -H --host=[USER@]HOST Operate on remote host\n" " -M --machine=CONTAINER Operate on local container\n" @@ -108,6 +123,7 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, + ARG_NO_ASK_PASSWORD, ARG_USER, ARG_SYSTEM, ARG_SCOPE, @@ -160,6 +176,7 @@ static int parse_argv(int argc, char *argv[]) { { "on-calendar", required_argument, NULL, ARG_ON_CALENDAR }, { "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY }, { "no-block", no_argument, NULL, ARG_NO_BLOCK }, + { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, {}, }; @@ -177,6 +194,10 @@ static int parse_argv(int argc, char *argv[]) { help(); return 0; + case ARG_NO_ASK_PASSWORD: + arg_ask_password = false; + break; + case ARG_VERSION: puts(PACKAGE_STRING); puts(SYSTEMD_FEATURES); @@ -681,6 +702,9 @@ static int start_transient_service( if (r < 0) return log_error_errno(r, "Failed to determine tty name: %m"); + if (unlockpt(master) < 0) + return log_error_errno(errno, "Failed to unlock tty: %m"); + } else if (arg_transport == BUS_TRANSPORT_MACHINE) { _cleanup_bus_unref_ sd_bus *system_bus = NULL; const char *s; @@ -717,9 +741,6 @@ static int start_transient_service( return log_oom(); } else assert_not_reached("Can't allocate tty via ssh"); - - if (unlockpt(master) < 0) - return log_error_errno(errno, "Failed to unlock tty: %m"); } if (!arg_no_block) { @@ -745,6 +766,10 @@ static int start_transient_service( if (r < 0) return bus_log_create_error(r); + r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password); + if (r < 0) + return bus_log_create_error(r); + /* Name and mode */ r = sd_bus_message_append(m, "ss", service, "fail"); if (r < 0) @@ -768,6 +793,8 @@ static int start_transient_service( if (r < 0) return bus_log_create_error(r); + polkit_agent_open_if_enabled(); + r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0) { log_error("Failed to start transient service unit: %s", bus_error_message(&error, -r)); @@ -860,6 +887,10 @@ static int start_transient_scope( if (r < 0) return bus_log_create_error(r); + r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password); + if (r < 0) + return bus_log_create_error(r); + /* Name and Mode */ r = sd_bus_message_append(m, "ss", scope, "fail"); if (r < 0) @@ -883,6 +914,8 @@ static int start_transient_scope( if (r < 0) return bus_log_create_error(r); + polkit_agent_open_if_enabled(); + r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0) { log_error("Failed to start transient scope unit: %s", bus_error_message(&error, -r)); @@ -1025,6 +1058,10 @@ static int start_transient_timer( if (r < 0) return bus_log_create_error(r); + r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password); + if (r < 0) + return bus_log_create_error(r); + /* Name and Mode */ r = sd_bus_message_append(m, "ss", timer, "fail"); if (r < 0) @@ -1077,6 +1114,8 @@ static int start_transient_timer( if (r < 0) return bus_log_create_error(r); + polkit_agent_open_if_enabled(); + r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0) { log_error("Failed to start transient timer unit: %s", bus_error_message(&error, -r)); diff --git a/src/shared/Makefile b/src/shared/Makefile new file mode 120000 index 000000000..d0b0e8e00 --- /dev/null +++ b/src/shared/Makefile @@ -0,0 +1 @@ +../Makefile \ No newline at end of file diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 1369a6145..d9cc19700 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -220,6 +220,7 @@ int bus_test_polkit( sd_bus_message *call, int capability, const char *action, + const char **details, uid_t good_user, bool *_challenge, sd_bus_error *e) { @@ -242,29 +243,52 @@ int bus_test_polkit( return 1; #ifdef ENABLE_POLKIT else { + _cleanup_bus_message_unref_ sd_bus_message *request = NULL; _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; int authorized = false, challenge = false; - const char *sender; + const char *sender, **k, **v; sender = sd_bus_message_get_sender(call); if (!sender) return -EBADMSG; - r = sd_bus_call_method( + r = sd_bus_message_new_method_call( call->bus, + &request, "org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority", "org.freedesktop.PolicyKit1.Authority", - "CheckAuthorization", - e, - &reply, - "(sa{sv})sa{ss}us", + "CheckAuthorization"); + if (r < 0) + return r; + + r = sd_bus_message_append( + request, + "(sa{sv})s", "system-bus-name", 1, "name", "s", sender, - action, - 0, - 0, - ""); + action); + if (r < 0) + return r; + + r = sd_bus_message_open_container(request, 'a', "{ss}"); + if (r < 0) + return r; + STRV_FOREACH_PAIR(k, v, details) { + r = sd_bus_message_append(request, "{ss}", *k, *v); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(request); + if (r < 0) + return r; + + r = sd_bus_message_append(request, "us", 0, NULL); + if (r < 0) + return r; + + r = sd_bus_call(call->bus, request, 0, e, &reply); if (r < 0) { /* Treat no PK available as access denied */ if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) { @@ -354,6 +378,7 @@ int bus_verify_polkit_async( sd_bus_message *call, int capability, const char *action, + const char **details, bool interactive, uid_t good_user, Hashmap **registry, @@ -362,7 +387,7 @@ int bus_verify_polkit_async( #ifdef ENABLE_POLKIT _cleanup_bus_message_unref_ sd_bus_message *pk = NULL; AsyncPolkitQuery *q; - const char *sender; + const char *sender, **k, **v; sd_bus_message_handler_t callback; void *userdata; int c; @@ -460,12 +485,27 @@ int bus_verify_polkit_async( r = sd_bus_message_append( pk, - "(sa{sv})sa{ss}us", + "(sa{sv})s", "system-bus-name", 1, "name", "s", sender, - action, - 0, - !!interactive, - NULL); + action); + if (r < 0) + return r; + + r = sd_bus_message_open_container(pk, 'a', "{ss}"); + if (r < 0) + return r; + + STRV_FOREACH_PAIR(k, v, details) { + r = sd_bus_message_append(pk, "{ss}", *k, *v); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(pk); + if (r < 0) + return r; + + r = sd_bus_message_append(pk, "us", !!interactive, NULL); if (r < 0) return r; diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h index 4ae216b7d..d2b2d701c 100644 --- a/src/shared/bus-util.h +++ b/src/shared/bus-util.h @@ -60,9 +60,9 @@ int bus_name_has_owner(sd_bus *c, const char *name, sd_bus_error *error); int bus_check_peercred(sd_bus *c); -int bus_test_polkit(sd_bus_message *call, int capability, const char *action, uid_t good_user, bool *_challenge, sd_bus_error *e); +int bus_test_polkit(sd_bus_message *call, int capability, const char *action, const char **details, uid_t good_user, bool *_challenge, sd_bus_error *e); -int bus_verify_polkit_async(sd_bus_message *call, int capability, const char *action, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error); +int bus_verify_polkit_async(sd_bus_message *call, int capability, const char *action, const char **details, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error); void bus_verify_polkit_async_registry_free(Hashmap *registry); int bus_open_system_systemd(sd_bus **_bus); diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c index 1a2c4b28c..31b4f6c68 100644 --- a/src/shared/cgroup-show.c +++ b/src/shared/cgroup-show.c @@ -152,7 +152,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns if (!k) return -ENOMEM; - if (!(flags & OUTPUT_SHOW_ALL) && cg_is_empty_recursive(NULL, k, false) > 0) + if (!(flags & OUTPUT_SHOW_ALL) && cg_is_empty_recursive(NULL, k) > 0) continue; if (!shown_pids) { @@ -161,8 +161,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns } if (last) { - printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_BRANCH), - basename(last)); + printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_BRANCH), cg_unescape(basename(last))); if (!p1) { p1 = strappend(prefix, draw_special_char(DRAW_TREE_VERTICAL)); @@ -185,8 +184,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns show_cgroup_one_by_path(path, prefix, n_columns, !!last, kernel_threads, flags); if (last) { - printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_RIGHT), - basename(last)); + printf("%s%s%s\n", prefix, draw_special_char(DRAW_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 d99aa1d6e..36150d874 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -24,7 +24,7 @@ #include #include -#include "conf-parser.h" +#include "sd-messages.h" #include "conf-files.h" #include "util.h" #include "macro.h" @@ -32,7 +32,8 @@ #include "log.h" #include "utf8.h" #include "path-util.h" -#include "sd-messages.h" +#include "signal-util.h" +#include "conf-parser.h" int config_item_table_lookup( const void *table, @@ -450,6 +451,7 @@ int config_parse_many(const char *conf_file, DEFINE_PARSER(int, int, safe_atoi) DEFINE_PARSER(long, long, safe_atoli) +DEFINE_PARSER(uint32, uint32_t, safe_atou32) DEFINE_PARSER(uint64, uint64_t, safe_atou64) DEFINE_PARSER(unsigned, unsigned, safe_atou) DEFINE_PARSER(double, double, safe_atod) @@ -574,6 +576,39 @@ int config_parse_bool(const char* unit, return 0; } +int config_parse_tristate( + 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 k, *t = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + /* A tristate is pretty much a boolean, except that it can + * also take the special value -1, indicating "uninitialized", + * much like NULL is for a pointer type. */ + + k = parse_boolean(rvalue); + if (k < 0) { + log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue); + return 0; + } + + *t = !!k; + return 0; +} + int config_parse_string( const char *unit, const char *filename, @@ -801,3 +836,61 @@ int config_parse_log_level( *o = (*o & LOG_FACMASK) | x; return 0; } + +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 *sig = data, r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(sig); + + r = signal_from_string_try_harder(rvalue); + if (r <= 0) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse signal name, ignoring: %s", rvalue); + return 0; + } + + *sig = r; + return 0; +} + +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) { + + unsigned long *personality = data, p; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(personality); + + p = personality_from_string(rvalue); + if (p == PERSONALITY_INVALID) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse personality, ignoring: %s", rvalue); + return 0; + } + + *personality = p; + return 0; +} diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index 6152ee33b..34e381578 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -104,12 +104,14 @@ int config_parse_many(const char *conf_file, /* possibly NULL */ int config_parse_int(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_unsigned(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_long(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_uint32(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_uint64(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_double(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_iec_size(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_si_size(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_iec_off(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_bool(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_tristate(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_string(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_path(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_strv(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); @@ -118,6 +120,8 @@ int config_parse_nsec(const char *unit, const char *filename, unsigned line, con int config_parse_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_log_facility(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_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); #define log_invalid_utf8(unit, level, config_file, config_line, error, rvalue) \ do { \ diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index 273dacff1..70220bdd1 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -33,7 +33,7 @@ static const char image_search_path[] = "/var/lib/machines\0" - "/var/lib/container\0" + "/var/lib/container\0" /* legacy */ "/usr/local/lib/machines\0" "/usr/lib/machines\0"; diff --git a/src/shared/pager.c b/src/shared/pager.c index 13f03e798..3992f9c83 100644 --- a/src/shared/pager.c +++ b/src/shared/pager.c @@ -31,18 +31,16 @@ #include "macro.h" #include "terminal-util.h" #include "signal-util.h" +#include "copy.h" static pid_t pager_pid = 0; noreturn static void pager_fallback(void) { - ssize_t n; - - do { - n = splice(STDIN_FILENO, NULL, STDOUT_FILENO, NULL, 64*1024, 0); - } while (n > 0); + int r; - if (n < 0) { - log_error_errno(errno, "Internal pager failed: %m"); + r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (off_t) -1, false); + if (r < 0) { + log_error_errno(r, "Internal pager failed: %m"); _exit(EXIT_FAILURE); } @@ -84,7 +82,7 @@ int pager_open(bool jump_to_end) { /* In the child start the pager */ if (pager_pid == 0) { - const char* less_opts; + const char* less_opts, *less_charset; (void) reset_all_signal_handlers(); (void) reset_signal_mask(); @@ -92,6 +90,7 @@ int pager_open(bool jump_to_end) { dup2(fd[0], STDIN_FILENO); safe_close_pair(fd); + /* Initialize a good set of less options */ less_opts = getenv("SYSTEMD_LESS"); if (!less_opts) less_opts = "FRSXMK"; @@ -99,6 +98,15 @@ int pager_open(bool jump_to_end) { less_opts = strjoina(less_opts, " +G"); setenv("LESS", less_opts, 1); + /* Initialize a good charset for less. This is + * particularly important if we output UTF-8 + * characters. */ + less_charset = getenv("SYSTEMD_LESSCHARSET"); + if (!less_charset && is_locale_utf8()) + less_charset = "utf-8"; + if (less_charset) + setenv("LESSCHARSET", less_charset, 1); + /* Make sure the pager goes away when the parent dies */ if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) _exit(EXIT_FAILURE); @@ -131,6 +139,8 @@ int pager_open(bool jump_to_end) { /* Return in the parent */ if (dup2(fd[1], STDOUT_FILENO) < 0) return log_error_errno(errno, "Failed to duplicate pager pipe: %m"); + if (dup2(fd[1], STDERR_FILENO) < 0) + return log_error_errno(errno, "Failed to duplicate pager pipe: %m"); safe_close_pair(fd); return 1; @@ -143,6 +153,11 @@ void pager_close(void) { /* Inform pager that we are done */ fclose(stdout); + stdout = NULL; + + fclose(stderr); + stderr = NULL; + kill(pager_pid, SIGCONT); (void) wait_for_terminate(pager_pid, NULL); pager_pid = 0; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 3cb5f6186..96c39aa0a 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -535,10 +535,8 @@ static int get_unit_list( return bus_log_create_error(r); r = sd_bus_call(bus, m, 0, &error, &reply); - if (r < 0) { - log_error("Failed to list units: %s", bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r)); r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); if (r < 0) @@ -605,7 +603,7 @@ static int get_unit_list_recursive( r = set_put(replies, reply); if (r < 0) { sd_bus_message_unref(reply); - return r; + return log_oom(); } if (arg_recursive) { @@ -614,7 +612,7 @@ static int get_unit_list_recursive( r = sd_get_machine_names(&machines); if (r < 0) - return r; + return log_error_errno(r, "Failed to get machine names: %m"); STRV_FOREACH(i, machines) { _cleanup_bus_flush_close_unref_ sd_bus *container = NULL; @@ -622,7 +620,7 @@ static int get_unit_list_recursive( r = sd_bus_open_system_machine(&container, *i); if (r < 0) { - log_error_errno(r, "Failed to connect to container %s: %m", *i); + log_warning_errno(r, "Failed to connect to container %s, ignoring: %m", *i); continue; } @@ -635,7 +633,7 @@ static int get_unit_list_recursive( r = set_put(replies, reply); if (r < 0) { sd_bus_message_unref(reply); - return r; + return log_oom(); } } @@ -1474,6 +1472,7 @@ static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, cha "Requisite\0" "RequisiteOverridable\0" "Wants\0" + "ConsistsOf\0" "BindsTo\0", [DEPENDENCY_REVERSE] = "RequiredBy\0" "RequiredByOverridable\0" @@ -1743,7 +1742,7 @@ static int get_machine_list( _cleanup_free_ char *hn = NULL; size_t sz = 0; char **i; - int c = 0; + int c = 0, r; hn = gethostname_malloc(); if (!hn) @@ -1761,7 +1760,10 @@ static int get_machine_list( c++; } - sd_get_machine_names(&m); + r = sd_get_machine_names(&m); + if (r < 0) + return log_error_errno(r, "Failed to get machine list: %m"); + STRV_FOREACH(i, m) { _cleanup_free_ char *class = NULL; @@ -3557,7 +3559,7 @@ static void print_status_info( if (i->control_group && (i->main_pid > 0 || i->control_pid > 0 || - ((arg_transport != BUS_TRANSPORT_LOCAL && arg_transport != BUS_TRANSPORT_MACHINE) || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group, false) == 0))) { + ((arg_transport != BUS_TRANSPORT_LOCAL && arg_transport != BUS_TRANSPORT_MACHINE) || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group) == 0))) { unsigned c; printf(" CGroup: %s\n", i->control_group); @@ -4267,10 +4269,8 @@ static int show_one( &error, &reply, "s", ""); - if (r < 0) { - log_error("Failed to get properties: %s", bus_error_message(&error, r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r)); r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}"); if (r < 0) diff --git a/src/systemd/sd-dhcp-lease.h b/src/systemd/sd-dhcp-lease.h index 5afa50a9d..ed5bceecd 100644 --- a/src/systemd/sd-dhcp-lease.h +++ b/src/systemd/sd-dhcp-lease.h @@ -34,6 +34,9 @@ sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease); int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr); int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime); +int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1); +int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2); +int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr); int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr); int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr); int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr); @@ -44,13 +47,9 @@ int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu); int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname); int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname); int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path); -int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routesgn); -int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const uint8_t **data, - size_t *data_len); -int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const uint8_t **client_id, - size_t *client_id_len); - -int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file); -int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file); +int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routes); +int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len); +int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len); +int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone); #endif diff --git a/src/systemd/sd-dhcp-server.h b/src/systemd/sd-dhcp-server.h index 9af3b6532..4b0c7a185 100644 --- a/src/systemd/sd-dhcp-server.h +++ b/src/systemd/sd-dhcp-server.h @@ -30,11 +30,11 @@ typedef struct sd_dhcp_server sd_dhcp_server; +int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex); + sd_dhcp_server *sd_dhcp_server_ref(sd_dhcp_server *server); sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server); -int sd_dhcp_server_new(sd_dhcp_server **ret, int ifindex); - int sd_dhcp_server_attach_event(sd_dhcp_server *client, sd_event *event, int priority); int sd_dhcp_server_detach_event(sd_dhcp_server *client); sd_event *sd_dhcp_server_get_event(sd_dhcp_server *client); @@ -44,8 +44,15 @@ bool sd_dhcp_server_is_running(sd_dhcp_server *server); int sd_dhcp_server_start(sd_dhcp_server *server); int sd_dhcp_server_stop(sd_dhcp_server *server); -int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen); -int sd_dhcp_server_set_lease_pool(sd_dhcp_server *server, struct in_addr *start, size_t size); +int sd_dhcp_server_configure_pool(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size); + +int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *timezone); +int sd_dhcp_server_set_dns(sd_dhcp_server *server, const struct in_addr ntp[], unsigned n); +int sd_dhcp_server_set_ntp(sd_dhcp_server *server, const struct in_addr dns[], unsigned n); + +int sd_dhcp_server_set_max_lease_time(sd_dhcp_server *server, uint32_t t); +int sd_dhcp_server_set_default_lease_time(sd_dhcp_server *server, uint32_t t); int sd_dhcp_server_forcerenew(sd_dhcp_server *server); + #endif diff --git a/src/systemd/sd-login.h b/src/systemd/sd-login.h index 9260396d5..59c6eedcd 100644 --- a/src/systemd/sd-login.h +++ b/src/systemd/sd-login.h @@ -81,34 +81,42 @@ int sd_pid_get_user_slice(pid_t pid, char **slice); * container. This will return an error for non-machine processes. */ int sd_pid_get_machine_name(pid_t pid, char **machine); -/* Similar to sd_pid_get_session(), but retrieves data about peer of - * connected AF_UNIX socket */ +/* Get the control group from a PID, relative to the root of the + * hierarchy. */ +int sd_pid_get_cgroup(pid_t pid, char **cgroup); + +/* Similar to sd_pid_get_session(), but retrieves data about the peer + * of a connected AF_UNIX socket */ int sd_peer_get_session(int fd, char **session); -/* Similar to sd_pid_get_owner_uid(), but retrieves data about peer of - * connected AF_UNIX socket */ +/* Similar to sd_pid_get_owner_uid(), but retrieves data about the peer of + * a connected AF_UNIX socket */ int sd_peer_get_owner_uid(int fd, uid_t *uid); -/* Similar to sd_pid_get_unit(), but retrieves data about peer of - * connected AF_UNIX socket */ +/* Similar to sd_pid_get_unit(), but retrieves data about the peer of + * a connected AF_UNIX socket */ int sd_peer_get_unit(int fd, char **unit); -/* Similar to sd_pid_get_user_unit(), but retrieves data about peer of - * connected AF_UNIX socket */ +/* Similar to sd_pid_get_user_unit(), but retrieves data about the peer of + * a connected AF_UNIX socket */ int sd_peer_get_user_unit(int fd, char **unit); -/* Similar to sd_pid_get_slice(), but retrieves data about peer of - * connected AF_UNIX socket */ +/* Similar to sd_pid_get_slice(), but retrieves data about the peer of + * a connected AF_UNIX socket */ int sd_peer_get_slice(int fd, char **slice); -/* Similar to sd_pid_get_user_slice(), but retrieves data about peer of - * connected AF_UNIX socket */ +/* Similar to sd_pid_get_user_slice(), but retrieves data about the peer of + * a connected AF_UNIX socket */ int sd_peer_get_user_slice(int fd, char **slice); -/* Similar to sd_pid_get_machine_name(), but retrieves data about peer - * of connected AF_UNIX socket */ +/* Similar to sd_pid_get_machine_name(), but retrieves data about the + * peer of a a connected AF_UNIX socket */ int sd_peer_get_machine_name(int fd, char **machine); +/* Similar to sd_pid_get_cgroup(), but retrieves data about the peer + * of a connected AF_UNIX socket. */ +int sd_peer_get_cgroup(pid_t pid, char **cgroup); + /* Get state from UID. Possible states: offline, lingering, online, active, closing */ int sd_uid_get_state(uid_t uid, char **state); diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h index 4d96c867d..4179015fb 100644 --- a/src/systemd/sd-network.h +++ b/src/systemd/sd-network.h @@ -122,6 +122,9 @@ int sd_network_link_get_carrier_bound_to(int ifindex, char ***carriers); /* Get the CARRIERS that are bound to current link. */ int sd_network_link_get_carrier_bound_by(int ifindex, char ***carriers); +/* Get the timezone that was learnt on a specific link. */ +int sd_network_link_get_timezone(int ifindex, char **timezone); + /* Returns whether or not domains that don't match any link should be resolved * on this link. 1 for yes, 0 for no and negative value for error */ int sd_network_link_get_wildcard_domain(int ifindex); diff --git a/src/systemd/sd-resolve.h b/src/systemd/sd-resolve.h index 36cedcd6b..80c5852e4 100644 --- a/src/systemd/sd-resolve.h +++ b/src/systemd/sd-resolve.h @@ -40,7 +40,6 @@ typedef struct sd_resolve_query sd_resolve_query; /* A callback on completion */ typedef int (*sd_resolve_getaddrinfo_handler_t)(sd_resolve_query *q, int ret, const struct addrinfo *ai, void *userdata); typedef int (*sd_resolve_getnameinfo_handler_t)(sd_resolve_query *q, int ret, const char *host, const char *serv, void *userdata); -typedef int (*sd_resolve_res_handler_t)(sd_resolve_query* q, int ret, unsigned char *answer, void *userdata); enum { SD_RESOLVE_GET_HOST = 1ULL, @@ -99,18 +98,6 @@ int sd_resolve_getaddrinfo(sd_resolve *resolve, sd_resolve_query **q, const char * if you want to query the hostname (resp. the service name). */ int sd_resolve_getnameinfo(sd_resolve *resolve, sd_resolve_query **q, const struct sockaddr *sa, socklen_t salen, int flags, uint64_t get, sd_resolve_getnameinfo_handler_t callback, void *userdata); -/* Issue a resolver query on the specified session. The arguments are - * compatible with those of libc's res_query(3). The function returns a new - * query object. When the query is completed, you may retrieve the results using - * sd_resolve_res_done(). */ -int sd_resolve_res_query(sd_resolve *resolve, sd_resolve_query **q, const char *dname, int clazz, int type, sd_resolve_res_handler_t callback, void *userdata); - -/* Issue a resolver query on the specified session. The arguments are - * compatible with those of libc's res_search(3). The function returns a new - * query object. When the query is completed, you may retrieve the results using - * sd_resolve_res_done(). */ -int sd_resolve_res_search(sd_resolve *resolve, sd_resolve_query **q, const char *dname, int clazz, int type, sd_resolve_res_handler_t callback, void *userdata); - sd_resolve_query *sd_resolve_query_ref(sd_resolve_query* q); sd_resolve_query *sd_resolve_query_unref(sd_resolve_query* q); diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c index 72f874d8a..de6c421b8 100644 --- a/src/test/test-cgroup-mask.c +++ b/src/test/test-cgroup-mask.c @@ -61,36 +61,36 @@ static int test_cgroup_mask(void) { root = UNIT_DEREF(parent->slice); /* Verify per-unit cgroups settings. */ - assert_se(unit_get_cgroup_mask(son) == (CGROUP_CPU | CGROUP_CPUACCT)); - assert_se(unit_get_cgroup_mask(daughter) == 0); - assert_se(unit_get_cgroup_mask(grandchild) == 0); - assert_se(unit_get_cgroup_mask(parent_deep) == CGROUP_MEMORY); - assert_se(unit_get_cgroup_mask(parent) == CGROUP_BLKIO); - assert_se(unit_get_cgroup_mask(root) == 0); + assert_se(unit_get_own_mask(son) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT)); + assert_se(unit_get_own_mask(daughter) == 0); + assert_se(unit_get_own_mask(grandchild) == 0); + assert_se(unit_get_own_mask(parent_deep) == CGROUP_MASK_MEMORY); + assert_se(unit_get_own_mask(parent) == CGROUP_MASK_BLKIO); + assert_se(unit_get_own_mask(root) == 0); /* Verify aggregation of member masks */ assert_se(unit_get_members_mask(son) == 0); assert_se(unit_get_members_mask(daughter) == 0); assert_se(unit_get_members_mask(grandchild) == 0); assert_se(unit_get_members_mask(parent_deep) == 0); - assert_se(unit_get_members_mask(parent) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY)); - assert_se(unit_get_members_mask(root) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO | CGROUP_MEMORY)); + assert_se(unit_get_members_mask(parent) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY)); + assert_se(unit_get_members_mask(root) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY)); /* Verify aggregation of sibling masks. */ - assert_se(unit_get_siblings_mask(son) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY)); - assert_se(unit_get_siblings_mask(daughter) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY)); + assert_se(unit_get_siblings_mask(son) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY)); + assert_se(unit_get_siblings_mask(daughter) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY)); assert_se(unit_get_siblings_mask(grandchild) == 0); - assert_se(unit_get_siblings_mask(parent_deep) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY)); - assert_se(unit_get_siblings_mask(parent) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO | CGROUP_MEMORY)); - assert_se(unit_get_siblings_mask(root) == (CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO | CGROUP_MEMORY)); + assert_se(unit_get_siblings_mask(parent_deep) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY)); + assert_se(unit_get_siblings_mask(parent) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY)); + assert_se(unit_get_siblings_mask(root) == (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY)); /* Verify aggregation of target masks. */ - assert_se(unit_get_target_mask(son) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY) & m->cgroup_supported)); - assert_se(unit_get_target_mask(daughter) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY) & m->cgroup_supported)); + assert_se(unit_get_target_mask(son) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY) & m->cgroup_supported)); + assert_se(unit_get_target_mask(daughter) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY) & m->cgroup_supported)); assert_se(unit_get_target_mask(grandchild) == 0); - assert_se(unit_get_target_mask(parent_deep) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_MEMORY) & m->cgroup_supported)); - assert_se(unit_get_target_mask(parent) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO | CGROUP_MEMORY) & m->cgroup_supported)); - assert_se(unit_get_target_mask(root) == ((CGROUP_CPU | CGROUP_CPUACCT | CGROUP_BLKIO | CGROUP_MEMORY) & m->cgroup_supported)); + assert_se(unit_get_target_mask(parent_deep) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_MEMORY) & m->cgroup_supported)); + assert_se(unit_get_target_mask(parent) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY) & m->cgroup_supported)); + assert_se(unit_get_target_mask(root) == ((CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY) & m->cgroup_supported)); manager_free(m); diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c index ecc9d70bf..4ecf09a29 100644 --- a/src/test/test-cgroup-util.c +++ b/src/test/test-cgroup-util.c @@ -295,6 +295,17 @@ static void test_shift_path(void) { test_shift_path_one("/foobar/waldo", "/fuckfuck", "/foobar/waldo"); } +static void test_mask_supported(void) { + + CGroupMask m; + CGroupController c; + + assert_se(cg_mask_supported(&m) >= 0); + + for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) + printf("'%s' is supported: %s\n", cgroup_controller_to_string(c), yes_no(m & CGROUP_CONTROLLER_TO_MASK(c))); +} + int main(void) { test_path_decode_unit(); test_path_get_unit(); @@ -309,6 +320,7 @@ int main(void) { test_controller_is_valid(); test_slice_to_path(); test_shift_path(); + TEST_REQ_RUNNING_SYSTEMD(test_mask_supported()); return 0; } diff --git a/src/test/test-cgroup.c b/src/test/test-cgroup.c index 4be69a408..37b1c3554 100644 --- a/src/test/test-cgroup.c +++ b/src/test/test-cgroup.c @@ -56,26 +56,26 @@ int main(int argc, char*argv[]) { assert_se(path_equal(path, "/sys/fs/cgroup/systemd/test-b/test-d")); free(path); - assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, "/test-a", false) > 0); - assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, "/test-b", false) > 0); - assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", false) > 0); - assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", false) == 0); + assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, "/test-a") > 0); + assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, "/test-b") > 0); + assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a") > 0); + assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b") == 0); assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, false, false, false, NULL) == 0); assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, false, false, false, NULL) > 0); assert_se(cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", SYSTEMD_CGROUP_CONTROLLER, "/test-a", false, false) > 0); - assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", false) == 0); - assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", false) > 0); + assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0); + assert_se(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b") > 0); assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-a", 0, false, false, false, NULL) > 0); assert_se(cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, false, false, false, NULL) == 0); cg_trim(SYSTEMD_CGROUP_CONTROLLER, "/", false); - assert_se(cg_delete(SYSTEMD_CGROUP_CONTROLLER, "/test-b") < 0); - assert_se(cg_delete(SYSTEMD_CGROUP_CONTROLLER, "/test-a") >= 0); + assert_se(cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, "/test-b") < 0); + assert_se(cg_rmdir(SYSTEMD_CGROUP_CONTROLLER, "/test-a") >= 0); assert_se(cg_split_spec("foobar:/", &c, &p) == 0); assert_se(streq(c, "foobar")); diff --git a/src/test/test-engine.c b/src/test/test-engine.c index a7ab21a41..6596069ad 100644 --- a/src/test/test-engine.c +++ b/src/test/test-engine.c @@ -38,7 +38,7 @@ int main(int argc, char *argv[]) { /* prepare the test */ assert_se(set_unit_path(TEST_DIR) >= 0); r = manager_new(MANAGER_USER, true, &m); - if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT)) { + if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT, -ENOEXEC)) { printf("Skipping test: manager_new: %s", strerror(-r)); return EXIT_TEST_SKIP; } diff --git a/src/test/test-path.c b/src/test/test-path.c index 5d190378f..676c9f179 100644 --- a/src/test/test-path.c +++ b/src/test/test-path.c @@ -40,7 +40,7 @@ static int setup_test(Manager **m) { assert_se(m); r = manager_new(MANAGER_USER, true, &tmp); - if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT)) { + if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT, -ENOEXEC)) { printf("Skipping test: manager_new: %s", strerror(-r)); return -EXIT_TEST_SKIP; } diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c index f915539e0..ebc9110c4 100644 --- a/src/test/test-sched-prio.c +++ b/src/test/test-sched-prio.c @@ -35,7 +35,7 @@ int main(int argc, char *argv[]) { /* prepare the test */ assert_se(set_unit_path(TEST_DIR) >= 0); r = manager_new(MANAGER_USER, true, &m); - if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT)) { + if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT, -ENOEXEC)) { printf("Skipping test: manager_new: %s", strerror(-r)); return EXIT_TEST_SKIP; } diff --git a/src/test/test-util.c b/src/test/test-util.c index dff38ab6f..8ceb71f22 100644 --- a/src/test/test-util.c +++ b/src/test/test-util.c @@ -270,6 +270,9 @@ static void test_parse_pid(void) { r = parse_pid("0xFFFFFFFFFFFFFFFFF", &pid); assert_se(r == -ERANGE); assert_se(pid == 65); + + r = parse_pid("junk", &pid); + assert_se(r == -EINVAL); } static void test_parse_uid(void) { @@ -282,6 +285,9 @@ static void test_parse_uid(void) { r = parse_uid("65535", &uid); assert_se(r == -ENXIO); + + r = parse_uid("asdsdas", &uid); + assert_se(r == -EINVAL); } static void test_safe_atou16(void) { diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c index 21d6ee4c0..6de9e246f 100644 --- a/src/timedate/timedated.c +++ b/src/timedate/timedated.c @@ -68,32 +68,15 @@ static int context_read_data(Context *c) { assert(c); - r = readlink_malloc("/etc/localtime", &t); - if (r < 0) { - if (r == -EINVAL) - log_warning("/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/."); - else - log_warning_errno(r, "Failed to get target of /etc/localtime: %m"); - } else { - const char *e; - - e = path_startswith(t, "/usr/share/zoneinfo/"); - if (!e) - e = path_startswith(t, "../usr/share/zoneinfo/"); - - if (!e) - log_warning("/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/."); - else { - c->zone = strdup(e); - if (!c->zone) - return log_oom(); - } - } + r = get_timezone(&t); + if (r == -EINVAL) + log_warning_errno(r, "/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/."); + else if (r < 0) + log_warning_errno(r, "Failed to get target of /etc/localtime: %m"); - if (isempty(c->zone)) { - free(c->zone); - c->zone = NULL; - } + free(c->zone); + c->zone = t; + t = NULL; c->local_rtc = clock_is_localtime() > 0; @@ -378,6 +361,7 @@ static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error * m, CAP_SYS_TIME, "org.freedesktop.timedate1.set-timezone", + NULL, interactive, UID_INVALID, &c->polkit_registry, @@ -445,6 +429,7 @@ static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error m, CAP_SYS_TIME, "org.freedesktop.timedate1.set-local-rtc", + NULL, interactive, UID_INVALID, &c->polkit_registry, @@ -560,6 +545,7 @@ static int method_set_time(sd_bus_message *m, void *userdata, sd_bus_error *erro m, CAP_SYS_TIME, "org.freedesktop.timedate1.set-time", + NULL, interactive, UID_INVALID, &c->polkit_registry, @@ -618,6 +604,7 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error m, CAP_SYS_TIME, "org.freedesktop.timedate1.set-ntp", + NULL, interactive, UID_INVALID, &c->polkit_registry, diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c index 6e7e1271f..589f1f782 100644 --- a/src/udev/udev-builtin-net_id.c +++ b/src/udev/udev-builtin-net_id.c @@ -280,8 +280,16 @@ static int names_pci(struct udev_device *dev, struct netnames *names) { assert(names); parent = udev_device_get_parent(dev); + + /* there can only ever be one virtio bus per parent device, so we can + safely ignore any virtio buses. see + */ + while (parent && streq_ptr("virtio", udev_device_get_subsystem(parent))) + parent = udev_device_get_parent(parent); + if (!parent) return -ENOENT; + /* check if our direct parent is a PCI device with no other bus in-between */ if (streq_ptr("pci", udev_device_get_subsystem(parent))) { names->type = NET_PCI; diff --git a/src/udev/udev-builtin-uaccess.c b/src/udev/udev-builtin-uaccess.c index 43bab8af6..7bf4e7f24 100644 --- a/src/udev/udev-builtin-uaccess.c +++ b/src/udev/udev-builtin-uaccess.c @@ -45,7 +45,7 @@ static int builtin_uaccess(struct udev_device *dev, int argc, char *argv[], bool seat = "seat0"; r = sd_seat_get_active(seat, NULL, &uid); - if (r == -ENOENT) { + if (r == -ENXIO || r == -ENODATA) { /* No active session on this seat */ r = 0; goto finish; diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 28ac44fb8..f354ced2e 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -1706,7 +1706,7 @@ int main(int argc, char *argv[]) { by PID1. otherwise we are not guaranteed to have a dedicated cgroup */ r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup); if (r < 0) { - if (r == -ENOENT) + if (r == -ENOENT || r == -ENOEXEC) log_debug_errno(r, "did not find dedicated cgroup: %m"); else log_warning_errno(r, "failed to get cgroup: %m"); diff --git a/test/udev-test.pl b/test/udev-test.pl index 64d7f9344..0a8930357 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -1487,7 +1487,7 @@ sub run_test { # due to mknod restrictions if (!($<==0)) { print "Must have root permissions to run properly.\n"; - exit; + exit($EXIT_TEST_SKIP); } # skip the test when running in a container diff --git a/units/systemd-bus-proxyd.service.m4.in b/units/systemd-bus-proxyd.service.m4.in index 64f5ac7d1..e75cdb1a5 100644 --- a/units/systemd-bus-proxyd.service.m4.in +++ b/units/systemd-bus-proxyd.service.m4.in @@ -18,3 +18,8 @@ PrivateDevices=yes PrivateNetwork=yes ProtectSystem=full ProtectHome=yes + +# The proxy manages connections of all users, so it needs an elevated file +# limit. It does proper per-user accounting (indirectly via kdbus), therefore, +# the effective per-user limits stay the same. +LimitNOFILE=16384 diff --git a/units/systemd-nspawn@.service.in b/units/systemd-nspawn@.service.in index 074b916d3..6b86e0a7f 100644 --- a/units/systemd-nspawn@.service.in +++ b/units/systemd-nspawn@.service.in @@ -13,7 +13,7 @@ Before=machines.target After=network.target [Service] -ExecStart=@bindir@/systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --network-veth --machine=%I +ExecStart=@bindir@/systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --network-veth --settings=override --machine=%I KillMode=mixed Type=notify RestartForceExitStatus=133 diff --git a/xorg/50-systemd-user.sh b/xorg/50-systemd-user.sh index f4df13b61..4d4976722 100755 --- a/xorg/50-systemd-user.sh +++ b/xorg/50-systemd-user.sh @@ -1,3 +1,7 @@ #!/bin/sh systemctl --user import-environment DISPLAY XAUTHORITY + +if which dbus-update-activation-environment >/dev/null 2>&1; then + dbus-update-activation-environment DISPLAY XAUTHORITY +fi