]> git.proxmox.com Git - systemd.git/commitdiff
Imported Upstream version 226
authorMartin Pitt <martin.pitt@ubuntu.com>
Wed, 9 Sep 2015 05:26:01 +0000 (07:26 +0200)
committerMartin Pitt <martin.pitt@ubuntu.com>
Wed, 9 Sep 2015 05:26:01 +0000 (07:26 +0200)
233 files changed:
CODING_STYLE
Makefile-man.am
Makefile.am
NEWS
README
TODO
configure.ac
hwdb/20-bluetooth-vendor-product.hwdb
hwdb/70-mouse.hwdb
hwdb/70-pointingstick.hwdb
m4/ax_compiler_vendor.m4 [new file with mode: 0644]
man/networkctl.xml
man/nss-myhostname.xml
man/nss-resolve.xml
man/os-release.xml
man/sd_get_seats.xml
man/sd_login_monitor_new.xml
man/sd_machine_get_class.xml
man/sd_pid_get_session.xml
man/sd_seat_get_active.xml
man/sd_session_is_active.xml
man/sd_uid_get_state.xml
man/standard-conf.xml
man/systemctl.xml
man/systemd-cgtop.xml
man/systemd-detect-virt.xml
man/systemd-gpt-auto-generator.xml
man/systemd-nspawn.xml
man/systemd-path.xml
man/systemd-resolved.service.xml
man/systemd-run.xml
man/systemd.netdev.xml
man/systemd.network.xml
man/systemd.nspawn.xml [new file with mode: 0644]
man/systemd.unit.xml
network/80-container-host0.network
po/POTFILES.in
po/pl.po
shell-completion/bash/networkctl [new file with mode: 0644]
shell-completion/bash/systemctl.in
shell-completion/bash/systemd-analyze
shell-completion/bash/systemd-path [new file with mode: 0644]
shell-completion/bash/systemd-run
src/analyze/analyze.c
src/basic/audit.c
src/basic/cgroup-util.c
src/basic/cgroup-util.h
src/basic/conf-files.h
src/basic/copy.c
src/basic/def.h
src/basic/macro.h
src/basic/missing.h
src/basic/process-util.c
src/basic/refcnt.h
src/basic/ring.h
src/basic/selinux-util.c
src/basic/set.h
src/basic/special.h
src/basic/terminal-util.c
src/basic/terminal-util.h
src/basic/time-util.c
src/basic/time-util.h
src/basic/unit-name.c
src/basic/unit-name.h
src/basic/util.c
src/basic/util.h
src/basic/virt.c
src/boot/efi/boot.c
src/cgls/cgls.c
src/cgtop/cgtop.c
src/core/automount.c
src/core/busname.c
src/core/cgroup.c
src/core/cgroup.h
src/core/dbus-cgroup.c
src/core/dbus-unit.c
src/core/dbus.c
src/core/dbus.h
src/core/device.c
src/core/execute.h
src/core/killall.c
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/load-fragment.h
src/core/main.c
src/core/manager.c
src/core/manager.h
src/core/mount-setup.c
src/core/mount.c
src/core/path.c
src/core/scope.c
src/core/selinux-access.c
src/core/selinux-setup.c
src/core/service.c
src/core/slice.c
src/core/snapshot.c
src/core/socket.c
src/core/swap.c
src/core/target.c
src/core/timer.c
src/core/unit.c
src/core/unit.h
src/gpt-auto-generator/gpt-auto-generator.c
src/hostname/hostnamed.c
src/import/importd.c
src/journal/journald-kmsg.c
src/libsystemd-network/dhcp-internal.h
src/libsystemd-network/dhcp-lease-internal.h
src/libsystemd-network/dhcp-option.c
src/libsystemd-network/dhcp-protocol.h
src/libsystemd-network/dhcp-server-internal.h
src/libsystemd-network/dhcp6-lease-internal.h
src/libsystemd-network/dhcp6-network.c
src/libsystemd-network/dhcp6-option.c
src/libsystemd-network/lldp-internal.c
src/libsystemd-network/lldp-internal.h
src/libsystemd-network/network-internal.c
src/libsystemd-network/network-internal.h
src/libsystemd-network/sd-dhcp-client.c
src/libsystemd-network/sd-dhcp-lease.c
src/libsystemd-network/sd-dhcp-server.c
src/libsystemd-network/sd-dhcp6-client.c
src/libsystemd-network/sd-dhcp6-lease.c
src/libsystemd-network/sd-icmp6-nd.c
src/libsystemd-network/sd-ipv4ll.c
src/libsystemd-network/sd-pppoe.c
src/libsystemd-network/test-dhcp-client.c
src/libsystemd-network/test-dhcp-option.c
src/libsystemd-network/test-dhcp-server.c
src/libsystemd-network/test-dhcp6-client.c
src/libsystemd-network/test-ipv4ll.c
src/libsystemd/libsystemd.sym
src/libsystemd/sd-bus/bus-container.c
src/libsystemd/sd-bus/bus-creds.c
src/libsystemd/sd-bus/bus-match.c
src/libsystemd/sd-bus/bus-message.c
src/libsystemd/sd-bus/bus-objects.c
src/libsystemd/sd-bus/sd-bus.c
src/libsystemd/sd-bus/test-bus-creds.c
src/libsystemd/sd-event/sd-event.c
src/libsystemd/sd-event/test-event.c
src/libsystemd/sd-hwdb/sd-hwdb.c
src/libsystemd/sd-login/sd-login.c
src/libsystemd/sd-login/test-login.c
src/libsystemd/sd-network/sd-network.c
src/libsystemd/sd-resolve/sd-resolve.c
src/libsystemd/sd-resolve/test-resolve.c
src/locale/localed.c
src/login/70-power-switch.rules
src/login/loginctl.c
src/login/logind-dbus.c
src/login/logind-seat-dbus.c
src/login/logind-session-dbus.c
src/login/logind-user-dbus.c
src/login/pam_systemd.c
src/machine/image-dbus.c
src/machine/machine-dbus.c
src/machine/machine.c
src/machine/machinectl.c
src/machine/machined-dbus.c
src/network/networkctl.c
src/network/networkd-address-pool.c
src/network/networkd-address-pool.h [new file with mode: 0644]
src/network/networkd-address.c
src/network/networkd-address.h [new file with mode: 0644]
src/network/networkd-dhcp4.c
src/network/networkd-fdb.c
src/network/networkd-fdb.h [new file with mode: 0644]
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-manager.c
src/network/networkd-netdev-bond.h
src/network/networkd-netdev-gperf.gperf
src/network/networkd-netdev-tunnel.c
src/network/networkd-netdev-tunnel.h
src/network/networkd-netdev.c
src/network/networkd-netdev.h
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h [new file with mode: 0644]
src/network/networkd-route.c
src/network/networkd-route.h [new file with mode: 0644]
src/network/networkd-util.c [new file with mode: 0644]
src/network/networkd-util.h [new file with mode: 0644]
src/network/networkd.h
src/nspawn/.gitignore [new file with mode: 0644]
src/nspawn/nspawn-cgroup.c [new file with mode: 0644]
src/nspawn/nspawn-cgroup.h [new file with mode: 0644]
src/nspawn/nspawn-expose-ports.c [new file with mode: 0644]
src/nspawn/nspawn-expose-ports.h [new file with mode: 0644]
src/nspawn/nspawn-gperf.gperf [new file with mode: 0644]
src/nspawn/nspawn-mount.c [new file with mode: 0644]
src/nspawn/nspawn-mount.h [new file with mode: 0644]
src/nspawn/nspawn-network.c [new file with mode: 0644]
src/nspawn/nspawn-network.h [new file with mode: 0644]
src/nspawn/nspawn-register.c [new file with mode: 0644]
src/nspawn/nspawn-register.h [new file with mode: 0644]
src/nspawn/nspawn-settings.c [new file with mode: 0644]
src/nspawn/nspawn-settings.h [new file with mode: 0644]
src/nspawn/nspawn-setuid.c [new file with mode: 0644]
src/nspawn/nspawn-setuid.h [new file with mode: 0644]
src/nspawn/nspawn.c
src/resolve/resolved-dns-packet.h
src/run/run.c
src/shared/Makefile [new symlink]
src/shared/bus-util.c
src/shared/bus-util.h
src/shared/cgroup-show.c
src/shared/conf-parser.c
src/shared/conf-parser.h
src/shared/machine-image.c
src/shared/pager.c
src/systemctl/systemctl.c
src/systemd/sd-dhcp-lease.h
src/systemd/sd-dhcp-server.h
src/systemd/sd-login.h
src/systemd/sd-network.h
src/systemd/sd-resolve.h
src/test/test-cgroup-mask.c
src/test/test-cgroup-util.c
src/test/test-cgroup.c
src/test/test-engine.c
src/test/test-path.c
src/test/test-sched-prio.c
src/test/test-util.c
src/timedate/timedated.c
src/udev/udev-builtin-net_id.c
src/udev/udev-builtin-uaccess.c
src/udev/udevd.c
test/udev-test.pl
units/systemd-bus-proxyd.service.m4.in
units/systemd-nspawn@.service.in
xorg/50-systemd-user.sh

index a96ddd35980fd11aa2cf81edfee01f77d1f1a496..f13f9becbc0839d246ebffa44c34bcfee6963f72 100644 (file)
   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 <stdio.h>
-          #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
index 65287371b921edfdd7596ccd95732247f15f11f0..3b8038611b7e96fd448914af2b5695fe828ce19a 100644 (file)
@@ -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 \
index c2973c0a67d7ea81e9178c461ac94933f527e775..8e0448f43321c44b4429b3d105f31415ddebd581 100644 (file)
@@ -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 1baa9aa1125e95106fbd8f1bb1aa09f5433c12ae..6803c6588ffb97785e2db8cd889726596834ca53 100644 (file)
--- a/NEWS
+++ b/NEWS
 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 d84bf8ea69e05ef3d115656c2eb815ef541449e9..27cc9a01681829af75365e8c06272be4ffbe7dc9 100644 (file)
--- 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 ce50f1655528586dfa568181cb091da2e7ad509a..4fdecebd0fa4fbb77e78d2cc4b37242f69721fd6 100644 (file)
--- 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:
index d9ab3624dd05264f73a791bac64a9fb2284b2ea0..2024939ad0b1eeb8493bb26a6e82e1df61c14254 100644 (file)
@@ -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}
index 5835d9a103240205a7a12efba64254970a3f6cfe..5a7f28729d4e36184ef2145fc80f156c16fab98e 100644 (file)
@@ -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
index a8bf055ba1fb95a9e55bc920e4a3720f2a9543e5..5a9034040c0b440a6f251b4bcc9d03700f4196ef 100644 (file)
@@ -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
 ##########################################
index 775ec7fea5b7128d9882c5ee6e14ff10d9f1436e..8e674b11233837ca739af02450c3b9fb12d1ab0e 100644 (file)
@@ -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 (file)
index 0000000..39ca3c0
--- /dev/null
@@ -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 <stevenj@alum.mit.edu>
+#   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 <http://www.gnu.org/licenses/>.
+#
+#   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`
+ ])
+])
index 388afbed934b1d7f5626786cbff076ff9346b783..46dab58d612d0077ba3b66c56b74bc099f7ecb43 100644 (file)
@@ -87,6 +87,7 @@
 
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
+      <xi:include href="standard-options.xml" xpointer="no-legend" />
       <xi:include href="standard-options.xml" xpointer="no-pager" />
 
     </variablelist>
index b7b7e1b555e59aa26e397c7f192f4c466787de47..4481fdf8cb8aa4b77dc381ff4cac0b6b1fca0b1a 100644 (file)
     <para>Here's an example <filename>/etc/nsswitch.conf</filename>
     file, that enables <command>myhostname</command> correctly:</para>
 
-<programlisting>passwd:         compat
-group:          compat
+<programlisting>passwd:         compat mymachines
+group:          compat mymachines
 shadow:         compat
 
 hosts:          files resolve mymachines <command>myhostname</command>
index dd402b359c967570c62d5bb78e31bc921df8242f..7d291b83c19908746fb08151ac53e47db1b7ca6c 100644 (file)
@@ -82,8 +82,8 @@
     <para>Here's an example <filename>/etc/nsswitch.conf</filename>
     file, that enables <command>resolve</command> correctly:</para>
 
-<programlisting>passwd:         compat
-group:          compat
+<programlisting>passwd:         compat mymachines
+group:          compat mymachines
 shadow:         compat
 
 hosts:          files <command>resolve</command> mymachines myhostname
index 4ca2e597063fc5ce0b9df484b662f00e18e66963..d2e2598204df7a08e213fe22b518763c54960f17 100644 (file)
       <varlistentry>
         <term><varname>CPE_NAME=</varname></term>
 
-        <listitem><para>A CPE name for the operating system, following
-        the <ulink url="https://cpe.mitre.org/specification/">Common
+        <listitem><para>A CPE name for the operating system, in URI
+        binding syntax, following the
+        <ulink url="http://scap.nist.gov/specifications/cpe/">Common
         Platform Enumeration Specification</ulink> as proposed by the
-        MITRE Corporation. This field is optional. Example:
+        NIST. This field is optional. Example:
         <literal>CPE_NAME="cpe:/o:fedoraproject:fedora:17"</literal>
         </para></listitem>
       </varlistentry>
index 4390d36ebe523d546cbc2ec3edecb3f612f9bbe8..f1981f7ea21ad9026b1bca9d015fccb4a53a9051 100644 (file)
     errno-style error code.</para>
   </refsect1>
 
+
+  <refsect1>
+    <title>Errors</title>
+
+    <para>Returned errors may indicate the following problems:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><constant>-EINVAL</constant></term>
+
+        <listitem><para>An input parameter was invalid (out of range,
+        or NULL, where that's not accepted).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ENOMEM</constant></term>
+
+        <listitem><para>Memory allocation failed.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>Notes</title>
 
index a7b47a3207f7635ed7e7670930aa58dccc091ad9..a8854dd5902f0df65f13ac7ae020d5ac5e57ef97 100644 (file)
     is no timeout to wait for this will fill in <constant>(uint64_t)
     -1</constant> instead. Note that <function>poll()</function> 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:</para>
 
     <programlisting>uint64_t t;
 int msec;
 sd_login_monitor_get_timeout(m, &amp;t);
 if (t == (uint64_t) -1)
-  msec = -1;
+         msec = -1;
 else {
-  struct timespec ts;
-  uint64_t n;
-  clock_getttime(CLOCK_MONOTONIC, &amp;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, &amp;ts);
+         n = (uint64_t) ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
+         msec = t > n ? (int) ((t - n + 999) / 1000) : 0;
 }</programlisting>
 
     <para>The code above does not do any error checking for brevity's
@@ -203,6 +203,29 @@ else {
     always returns <constant>NULL</constant>.</para>
   </refsect1>
 
+  <refsect1>
+    <title>Errors</title>
+
+    <para>Returned errors may indicate the following problems:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><constant>-EINVAL</constant></term>
+
+        <listitem><para>An input parameter was invalid (out of range,
+        or NULL, where that's not accepted). The specified category to
+        watch is not known.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ENOMEM</constant></term>
+
+        <listitem><para>Memory allocation failed.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>Notes</title>
 
index 5b881ccea1f63f1743acfc568d695f3a9b0e523b..9ad7f3fc668a60103f15e73a3149061213d5b396 100644 (file)
@@ -56,7 +56,7 @@
       <funcprototype>
         <funcdef>int <function>sd_machine_get_class</function></funcdef>
         <paramdef>const char* <parameter>machine</parameter></paramdef>
-        <paramdef>char *<parameter>class</parameter></paramdef>
+        <paramdef>char **<parameter>class</parameter></paramdef>
       </funcprototype>
 
       <funcprototype>
     code.</para>
   </refsect1>
 
+  <refsect1>
+    <title>Errors</title>
+
+    <para>Returned errors may indicate the following problems:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><constant>-ENXIO</constant></term>
+
+        <listitem><para>The specified machine does not exist or is currently not running.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-EINVAL</constant></term>
+
+        <listitem><para>An input parameter was invalid (out of range,
+        or NULL, where that's not accepted).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ENOMEM</constant></term>
+
+        <listitem><para>Memory allocation failed.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>Notes</title>
 
index 9c6706caf8115750bf9e45f5273e8f2e6646eeb9..035effcaa90f1ded4c26aa6c3f5c66d3ac2c428e 100644 (file)
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*-nxml-*-->
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
 
@@ -50,6 +50,7 @@
     <refname>sd_pid_get_machine_name</refname>
     <refname>sd_pid_get_slice</refname>
     <refname>sd_pid_get_user_slice</refname>
+    <refname>sd_pid_get_cgroup</refname>
     <refname>sd_peer_get_session</refname>
     <refname>sd_peer_get_unit</refname>
     <refname>sd_peer_get_user_unit</refname>
@@ -57,6 +58,7 @@
     <refname>sd_peer_get_machine_name</refname>
     <refname>sd_peer_get_slice</refname>
     <refname>sd_peer_get_user_slice</refname>
+    <refname>sd_peer_get_cgroup</refname>
     <refpurpose>Determine session, unit, owner of a session,
     container/VM or slice of a specific PID or socket
     peer</refpurpose>
         <paramdef>char **<parameter>slice</parameter></paramdef>
       </funcprototype>
 
+      <funcprototype>
+        <funcdef>int <function>sd_pid_get_cgroup</function></funcdef>
+        <paramdef>pid_t <parameter>pid</parameter></paramdef>
+        <paramdef>char **<parameter>cgroup</parameter></paramdef>
+      </funcprototype>
+
       <funcprototype>
         <funcdef>int <function>sd_peer_get_session</function></funcdef>
         <paramdef>int <parameter>fd</parameter></paramdef>
         <paramdef>int <parameter>fd</parameter></paramdef>
         <paramdef>char **<parameter>slice</parameter></paramdef>
       </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_peer_get_cgroup</function></funcdef>
+        <paramdef>int <parameter>fd</parameter></paramdef>
+        <paramdef>char **<parameter>cgroup</parameter></paramdef>
+      </funcprototype>
     </funcsynopsis>
   </refsynopsisdiv>
 
     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
     <citerefentry
     project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     call after use.</para>
     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 <citerefentry
+    will fail with -ENODATA (More specifically: this call will not
+    work for kernel threads.) The returned string needs to be freed
+    with the libc <citerefentry
     project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     call after use.</para>
 
     multiple login sessions of the same user, where
     <function>sd_pid_get_session()</function> 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.</para>
+    of a user this function will fail with -ENODATA.</para>
 
     <para><function>sd_pid_get_machine_name()</function> may be used
     to determine the name of the VM or container is a member of. The
     <citerefentry
     project='man-pages'><refentrytitle>free</refentrytitle><manvolnum>3</manvolnum></citerefentry>
     call after use. For processes not part of a VM or containers this
-    function fails with -ENXIO.</para>
+    function fails with -ENODATA.</para>
 
     <para><function>sd_pid_get_slice()</function> may be used to
     determine the slice unit the process is a member of. See
     returns the user slice (as managed by the user's systemd instance)
     of a process.</para>
 
+    <para><function>sd_pid_get_cgroup()</function> 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
+    <filename>/sys/fs/cgroup/</filename> (if the unified control group
+    setup is used), or
+    <filename>/sys/fs/cgroup/<replaceable>HIERARCHY</replaceable>/</filename>
+    (if the legacy multi-hierarchy control group setup is used).</para>
+
     <para>If the <varname>pid</varname> parameter of any of these
     functions is passed as 0, the operation is executed for the
     calling process.</para>
     <function>sd_peer_get_user_unit()</function>,
     <function>sd_peer_get_owner_uid()</function>,
     <function>sd_peer_get_machine_name()</function>,
-    <function>sd_peer_get_slice()</function> and
-    <function>sd_peer_get_user_slice()</function> 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
-    <filename>/proc</filename>, and hence are not suitable for
-    authorization purposes, as they are subject to races.</para>
+    <function>sd_peer_get_slice()</function>,
+    <function>sd_peer_get_user_slice()</function> and
+    <function>sd_peer_get_cgroup()</function> 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 <filename>/proc</filename>,
+    and hence are not suitable for authorization purposes, as they are
+    subject to races.</para>
   </refsect1>
 
   <refsect1>
     <variablelist>
 
       <varlistentry>
-        <term><constant>-ENXIO</constant></term>
+        <term><constant>-ESRCH</constant></term>
+
+        <listitem><para>The specified PID does not refer to a running
+        process.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-BADF</constant></term>
+
+        <listitem><para>The specified socket file descriptor was
+        invalid.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ENODATA</constant></term>
 
         <listitem><para>Given field is not specified for the described
         process or peer.</para>
       </varlistentry>
 
       <varlistentry>
-        <term><constant>-ESRCH</constant></term>
+        <term><constant>-EINVAL</constant></term>
 
-        <listitem><para>The specified PID does not refer to a running
-        process.</para>
-        </listitem>
+        <listitem><para>An input parameter was invalid (out of range,
+        or NULL, where that's not accepted).</para></listitem>
       </varlistentry>
 
       <varlistentry>
index 3c57ec9ea409f0dad88797bce124252be184e7ae..4d3e0822e0fb2bf1a409a488edb02e86fa671dbc 100644 (file)
     errno-style error code.</para>
   </refsect1>
 
+  <refsect1>
+    <title>Errors</title>
+
+    <para>Returned errors may indicate the following problems:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><constant>-ENODATA</constant></term>
+
+        <listitem><para>Given field is not specified for the described
+        seat.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ENXIO</constant></term>
+
+        <listitem><para>The specified seat is unknown.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-EINVAL</constant></term>
+
+        <listitem><para>An input parameter was invalid (out of range,
+        or NULL, where that's not accepted).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ENOMEM</constant></term>
+
+        <listitem><para>Memory allocation failed.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>Notes</title>
 
index 4ca3a6c15057ae24b715c0de9fbf9316fbc57dd2..7de9523789c4198cb86cfcab2347f46db7cdd95b 100644 (file)
     negative errno-style error code.</para>
   </refsect1>
 
+  <refsect1>
+    <title>Errors</title>
+
+    <para>Returned errors may indicate the following problems:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><constant>-ENXIO</constant></term>
+
+        <listitem><para>The specified session does not exist.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ENODATA</constant></term>
+
+        <listitem><para>Given field is not specified for the described
+        session.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-EINVAL</constant></term>
+
+        <listitem><para>An input parameter was invalid (out of range,
+        or NULL, where that's not accepted).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ENOMEM</constant></term>
+
+        <listitem><para>Memory allocation failed.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>Notes</title>
 
index b158f3528cf193e2b3f82ba7b48ca83fb2d1511c..13ddf08c65156f02cc5dcc69a751b730ce6b7938 100644 (file)
     errno-style error code.</para>
   </refsect1>
 
+  <refsect1>
+    <title>Errors</title>
+
+    <para>Returned errors may indicate the following problems:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><constant>-ENODATA</constant></term>
+
+        <listitem><para>Given field is not specified for the described
+        user.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ENXIO</constant></term>
+
+        <listitem><para>The specified seat is unknown.</para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-EINVAL</constant></term>
+
+        <listitem><para>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.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>-ENOMEM</constant></term>
+
+        <listitem><para>Memory allocation failed.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>Notes</title>
 
index 004f53f70c8b28e04243c19da2c3f18bee0c200b..ffc6f76294afae31de0660d700f3166a4e4117e3 100644 (file)
@@ -30,7 +30,9 @@
     the vendor, the recommended way is to place a symlink to
     <filename>/dev/null</filename> in the configuration directory in
     <filename>/etc/</filename>, with the same filename as the vendor
-    configuration file.</para>
+    configuration file. If the vendor configuration file is included in
+    the initrd image, the image has to be regenerated.</para>
+
   </refsection>
 
   <refsection id='main-conf'>
index 20d143741b7ac390b4b5f5e20f91665db71f14f1..37ba4ab6dedb359fb1a125f00a2a118d11ba77c0 100644 (file)
@@ -918,6 +918,7 @@ kobject-uevent 1 systemd-udevd-kernel.socket systemd-udevd.service
             <varname>RequiresOverridable=</varname>,
             <varname>Requisite=</varname>,
             <varname>RequisiteOverridable=</varname>,
+            <varname>ConsistsOf=</varname>,
             <varname>Wants=</varname>, <varname>BindsTo=</varname>
             dependencies. If no unit is specified,
             <filename>default.target</filename> is implied.</para>
index d4b041a1f96f9266a86304aa066abbddd8e45d51..0e0ea3ba7ab07da2745b210ca976dbf4e62b3e0e 100644 (file)
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*-nxml-*-->
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
 
     regular intervals (by default every 1s), similar in style to
     <citerefentry project='man-pages'><refentrytitle>top</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
 
-    <para>If <command>systemd-cgtop</command> is not connected to a tty, no
-    column headers are printed and the default is to only run one iteration.
-    The <varname>--iterations</varname> argument, if given, is still honored.
-    This mode is suitable for scripting.</para>
+    <para>If <command>systemd-cgtop</command> is not connected to a
+    tty, no column headers are printed and the default is to only run
+    one iteration.  The <varname>--iterations=</varname> argument, if
+    given, is honored. This mode is suitable for scripting.</para>
 
     <para>Resource usage is only accounted for control groups in the
     relevant hierarchy, i.e. CPU usage is only accounted for control
     <variablelist>
       <varlistentry>
         <term><option>-p</option></term>
+        <term><option>--order=path</option></term>
 
         <listitem><para>Order by control group
         path name.</para></listitem>
 
       <varlistentry>
         <term><option>-t</option></term>
+        <term><option>--order=tasks</option></term>
 
-        <listitem><para>Order by number of tasks in control group
-        (i.e. threads and processes).</para></listitem>
+        <listitem><para>Order by number of processes in control group.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>-c</option></term>
+        <term><option>--order=cpu</option></term>
 
         <listitem><para>Order by CPU load.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>-m</option></term>
+        <term><option>--order=memory</option></term>
 
         <listitem><para>Order by memory usage.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>-i</option></term>
+        <term><option>--order=io</option></term>
 
         <listitem><para>Order by disk I/O load.</para></listitem>
       </varlistentry>
 
         <listitem><para>Run in "batch" mode: do not accept input and
         run until the iteration limit set with
-        <option>--iterations</option> is exhausted or until killed.
+        <option>--iterations=</option> is exhausted or until killed.
         This mode could be useful for sending output from
         <command>systemd-cgtop</command> to other programs or to a
         file.</para></listitem>
         numbers.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--cpu=percentage</option></term>
+        <term><option>--cpu=time</option></term>
+
+        <listitem><para>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 <keycap>%</keycap> key.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>-k</option></term>
+
+        <listitem><para>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 <keycap>k</keycap> key.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><option>--recursive=</option></term>
+
+        <listitem><para>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 <literal>yes</literal>. 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
+        <keycap>r</keycap> key.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>-n</option></term>
         <term><option>--iterations=</option></term>
 
-        <listitem><para>Perform only this many iterations. A value of 0
-        indicates that the program should run indefinitely.</para></listitem>
+        <listitem><para>Perform only this many iterations. A value of
+        0 indicates that the program should run
+        indefinitely.</para></listitem>
       </varlistentry>
 
       <varlistentry>
         <term><option>--delay=</option></term>
 
         <listitem><para>Specify refresh delay in seconds (or if one of
-        <literal>ms</literal>,
-        <literal>us</literal>,
+        <literal>ms</literal>, <literal>us</literal>,
         <literal>min</literal> is specified as unit in this time
-        unit).</para></listitem>
+        unit). This setting may also be increased and decreased at
+        runtime by pressing the <keycap>+</keycap> and
+        <keycap>-</keycap> keys.</para></listitem>
       </varlistentry>
 
       <varlistentry>
 
   </refsect1>
 
-
   <refsect1>
     <title>Keys</title>
 
 
     <variablelist>
       <varlistentry>
-        <term>h</term>
+        <term><keycap>h</keycap></term>
 
         <listitem><para>Shows a short help text.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term>SPACE</term>
+        <term><keycap function="space"/></term>
 
         <listitem><para>Immediately refresh output.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term>q</term>
+        <term><keycap>q</keycap></term>
 
         <listitem><para>Terminate the program.</para></listitem>
       </varlistentry>
 
-
       <varlistentry>
-        <term>p</term>
-        <term>t</term>
-        <term>c</term>
-        <term>m</term>
-        <term>i</term>
+        <term><keycap>p</keycap></term>
+        <term><keycap>t</keycap></term>
+        <term><keycap>c</keycap></term>
+        <term><keycap>m</keycap></term>
+        <term><keycap>i</keycap></term>
 
         <listitem><para>Sort the control groups by path, number of
-        tasks, CPU load, memory usage, or IO load, respectively.
-        </para></listitem>
+        tasks, CPU load, memory usage, or IO load, respectively.  This
+        setting may also be controlled using the
+        <option>--order=</option> command line
+        switch.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term>%</term>
+        <term><keycap>%</keycap></term>
 
         <listitem><para>Toggle between showing CPU time as time or
-        percentage.</para></listitem>
+        percentage. This setting may also be controlled using the
+        <option>--cpu=</option> command line switch.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term>+</term>
-        <term>-</term>
+        <term><keycap>+</keycap></term>
+        <term><keycap>-</keycap></term>
 
         <listitem><para>Increase or decrease refresh delay,
-        respectively.</para></listitem>
+        respectively. This setting may also be controlled using the
+        <option>--delay=</option> command line
+        switch.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><keycap>k</keycap></term>
+
+        <listitem><para>Toggle between including or excluding kernel
+        threads in control group task counts. This setting may also be
+        controlled using the <option>-k</option> command line
+        switch.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><keycap>r</keycap></term>
+
+        <listitem><para>Toggle between recursively including or
+        excluding tasks in child control groups in control group task
+        counts. This setting may also be controlled using the
+        <option>--recursive=</option> command line
+        switch.</para></listitem>
       </varlistentry>
 
     </variablelist>
index 40755a24d0cabd5d38427df66cef1359a8e3b688..9ea9141d4d08855ecb95e1503894c1646fb2daeb 100644 (file)
@@ -88,7 +88,7 @@
         </thead>
         <tbody>
           <row>
-      <entry morerows="8">VM</entry>
+      <entry morerows="9">VM</entry>
       <entry><varname>qemu</varname></entry>
       <entry>QEMU software virtualization</entry>
           </row>
       <entry>User-mode Linux</entry>
           </row>
 
+          <row>
+      <entry><varname>parallels</varname></entry>
+      <entry>Parallels Desktop, Parallels Server</entry>
+          </row>
+
           <row>
       <entry morerows="5">container</entry>
       <entry><varname>openvz</varname></entry>
index 27ec72c986fc44f6467f39fc2423c9dd850d79dd..f569ea3cde8a848e4b75b5c4073e1232c6e092b3 100644 (file)
@@ -69,7 +69,7 @@
     units are explicitly configured (for example, listed in
     <citerefentry
     project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>),
-    the units this generator creates are overriden, but additional
+    the units this generator creates are overridden, but additional
     automatic dependencies might be created.</para>
 
     <para>This generator will only look for root partitions on the
index 496674925973d77bbff73dcd9ff147de88d00f10..bc5dacd98f93a702951496f832edf27887e4ded3 100644 (file)
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*-nxml-*-->
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
 
         <term><option>--bind-ro=</option></term>
 
         <listitem><para>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
         <literal>\:</literal> may be used to embed colons in either path.
         This option may be specified multiple times for
         creating multiple independent bind mount points. The
       </varlistentry>
 
       <varlistentry>
-        <term><option>--volatile</option><replaceable>=MODE</replaceable></term>
+        <term><option>--volatile</option></term>
+        <term><option>--volatile=</option><replaceable>MODE</replaceable></term>
 
         <listitem><para>Boots the container in volatile mode. When no
         mode parameter is passed or when mode is specified as
-        <literal>yes</literal> full volatile mode is enabled. This
+        <option>yes</option> full volatile mode is enabled. This
         means the root directory is mounted as mostly unpopulated
         <literal>tmpfs</literal> instance, and
         <filename>/usr</filename> 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 <literal>state</literal> the OS tree is
+        is specified as <option>state</option> the OS tree is
         mounted read-only, but <filename>/var</filename> is mounted as
         <literal>tmpfs</literal> 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
-        <literal>no</literal> (the default) the whole OS tree is made
+        <option>no</option> (the default) the whole OS tree is made
         available writable.</para>
 
-        <para>Note that setting this to <literal>yes</literal> or
-        <literal>state</literal> will only work correctly with
+        <para>Note that setting this to <option>yes</option> or
+        <option>state</option> will only work correctly with
         operating systems in the container that can boot up with only
         <filename>/usr</filename> mounted, and are able to populate
         <filename>/var</filename> automatically, as
         needed.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--settings=</option><replaceable>MODE</replaceable></term>
+
+        <listitem><para>Controls whether
+        <command>systemd-nspawn</command> shall search for and use
+        additional per-container settings from
+        <filename>.nspawn</filename> files. Takes a boolean or the
+        special values <option>override</option> or
+        <option>trusted</option>.</para>
+
+        <para>If enabled (the default) a settings file named after the
+        machine (as specified with the <option>--machine=</option>
+        setting, or derived from the directory or image file name)
+        with the suffix <filename>.nspawn</filename> is searched in
+        <filename>/etc/systemd/nspawn/</filename> and
+        <filename>/run/systemd/nspawn/</filename>. 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 <filename>.nspawn</filename> 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
+        <filename>.nspawn</filename> files consult
+        <citerefentry><refentrytitle>systemd.nspawn</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+
+        <para>If this option is set to <option>override</option> the
+        file is searched, read and used the same way, however the order of
+        precedence is reversed: settings read from the
+        <filename>.nspawn</filename> file will take precedence over
+        the corresponding command line options, if both are
+        specified.</para>
+
+        <para>If this option is set to <option>trusted</option> the
+        file is searched, read and used the same way, but regardless
+        if found in <filename>/etc/systemd/nspawn/</filename>,
+        <filename>/run/systemd/nspawn/</filename> 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.</para>
+
+        <para>If disabled no <filename>.nspawn</filename> file is read
+        and no settings except the ones on the command line are in
+        effect.</para></listitem>
+      </varlistentry>
+
       <xi:include href="standard-options.xml" xpointer="help" />
       <xi:include href="standard-options.xml" xpointer="version" />
     </variablelist>
     <title>See Also</title>
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd.nspawn</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry project='man-pages'><refentrytitle>chroot</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry project='mankier'><refentrytitle>dnf</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry project='die-net'><refentrytitle>yum</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
index dfc75ee0ff44cb759b990a7f70f57b3de5f19c2b..4f790d2cdab41f219791ca1dea8bd10fa40d2e87 100644 (file)
@@ -64,9 +64,9 @@
 
     <para>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 <literal>search-</literal>
-    don't refer to individual paths, but instead 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.</para>
   </refsect1>
 
index 27662456eaedd3c201dd53335a67f1410ee9e74a..96dc4f66205dd66e885d58e081b98a4975c7a0cd 100644 (file)
     resolver and an LLMNR resolver and responder. It also generates
     <filename>/run/systemd/resolve/resolv.conf</filename> for
     compatibility which may be symlinked from
-    <filename>/etc/resolv.conf</filename>.</para>
+    <filename>/etc/resolv.conf</filename>. The glibc NSS module
+    <citerefentry><refentrytitle>nss-resolve</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+    is necessary to allow libc's NSS resolver functions to resolve
+    host names via <command>systemd-resolved</command>.</para>
 
     <para>The DNS servers contacted are determined from the global
-    settings in
-    <citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
-    the per-link static settings in <filename>.network</filename>
-    files, and the per-link dynamic settings received over DHCP. See
+    settings in <filename>/etc/systemd/resolved.conf</filename>, the
+    per-link static settings in <filename>/etc/systemd/network/*.network</filename> files,
+    and the per-link dynamic settings received over DHCP. See
+    <citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+    and
     <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-    for more details.</para>
+    for details. To improve compatibility
+    <filename>/etc/resolv.conf</filename> is read in order to discover
+    configured system DNS servers, however only if it is not a symlink
+    to <filename>/run/systemd/resolve/resolv.conf</filename> (see above).</para>
 
     <para><command>systemd-resolved</command> synthesizes DNS RRs for the following cases:</para>
 
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>nss-resolve</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>systemd-networkd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     </para>
index 80db14870256c6edc2c05995e6a82a8b99673477..b220e0dce1e5bd7f544a1ec368831930a259eb39 100644 (file)
     <para>The following options are understood:</para>
 
     <variablelist>
+      <varlistentry>
+        <term><option>--no-ask-password</option></term>
+
+        <listitem><para>Do not query the user for authentication for
+        privileged operations.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--scope</option></term>
 
index 2680627a781efc53d006b834625b1dcb274b6dc0..05bbad7f65500911f53df851b5ac569d6a121e41 100644 (file)
           </para>
         </listitem>
       </varlistentry>
+      <varlistentry>
+        <term><varname>EncapsulationLimit=</varname></term>
+        <listitem>
+          <para>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 <ulink url="https://tools.ietf.org/html/rfc2473#section-4.1.1"> RFC 2473</ulink>).
+          The valid range is 0-255 and <literal>none</literal>. Defaults to 4.
+        </para>
+        </listitem>
+      </varlistentry>
       <varlistentry>
         <term><varname>Mode=</varname></term>
         <listitem>
index e8a164d22d937cfe0cff8e92c0473790e41cdc52..2fb4733494384d60a1809e07110e6bc03c288266 100644 (file)
@@ -1,4 +1,4 @@
-<?xml version='1.0'?> <!--*-nxml-*-->
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
 <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
 
         <varlistentry>
           <term><varname>DHCP=</varname></term>
           <listitem>
-            <para>Enables DHCPv4 and/or DHCPv6 support. Accepts
+            <para>Enables DHCPv4 and/or DHCPv6 client support. Accepts
             <literal>yes</literal>, <literal>no</literal>,
             <literal>ipv4</literal>, or <literal>ipv6</literal>.</para>
 
         <varlistentry>
           <term><varname>DHCPServer=</varname></term>
           <listitem>
-            <para>A boolean. Enables a basic DHCPv4 server on the
-            device. Mostly useful for handing out leases to container
-            instances.</para>
+            <para>A boolean. Enables DHCPv4 server support. Defaults
+            to <literal>no</literal>. Further settings for the DHCP
+            server may be set in the <literal>[DHCPServer]</literal>
+            section described below.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
 
   <refsect1>
     <title>[DHCP] Section Options</title>
-      <para>The <literal>[DHCP]</literal> section accepts the following keys:</para>
+      <para>The <literal>[DHCP]</literal> section configures the
+      DHCPv4 and DHCP6 client, if it is enabled with the
+      <varname>DHCP=</varname> setting described above:</para>
 
       <variablelist class='network-directives'>
         <varlistentry>
             any statically configured ones.</para>
 
             <para>This corresponds to the <option>nameserver</option>
-            option in <citerefentry project='man-pages'><refentrytitle>resolv.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+            option in <citerefentry
+            project='man-pages'><refentrytitle>resolv.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
           </listitem>
         </varlistentry>
         <varlistentry>
           <term><varname>UseHostname=</varname></term>
           <listitem>
             <para>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
             </para>
           </listitem>
         </varlistentry>
             table with metric of 1024.</para>
           </listitem>
         </varlistentry>
+
+        <varlistentry>
+          <term><varname>UseTimezone=</varname></term>
+
+          <listitem><para>When true, the timezone received from the
+          DHCP server will be set as as timezone of the local
+          system. Defaults to <literal>no</literal>.</para></listitem>
+        </varlistentry>
+
         <varlistentry>
           <term><varname>CriticalConnection=</varname></term>
           <listitem>
             DHCP server.</para>
           </listitem>
         </varlistentry>
-           </variablelist>
+      </variablelist>
+
+  </refsect1>
+
+  <refsect1>
+    <title>[DHCPServer] Section Options</title>
+    <para>The <literal>[DHCPServer]</literal> section contains
+    settings for the DHCP server, if enabled via the
+    <varname>DHCPServer=</varname> option described above:</para>
+
+    <variablelist class='network-directives'>
+
+      <varlistentry>
+        <term><varname>PoolOffset=</varname></term>
+        <term><varname>PoolSize=</varname></term>
+
+        <listitem><para>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. <varname>PoolOffset=</varname> takes the offset of the pool
+        from the start of subnet, or zero to use the default value.
+        <varname>PoolSize=</varname> 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.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>DefaultLeaseTimeSec=</varname></term>
+        <term><varname>MaxLeaseTimeSec=</varname></term>
+
+        <listitem><para>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.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>EmitDNS=</varname></term>
+        <term><varname>DNS=</varname></term>
+
+        <listitem><para>Configures whether the DHCP leases handed out
+        to clients shall contain DNS server information. The
+        <varname>EmitDNS=</varname> setting takes a boolean argument
+        and defaults to <literal>yes</literal>. The DNS servers to
+        pass to clients may be configured with the
+        <varname>DNS=</varname> option, which takes a list of IPv4
+        addresses. If the <varname>EmitDNS=</varname> 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
+        <filename>/etc/resolv.conf</filename> 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
+        <varname>MaxLeaseTimeSec=</varname> described
+        above.</para></listitem>
+      </varlistentry>
 
+      <varlistentry>
+        <term><varname>EmitNTP=</varname></term>
+        <term><varname>NTP=</varname></term>
+
+        <listitem><para>Similar to the <varname>EmitDNS=</varname> and
+        <varname>DNS=</varname> 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
+        <varname>EmitDNS=</varname> and
+        <varname>DNS=</varname>.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>EmitTimezone=</varname></term>
+        <term><varname>Timezone=</varname></term>
+
+        <listitem><para>Configures whether the DHCP leases handed out
+        to clients shall contain timezone information. The
+        <varname>EmitTimezone=</varname> setting takes a boolean
+        argument and defaults to <literal>yes</literal>. The
+        <varname>Timezone=</varname> setting takes a timezone string
+        (such as <literal>Europe/Berlin</literal> or
+        <literal>UTC</literal>) to pass to clients. If no explicit
+        timezone is set the system timezone of the local host is
+        propagated, as determined by the
+        <filename>/etc/localtime</filename> symlink.</para></listitem>
+      </varlistentry>
+
+    </variablelist>
   </refsect1>
 
   <refsect1>
diff --git a/man/systemd.nspawn.xml b/man/systemd.nspawn.xml
new file mode 100644 (file)
index 0000000..7bfafb4
--- /dev/null
@@ -0,0 +1,383 @@
+<?xml version='1.0'?> <!--*- Mode: nxml; nxml-child-indent: 2; indent-tabs-mode: nil -*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+  "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
+<!ENTITY % entities SYSTEM "custom-entities.ent" >
+%entities;
+]>
+
+<!--
+  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 <http://www.gnu.org/licenses/>.
+-->
+
+<refentry id="systemd.nspawn">
+
+  <refentryinfo>
+    <title>systemd.nspawn</title>
+    <productname>systemd</productname>
+
+    <authorgroup>
+      <author>
+        <contrib>Developer</contrib>
+        <firstname>Lennart</firstname>
+        <surname>Poettering</surname>
+        <email>lennart@poettering.net</email>
+      </author>
+    </authorgroup>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>systemd.nspawn</refentrytitle>
+    <manvolnum>5</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>systemd.nspawn</refname>
+    <refpurpose>Container settings</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <para><filename>/etc/systemd/nspawn/<replaceable>machine</replaceable>.nspawn</filename></para>
+    <para><filename>/run/systemd/nspawn/<replaceable>machine</replaceable>.nspawn</filename></para>
+    <para><filename>/var/lib/machines/<replaceable>machine</replaceable>.nspawn</filename></para>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para>An nspawn container settings file (suffix
+    <filename>.nspawn</filename>) encodes additional runtime
+    information about a local container, and is searched, read and
+    used by
+    <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    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 <command>systemd-nspawn</command> command
+    line, and make it easier to persistently attach specific settings
+    to specific containers. The syntax of these files is inspired by
+    <filename>.desktop</filename> files following the <ulink
+    url="http://standards.freedesktop.org/desktop-entry-spec/latest/">XDG
+    Desktop Entry Specification</ulink>, which are in turn inspired by
+    Microsoft Windows <filename>.ini</filename> files.</para>
+
+    <para>Boolean arguments used in these settings files can be
+    written in various formats. For positive settings the strings
+    <option>1</option>, <option>yes</option>, <option>true</option>
+    and <option>on</option> are equivalent. For negative settings, the
+    strings <option>0</option>, <option>no</option>,
+    <option>false</option> and <option>off</option> are
+    equivalent.</para>
+
+    <para>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.</para>
+
+  </refsect1>
+
+  <refsect1>
+    <title><filename>.nspawn</filename> File Discovery</title>
+
+    <para>Files are searched by appending the
+    <filename>.nspawn</filename> suffix to the machine name of the
+    container, as specified with the <option>--machine=</option>
+    switch of <command>systemd-nspawn</command>, or derived from the
+    directory or image file name. This file is first searched in
+    <filename>/etc/systemd/nspawn/</filename> and
+    <filename>/run/systemd/nspawn/</filename>. 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.</para>
+
+    <para>Persistent settings file created and maintained by the
+    administrator (and thus trusted) should be placed in
+    <filename>/etc/systemd/nspawn/</filename>, while automatically
+    downloaded (and thus potentially untrusted) settings files are
+    placed in <filename>/var/lib/machines/</filename> instead (next to
+    the container images), where their security impact is limited. In
+    order to add privileged settings to <filename>.nspawn</filename>
+    files acquired from the image vendor it is recommended to copy the
+    settings files into <filename>/etc/systemd/nspawn/</filename> 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
+    <command>systemd-nspawn</command>'s <option>--settings=</option>
+    switch, see
+    <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+    for details.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>[Exec] Section Options</title>
+
+    <para>Settings files may include an <literal>[Exec]</literal>
+    section, which carries various execution parameters:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><varname>Boot=</varname></term>
+
+        <listitem><para>Takes a boolean argument, defaults to off. If
+        enabled <command>systemd-nspawn</command> will automatically
+        search for an <filename>init</filename> executable and invoke
+        it. In this case the specified parameters using
+        <varname>Parameters=</varname> are passed as additional
+        arguments to the <filename>init</filename> process. This
+        setting corresponds to the <option>--boot</option> switch on
+        the <command>systemd-nspawn</command> command
+        line. </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Parameters=</varname></term>
+
+        <listitem><para>Takes a space separated list of
+        arguments. This is either a command line, beginning with the
+        binary name to execute, or – if <varname>Boot=</varname> is
+        enabled – the list of arguments to pass to the init
+        process. This setting corresponds to the command line
+        parameters passed on the <command>systemd-nspawn</command>
+        command line.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Environment=</varname></term>
+
+        <listitem><para>Takes an environment variable assignment
+        consisting of key and value, separated by
+        <literal>=</literal>. 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 <option>--setenv=</option> command line
+        switch.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>User=</varname></term>
+
+        <listitem><para>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 <option>--user=</option> command line
+        switch.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Capability=</varname></term>
+        <term><varname>DropCapability=</varname></term>
+
+        <listitem><para>Takes a space separated list of Linux process
+        capabilities (see
+        <citerefentry><refentrytitle>capabilities</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+        for details). The <varname>Capability=</varname> setting
+        specifies additional capabilities to pass on top of the
+        default set of capabilities. The
+        <varname>DropCapability=</varname> setting specifies
+        capabilities to drop from the default set. These settings
+        correspond to the <option>--capability=</option> and
+        <option>--drop-capability=</option> command line
+        switches. Note that <varname>Capability=</varname> is a
+        privileged setting, and only takes effect in
+        <filename>.nspawn</filename> files in
+        <filename>/etc/systemd/nspawn/</filename> and
+        <filename>/run/system/nspawn/</filename> (see above). On the
+        other hand <varname>DropCapability=</varname> takes effect in
+        all cases.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Personality=</varname></term>
+
+        <listitem><para>Configures the kernel personality for the
+        container. This is equivalent to the
+        <option>--personality=</option> switch.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>MachineID=</varname></term>
+
+        <listitem><para>Configures the 128bit machine ID (UUID) to pass to
+        the container. This is equivalent to the
+        <option>--uuid=</option> command line switch. This option is
+        privileged (see above). </para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[Files] Section Options</title>
+
+    <para>Settings files may include a <literal>[Files]</literal>
+    section, which carries various parameters configuring the file
+    system of the container:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><varname>ReadOnly=</varname></term>
+
+        <listitem><para>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
+        <option>--read-only</option> command line
+        switch.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Volatile=</varname></term>
+
+        <listitem><para>Takes a boolean argument, or the special value
+        <literal>state</literal>. This configures whether to run the
+        container with volatile state and/or configuration. This
+        option is equivalent to <option>--volatile=</option>, see
+        <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+        for details about the specific options
+        supported.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Bind=</varname></term>
+        <term><varname>BindReadOnly=</varname></term>
+
+        <listitem><para>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 <option>--bind=</option> and
+        <option>--bind-ro=</option>, see
+        <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+        for details about the specific options supported. This setting
+        is privileged (see above).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>TemporaryFileSystem=</varname></term>
+
+        <listitem><para>Adds a <literal>tmpfs</literal> 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 <literal>tmpfs</literal> mounts. This
+        option is equivalent to the command line switch
+        <option>--tmpfs=</option>, see
+        <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+        for details about the specific options supported. This setting
+        is privileged (see above).</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>[Network] Section Options</title>
+
+    <para>Settings files may include a <literal>[Network]</literal>
+    section, which carries various parameters configuring the network
+    connectivity of the container:</para>
+
+    <variablelist>
+
+      <varlistentry>
+        <term><varname>Private=</varname></term>
+
+        <listitem><para>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
+        <option>--private-network</option> command line
+        switch.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>VirtualEthernet=</varname></term>
+
+        <listitem><para>Takes a boolean argument. Configures whether
+        to create a virtual ethernet connection
+        (<literal>veth</literal>) between host and the container. This
+        setting implies <varname>Private=yes</varname>. This setting
+        corresponds to the <option>--network-veth</option> command
+        line switch. This option is privileged (see
+        above).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Interface=</varname></term>
+
+        <listitem><para>Takes a space separated list of interfaces to
+        add to the container. This option corresponds to the
+        <option>--network-interface=</option> command line switch and
+        implies <varname>Private=yes</varname>. This option is
+        privileged (see above).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>MACVLAN=</varname></term>
+        <term><varname>IPVLAN=</varname></term>
+
+        <listitem><para>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
+        <option>--network-macvlan=</option> and
+        <option>--network-ipvlan=</option> command line switches and
+        imply <varname>Private=yes</varname>. These options are
+        privileged (see above).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Bridge=</varname></term>
+
+        <listitem><para>Takes an interface name. This setting implies
+        <varname>VirtualEthernet=yes</varname> and
+        <varname>Private=yes</varname> 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
+        <option>--network-bridge=</option> command line switch. This
+        option is privileged (see above).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><varname>Port=</varname></term>
+
+        <listitem><para>Exposes a TCP or UDP port of the container on
+        the host. This option corresponds to the
+        <option>--port=</option> command line switch, see
+        <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+        for the precise syntax of the argument this option takes. This
+        option is privileged (see above).</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd.directives</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+    </para>
+  </refsect1>
+
+</refentry>
index 407f6d32eb5e55392a0b0bc05ef71e1828a8a305..ea58580bba461723d413a1274c78c97587a858af 100644 (file)
   </refsect1>
 
   <refsect1>
-    <title>Unit Load Path</title>
+    <title>Unit File Load Path</title>
 
     <para>Unit files are loaded from a set of paths determined during
     compilation, described in the two tables below. Unit files found
index a778ecfef195fffc7ee8937fc0d2e8e196db17f0..b65cc6acbe446a4bea73dda7d61ac4b8c82b39fe 100644 (file)
@@ -12,3 +12,6 @@ Name=host0
 [Network]
 DHCP=yes
 LinkLocalAddressing=yes
+
+[DHCP]
+UseTimezone=yes
index b4c1121d1ce8bf08c6dc7fe35662f7234386116f..f33c53fb4a34b5f76ae539e48e582dafb71c2a55 100644 (file)
@@ -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
index ff8053f1779d520eeaadf359a23e3f09d81aff28..d3e2ae8418d58582ff863f197cd2114c7e0dabfa 100644 (file)
--- 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 <piotrdrag@gmail.com>\n"
 "Language-Team: Polish <trans-pl@lists.fedoraproject.org>\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 (file)
index 0000000..7ca2aa5
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+
+__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
index 0bcd49f923620d7422e8c91a0beb68ea0f970f2d..4d63e2870f177acc92a0ba9bf8f1f835318c157d 100644 (file)
@@ -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
index 00947029c677593229d4440a1b64f2355f64cbde..7a5f46ba1d63354e5d44e845f2182e059946939f 100644 (file)
@@ -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 (file)
index 0000000..cdaf297
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+
+__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
index 712655caf498c3988127a5668c79a551b35f0190..a948677516ea3366cd5a5b2a0b60cbf9bee8cb3b 100644 (file)
@@ -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
             ;;
index db1e7f3f37485b1ba0f5eaa25d67a2f92cab3827..ab7fb53269c8ac9ae25ac622e4f1bcd73e4d4c7a 100644 (file)
@@ -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;
         }
index 54148fcf1836a40c0062a649f6ea676d77a9a2b8..1f593aa813571673b448873ce6bad9ad35f60cc5 100644 (file)
@@ -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;
 
index 6b3162a35fc0b6e5cc089e75f2fc9d30b4622ddb..f661a54d991dc76f5053c453b0a3b8ff4d5829d3 100644 (file)
@@ -29,7 +29,6 @@
 #include <sys/types.h>
 #include <ftw.h>
 
-#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);
index fd72e9e5c5a73facc28e0d8352931b987faed54c..6fd6d8059031f2f662a52822fec77ca7258a24c1 100644 (file)
 #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_;
index 3169a907f116d9ac05fa1e15654bbe24f3361204..d8aebc5e5b8ddb5ac4e9572d3bd6ecfd59c9b24f 100644 (file)
@@ -22,7 +22,6 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-
-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);
index 33427c6a73fb1bfbbf67b0d8ce193880c1755e84..cc5faa80a17e24a8714e0c633958067a637a4a20 100644 (file)
@@ -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;
                 }
 
index 5aaba1fe87264d06b2ca6448eafae0e167a9f07b..7c4161eb7277f2a651ad3f4d56287818ebb5a9d0 100644 (file)
@@ -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
index 627d768b76bada29f5f7d106a13ca215a55470cf..cbc3ca97b832bae46beaace384c3d6523d7916b6 100644 (file)
@@ -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)))
 
index 34ab0254ddabc45007bf0e80f22da67cdbf74add..dc1f244d4c5e0d5b4608460c8dd8768ebe9b4509 100644 (file)
@@ -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
index 61f188467f8a15e4afe439a6c34f523a065855cc..cff2d2a0348c091c0d33618f3c7a1561c71ea253 100644 (file)
@@ -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");
index 0502c20a2e50448028013341f150990f36e9d571..8a39d69fe4090cd1c49823b79de11be76cc6633e 100644 (file)
@@ -21,7 +21,9 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-/* 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;
index a7c44d1b56fd505a6dc2af5e908f367ee4a77543..dbd6296384ce04493532406a1ef67d278a3b411b 100644 (file)
@@ -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;
 }
index 7c58985cd26ae29975e4e7fc5458a5e5dd6b12d6..a39a0f775a1bc706ad2145af91eaa67307e90e2e 100644 (file)
@@ -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;
         }
index 51e40d3a6c506c9ccfce9a51bb9587c2483807c4..4554ef2d49fae8b19191484437f578daa06c2824 100644 (file)
@@ -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 */
index e51310eb6dafa13860426a541d784ca1827b7a43..f30458f25ae569548cb6ca5b3537d46de77bdd52 100644 (file)
 #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"
index cf55263bbfe985c8c0f62927110c36e2624739b2..c5ef5ab0d1e311ed5b4406039d667707eca687e3 100644 (file)
@@ -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;
+}
index 188714f2284f671370940e0eca1b89468c1323fd..b9a3809a6c821745a5c741c871455f036cf5cc77 100644 (file)
@@ -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);
index e278196c90dbe5f7aacf714fc25586ccca0dc4dd..531931f6e1434e6474c3708e3577b51ccdc213de 100644 (file)
@@ -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;
+}
index 2aba0422179480ec684dffa84243e39536c390e6..de881e8fe14d7a87636e4a4ff3f4812e3f812f29 100644 (file)
@@ -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);
index fa530da456b5fe9785cbcd3dca9e71a482bce26c..8742ee757f26c3b7c5362d7b89ff2da9006b44a5 100644 (file)
@@ -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);
index b2043d0870b0f9deb2a9219bc786bacfb071354a..28b3a555f3693354962315bd1ca0bdac2737a3e6 100644 (file)
@@ -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,
index f752595ca1187423caf3e1402a1fbc85d6e1c567..86aacad30744b03eb0df36f93d4a29a739eab6cc 100644 (file)
@@ -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;
index 1ead7b5419b3fda7971003cc4a217122550a6f76..0fafebd52da28ec44022ec6b97276320385dedab 100644 (file)
@@ -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);
 
index a8d26716a1de41e70fc2ed8c6bd26b31ab4542b7..4a4bebd528bc180cf8b43ce049fa40d6ff7ecddd 100644 (file)
@@ -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;
index 4ac193e22a61d961af5669b5f5d3b8de7c977df4..38b79da8866defe6e2c1fe7625dfb671e9689a7d 100644 (file)
@@ -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;
 
index b8d1d2ccaf8e5f05dcc76fc4a21c1c94c18f9566..4fb642e7b38f4f536acab8f93d610cdaa5e925ad 100644 (file)
@@ -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;
 }
index f953c9e62413ce3c9c0cc0fbbfae345b8fe07e49..1c94bea31ae3050cffba07b69eb0a8a981857cf7 100644 (file)
@@ -19,7 +19,6 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#define __STDC_FORMAT_MACROS
 #include <errno.h>
 #include <string.h>
 #include <stdlib.h>
@@ -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;
 }
index 4af381b4b62510be609754378f8187156a16d834..b8171ddad7a7dba9dfb3e0296658a7712ef86c2b 100644 (file)
@@ -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,
index 9530a8731178f8de72248d9b1e2e42a00f89a254..d3c12822392fcb14e41cfc476656d06e1c0c37f6 100644 (file)
@@ -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 = {
index 6474e08bd2b331913312207bc4d4e0813a9905ec..9a025cf929b16d940288ca666628bb293c40fb59 100644 (file)
@@ -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",
index 869ddae8c40e15ee812f7311020c4c5a0486bb61..438f5bf50ff20a2ab3e63bd3d91db40c018db197 100644 (file)
@@ -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_;
index 9dcc51f2409d914d33134c5cd4e1e7b6b9c9e7e9..e8fd44e2942be31518ea5c1fa05ec4fcdbc63ffe 100644 (file)
@@ -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)
index 0a9effda71816a4f476da537f268edd1dc7e5ec0..f9275ed935f00d08452f48a6d90f6fb20c0f4009 100644 (file)
@@ -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",
index 44bf5cab285baa169d5ce32c563ad92fc1b98792..0a2180c6a7e54d044008fd36534fd343af83006a 100644 (file)
@@ -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);
 }
index 48327220692b04a66821fb350f8b7f04e75c9e57..4f06ad11c41818a44948fbad97b8cd67988480ed 100644 (file)
@@ -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);
index e7efcf0f0a755b8a50cb32c840f5889f2b70966f..3f574b1832ecdf5b3883741b7702078bd8e077c8 100644 (file)
@@ -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,
index 8d14fe23d07b89810d5c767165c78506c4c0245a..a750246a890acb8297613613a48080df6daa29df 100644 (file)
@@ -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;
index 2a9d72c901a2b5d2b8877de353dc6051c72315e8..ee5d388560d7da1f45069e4d15263f211a2d2447 100644 (file)
@@ -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();
                         }
index 60b97722dbf9ca289aec80bba5653709ab7ca733..edd55b9e4562bfcdfddad5ebb03bbcee7deb7f84 100644 (file)
@@ -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
index b3bf8bdb404c89f9d025bd7361f372e8ed68b3b0..542bf8eb26e8528ea10ae219fdb89f6c0dc93831 100644 (file)
@@ -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" },
index fcca2b0221b772b1c1cd34e87f46e161316e6cad..1d128716c4d3b67d938394c4a86f357df9943185 100644 (file)
@@ -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);
index 87b97aa88373c63167222f7a70cf9af8bfbf4c82..4cd2b08c386877ba8635eea3dbc3458b4435c31d 100644 (file)
@@ -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",
index ecea89c377a6145bb6764766469371a8303f3bb9..fc10ddb5d9466d29ac03fdc9353ad44b3dfc9bc8 100644 (file)
@@ -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, ...) {
index 1e01f2bdef0145b012eb5a6a11b83df7cbcffc5c..9956cb7700dbaa6bc47d9fd22d340bf35fe61377 100644 (file)
@@ -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);
index 1782d4072062f48d97f7d34b023f13e2beaa564d..c6f356991556f3717743aceea33499928e884310 100644 (file)
@@ -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);
index c0d1cdfbd4d18e4c4c48e55d8dbfba17cc9c8bf2..2b81d17b9c92ec525067ac6a5b84ef3e95c10492 100644 (file)
@@ -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,
index 20995d920c76d6d7d1d94c68b1984b26e1f95936..e9111d06128f4becfa11dcea8a882ae1c00a8bf5 100644 (file)
@@ -770,6 +770,5 @@ const UnitVTable path_vtable = {
 
         .reset_failed = path_reset_failed,
 
-        .bus_interface = "org.freedesktop.systemd1.Path",
         .bus_vtable = bus_path_vtable
 };
index ab1769b46b334d143afb184db35fcab0f12fca93..35cf0621bed55a6f150964f5028f94ce1ac7c5e1 100644 (file)
 #include <errno.h>
 #include <unistd.h>
 
-#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,
 };
index f920c2e2cd825fecf081baf4937739e5aa3517a2..40ca0c616635159c9a0e3c4516210725c56afae7 100644 (file)
@@ -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;
index a4678500e6b9c414866fa9c5d1f46efe02cdce42..e5b457643b0d6ad9aac16d007668035baf7d5476 100644 (file)
@@ -34,6 +34,7 @@
 #include "log.h"
 
 #ifdef HAVE_SELINUX
+_printf_(2,3)
 static int null_log(int type, const char *fmt, ...) {
         return 0;
 }
index b790ec98befa71ca438ed10f99c2a03bad4866ad..292fe50de81e8bbe610d4ebb07a5bcbca05a867f 100644 (file)
@@ -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,
index 064eb5d93327b5cda413982c6dfb415a76fd5ff8..b414462066e891df87c341e9f04ec5d00931e794 100644 (file)
 
 #include <errno.h>
 
-#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.",
index 9518e21f36b4f2698cf97bd251b779a9019583d2..336ff20f84bdac1a1503fec526374c18702ad7d8 100644 (file)
@@ -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
 };
index a3870574732edbace69b1d8b12cd111a7bcc9fdd..1014fad626dcaef7f40fafbca302823a2edb7bf7 100644 (file)
@@ -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,
index 0bc3827ff061542490cb474f0c38e538a506dbdf..4f3ddc9f04e7ba20a37c9c4dd45b0898984e4da6 100644 (file)
@@ -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,
index b492a7c4c7f046c50d76f16ef18258f1eaabee27..f714cb31c21ea93aa5df792b945261d03bc89063 100644 (file)
@@ -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 = {
index 89758c6b1930ffb7385fc8c89e7310adeb0bea7a..eb6567bbfae5768335bc35eb64de889a72d591c1 100644 (file)
@@ -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,
 
index 43a5ca10641448da0f69ebfd35b102db700076bb..1aa0321b23ff712de281d25aadf12035bb7cea08 100644 (file)
 #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);
index 9df5a7e6fb351ab1eddbd3c182051e990a9b57dd..3c7684411bf1eb4cc571698128bc0d4c7514f5fc 100644 (file)
@@ -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);
 
index 0a34f86be7cde2c44021b8b14575073f2fedcf2b..cf25d9847f02a0386e43a31543b795bfafa1b066 100644 (file)
@@ -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) {
index a78516c8b53edd8103caeb1f45a37bbb52d3034d..c423be3767f4db40bc116222aec1217a873f394a 100644 (file)
@@ -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,
index 8b508eaeec857372aa8376d2809eb2a6721bee67..ffff94ee721beafee1ed3b7c3121a7d0182d2e8c 100644 (file)
@@ -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,
index e5be7f77666301c21245d812d874a7964132843f..51fe3aa50af40a314dccd9131235fc5946a01e01 100644 (file)
@@ -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;
index 7c60ef123c85bb5a935f115bf016463bba2fee0e..df6f882af5a14cbf19dfdf085f216d269697da8e 100644 (file)
@@ -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,
index 5a3fcddb1b513c369019c8f1c0b90d35663e8104..c6b97ca8f7924e6acf097c25873e36774f224a00 100644 (file)
@@ -25,7 +25,6 @@
 #include <stdint.h>
 #include <linux/if_packet.h>
 
-#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)
index b6110c5f166cc53b2a650e30e68f6012b6089aa7..36be7d54ed2373af33a511f15b555fce8ca14348 100644 (file)
@@ -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;
         }
index 4308723f9106e25fb6301c80a0dd5f9c6a9b6938..88a81d2866a7312d04198b9f23342aff47c5c586 100644 (file)
@@ -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,
index 58750c441813807a0f44cc42baeed9e6aaf7999c..5dc3c7aa2624d38ea0b4e5a65e29db224468b256 100644 (file)
@@ -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);
index 037f580eb65bcf7cdab9707620a3914ec2a4e2e2..4edecf7711b8fa56bf8f32f7fc23022a229f839e 100644 (file)
 
 #include <stdint.h>
 
-#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;
index fe56c102732791ecd183e748a506c7303378b12d..187975364b5aefe19c675cd0f241e71e3634420e 100644 (file)
@@ -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,
index 2fa4d5fac806a7a1409f624548356034a35c73cb..f41bebced0f1583a0c6e603e40c3b3380cdd7e5d 100644 (file)
@@ -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;
index 0f354461f7ec16ac6795603a35fcf2bba3da798a..3c04898e92885f473f76b33732793b1ea1f5e23f 100644 (file)
@@ -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;
 
index 8e09ee8f3a9e657601381071d0a540f50358d587..f4eadbb87e6ad34949d69d830558c8fc97782a21 100644 (file)
@@ -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;
 
index d8357c687e98586996f453293b139a5bf090e9ec..26bd4088d9bfc0789038caa713e5ece7029b959a 100644 (file)
@@ -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);
index dca82646ceff343fd9824ce3173d215136fee0fe..d5d4ef42f246face3931d2f23fd9bd13aa86ca57 100644 (file)
@@ -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);
index 46104afdedaa6d4d15661ccb6d75a646d36e4768..c12768cf0e5b002a66eb48162515f81b292ca735 100644 (file)
@@ -27,7 +27,6 @@
 #include <sys/ioctl.h>
 
 #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;
index f5b9e225899473181d594461a6aa8806b5c3794a..aa07846693dcdddc6e030a07198c1ce0bff2c141 100644 (file)
 #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;
+}
index faeab0fd309bfa73661227537c08ee3694c68cfa..1f167485e333635554c613f13d1061b984a80142 100644 (file)
 
 #include <sys/ioctl.h>
 
+#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;
+}
index 10c3654020bf47280434ab971362b89285fb496d..5489c77864e3ef5814f781eccc771eac952335dc 100644 (file)
@@ -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;
 
index f0494b3c911b149bf6e0dbd92b28dbeb9ce9e394..f34af6eabaa56f68dd7fcea59bdb21791249697a 100644 (file)
@@ -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);
 
index 2f867e85623b31e9d5c3eeef8839acdffc3cd744..e80232a7e0d0d3ff054e68012051b5d3566f7db6 100644 (file)
@@ -25,7 +25,6 @@
 #include <sys/ioctl.h>
 
 #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 = { };
index f080c5c0a7f449978d978b3fb7d8ac661a947d54..0fc05e1484528b7781bda4777354fc5fe7444072 100644 (file)
@@ -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;
index ff064f563f47d7ac589a44663d461900dd71f276..c6c9da812b591dab2bbd0de8fe617b3c4ef65297 100644 (file)
@@ -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;
index 200499d613bfccc193eea28f224246351e03ddd9..29c20b77e38111550b3c183478b4c006557c4d9d 100644 (file)
@@ -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;
 
index a63a4ea738ca8f54dda6d5691efe5485a161457e..b1ef1748490732865efc4a457534ad0f37bab630 100644 (file)
@@ -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)
index 9f60ab761e56f9193f8e980a87aae7103e19c6d0..7d8a1f6bd9bdb27465bceb67ee2992b488f651a3 100644 (file)
 #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;
index 6e62262443972699356ef3c43f310ba72f4b82d1..12daac3211005bde7e41bee1356b1e062bfb1bd3 100644 (file)
@@ -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;
index 5677bfb2d26253ee8a01cdfe4f1e11ccce66cbfe..d60ee98b255f99689c5f88f9ea11527c5f664e2c 100644 (file)
@@ -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);
index 7bf1d66dde1696df669e9c78b49d7789ec877d5d..d5ad127bcbfde070c026fd2beb9a29eeb2373669 100644 (file)
@@ -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;
index 101e4af18d6b78546728f29b841c724aa1989a60..5c607f49b1b5f16e56ee1c035d81bff7498ae5b1 100644 (file)
 #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;
 }
index 1c365b7fcd54960da7dfc2e1cae6504882cbe304..c3cc2b7212f73c8c3d62fb0d943fb2b87d2a70d0 100644 (file)
@@ -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) {
index 2b83f439a7ba49f1d3f7582646a5d41c4e329535..7234e7926ac9e098b4e3ee2e43654cda0a644e7a 100644 (file)
@@ -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;
index a21ed8cb62859ca19b8e56d49576820e2fd874b2..366a0264263e76abf857d8fbed3140953cecefd7 100644 (file)
@@ -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;
 }
index 0593aa658aab62d7c5098ea223ada6b178983159..1d061cb9cfe6921ca281db8d80506f127cf04962 100644 (file)
@@ -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)
index 5285278d92aa0480b91292732c74242d1e40ad6f..4a9181613a6b0f69294102bff69efa9af4443c11 100644 (file)
@@ -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;
index edd5033db212d4023733ebf3193202fe5b0421c5..580117165a17bfa1d5a804c695ceacd5179b7147 100644 (file)
 #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);
 
index 0e33ced342bf4040de3e56898465cf0266665617..fd39a562257f73d95d7f740d0c1f7213c9f08895 100644 (file)
@@ -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;
         }
index 408e1679a24785e290a8bfde86f417cc84a1a54d..c092e56b7a7aa54e018e9576bf8deddb5e9b7a3f 100644 (file)
@@ -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;
 }
index 40aa77ee5ca2af1c441cd16a90a532250e7a9350..06c98314e8a9891d295e0a404e2e747697263aea 100644 (file)
@@ -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;
index 0eadc8c747ab6a0031c10f2250cc85d6ff44c12b..265c7c7db2aa378c5b911e6b8cc4aaec98f66438 100644 (file)
 #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) {
index 05affa442dbbf90b6e36a3dbd2436f195485fd9d..f734ce9eee5d9524e72daf6effd4e75a37fd5c01 100644 (file)
@@ -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);
index b63fdf8fcbfd7a39dd647e0bdd2635b2a4bc9b1e..87d87359b857180cc40403952d40eb3e82e1af23 100644 (file)
@@ -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;
index b0dc82259128566b1690daa474c952813833cc16..888b372c994841807d351577e4639f2e26387034 100644 (file)
@@ -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);
 }
 
index 354a4071b7efe4fa3fe1e9a3431ef026daae0354..e8056529f54665513565a30c9a5e56723f443570 100644 (file)
@@ -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) {
index e9489f04c28a035982b5ea3505a643fcd1789f90..e304588c5876df4143d32e9193bc4e4141ea55e8 100644 (file)
@@ -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,
index 71f9fe6c72ee09ce359ee0331bae4ac311558389..36d2a3eb40632458485f7ec5a30ce52327218615 100644 (file)
@@ -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"
 
index 5fa98e069fba2f79662bd5c285e97af7c52bdd42..a7e64071cf95b667ef9ae052a6276e76d4fb4423 100644 (file)
@@ -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();
index 5b2b36b9c03f2ab4860e193787338b2ccb707999..b6fa50aa5252cbf1b12523b5ddc6a0bf8c6a77d6 100644 (file)
@@ -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,
index ce67ffde37889b3a7b69fe3c4ecf0a4cfc8db5b0..346e1d2cece27f970ee2d537c13a106a66a7f6b1 100644 (file)
@@ -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,
index 563153e2d92b64644f35e3c587cbd4da43a71bd3..e6b4ccd7c6e46150a3fe0e2a432d748c25292822 100644 (file)
@@ -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,
index 36c0e8626dd2e206477c51504a0e1f211b50c91a..20ea2fbdc415882b28461ab7a70db53378b25921 100644 (file)
@@ -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,
index f83d18b0358f413ab4174d07b4fe61c1be888c15..f66f1ce8428bfcfc6ee135dd940e330e4cd62ae1 100644 (file)
@@ -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(
index 95d7bca4bf9db6bb492a240efa6702aaa45d2117..2453a9ff04580d442daf9b99c6e07a883412d113 100644 (file)
@@ -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,
index af2b8eff063b79b04190c8a19d3be7121963304c..cc3811670455d1bcdcec480559d3963d16b1c109 100644 (file)
@@ -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,
index 0d1b119dc1b248b88317b4b756d47057f3fa22b9..a056ec3b080e8c446d0e55cc247d0c0e70b1bf71 100644 (file)
@@ -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)
index 926035d185320ed6b37ea391be060050fdbf7ed4..bb8c5ac64b6f92bbbf3eb779725ec6816287796e 100644 (file)
@@ -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();
index 0d52c693e4b61b86ac677830898ee499267c0728..eef9c5fa5f10e80d304ab70681b02bab73c117b1 100644 (file)
@@ -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,
index c9b53dc3f629ff69a2d37104a3c80f2086e51dd2..786579def07f523269d6489cd4b24070a05bae70 100644 (file)
@@ -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;
 }
 
index 584a956a7e9d68ca4d8accae82390b3d813d62b1..d609daafde99aa58703d72d27573f5bfc93924cd 100644 (file)
@@ -19,9 +19,8 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-
 #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 (file)
index 0000000..e6207cc
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+***/
+
+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);
index 172ca43a7db54a0ad0ea64833a49ca19addb4bf4..b0d296941e11439ced7739e928111f799b1c95b4 100644 (file)
 #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 (file)
index 0000000..39789a2
--- /dev/null
@@ -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 <teg@jklm.no>
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#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);
index 1b2ff7c76976562142408222753445c939f7d76a..4ffb01382f70b6f542c333389acc941eccc65a49 100644 (file)
@@ -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;
index 0f2510e904bd58c46429d6f4152328105749188d..22efadb843eb2374f76ae49d6cd70c982b95ecc9 100644 (file)
 #include <net/if.h>
 #include <net/ethernet.h>
 
-#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 (file)
index 0000000..f0efb90
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+***/
+
+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);
index cc9dc393c6a61af478bd728caee25c0d4aee413b..1dc9db0fca156444f76fedc925c7f070aa6f7cc3 100644 (file)
 #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;
 
index 2dcbbda607aa5a6193df0532e268098a3ed7d2b0..f588faf209fba63efee3e72934907746e86651c0 100644 (file)
@@ -1,5 +1,7 @@
 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
 
+#pragma once
+
 /***
   This file is part of systemd.
 
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#pragma once
-
 #include <endian.h>
 
-#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);
index 09a929e0da4322d064c0296c33954f6e09020a9d..92b607297da66366b5f979181b471087a8b6d0cf 100644 (file)
 #include <sys/socket.h>
 #include <linux/if.h>
 
+#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");
index 9991fa731f8e03273ee1212378c9e003f99ff261..0cdce1605ed552b0dbde5bd37c96256ae4bd1c2b 100644 (file)
@@ -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 <http://www.gnu.org/licenses/>.
 ***/
 
-#pragma once
+#include "in-addr-util.h"
 
 typedef struct Bond Bond;
 
index 9469160eba922418a520e89e254b842165ffbfe4..e0bd0e024aa3d1ce53c3cc2c24ea3749e483d8c5 100644 (file)
@@ -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)
index 265e67b7e326685d17ebca744c5b1c544429a3ad..a906e473b672d688298a6eaa7dc134fc5db72712 100644 (file)
@@ -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);
 
index e4fa74aef4cf6f3dc4da01ba6905e35c45e84f61..fa7decce18372e23b29b74a8426198d772077371 100644 (file)
@@ -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);
index cd31387b410ac02d389de780a8dabd00ed8d61ed..ff1edf2c39c575bfc857e9be085517d89d87ec09 100644 (file)
 
 #include <net/if.h>
 
-#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,
index 19fb5bb185ef13f44004e18b674042d1a8e26d84..1f8510c4f717d56c335b85cb8c7c2bc691c9cae5 100644 (file)
 
 #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 {
index 7ac7ef1ea3a2342b6f209c89c8940f7c3f9580f1..10ca9dae35794ab270d36ce5faae8860445f2997 100644 (file)
@@ -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)
index 6587ea994cf6536b21bc5074087501c7b1764b8d..ee14401982f2c487b584d46a41b180ae5eeb5b9f 100644 (file)
 #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 (file)
index 0000000..c3439a7
--- /dev/null
@@ -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 <teg@jklm.no>
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "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_;
index 31b10c458d77aaeedd8685eb1ea7fd1b9f3a57ec..fbaad40579e20b32e16503cb66410ac182fc01a9 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-
-#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 (file)
index 0000000..d090b9c
--- /dev/null
@@ -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 <teg@jklm.no>
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct 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 (file)
index 0000000..a41cd86
--- /dev/null
@@ -0,0 +1,144 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2013 Tom Gundersen <teg@jklm.no>
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "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 (file)
index 0000000..cc41aae
--- /dev/null
@@ -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 <teg@jklm.no>
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "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_;
index 5340922bf1479cd43bc8e6de5776db8eceec1eed..eea57ac15843a4243e3779a97e6acf9b540d0633 100644 (file)
 #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 (file)
index 0000000..85c81ff
--- /dev/null
@@ -0,0 +1 @@
+/nspawn-gperf.c
diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c
new file mode 100644 (file)
index 0000000..c0e9ccd
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/mount.h>
+
+#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 (file)
index 0000000..985fdfa
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <stdbool.h>
+
+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 (file)
index 0000000..38250b6
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+***/
+
+#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 (file)
index 0000000..39cec28
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+
+#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 (file)
index 0000000..074b380
--- /dev/null
@@ -0,0 +1,38 @@
+%{
+#include <stddef.h>
+#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 (file)
index 0000000..2bca39f
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/mount.h>
+
+#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 (file)
index 0000000..5abd44c
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+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 (file)
index 0000000..74abe53
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <linux/veth.h>
+#include <net/if.h>
+
+#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 (file)
index 0000000..311e6d0
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <net/if.h>
+
+#include <sys/types.h>
+#include <stdbool.h>
+
+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 (file)
index 0000000..b2776a6
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+***/
+
+#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 (file)
index 0000000..b27841f
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#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 (file)
index 0000000..419f5d1
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+***/
+
+#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 (file)
index 0000000..4cec40c
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdio.h>
+
+#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 (file)
index 0000000..eda7f62
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <grp.h>
+
+#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 (file)
index 0000000..33be44a
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+***/
+
+int change_uid_gid(const char *user, char **ret);
index 837947ee285980e0742060b1a848ec4f5aca3b42..33943a4b2f0b0847505c56ed23277f707130450a 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
-#include <signal.h>
-#include <sched.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/mount.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
+#ifdef HAVE_BLKID
+#include <blkid/blkid.h>
+#endif
 #include <errno.h>
-#include <sys/prctl.h>
 #include <getopt.h>
-#include <grp.h>
-#include <linux/fs.h>
-#include <sys/socket.h>
-#include <linux/netlink.h>
-#include <net/if.h>
-#include <linux/veth.h>
-#include <sys/personality.h>
 #include <linux/loop.h>
-#include <sys/file.h>
-
-#ifdef HAVE_SELINUX
-#include <selinux/selinux.h>
-#endif
-
+#include <sched.h>
 #ifdef HAVE_SECCOMP
 #include <seccomp.h>
 #endif
-
-#ifdef HAVE_BLKID
-#include <blkid/blkid.h>
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
 #endif
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/mount.h>
+#include <sys/personality.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #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(&current, ":", 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(&current, &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;
 }
index 5628e579ad17e0e9f6c0e353ca08e1704255ffb6..fbbabaf232d7d47e767866bbf9b35317c8c8ebaf 100644 (file)
@@ -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;
index 3dd97022de20a9b36f359560f86d7ad7c51a6613..657c6fcaf10a88d0c7acf0a5ff0bbf095d6750c6 100644 (file)
@@ -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 (symlink)
index 0000000..d0b0e8e
--- /dev/null
@@ -0,0 +1 @@
+../Makefile
\ No newline at end of file
index 1369a61458af9f098f3586b6a2e519fd5ad2b067..d9cc19700e32fbb580708aa370434ee19a8757fc 100644 (file)
@@ -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;
 
index 4ae216b7d9c400cab586d4e18fccb0a23260d3a3..d2b2d701cebdef377e041ce0706fd015ee26e803 100644 (file)
@@ -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);
index 1a2c4b28cdef35510425f4c1f982932ae75f718e..31b4f6c684a9d7fa82aab08e4f3858e85f04d081 100644 (file)
@@ -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, "  ");
index d99aa1d6e952ebac30a17ef8da9aab7ca0aad848..36150d87449ae3cf2cccdba25172a393cff7278b 100644 (file)
@@ -24,7 +24,7 @@
 #include <errno.h>
 #include <stdlib.h>
 
-#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;
+}
index 6152ee33b93af8bb28113c84bef6850c8e32e24b..34e3815782d0786d8d15e88562ca1de9e1ea0fe1 100644 (file)
@@ -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 {                                                            \
index 273dacff1f2ac610adf80812ad0669c0e88912f9..70220bdd14ada0d621b5e89ad2e2fe6273458002 100644 (file)
@@ -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";
 
index 13f03e798b2b83ec030941d1918ac3ab02e25a38..3992f9c837a5bd2fa168dfe3efcc4863b6fb1e27 100644 (file)
 #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;
index 3cb5f618687a4612cc0ef42b3404c6a0663a5ecb..96c39aa0a9a10bfd7af09dc04edd8c7a50ab9eb3 100644 (file)
@@ -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)
index 5afa50a9d0e729cf7e734db256028cecbf955074..ed5bceecdd8b9c7ccab681e83907d724bdbe17f4 100644 (file)
@@ -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
index 9af3b6532d952125da52c547beb0f10637f4b43b..4b0c7a18526804e7c9f622abb7f7699f66ebc0c1 100644 (file)
 
 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
index 9260396d5d5431df5ceda11bf8f2138952f86935..59c6eedcda5f6702dca85b3e8d8e6dd78aec979d 100644 (file)
@@ -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
+ * 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
+ * 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
+ * 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
+ * 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
+ * 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);
 
index 4d96c867d958c91a0585242d7800fa56299d2abf..4179015fbfd068f3e52a48929e8c752d0b0e750e 100644 (file)
@@ -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);
index 36cedcd6b593715f71ec76f28b2d200e5367787d..80c5852e455dde8fb8bb280f0487a4af31f9621a 100644 (file)
@@ -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);
 
index 72f874d8a91f55fa6cd0c30bc23e48dfbe8d90fe..de6c421b82cb44de829e875c78abe6c17471b2d7 100644 (file)
@@ -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);
 
index ecc9d70bf4a5c795150b630499d1b28fb27318ba..4ecf09a29e0df0712da8f438001dbcae1c68b3aa 100644 (file)
@@ -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;
 }
index 4be69a408d8d9930e49bf15c7b8cc1bbc665d71a..37b1c3554aa1116839a6df77e914278ee7b7a694 100644 (file)
@@ -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"));
index a7ab21a415518cc9fb7404f7aef1dd007b293760..6596069ade5102dcbcc115c5f6534e9b9b610727 100644 (file)
@@ -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;
         }
index 5d190378f18b42c0e5d58a0a1c261078765d6830..676c9f17932ab94a6812792455917c4d0ba68b5c 100644 (file)
@@ -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;
         }
index f915539e00ec0969b04bf3404397305c07c2cb5a..ebc9110c4d700d35b6706db55f59b44431568dce 100644 (file)
@@ -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;
         }
index dff38ab6f68fb23ac17ba42169536f7cf673580b..8ceb71f22a9fea63a7c6c712e1f7e6b0f30f8c3d 100644 (file)
@@ -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) {
index 21d6ee4c0c556ae9d2b418319881854bdeedc267..6de9e246f65e364f03708986085b8fe861b279f2 100644 (file)
@@ -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,
index 6e7e1271fb64420e9d87fe9d47e3dc45ba60476d..589f1f7822609c9897ee199c11238b1806878180 100644 (file)
@@ -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
+           <http://lists.linuxfoundation.org/pipermail/virtualization/2015-August/030331.html> */
+        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;
index 43bab8af6388a3d34cb50c7a47c8147541f13403..7bf4e7f24d806302eb64ecbee85d9c85b382e367 100644 (file)
@@ -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;
index 28ac44fb8ebb6b1a278ce27d1cb2f27c070e9275..f354ced2e271908d530533710ca5256a4b3482b6 100644 (file)
@@ -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");
index 64d7f934445ef181fa133defdeec8b7515d6add0..0a8930357005749ad209654596dab3ea8617e7b7 100755 (executable)
@@ -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
index 64f5ac7d17a892876240f8efe2aa37c1d09a8b7a..e75cdb1a597c79d513589dd258f7fbf0ed1cfd40 100644 (file)
@@ -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
index 074b916d3860045242c46dbde702a0da13297a1f..6b86e0a7f7758772ef5e1488b11090ece51a0c45 100644 (file)
@@ -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
index f4df13b61958eac15f78b295d85daf7c4669542c..4d497672283bbf463b9a93fe2559a86f1d19b7e7 100755 (executable)
@@ -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